ants-2.2.0/000077500000000000000000000000001311104306400124515ustar00rootroot00000000000000ants-2.2.0/.gitignore000066400000000000000000000002601311104306400144370ustar00rootroot00000000000000*.sample *~ *.[oa] .DS_Store ._* .nfs* *.nii.gz *.nrrd *.nhdr *.raw .svn CMakeCache.txt CMakeFiles/* .RData .Rhistory .RDataTmp .ExternalData_MD5* *.pyc crash* .*.swp bin buildants-2.2.0/.htaccess000066400000000000000000000000371311104306400142470ustar00rootroot00000000000000ErrorDocument 404 /index.php ants-2.2.0/.mailmap000066400000000000000000000034551311104306400141010ustar00rootroot00000000000000Brian Avants Brian Avants brian avants Brian Avants BBAvants Brian Avants stnava Brian Avants b avants Nick Tustison Nick Tustison ntustison Nick Tustison ntustison Hans J. Johnson Hans J. Johnson Hans Johnson Hans J. Johnson hjmjohnson Jeffrey Duda Jeffrey Duda Jeff Duda Jeffrey Duda Jeff Duda Ben Kandel Ben Kandel bkandel Ben Kandel bkandel Gang Song Paramveer Dhillon Paramveer Dhillon paramveerdhillon Kent Williams Kent Williams chaircrusher Philip Cook Philip Cook Phil Cook Yaroslav Halchenko Baohua Wu Michael Stauffer Ali Ghayoor Craig Stark adrienkaiser hwang3 Chris Filo Gorgolewski Niels van Strian Andrey Fedorov Paul Yushkevich ants-2.2.0/.travis.yml000066400000000000000000000013771311104306400145720ustar00rootroot00000000000000# use containers sudo: false os: - linux - osx language: cpp cache: ccache compiler: - clang - gcc addons: apt: sources: - george-edison55-precise-backports packages: - cmake - cmake-data before_install: - if [ "$TRAVIS_OS_NAME" == "osx" ]; then echo "need cmake 3.2"; fi - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update && brew install ccache; fi - if [ "$TRAVIS_OS_NAME" == "osx" ]; then export PATH="/usr/local/opt/ccache/libexec:$PATH"; fi - mkdir antsbin - cd antsbin script: - cmake -DRUN_LONG_TESTS=OFF -DRUN_SHORT_TESTS=ON ./.. && make -j 2 && cd ANTS-build/ && make test notifications: email: recipients: - arman.eshaghi@gmail.com - stnava@gmail.com - ntustison@gmail.com ants-2.2.0/ANTS.cmake000066400000000000000000000126101311104306400142200ustar00rootroot00000000000000 include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) configure_file(${CMAKE_CURRENT_LIST_DIR}/CTestCustom.cmake ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake COPYONLY) set(CMAKE_MODULE_PATH ${${PROJECT_NAME}_SOURCE_DIR}/CMake ${${PROJECT_NAME}_BINARY_DIR}/CMake ${CMAKE_MODULE_PATH} ) set (CMAKE_INCLUDE_DIRECTORIES_BEFORE ON) #----------------------------------------------------------------------------- # Version information include(Version.cmake) set(${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}") if(DEFINED ${PROJECT_NAME}_VERSION_PATCH) set(${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION}.${${PROJECT_NAME}_VERSION_PATCH}") if(DEFINED ${PROJECT_NAME}_VERSION_TWEAK) set(${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION}.${${PROJECT_NAME}_VERSION_TWEAK}") endif() endif() if(DEFINED ${PROJECT_NAME}_VERSION_RC) set(${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION}${${PROJECT_NAME}_VERSION_RC}") endif() if(DEFINED ${PROJECT_NAME}_VERSION_POST) set(${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION}.post${${PROJECT_NAME}_VERSION_POST}") elseif(DEFINED ${PROJECT_NAME}_VERSION_DEV) set(${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION}.dev${${PROJECT_NAME}_VERSION_DEV}") endif() option( ${PROJECT_NAME}_BUILD_DISTRIBUTE "Remove '-g#####' from version. ( for official distribution only )" OFF ) mark_as_advanced( ${PROJECT_NAME}_BUILD_DISTRIBUTE ) if( NOT ${PROJECT_NAME}_BUILD_DISTRIBUTE AND NOT ${PROJECT_NAME}_VERSION_HASH STREQUAL "GITDIR-NOTFOUND") set(${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION}-g${${PROJECT_NAME}_VERSION_HASH}") endif() message(STATUS "Building ${PROJECT_NAME} version \"${${PROJECT_NAME}_VERSION}\"") # Set up ITK find_package(ITK 4 REQUIRED) include(${ITK_USE_FILE}) # Set up which ANTs apps to build option(BUILD_ALL_ANTS_APPS "Use All ANTs Apps" ON) # Set up VTK option(USE_VTK "Use VTK Libraries" OFF) if(USE_VTK) find_package(VTK) if(VTK_VERSION_MAJOR GREATER 6) find_package(VTK COMPONENTS vtkRenderingVolumeOpenGL2 vtkCommonCore vtkCommonDataModel vtkIOGeometry vtkIOXML vtkIOLegacy vtkIOPLY vtkFiltersModeling vtkImagingStencil vtkImagingGeneral vtkRenderingAnnotation ) else(VTK_VERSION_MAJOR GREATER 6) find_package(VTK COMPONENTS vtkRenderingVolumeOpenGL vtkCommonCore vtkCommonDataModel vtkIOGeometry vtkIOXML vtkIOLegacy vtkIOPLY vtkFiltersModeling vtkImagingStencil vtkImagingGeneral vtkRenderingAnnotation) endif(VTK_VERSION_MAJOR GREATER 6) if(VTK_FOUND) include(${VTK_USE_FILE}) include_directories(${VTK_INCLUDE_DIRS}) set(INIT_VTK_LIBRARIES ${VTK_LIBRARIES}) else(VTK_FOUND) message("Cannot build some programs without VTK. Please set VTK_DIR if you need these programs.") endif(VTK_FOUND) endif(USE_VTK) # With MS compilers on Win64, we need the /bigobj switch, else generated # code results in objects with number of sections exceeding object file # format. # see http://msdn.microsoft.com/en-us/library/ms173499.aspx if(CMAKE_CL_64 OR MSVC) add_definitions(/bigobj) endif() option(ITK_USE_FFTWD "Use double precision fftw if found" OFF) option(ITK_USE_FFTWF "Use single precision fftw if found" OFF) option(ITK_USE_SYSTEM_FFTW "Use an installed version of fftw" OFF) if (ITK_USE_FFTWD OR ITK_USE_FFTWF) if(ITK_USE_SYSTEM_FFTW) find_package( FFTW ) link_directories(${FFTW_LIBDIR}) else(ITK_USE_SYSTEM_FFTW) link_directories(${ITK_DIR}/fftw/lib) include_directories(${ITK_DIR}/fftw/include) endif(ITK_USE_SYSTEM_FFTW) endif(ITK_USE_FFTWD OR ITK_USE_FFTWF) # These are configure time options that specify which # subset of tests should be run option(RUN_SHORT_TESTS "Run the quick unit tests." ON ) option(RUN_LONG_TESTS "Run the time consuming tests. i.e. real world registrations" OFF ) option(OLD_BASELINE_TESTS "Use reported metrics from old tests" OFF ) #----------------------------------------------------------------------------- include(CTest) enable_testing() #Set the global max TIMEOUT for CTest jobs. This is very large for the moment #and should be revisted to reduce based on "LONG/SHORT" test times, set to 1 hr for now set(CTEST_TEST_TIMEOUT 1800 CACHE STRING "Maximum seconds allowed before CTest will kill the test." FORCE) set(DART_TESTING_TIMEOUT ${CTEST_TEST_TIMEOUT} CACHE STRING "Maximum seconds allowed before CTest will kill the test." FORCE) configure_file(${CMAKE_CURRENT_LIST_DIR}/CTestCustom.cmake ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake COPYONLY) include_directories( ${BOOST_INCLUDE_DIR} ) #Define where to find Boost includes link_directories( ${ITK_LIBRARY_PATH} ) # message("${ITK_LIBRARIES}") #---------------------------------------------------------------------------- # Setup ants build environment set(PICSL_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/Utilities ${CMAKE_CURRENT_SOURCE_DIR}/ImageRegistration ${CMAKE_CURRENT_SOURCE_DIR}/ImageSegmentation # ${CMAKE_CURRENT_SOURCE_DIR}/GraphTheory ${CMAKE_CURRENT_SOURCE_DIR}/Tensor ${CMAKE_CURRENT_SOURCE_DIR}/Temporary ${CMAKE_CURRENT_SOURCE_DIR}/Examples ${CMAKE_CURRENT_BINARY_DIR} ) include_directories(${PICSL_INCLUDE_DIRS}) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/ANTsVersionConfig.h.in" "${CMAKE_CURRENT_BINARY_DIR}/ANTsVersionConfig.h" @ONLY IMMEDIATE) add_subdirectory(Examples) ants-2.2.0/ANTSCopyright.txt000066400000000000000000000027761311104306400156640ustar00rootroot00000000000000ConsortiumOfANTS® - http://www.picsl.upenn.edu/ANTS/ Copyright (c) 2009-2013 (updated to current year ad infinitum) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the consortium nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE CONSORTIUM AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ants-2.2.0/ANTSnightly.sh000066400000000000000000000071511311104306400151550ustar00rootroot00000000000000#!/bin/bash # package=ANTS repository=git@github.com:BRAINSia/ANTS.git # # when run by cron, the path variable is only /bin:/usr/bin export PATH="/opt/cmake/bin:/usr/local/bin:/usr/sbin:$PATH" # # make the testing directory, based on current user name #user=`who -m | sed -e 's/ .*$//'` user=${LOGNAME} ThisComputer=`hostname` # # the default is to use /brainsdev/kent -- which is # appropriate on the b2dev VMs. if [ $# = 0 ] ; then startdir=/scratch/kent/Testing else startdir=$1 shift fi # # needed for ssl authentication for git export GIT_SSL_NO_VERIFY=true CXXFLAGS="${CXXFLAGS:-}" CFLAGS="${CFLAGS:-}" LDFLAGS="${LDFLAGS:-}" # turn on coverage at command line if [ $# = 0 ] ; then coverage=0 else if [ $1 = "coverage" ] ; then coverage=1 shift fi fi OS=$(uname -s) NPROCS=1 # if [ "${OS}" = "Linux" ] ; then # NPROCS=$(grep -c ^processor /proc/cpuinfo) # export CFLAGS="${CFLAGS} -fpic" # export CXXFLAGS="${CXXFLAGS} -fpic" # else # NPROCS=$(system_profiler | awk '/Number Of Cores/{print $5}{next;}') # fi # create the testing directory if necessary mkdir -p ${startdir} if [ ! -d ${startdir} ] ; then echo ${startdir} cannot be created, exiting exit 1 fi cd ${startdir} mkdir -p ${startdir}/${ThisComputer}/${package} cd ${startdir}/${ThisComputer}/${package} top=`pwd` echo WORKING IN $top # check out package in a directory unique to each host -- this is unfortunately necessary # because svn can't update a directory checked out by a newer version of svn, so # every host has their own copy of BRAINS3 so that it's compatible with the local svn version. if [ -d ${package} ] ; then cd ${package} git pull else git clone ${repository} fi if [ $? != 0 ] then echo ${package} checkout failed, continuing with old version fi OsName=$(uname) if [ "${OsName}" = "Darwin" ] ; then Compiler=clang-`clang -v 2>&1 | head -1 | awk '{print $4}'` Compiler=${Compiler}-`clang -v 2>&1 | tail -2 | head -1 | awk '{print $2}'` export CC=`which clang` export CXX=`which clang++` else which gcc > /dev/null 2>&1 if [ $? == 0 ] ; then Compiler=gcc-`gcc -dumpversion`-`gcc -dumpmachine` else Compiler=unknown fi fi echo "Compiler=${Compiler} CC=${CC} CXX=${CXX}" for BUILD_TYPE in Debug Release do BuildDir=${top}/${BUILD_TYPE} if [ "$BUILD_TYPE" = "Debug" -a "$coverage" = "1" ] ; then CXXFLAGS="${CXXFLAGS} -g -O0 -Wall -W -Wshadow -Wunused-variable \ -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers \ -Wno-deprecated -Woverloaded-virtual -Wwrite-strings -fprofile-arcs -ftest-coverage" CFLAGS="${CFLAGS} -g -O0 -Wall -W -fprofile-arcs -ftest-coverage" LDFLAGS="${LDFLAGS} -fprofile-arcs -ftest-coverage" fi mkdir -p ${BuildDir} cd ${BuildDir} rm -f CMakeCache.txt # force reconfigure. find . -name '*-configure' | xargs rm -f # # the Build type cmake -DSITE:STRING=${ThisComputer} \ -G "Unix Makefiles" \ -DCMAKE_C_FLAGS:STRING="${CFLAGS}" \ -DCMAKE_CXX_FLAGS:STRING="${CXXFLAGS}" \ -DCMAKE_EXE_LINKER_FLAGS:STRING="${LDFLAGS}" \ -DCMAKE_MODULE_LINKER_FLAGS:STRING="${LDFLAGS}" \ -DCMAKE_SHARED_LINKER_FLAGS:STRING="${LDFLAGS}" \ -DBUILDNAME:STRING="${OsName}-${Compiler}-${BUILD_TYPE}" \ -DBUILD_SHARED_LIBS:BOOL=Off \ -DCMAKE_BUILD_TYPE:STRING=${BUILD_TYPE} \ ${top}/${package} echo "Building in `pwd`" scriptname=`basename $0` make -j ${NPROCS} cd ${package}-build make clean if [ $scriptname = "nightly.sh" ] ; then ctest -j ${NPROCS} -D Nightly else ctest -j ${NPROCS} -D Experimental fi cd .. done cd ${top} ants-2.2.0/ANTsVersionConfig.h.in000066400000000000000000000017371311104306400165400ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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. * *=========================================================================*/ #define ANTS_VERSION_MAJOR @ANTS_VERSION_MAJOR@ #define ANTS_VERSION_MINOR @ANTS_VERSION_MINOR@ #define ANTS_VERSION_PATCH @ANTS_VERSION_PATCH@ #define ANTS_VERSION_TWEAK @ANTS_VERSION_TWEAK@ #define ANTS_VERSION "@ANTS_VERSION@" ants-2.2.0/CMake/000077500000000000000000000000001311104306400134315ustar00rootroot00000000000000ants-2.2.0/CMake/ANTSExternalData.cmake000066400000000000000000000042271311104306400175020ustar00rootroot00000000000000get_filename_component(_ITKExternalData_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include(${_ITKExternalData_DIR}/ExternalData.cmake) ## The user can specify an environmental variable for shared locations of ## of object files to prevent continous downloading of common objects. if(NOT ExternalData_OBJECT_STORES) # Use ExternalData_OBJECT_STORES from environment as default. set(ExternalData_OBJECT_STORES_DEFAULT "") if(DEFINED "ENV{ExternalData_OBJECT_STORES}") file(TO_CMAKE_PATH "$ENV{ExternalData_OBJECT_STORES}" ExternalData_OBJECT_STORES_DEFAULT) endif() endif() set(ExternalData_OBJECT_STORES "${ExternalData_OBJECT_STORES_DEFAULT}" CACHE STRING "Semicolon-separated list of local directories holding data objects in the layout %(algo)/%(hash).") mark_as_advanced(ExternalData_OBJECT_STORES) if(NOT ExternalData_OBJECT_STORES) set(ExternalData_OBJECT_STORES "${CMAKE_BINARY_DIR}/ExternalData/Objects") file(MAKE_DIRECTORY "${ExternalData_OBJECT_STORES}") endif() list(APPEND ExternalData_OBJECT_STORES # Local data store populated by the ITK pre-commit hook "${CMAKE_SOURCE_DIR}/.ExternalData" ) set(ExternalData_BINARY_ROOT ${CMAKE_BINARY_DIR}/ExternalData) set(ExternalData_URL_TEMPLATES "" CACHE STRING "Additional URL templates for the ExternalData CMake script to look for testing data. E.g. file:///var/bigharddrive/%(algo)/%(hash)") mark_as_advanced(ExternalData_URL_TEMPLATES) list(APPEND ExternalData_URL_TEMPLATES # Local data store populated by the ITK pre-commit hook "file:///${${PROJECT_NAME}_SOURCE_DIR}/.ExternalData/%(algo)/%(hash)" # Data published by Iowa Psychiatry web interface ## The primary home for data "http://slicer.kitware.com/midas3/api/rest?method=midas.bitstream.download&checksum=%(hash)" # Data published by MIDAS "http://midas3.kitware.com/midas/api/rest?method=midas.bitstream.download&checksum=%(hash)&algorithm=%(algo)" # Data published by developers using git-gerrit-push. "http://www.itk.org/files/ExternalData/%(algo)/%(hash)" ) # Tell ExternalData commands to transform raw files to content links. # TODO: Condition this feature on presence of our pre-commit hook. set(ExternalData_LINK_CONTENT MD5) ants-2.2.0/CMake/ExternalData.cmake000066400000000000000000000762511311104306400170220ustar00rootroot00000000000000# - Manage data files stored outside source tree # Use this module to unambiguously reference data files stored outside the # source tree and fetch them at build time from arbitrary local and remote # content-addressed locations. Functions provided by this module recognize # arguments with the syntax "DATA{}" as references to external data, # replace them with full paths to local copies of those data, and create build # rules to fetch and update the local copies. # # The DATA{} syntax is literal and the is a full or relative path # within the source tree. The source tree must contain either a real data # file at or a "content link" at containing a hash of the # real file using a hash algorithm corresponding to . For example, the # argument "DATA{img.png}" may be satisfied by either a real "img.png" file in # the current source directory or a "img.png.md5" file containing its MD5 sum. # # The 'ExternalData_Expand_Arguments' function evaluates DATA{} references # in its arguments and constructs a new list of arguments: # ExternalData_Expand_Arguments( # # Name of data management target # # Output variable # [args...] # Input arguments, DATA{} allowed # ) # It replaces each DATA{} reference in an argument with the full path of a # real data file on disk that will exist after the builds. # # The 'ExternalData_Add_Test' function wraps around the CMake add_test() # command but supports DATA{} references in its arguments: # ExternalData_Add_Test( # # Name of data management target # ... # Arguments of add_test(), DATA{} allowed # ) # It passes its arguments through ExternalData_Expand_Arguments and then # invokes add_test() using the results. # # The 'ExternalData_Add_Target' function creates a custom target to manage # local instances of data files stored externally: # ExternalData_Add_Target( # # Name of data management target # ) # It creates custom commands in the target as necessary to make data files # available for each DATA{} reference previously evaluated by other functions # provided by this module. A list of URL templates must be provided in the # variable ExternalData_URL_TEMPLATES using the placeholders "%(algo)" and # "%(hash)" in each template. Data fetch rules try each URL template in order # by substituting the hash algorithm name for "%(algo)" and the hash value for # "%(hash)". # # The following hash algorithms are supported: # %(algo) Description # ------- ----- ----------- # MD5 .md5 Message-Digest Algorithm 5, RFC 1321 # Note that the hashes are used only for unique data identification and # download verification. This is not security software. # # Example usage: # include(ExternalData) # set(ExternalData_URL_TEMPLATES "file:///local/%(algo)/%(hash)" # "http://data.org/%(algo)/%(hash)") # ExternalData_Add_Test(MyData # NAME MyTest # COMMAND MyExe DATA{MyInput.png} # ) # ExternalData_Add_Target(MyData) # When test "MyTest" runs the "DATA{MyInput.png}" argument will be replaced by # the full path to a real instance of the data file "MyInput.png" on disk. If # the source tree contains a content link such as "MyInput.png.md5" then the # "MyData" target creates a real "MyInput.png" in the build tree. # # The DATA{} syntax can be told to fetch a file series using the form # "DATA{,:}", where the ":" is literal. If the source tree contains a # group of files or content links named like a series then a reference to one # member adds rules to fetch all of them. Although all members of a series # are fetched, only the file originally named by the DATA{} argument is # substituted for it. The default configuration recognizes file series names # ending with "#.ext", "_#.ext", ".#.ext", or "-#.ext" where "#" is a sequence # of decimal digits and ".ext" is any single extension. Configure it with a # regex that parses and parts from the end of : # ExternalData_SERIES_PARSE = regex of the form ()()$ # For more complicated cases set: # ExternalData_SERIES_PARSE = regex with at least two () groups # ExternalData_SERIES_PARSE_PREFIX = regex group number, if any # ExternalData_SERIES_PARSE_NUMBER = regex group number # ExternalData_SERIES_PARSE_SUFFIX = regex group number # Configure series number matching with a regex that matches the # part of series members named : # ExternalData_SERIES_MATCH = regex matching in all series members # Note that the of a series does not include a hash-algorithm # extension. # # The DATA{} syntax can alternatively match files associated with the named # file and contained in the same directory. Associated files may be specified # by options using the syntax DATA{,,,...}. Each option may # specify one file by name or specify a regular expression to match file names # using the syntax REGEX:. For example, the arguments # DATA{MyData/MyInput.mhd,MyInput.img} # File pair # DATA{MyData/MyFrames00.png,REGEX:MyFrames[0-9]+\\.png} # Series # will pass MyInput.mha and MyFrames00.png on the command line but ensure # that the associated files are present next to them. # # The DATA{} syntax may reference a directory using a trailing slash and a # list of associated files. The form DATA{/,,,...} adds # rules to fetch any files in the directory that match one of the associated # file options. For example, the argument DATA{MyDataDir/,REGEX:.*} will pass # the full path to a MyDataDir directory on the command line and ensure that # the directory contains files corresponding to every file or content link in # the MyDataDir source directory. # # The variable ExternalData_LINK_CONTENT may be set to the name of a supported # hash algorithm to enable automatic conversion of real data files referenced # by the DATA{} syntax into content links. For each such a content # link named "" is created. The original file is renamed to the # form ".ExternalData__" to stage it for future transmission to # one of the locations in the list of URL templates (by means outside the # scope of this module). The data fetch rule created for the content link # will use the staged object if it cannot be found using any URL template. # # The variable ExternalData_OBJECT_STORES may be set to a list of local # directories that store objects using the layout /%(algo)/%(hash). # These directories will be searched first for a needed object. If the object # is not available in any store then it will be fetched remotely using the URL # templates and added to the first local store listed. If no stores are # specified the default is a location inside the build tree. # # The variable ExternalData_SOURCE_ROOT may be set to the highest source # directory containing any path named by a DATA{} reference. The default is # CMAKE_SOURCE_DIR. ExternalData_SOURCE_ROOT and CMAKE_SOURCE_DIR must refer # to directories within a single source distribution (e.g. they come together # in one tarball). # # The variable ExternalData_BINARY_ROOT may be set to the directory to hold # the real data files named by expanded DATA{} references. The default is # CMAKE_BINARY_DIR. The directory layout will mirror that of content links # under ExternalData_SOURCE_ROOT. # # Variables ExternalData_TIMEOUT_INACTIVITY and ExternalData_TIMEOUT_ABSOLUTE # set the download inactivity and absolute timeouts, in seconds. The defaults # are 60 seconds and 300 seconds, respectively. Set either timeout to 0 # seconds to disable enforcement. The inactivity timeout is enforced only # with CMake >= 2.8.5. #============================================================================= # Copyright 2010-2013 Kitware, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # * Neither the names of Kitware, Inc., the Insight Software Consortium, # nor the names of their contributors may be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================= function(ExternalData_add_test target) # Expand all arguments as a single string to preserve escaped semicolons. ExternalData_expand_arguments("${target}" testArgs "${ARGN}") add_test(${testArgs}) endfunction() function(ExternalData_add_target target) if(NOT ExternalData_URL_TEMPLATES) message(FATAL_ERROR "ExternalData_URL_TEMPLATES is not set!") endif() if(NOT ExternalData_OBJECT_STORES) set(ExternalData_OBJECT_STORES ${CMAKE_BINARY_DIR}/ExternalData/Objects) endif() set(config ${CMAKE_CURRENT_BINARY_DIR}/${target}_config.cmake) configure_file(${_ExternalData_SELF_DIR}/ExternalData_config.cmake.in ${config} @ONLY) set(files "") # Set "_ExternalData_FILE_${file}" for each output file to avoid duplicate # rules. Use local data first to prefer real files over content links. # Custom commands to copy or link local data. get_property(data_local GLOBAL PROPERTY _ExternalData_${target}_LOCAL) foreach(entry IN LISTS data_local) string(REPLACE "|" ";" tuple "${entry}") list(GET tuple 0 file) list(GET tuple 1 name) if(NOT DEFINED "_ExternalData_FILE_${file}") set("_ExternalData_FILE_${file}" 1) add_custom_command( COMMENT "Generating ${file}" OUTPUT "${file}" COMMAND ${CMAKE_COMMAND} -Drelative_top=${CMAKE_BINARY_DIR} -Dfile=${file} -Dname=${name} -DExternalData_ACTION=local -DExternalData_CONFIG=${config} -P ${_ExternalData_SELF} MAIN_DEPENDENCY "${name}" ) list(APPEND files "${file}") endif() endforeach() # Custom commands to fetch remote data. get_property(data_fetch GLOBAL PROPERTY _ExternalData_${target}_FETCH) foreach(entry IN LISTS data_fetch) string(REPLACE "|" ";" tuple "${entry}") list(GET tuple 0 file) list(GET tuple 1 name) list(GET tuple 2 ext) set(stamp "${ext}-stamp") if(NOT DEFINED "_ExternalData_FILE_${file}") set("_ExternalData_FILE_${file}" 1) add_custom_command( # Users care about the data file, so hide the hash/timestamp file. COMMENT "Generating ${file}" # The hash/timestamp file is the output from the build perspective. # List the real file as a second output in case it is a broken link. # The files must be listed in this order so CMake can hide from the # make tool that a symlink target may not be newer than the input. OUTPUT "${file}${stamp}" "${file}" # Run the data fetch/update script. COMMAND ${CMAKE_COMMAND} -Drelative_top=${CMAKE_BINARY_DIR} -Dfile=${file} -Dname=${name} -Dext=${ext} -DExternalData_ACTION=fetch -DExternalData_CONFIG=${config} -P ${_ExternalData_SELF} # Update whenever the object hash changes. MAIN_DEPENDENCY "${name}${ext}" ) list(APPEND files "${file}${stamp}") endif() endforeach() # Custom target to drive all update commands. add_custom_target(${target} ALL DEPENDS ${files}) endfunction() function(ExternalData_expand_arguments target outArgsVar) # Replace DATA{} references with real arguments. set(data_regex "DATA{([^;{}\r\n]*)}") set(other_regex "([^D]|D[^A]|DA[^T]|DAT[^A]|DATA[^{])+|.") set(outArgs "") # This list expansion un-escapes semicolons in list element values so we # must re-escape them below anywhere a new list expansion will occur. foreach(arg IN LISTS ARGN) if("x${arg}" MATCHES "${data_regex}") # Re-escape in-value semicolons before expansion in foreach below. string(REPLACE ";" "\\;" tmp "${arg}") # Split argument into DATA{}-pieces and other pieces. string(REGEX MATCHALL "${data_regex}|${other_regex}" pieces "${tmp}") # Compose output argument with DATA{}-pieces replaced. set(outArg "") foreach(piece IN LISTS pieces) if("x${piece}" MATCHES "^x${data_regex}$") # Replace this DATA{}-piece with a file path. string(REGEX REPLACE "${data_regex}" "\\1" data "${piece}") _ExternalData_arg("${target}" "${piece}" "${data}" file) set(outArg "${outArg}${file}") else() # No replacement needed for this piece. set(outArg "${outArg}${piece}") endif() endforeach() else() # No replacements needed in this argument. set(outArg "${arg}") endif() # Re-escape in-value semicolons in resulting list. string(REPLACE ";" "\\;" outArg "${outArg}") list(APPEND outArgs "${outArg}") endforeach() set("${outArgsVar}" "${outArgs}" PARENT_SCOPE) endfunction() #----------------------------------------------------------------------------- # Private helper interface set(_ExternalData_REGEX_ALGO "MD5") set(_ExternalData_REGEX_EXT "md5") set(_ExternalData_SELF "${CMAKE_CURRENT_LIST_FILE}") get_filename_component(_ExternalData_SELF_DIR "${_ExternalData_SELF}" PATH) function(_ExternalData_compute_hash var_hash algo file) if("${algo}" MATCHES "^${_ExternalData_REGEX_ALGO}$") # TODO: Require CMake 2.8.7 to support other hashes with file(${algo} ...) execute_process(COMMAND "${CMAKE_COMMAND}" -E md5sum "${file}" OUTPUT_VARIABLE output) string(SUBSTRING "${output}" 0 32 hash) set("${var_hash}" "${hash}" PARENT_SCOPE) else() message(FATAL_ERROR "Hash algorithm ${algo} unimplemented.") endif() endfunction() function(_ExternalData_random var) if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.5) string(RANDOM LENGTH 6 random) elseif(EXISTS /dev/urandom) file(READ /dev/urandom random LIMIT 4 HEX) else() message(FATAL_ERROR "CMake >= 2.8.5 required in this environment") endif() set("${var}" "${random}" PARENT_SCOPE) endfunction() function(_ExternalData_exact_regex regex_var string) string(REGEX REPLACE "([][+.*()^])" "\\\\\\1" regex "${string}") set("${regex_var}" "${regex}" PARENT_SCOPE) endfunction() function(_ExternalData_atomic_write file content) _ExternalData_random(random) set(tmp "${file}.tmp${random}") file(WRITE "${tmp}" "${content}") file(RENAME "${tmp}" "${file}") endfunction() function(_ExternalData_link_content name var_ext) if("${ExternalData_LINK_CONTENT}" MATCHES "^(${_ExternalData_REGEX_ALGO})$") set(algo "${ExternalData_LINK_CONTENT}") else() message(FATAL_ERROR "Unknown hash algorithm specified by ExternalData_LINK_CONTENT:\n" " ${ExternalData_LINK_CONTENT}") endif() _ExternalData_compute_hash(hash "${algo}" "${name}") get_filename_component(dir "${name}" PATH) set(staged "${dir}/.ExternalData_${algo}_${hash}") string(TOLOWER ".${algo}" ext) _ExternalData_atomic_write("${name}${ext}" "${hash}\n") file(RENAME "${name}" "${staged}") set("${var_ext}" "${ext}" PARENT_SCOPE) file(RELATIVE_PATH relname "${ExternalData_SOURCE_ROOT}" "${name}${ext}") message(STATUS "Linked ${relname} to ExternalData ${algo}/${hash}") endfunction() function(_ExternalData_arg target arg options var_file) # Separate data path from the options. string(REPLACE "," ";" options "${options}") list(GET options 0 data) list(REMOVE_AT options 0) # Interpret trailing slashes as directories. set(data_is_directory 0) if("x${data}" MATCHES "^x(.*)([/\\])$") set(data_is_directory 1) set(data "${CMAKE_MATCH_1}") endif() # Convert to full path. if(IS_ABSOLUTE "${data}") set(absdata "${data}") else() set(absdata "${CMAKE_CURRENT_SOURCE_DIR}/${data}") endif() get_filename_component(absdata "${absdata}" ABSOLUTE) # Convert to relative path under the source tree. if(NOT ExternalData_SOURCE_ROOT) set(ExternalData_SOURCE_ROOT "${CMAKE_SOURCE_DIR}") endif() set(top_src "${ExternalData_SOURCE_ROOT}") file(RELATIVE_PATH reldata "${top_src}" "${absdata}") if(IS_ABSOLUTE "${reldata}" OR "${reldata}" MATCHES "^\\.\\./") message(FATAL_ERROR "Data file referenced by argument\n" " ${arg}\n" "does not lie under the top-level source directory\n" " ${top_src}\n") endif() if(data_is_directory AND NOT IS_DIRECTORY "${top_src}/${reldata}") message(FATAL_ERROR "Data directory referenced by argument\n" " ${arg}\n" "corresponds to source tree path\n" " ${reldata}\n" "that does not exist as a directory!") endif() if(NOT ExternalData_BINARY_ROOT) set(ExternalData_BINARY_ROOT "${CMAKE_BINARY_DIR}") endif() set(top_bin "${ExternalData_BINARY_ROOT}") # Handle in-source builds gracefully. if("${top_src}" STREQUAL "${top_bin}") if(ExternalData_LINK_CONTENT) message(WARNING "ExternalData_LINK_CONTENT cannot be used in-source") set(ExternalData_LINK_CONTENT 0) endif() set(top_same 1) endif() set(external "") # Entries external to the source tree. set(internal "") # Entries internal to the source tree. set(have_original ${data_is_directory}) # Process options. set(series_option "") set(associated_files "") set(associated_regex "") foreach(opt ${options}) if("x${opt}" MATCHES "^xREGEX:[^:/]+$") # Regular expression to match associated files. string(REGEX REPLACE "^REGEX:" "" regex "${opt}") list(APPEND associated_regex "${regex}") elseif("x${opt}" MATCHES "^x:$") # Activate series matching. set(series_option "${opt}") elseif("x${opt}" MATCHES "^[^][:/*?]+$") # Specific associated file. list(APPEND associated_files "${opt}") else() message(FATAL_ERROR "Unknown option \"${opt}\" in argument\n" " ${arg}\n") endif() endforeach() if(series_option) if(data_is_directory) message(FATAL_ERROR "Series option \"${series_option}\" not allowed with directories.") endif() if(associated_files OR associated_regex) message(FATAL_ERROR "Series option \"${series_option}\" not allowed with associated files.") endif() # Load a whole file series. _ExternalData_arg_series() elseif(data_is_directory) if(associated_files OR associated_regex) # Load listed/matching associated files in the directory. _ExternalData_arg_associated() else() message(FATAL_ERROR "Data directory referenced by argument\n" " ${arg}\n" "must list associated files.") endif() else() # Load the named data file. _ExternalData_arg_single() if(associated_files OR associated_regex) # Load listed/matching associated files. _ExternalData_arg_associated() endif() endif() if(NOT have_original) message(FATAL_ERROR "Data file referenced by argument\n" " ${arg}\n" "corresponds to source tree path\n" " ${reldata}\n" "that does not exist as a file (with or without an extension)!") endif() if(external) # Make the series available in the build tree. set_property(GLOBAL APPEND PROPERTY _ExternalData_${target}_FETCH "${external}") set_property(GLOBAL APPEND PROPERTY _ExternalData_${target}_LOCAL "${internal}") set("${var_file}" "${top_bin}/${reldata}" PARENT_SCOPE) else() # The whole series is in the source tree. set("${var_file}" "${top_src}/${reldata}" PARENT_SCOPE) endif() endfunction() macro(_ExternalData_arg_associated) # Associated files lie in the same directory. if(data_is_directory) set(reldir "${reldata}") else() get_filename_component(reldir "${reldata}" PATH) endif() if(reldir) set(reldir "${reldir}/") endif() _ExternalData_exact_regex(reldir_regex "${reldir}") # Find files named explicitly. foreach(file ${associated_files}) _ExternalData_exact_regex(file_regex "${file}") _ExternalData_arg_find_files("${reldir}${file}" "${reldir_regex}${file_regex}") endforeach() # Find files matching the given regular expressions. set(all "") set(sep "") foreach(regex ${associated_regex}) set(all "${all}${sep}${reldir_regex}${regex}") set(sep "|") endforeach() _ExternalData_arg_find_files("${reldir}" "${all}") endmacro() macro(_ExternalData_arg_single) # Match only the named data by itself. _ExternalData_exact_regex(data_regex "${reldata}") _ExternalData_arg_find_files("${reldata}" "${data_regex}") endmacro() macro(_ExternalData_arg_series) # Configure series parsing and matching. set(series_parse_prefix "") set(series_parse_number "\\1") set(series_parse_suffix "\\2") if(ExternalData_SERIES_PARSE) if(ExternalData_SERIES_PARSE_NUMBER AND ExternalData_SERIES_PARSE_SUFFIX) if(ExternalData_SERIES_PARSE_PREFIX) set(series_parse_prefix "\\${ExternalData_SERIES_PARSE_PREFIX}") endif() set(series_parse_number "\\${ExternalData_SERIES_PARSE_NUMBER}") set(series_parse_suffix "\\${ExternalData_SERIES_PARSE_SUFFIX}") elseif(NOT "x${ExternalData_SERIES_PARSE}" MATCHES "^x\\([^()]*\\)\\([^()]*\\)\\$$") message(FATAL_ERROR "ExternalData_SERIES_PARSE is set to\n" " ${ExternalData_SERIES_PARSE}\n" "which is not of the form\n" " ()()$\n" "Fix the regular expression or set variables\n" " ExternalData_SERIES_PARSE_PREFIX = regex group number, if any\n" " ExternalData_SERIES_PARSE_NUMBER = regex group number\n" " ExternalData_SERIES_PARSE_SUFFIX = regex group number\n" ) endif() set(series_parse "${ExternalData_SERIES_PARSE}") else() set(series_parse "([0-9]*)(\\.[^./]*)$") endif() if(ExternalData_SERIES_MATCH) set(series_match "${ExternalData_SERIES_MATCH}") else() set(series_match "[_.-]?[0-9]*") endif() # Parse the base, number, and extension components of the series. string(REGEX REPLACE "${series_parse}" "${series_parse_prefix};${series_parse_number};${series_parse_suffix}" tuple "${reldata}") list(LENGTH tuple len) if(NOT "${len}" EQUAL 3) message(FATAL_ERROR "Data file referenced by argument\n" " ${arg}\n" "corresponds to path\n" " ${reldata}\n" "that does not match regular expression\n" " ${series_parse}") endif() list(GET tuple 0 relbase) list(GET tuple 2 ext) # Glob files that might match the series. # Then match base, number, and extension. _ExternalData_exact_regex(series_base "${relbase}") _ExternalData_exact_regex(series_ext "${ext}") _ExternalData_arg_find_files("${relbase}*${ext}" "${series_base}${series_match}${series_ext}") endmacro() function(_ExternalData_arg_find_files pattern regex) file(GLOB globbed RELATIVE "${top_src}" "${top_src}/${pattern}*") foreach(entry IN LISTS globbed) if("x${entry}" MATCHES "^x(.*)(\\.(${_ExternalData_REGEX_EXT}))$") set(relname "${CMAKE_MATCH_1}") set(alg "${CMAKE_MATCH_2}") else() set(relname "${entry}") set(alg "") endif() if("x${relname}" MATCHES "^x${regex}$" AND NOT IS_DIRECTORY "${top_src}/${entry}") set(name "${top_src}/${relname}") set(file "${top_bin}/${relname}") if(alg) list(APPEND external "${file}|${name}|${alg}") elseif(ExternalData_LINK_CONTENT) _ExternalData_link_content("${name}" alg) list(APPEND external "${file}|${name}|${alg}") elseif(NOT top_same) list(APPEND internal "${file}|${name}") endif() if("${relname}" STREQUAL "${reldata}") set(have_original 1) endif() endif() endforeach() set(external "${external}" PARENT_SCOPE) set(internal "${internal}" PARENT_SCOPE) set(have_original "${have_original}" PARENT_SCOPE) endfunction() #----------------------------------------------------------------------------- # Private script mode interface if(CMAKE_GENERATOR OR NOT ExternalData_ACTION) return() endif() if(ExternalData_CONFIG) include(${ExternalData_CONFIG}) endif() if(NOT ExternalData_URL_TEMPLATES) message(FATAL_ERROR "No ExternalData_URL_TEMPLATES set!") endif() function(_ExternalData_link_or_copy src dst) # Create a temporary file first. get_filename_component(dst_dir "${dst}" PATH) file(MAKE_DIRECTORY "${dst_dir}") _ExternalData_random(random) set(tmp "${dst}.tmp${random}") if(UNIX) # Create a symbolic link. set(tgt "${src}") if(relative_top) # Use relative path if files are close enough. file(RELATIVE_PATH relsrc "${relative_top}" "${src}") file(RELATIVE_PATH relfile "${relative_top}" "${dst}") if(NOT IS_ABSOLUTE "${relsrc}" AND NOT "${relsrc}" MATCHES "^\\.\\./" AND NOT IS_ABSOLUTE "${reldst}" AND NOT "${reldst}" MATCHES "^\\.\\./") file(RELATIVE_PATH tgt "${dst_dir}" "${src}") endif() endif() execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${tgt}" "${tmp}" RESULT_VARIABLE result) else() # Create a copy. execute_process(COMMAND "${CMAKE_COMMAND}" -E copy "${src}" "${tmp}" RESULT_VARIABLE result) endif() if(result) file(REMOVE "${tmp}") message(FATAL_ERROR "Failed to create\n ${tmp}\nfrom\n ${obj}") endif() # Atomically create/replace the real destination. file(RENAME "${tmp}" "${dst}") endfunction() function(_ExternalData_download_file url file err_var msg_var) set(retry 3) while(retry) math(EXPR retry "${retry} - 1") if("${CMAKE_VERSION}" VERSION_GREATER 2.8.4.20110602) if(ExternalData_TIMEOUT_INACTIVITY) set(inactivity_timeout INACTIVITY_TIMEOUT ${ExternalData_TIMEOUT_INACTIVITY}) elseif(NOT "${ExternalData_TIMEOUT_INACTIVITY}" EQUAL 0) set(inactivity_timeout INACTIVITY_TIMEOUT 60) else() set(inactivity_timeout "") endif() else() set(inactivity_timeout "") endif() if(ExternalData_TIMEOUT_ABSOLUTE) set(absolute_timeout TIMEOUT ${ExternalData_TIMEOUT_ABSOLUTE}) elseif(NOT "${ExternalData_TIMEOUT_ABSOLUTE}" EQUAL 0) set(absolute_timeout TIMEOUT 300) else() set(absolute_timeout "") endif() file(DOWNLOAD "${url}" "${file}" STATUS status LOG log ${inactivity_timeout} ${absolute_timeout} SHOW_PROGRESS) list(GET status 0 err) list(GET status 1 msg) if(err) if("${msg}" MATCHES "HTTP response code said error" AND "${log}" MATCHES "error: 503") set(msg "temporarily unavailable") endif() elseif("${log}" MATCHES "\nHTTP[^\n]* 503") set(err TRUE) set(msg "temporarily unavailable") endif() if(NOT err OR NOT "${msg}" MATCHES "partial|timeout|temporarily") break() elseif(retry) message(STATUS "[download terminated: ${msg}, retries left: ${retry}]") endif() endwhile() set("${err_var}" "${err}" PARENT_SCOPE) set("${msg_var}" "${msg}" PARENT_SCOPE) endfunction() function(_ExternalData_download_object name hash algo var_obj) # Search all object stores for an existing object. foreach(dir ${ExternalData_OBJECT_STORES}) set(obj "${dir}/${algo}/${hash}") if(EXISTS "${obj}") message(STATUS "Found object: \"${obj}\"") set("${var_obj}" "${obj}" PARENT_SCOPE) return() endif() endforeach() # Download object to the first store. list(GET ExternalData_OBJECT_STORES 0 store) set(obj "${store}/${algo}/${hash}") _ExternalData_random(random) set(tmp "${obj}.tmp${random}") set(found 0) set(tried "") foreach(url_template IN LISTS ExternalData_URL_TEMPLATES) string(REPLACE "%(hash)" "${hash}" url_tmp "${url_template}") string(REPLACE "%(algo)" "${algo}" url "${url_tmp}") message(STATUS "Fetching \"${url}\"") _ExternalData_download_file("${url}" "${tmp}" err errMsg) set(tried "${tried}\n ${url}") if(err) set(tried "${tried} (${errMsg})") else() # Verify downloaded object. _ExternalData_compute_hash(dl_hash "${algo}" "${tmp}") if("${dl_hash}" STREQUAL "${hash}") set(found 1) break() else() set(tried "${tried} (wrong hash ${algo}=${dl_hash})") if("$ENV{ExternalData_DEBUG_DOWNLOAD}" MATCHES ".") file(RENAME "${tmp}" "${store}/${algo}/${dl_hash}") endif() endif() endif() file(REMOVE "${tmp}") endforeach() get_filename_component(dir "${name}" PATH) set(staged "${dir}/.ExternalData_${algo}_${hash}") if(found) file(RENAME "${tmp}" "${obj}") message(STATUS "Downloaded object: \"${obj}\"") elseif(EXISTS "${staged}") set(obj "${staged}") message(STATUS "Staged object: \"${obj}\"") else() message(FATAL_ERROR "Object ${algo}=${hash} not found at:${tried}") endif() set("${var_obj}" "${obj}" PARENT_SCOPE) endfunction() if("${ExternalData_ACTION}" STREQUAL "fetch") foreach(v ExternalData_OBJECT_STORES file name ext) if(NOT DEFINED "${v}") message(FATAL_ERROR "No \"-D${v}=\" value provided!") endif() endforeach() file(READ "${name}${ext}" hash) string(STRIP "${hash}" hash) if("${ext}" MATCHES "^\\.(${_ExternalData_REGEX_EXT})$") string(TOUPPER "${CMAKE_MATCH_1}" algo) else() message(FATAL_ERROR "Unknown hash algorithm extension \"${ext}\"") endif() _ExternalData_download_object("${name}" "${hash}" "${algo}" obj) # Check if file already corresponds to the object. set(stamp "${ext}-stamp") set(file_up_to_date 0) if(EXISTS "${file}" AND EXISTS "${file}${stamp}") file(READ "${file}${stamp}" f_hash) string(STRIP "${f_hash}" f_hash) if("${f_hash}" STREQUAL "${hash}") #message(STATUS "File already corresponds to object") set(file_up_to_date 1) endif() endif() if(file_up_to_date) # Touch the file to convince the build system it is up to date. execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${file}") else() _ExternalData_link_or_copy("${obj}" "${file}") endif() # Atomically update the hash/timestamp file to record the object referenced. _ExternalData_atomic_write("${file}${stamp}" "${hash}\n") elseif("${ExternalData_ACTION}" STREQUAL "local") foreach(v file name) if(NOT DEFINED "${v}") message(FATAL_ERROR "No \"-D${v}=\" value provided!") endif() endforeach() _ExternalData_link_or_copy("${name}" "${file}") else() message(FATAL_ERROR "Unknown ExternalData_ACTION=[${ExternalData_ACTION}]") endif() ants-2.2.0/CMake/ExternalData_config.cmake.in000066400000000000000000000004141311104306400207400ustar00rootroot00000000000000set(ExternalData_OBJECT_STORES "@ExternalData_OBJECT_STORES@") set(ExternalData_URL_TEMPLATES "@ExternalData_URL_TEMPLATES@") set(ExternalData_TIMEOUT_INACTIVITY "@ExternalData_TIMEOUT_INACTIVITY@") set(ExternalData_TIMEOUT_ABSOLUTE "@ExternalData_TIMEOUT_ABSOLUTE@") ants-2.2.0/CMake/GetGitRevisionDescription.cmake000066400000000000000000000116151311104306400215450ustar00rootroot00000000000000# - Returns a version string from Git # # These functions force a re-configure on each git commit so that you can # trust the values of the variables in your build system. # # get_git_head_revision( [ ...]) # # Returns the ref and sha hash of the current head revision # # git_describe( [ ...]) # # Returns the results of git describe on the source tree, and adjusting # the output so that it tests false if an error occurs. # # git_get_exact_tag( [ ...]) # # Returns the results of git describe --exact-match on the source tree, # and adjusting the output so that it tests false if there was no exact # matching tag. # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) return() endif() set(__get_git_revision_description YES) # We must run the following at "include" time, not at function call time, # to find the path to this module rather than the path to a calling list file get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) find_package(Git QUIET) function(get_git_head_revision _refvar _hashvar) if(NOT GIT_EXECUTABLE) set(${_refvar} "GIT-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --git-dir WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE GIT_DIR ERROR_VARIABLE error RESULT_VARIABLE failed OUTPUT_STRIP_TRAILING_WHITESPACE ) if(NOT IS_ABSOLUTE "${GIT_DIR}") set(GIT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${GIT_DIR}") endif() if(failed OR NOT EXISTS "${GIT_DIR}/HEAD") # not in git set(${_refvar} "GITDIR-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) return() endif() set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") if(NOT EXISTS "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}") endif() configure_file("${GIT_DIR}/HEAD" "${GIT_DATA}/HEAD" COPYONLY) file(STRINGS "${GIT_DIR}/HEAD" head LIMIT_COUNT 1 LIMIT_INPUT 1024) if("${head}" MATCHES "^ref: (.*)$") set(HEAD_REF "${CMAKE_MATCH_1}") if(EXISTS "${GIT_DIR}/${HEAD_REF}") configure_file("${GIT_DIR}/${HEAD_REF}" "${GIT_DATA}/HEAD-REF" COPYONLY) endif() else() set(HEAD_REF "") endif() execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE HEAD_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_VARIABLE error RESULT_VARIABLE failed) if(failed) set(HEAD_HASH "HEAD-HASH-NOTFOUND") endif() set(${_refvar} "${HEAD_REF}" PARENT_SCOPE) set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) endfunction() # get the number of commits since the file has last been modified function(git_commits_since file _commits ) get_git_head_revision(ref head) execute_process(COMMAND ${GIT_EXECUTABLE} rev-list ${head} -n 1 -- ${file} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE tag OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_VARIABLE error RESULT_VARIABLE failed ) if(failed) set( tag "") endif() execute_process(COMMAND ${GIT_EXECUTABLE} rev-list ${tag}..${head} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE rev_list OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_VARIABLE error RESULT_VARIABLE failed ) if(failed) set( rev_list "") endif() string( REGEX MATCHALL "[a-fA-F0-9]+" rev_list "${rev_list}") list( LENGTH rev_list COUNT) set(${_commits} "${COUNT}" PARENT_SCOPE) endfunction() function(git_describe _var) get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() # TODO sanitize #if((${ARGN}" MATCHES "&&") OR # (ARGN MATCHES "||") OR # (ARGN MATCHES "\\;")) # message("Please report the following error to the project!") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") #endif() #message(STATUS "Arguments to execute_process: ${ARGN}") execute_process(COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_get_exact_tag _var) git_describe(out --exact-match ${ARGN}) set(${_var} "${out}" PARENT_SCOPE) endfunction() ants-2.2.0/CMake/ITKSetStandardCompilerFlags.cmake000066400000000000000000000300431311104306400216670ustar00rootroot00000000000000# trict-null-sentinel Check the set of common warning flags supported by C and C++ compilers # check_compiler_warning_flags( ) # - variable to store valid C warning flags # - variable to store valid CXX warning flags # This internally calls the check_c_compiler_flag and check_cxx_compiler_flag macros. # To create a portable build system, it is best to not # test for platforms, but to test for features. # # Instead of testing "if Windows then do this", test for # "if the -Wno-invalid-offsetof flag works then use it". # # Typical use of this module is: # # include(CheckCompilerWarningFlags) # check_compiler_warning_flags(C_WARNING_FLAGS CXX_WARNING_FLAGS) # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_WARNING_FLAGS}") # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNING_FLAGS}") include(ITK_CheckCCompilerFlag) include(ITK_CheckCXXCompilerFlag) function(check_c_compiler_warning_flags c_flag_var) set(local_c_flags "") set(flag_list "${ARGN}") foreach(flag IN LISTS flag_list) ITK_CHECK_C_COMPILER_FLAG(${flag} C_HAS_WARNING${flag}) if(${C_HAS_WARNING${flag}}) set(local_c_flags "${local_c_flags} ${flag}") endif() endforeach() set(${c_flag_var} "${local_c_flags}" PARENT_SCOPE) endfunction() function(check_cxx_compiler_warning_flags cxx_flag_var) set(local_cxx_flags "") set(flag_list "${ARGN}") foreach(flag IN LISTS flag_list) ITK_CHECK_CXX_COMPILER_FLAG(${flag} CXX_HAS_WARNING${flag}) if(${CXX_HAS_WARNING${flag}}) set(local_cxx_flags "${local_cxx_flags} ${flag}") endif() endforeach() set(${cxx_flag_var} "${local_cxx_flags}" PARENT_SCOPE) endfunction() function(check_compiler_warning_flags c_warning_flags_var cxx_warning_flags_var) set(${c_warning_flags_var} "" PARENT_SCOPE) set(${cxx_warning_flags_var} "" PARENT_SCOPE) # Check this list on C compiler only set(c_flags -Wno-uninitialized -Wno-unused-parameter ) ## On windows, the most verbose compiler options ## is reporting 1000's of wanings in windows ## header files, for now, limit the number of ## warnings to level 3 if( WIN32 ) set(VerboseWarningsFlag -W3 ) ## A better solution would be to use -Wall, ## and then disable warnings one by one ## set(VerboseWarningsFlag -Wall -wd4820 -wd4682 ) else() ## with Intel compiler, the -Wall compiler options ## is reporting 1000's of remarks of trivial items ## that will only slow day-to-day operations ## specify -w2 to restrict to only warnings and errors if (${CMAKE_C_COMPILER} MATCHES "icc.*$") set(USING_INTEL_ICC_COMPILER TRUE) endif() if (${CMAKE_CXX_COMPILER} MATCHES "icpc.*$") set(USING_INTEL_ICC_COMPILER TRUE) endif() if(USING_INTEL_ICC_COMPILER) # NOTE -w2 is close to gcc's -Wall warning level, -w5 is intels -Wall warning level, and it is too verbose. set(VerboseWarningsFlag -w2 -wd1268 -wd981 -wd383 -wd1418 -wd1419 -wd2259 -wd1572 -wd424 ) #-wd424 #Needed for Intel compilers with remarki #424: extra ";" ignored #-wd383 #Needed for Intel compilers with remark #383: value copied to temporary, reference to temporary used #-wd981 #Needed for Intel compilers with remark #981: operands are evaluated in unspecified order #-wd1418 #Needed for Intel compilers with remark #1418: external function definition with no prior declaration #-wd1419 #Needed for Intel compilers with remark #1419: external declaration in primary source file #-wd1572 #Needed for Intel compilers with remark #1572: floating-point equality and inequality comparisons are unreliable #-wd2259 #Needed for Intel compilers with remark #2259: non-pointer conversion from "itk::SizeValueType={unsigned long}" to "double" may lose significant bits #-wd1268 #Needed for Intel compliers with warning #1268: support for exported templates is disabled else() set(VerboseWarningsFlag -Wall ) endif () endif() # Check this list on both C and C++ compilers set(c_and_cxx_flags ${VerboseWarningsFlag} -Wcast-align -Wdisabled-optimization -Wextra -Wformat=2 -Winvalid-pch -Wno-format-nonliteral -Wpointer-arith -Wshadow -Wunused -Wwrite-strings -funit-at-a-time -Wno-strict-overflow ) # Check this list on C++ compiler only set(cxx_flags -Wno-deprecated -Wno-invalid-offsetof -Woverloaded-virtual -Wstrict-null-sentinel ) ##-Wno-c++0x-static-nonintegral-init ## Clang compiler likes to warn about this feature that is technically only in ## c++0x, but works on many compilers, and if it fails, then alternate methods are used check_c_compiler_warning_flags(CMAKE_C_WARNING_FLAGS ${c_flags} ${c_and_cxx_flags}) check_cxx_compiler_warning_flags(CMAKE_CXX_WARNING_FLAGS ${c_and_cxx_flags} ${cxx_flags}) set(${c_warning_flags_var} "${CMAKE_C_WARNING_FLAGS}" PARENT_SCOPE) set(${cxx_warning_flags_var} "${CMAKE_CXX_WARNING_FLAGS}" PARENT_SCOPE) endfunction() macro(check_compiler_platform_flags) # On Visual Studio 8 MS deprecated C. This removes all 1.276E1265 security # warnings if(WIN32) if(NOT MINGW) if(NOT ITK_ENABLE_VISUAL_STUDIO_DEPRECATED_C_WARNINGS) add_definitions( -D_CRT_FAR_MAPPINGS_NO_DEPRECATE -D_CRT_IS_WCTYPE_NO_DEPRECATE -D_CRT_MANAGED_FP_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_DEPRECATE_GLOBALS -D_CRT_SETERRORMODE_BEEP_SLEEP_NO_DEPRECATE -D_CRT_TIME_FUNCTIONS_NO_DEPRECATE -D_CRT_VCCLRIT_NO_DEPRECATE -D_SCL_SECURE_NO_DEPRECATE ) endif() # With MS compilers on Win64, we need the /bigobj switch, else generated # code results in objects with number of sections exceeding object file # format. # see http://msdn.microsoft.com/en-us/library/ms173499.aspx if(MSVC_VERSION GREATER 1310) set(ITK_REQUIRED_CXX_FLAGS "${ITK_REQUIRED_CXX_FLAGS} /bigobj") endif() endif() endif() if(WIN32) # Some libraries (e.g. vxl libs) have no dllexport markup, so we can # build full shared libraries only with the GNU toolchain. For non # gnu compilers on windows, only Common is shared. This allows for # plugin type applications to use a dll for ITKCommon which will contain # the static for Modified time. if(CMAKE_COMPILER_IS_GNUCXX) # CMake adds --enable-all-exports on Cygwin (since Cygwin is # supposed to be UNIX-like), but we need to add it explicitly for # a native windows build with the MinGW tools. if(MINGW) set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared -Wl,--export-all-symbols -Wl,--enable-auto-import") set(CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS "-shared -Wl,--export-all-symbols -Wl,--enable-auto-import") set(CMAKE_EXE_LINKER_FLAGS "-Wl,--enable-auto-import") endif() else() if(BUILD_SHARED_LIBS) set(ITK_LIBRARY_BUILD_TYPE "SHARED") else() set(ITK_LIBRARY_BUILD_TYPE "STATIC") endif() set(BUILD_SHARED_LIBS OFF) endif() endif() #----------------------------------------------------------------------------- #ITK requires special compiler flags on some platforms. if(CMAKE_COMPILER_IS_GNUCXX) # GCC's -Warray-bounds has been shown to throw false positives with -O3 on 4.8. if(UNIX AND ( ("${CMAKE_CXX_COMPILER_VERSION}" VERSION_EQUAL "4.8") OR ("${CMAKE_CXX_COMPILER_VERSION}" VERSION_GREATER "4.8" AND "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "4.9") )) set(ITK_REQUIRED_CXX_FLAGS "${ITK_REQUIRED_CXX_FLAGS} -Wno-array-bounds") endif() if(APPLE) option(ITK_USE_64BITS_APPLE_TRUNCATION_WARNING "Turn on warnings on 64bits to 32bits truncations." OFF) mark_as_advanced(ITK_USE_64BITS_APPLE_TRUNCATION_WARNING) execute_process(COMMAND "${CMAKE_C_COMPILER}" --version OUTPUT_VARIABLE _version ERROR_VARIABLE _version) # -fopenmp breaks compiling the HDF5 library in shared library mode # on the OS X platform -- at least with gcc 4.2 from Xcode. set(compile_flag_lists CMAKE_C_FLAGS CMAKE_CXX_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO) foreach(listname ${compile_flag_lists}) if("${${listname}}" MATCHES ".*-fopenmp.*") string(REPLACE "-fopenmp" "" tmpFlags "${${listname}}") set(${listname} "${tmpFlags}") message("-fopenmp causes incorrect compliation of HDF, removing from ${listname}") endif() endforeach() endif() # gcc must have -msse2 option to enable sse2 support if(VNL_CONFIG_ENABLE_SSE2 OR VNL_CONFIG_ENABLE_SSE2_ROUNDING) set(ITK_REQUIRED_CXX_FLAGS "${ITK_REQUIRED_CXX_FLAGS} -msse2") endif() endif() #----------------------------------------------------------------------------- # for the gnu compiler a -D_PTHREADS is needed on sun # for the native compiler a -mt flag is needed on the sun if(CMAKE_SYSTEM MATCHES "SunOS.*") if(CMAKE_COMPILER_IS_GNUCXX) set(ITK_REQUIRED_CXX_FLAGS "${ITK_REQUIRED_CXX_FLAGS} -D_PTHREADS") set(ITK_REQUIRED_LINK_FLAGS "${ITK_REQUIRED_LINK_FLAGS} -lrt") else() set(ITK_REQUIRED_CXX_FLAGS "${ITK_REQUIRED_CXX_FLAGS} -mt") set(ITK_REQUIRED_C_FLAGS "${ITK_REQUIRED_C_FLAGS} -mt") endif() # Add flags for the SUN compiler to provide all the methods for std::allocator. # CHECK_CXX_SOURCE_COMPILES("-features=no%anachronisms" SUN_COMPILER) if(SUN_COMPILER) CHECK_CXX_SOURCE_COMPILES("-library=stlport4" SUN_COMPILER_HAS_STL_PORT_4) if(SUN_COMPILER_HAS_STL_PORT_4) set(ITK_REQUIRED_CXX_FLAGS "${ITK_REQUIRED_CXX_FLAGS} -library=stlport4") endif() endif() endif() # mingw thread support if(MINGW) set(ITK_REQUIRED_CXX_FLAGS "${ITK_REQUIRED_CXX_FLAGS} -mthreads") set(ITK_REQUIRED_C_FLAGS "${ITK_REQUIRED_C_FLAGS} -mthreads") set(ITK_REQUIRED_LINK_FLAGS "${ITK_REQUIRED_LINK_FLAGS} -mthreads") endif() #----------------------------------------------------------------------------- # The frename-registers option does not work due to a bug in the gnu compiler. # It must be removed or data errors will be produced and incorrect results # will be produced. This is first documented in the gcc4 man page. if(CMAKE_COMPILER_IS_GNUCXX) set(ALL_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} ${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) separate_arguments(ALL_FLAGS) foreach(COMP_OPTION ${ALL_FLAGS}) if("${COMP_OPTION}" STREQUAL "-frename-registers") message(FATAL_ERROR "-frename-registers causes runtime bugs. It must be removed from your compilation options.") endif() endforeach() endif() #----------------------------------------------------------------------------- # Set the compiler-specific flag for disabling optimization. if(MSVC) set(ITK_CXX_DISABLE_OPTIMIZATION_FLAG "/Od") elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(GNU|Intel)$") set(ITK_CXX_DISABLE_OPTIMIZATION_FLAG "-O0") endif() if(DEFINED ITK_CXX_DISABLE_OPTIMIZATION_FLAG) CHECK_CXX_SOURCE_COMPILES("${ITK_CXX_DISABLE_OPTIMIZATION_FLAG}" CXX_HAS_DISABLE_OPTIMIZATION_FLAG) endif() endmacro()#End the platform check function #----------------------------------------------------------------------------- #Check the set of warning flags the compiler supports check_compiler_warning_flags(C_WARNING_FLAGS CXX_WARNING_FLAGS) # Append ITK warnings to the CMake flags. # We do not set them in ITK_REQUIRED FLAGS because all project which # use ITK don't require these flags . set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_WARNING_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNING_FLAGS}") #----------------------------------------------------------------------------- #Check the set of platform flags the compiler supports check_compiler_platform_flags() ants-2.2.0/CMake/ITK_CheckCCompilerFlag.cmake000066400000000000000000000040531311104306400205510ustar00rootroot00000000000000# - Check whether the C compiler supports a given flag. # CHECK_C_COMPILER_FLAG( ) # - the compiler flag # - variable to store the result # This internally calls the check_c_source_compiles macro. # See help for CheckCSourceCompiles for a listing of variables # that can modify the build. #============================================================================= # Copyright 2006-2010 Kitware, Inc. # Copyright 2006 Alexander Neundorf # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) include(CheckCSourceCompiles) macro (ITK_CHECK_C_COMPILER_FLAG _FLAG _RESULT) set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") CHECK_C_SOURCE_COMPILES("int main(void) { return 0; }" ${_RESULT} # Some compilers do not fail with a bad flag FAIL_REGEX "warning: command line option .* is valid for .* but not for C" # Apple gcc FAIL_REGEX "unrecognized .*option" # GNU FAIL_REGEX "unknown .*option" # Clang FAIL_REGEX "ignoring unknown option" # MSVC FAIL_REGEX "warning D9002" # MSVC, any lang FAIL_REGEX "[Uu]nknown option" # HP FAIL_REGEX "[Ww]arning: [Oo]ption" # SunPro FAIL_REGEX "command option .* is not recognized" # XL FAIL_REGEX "warning #10156: ignoring option" # INTEL compilers ) set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") endmacro () ants-2.2.0/CMake/ITK_CheckCXXCompilerFlag.cmake000066400000000000000000000040521311104306400210300ustar00rootroot00000000000000# - Check whether the CXX compiler supports a given flag. # CHECK_CXX_COMPILER_FLAG( ) # - the compiler flag # - variable to store the result # This internally calls the check_cxx_source_compiles macro. See help # for CheckCXXSourceCompiles for a listing of variables that can # modify the build. #============================================================================= # Copyright 2006-2010 Kitware, Inc. # Copyright 2006 Alexander Neundorf # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) include(CheckCXXSourceCompiles) macro (ITK_CHECK_CXX_COMPILER_FLAG _FLAG _RESULT) set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") CHECK_CXX_SOURCE_COMPILES("int main() { return 0;}" ${_RESULT} # Some compilers do not fail with a bad flag FAIL_REGEX "unrecognized .*option" # GNU FAIL_REGEX "unknown .*option" # Clang FAIL_REGEX "ignoring unknown option" # MSVC FAIL_REGEX "warning D9002" # MSVC, any lang FAIL_REGEX "[Uu]nknown option" # HP FAIL_REGEX "[Ww]arning: [Oo]ption" # SunPro FAIL_REGEX "command option .* is not recognized" # XL FAIL_REGEX "not supported in this configuration; ignored" # AIX FAIL_REGEX "File with unknown suffix passed to linker" # PGI FAIL_REGEX "warning #10156: ignoring option" # INTEL compilers ) set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") endmacro () ants-2.2.0/CMake/PreventInBuildInstalls.cmake000066400000000000000000000005641311104306400210440ustar00rootroot00000000000000# Adapated from ITKv4/CMake/PreventInBuildInstalls.cmake string(TOLOWER "${CMAKE_INSTALL_PREFIX}" _PREFIX) string(TOLOWER "${${CMAKE_PROJECT_NAME}_BINARY_DIR}" _BUILD) if("${_PREFIX}" STREQUAL "${_BUILD}") message(FATAL_ERROR "The current CMAKE_INSTALL_PREFIX points at the build tree:\n" " ${CMAKE_INSTALL_PREFIX}\n" "This is not supported." ) endif() ants-2.2.0/CMake/PreventInSourceBuilds.cmake000066400000000000000000000053371311104306400207010ustar00rootroot00000000000000# Adapated from ITKv4/CMake/PreventInSourceBuilds.cmake # # This function will prevent in-source builds function(AssureOutOfSourceBuilds PROJECT_NAME) # make sure the user doesn't play dirty with symlinks get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH) # disallow in-source builds if("${srcdir}" STREQUAL "${bindir}") message("######################################################") message("# ${PROJECT_NAME} should not be configured & built in the ${PROJECT_NAME} source directory") message("# You must run cmake in a build directory.") message("# For example:") message("#") message("# mkdir ${PROJECT_NAME}-Sandbox ; cd ${PROJECT_NAME}-sandbox") message("#") message("# Check out source code in ${PROJECT_NAME}-sandbox") message("#") message("# mkdir ${PROJECT_NAME}-build") message("#") message("# this will create the following directory structure") message("#") message("# ${PROJECT_NAME}-Sandbox") message("# +--${PROJECT_NAME}") message("# +--${PROJECT_NAME}-build") message("#") message("# Then you can proceed to configure and build") message("# by using the following commands") message("#") message("# cd ${PROJECT_NAME}-build") message("# cmake ../${PROJECT_NAME} # or ccmake, or cmake-gui") message("# make") message("#") message("# NOTE: Given that you already tried to make an in-source build") message("# CMake have already created several files & directories") message("# in your source tree.") message("#") message("# The following command will show you all files not part of ${PROJECT_NAME}") message("# cd ${PROJECT_NAME}-Sandbox/${PROJECT_NAME}") message("# svn status | grep '[^?]' | awk '{print \$2}'") message("#") message("# WARNING: if you have added files to ${PROJECT_NAME} but not used svn add") message("# to add them to SVN's version control, this command will display them") message("# along with the files CMake created during configuration. You will need") message("# to either save them outside the ${PROJECT_NAME} source tree, or run svn add") message("# to let SVN know they are legitimate source files.") message("# Once you've verified that all unknown files are the result of CMake") message("# configuration, you can run this command to clean them up") message("# svn status | grep '[^?]' | awk '{print \$2}' | xargs rm -fr") message("###########################################################################") message(FATAL_ERROR "Quitting configuration") endif() endfunction() AssureOutOfSourceBuilds(${CMAKE_PROJECT_NAME}) ants-2.2.0/CMake/ProjectSourceVersion.cmake000066400000000000000000000077661311104306400206100ustar00rootroot00000000000000# # This CMake code extracts the information from the git repository, # and automatically causes a reconfigure if the git HEAD changes. The # following variable may be defined after execution: # # _GIT_VERSION_HASH - the SHA1 hash of the current HEAD # # Based on the most recent tag starting with the letter "v" for # version, which is expected to be of the form # vN.N[.N[.N][(a|b|c|rc[N])] the following is extracted or undefined: # # _GIT_VERSION_MAJOR # _GIT_VERSION_MINOR # _GIT_VERSION_PATCH # _GIT_VERSION_TWEAK # _GIT_VERSION_RC # # If the current project's version ( defiend by # ${CMAKE_PROJECT_NAME}_VERSION_MAJOR and MINOR and PATCH and TWEAK # match that of the tag, then it'll be considered that the project is # in post release mode otherwise it's considered underdevelopment. # # Only one of the following variables will be defined. # _GIT_VERSION_DEV is defined as number of commits # since the projects Version.cmake file has been modified. While # _GIT_VERSION_POST is defined as the number of commits since the tag. # include(GetGitRevisionDescription) get_git_head_revision(GIT_REFVAR _GIT_VERSION_HASH) # if there is not git directory we should be in a distributed package # we will use version provided in Version.cmake if(_GIT_VERSION_HASH STREQUAL "GITDIR-NOTFOUND") return() endif() if(_GIT_VERSION_HASH MATCHES "[a-fA-F0-9]+") string(SUBSTRING "${_GIT_VERSION_HASH}" 0 5 _GIT_VERSION_HASH) endif() # find the closest anotated tag with the v prefix for version git_describe(_GIT_TAG "--match=v*") git_commits_since("${PROJECT_SOURCE_DIR}/Version.cmake" _GIT_VERSION_COUNT) set(VERSION_REGEX "^v([0-9]+)\\.([0-9]+)+(\\.([0-9]+))?(\\.([0-9]+))?((a|b|c|rc)[0-9]*)?(-[0-9]+)?") string(REGEX MATCH "${VERSION_REGEX}" _out "${_GIT_TAG}") if("${_out}" STREQUAL "") message(WARNING "git tag: \"${_GIT_TAG}\" does not match expected version format!") return() endif() set(_GIT_VERSION_MAJOR "${CMAKE_MATCH_1}") set(_GIT_VERSION_MINOR "${CMAKE_MATCH_2}") if(NOT "${CMAKE_MATCH_4}" STREQUAL "") set(_GIT_VERSION_PATCH "${CMAKE_MATCH_4}") endif() if(NOT "${CMAKE_MATCH_6}" STREQUAL "") set(_GIT_VERSION_TWEAK "${CMAKE_MATCH_6}") endif() if(NOT "${CMAKE_MATCH_7}" STREQUAL "") set(_GIT_VERSION_RC "${CMAKE_MATCH_7}" ) # a,b,rc01 etc endif() if(NOT "${CMAKE_MATCH_9}" STREQUAL "") #trim leading '-' string(SUBSTRING "${CMAKE_MATCH_9}" 1 -1 CMAKE_MATCH_9) set(_GIT_TAG_COUNT "${CMAKE_MATCH_9}") endif() set(_GIT_VERSION "${_GIT_VERSION_MAJOR}.${_GIT_VERSION_MINOR}") if(DEFINED _GIT_VERSION_PATCH) set(_GIT_VERSION "${_GIT_VERSION}.${_GIT_VERSION_PATCH}") if(DEFINED _GIT_VERSION_TWEAK) set(_GIT_VERSION "${_GIT_VERSION}.${_GIT_VERSION_TWEAK}") elseif(DEFINED ${CMAKE_PROJECT_NAME}_VERSION_TWEAK) set(_GIT_VERSION "${_GIT_VERSION}.0") endif() elseif(DEFINED ${CMAKE_PROJECT_NAME}_VERSION_PATCH) set(_GIT_VERSION "${_GIT_VERSION}.0") if(DEFINED ${CMAKE_PROJECT_NAME}_VERSION_TWEAK) set(_GIT_VERSION "${_GIT_VERSION}.0") endif() endif() set(_${CMAKE_PROJECT_NAME}_VERSION "${${CMAKE_PROJECT_NAME}_VERSION_MAJOR}.${${CMAKE_PROJECT_NAME}_VERSION_MINOR}") if(DEFINED ${CMAKE_PROJECT_NAME}_VERSION_PATCH) set(_${CMAKE_PROJECT_NAME}_VERSION "${_${CMAKE_PROJECT_NAME}_VERSION}.${${CMAKE_PROJECT_NAME}_VERSION_PATCH}") if(DEFINED ${CMAKE_PROJECT_NAME}_VERSION_TWEAK) set(_${CMAKE_PROJECT_NAME}_VERSION "${_${CMAKE_PROJECT_NAME}_VERSION}.${${CMAKE_PROJECT_NAME}_VERSION_TWEAK}") endif() endif() if(_GIT_VERSION VERSION_EQUAL _${CMAKE_PROJECT_NAME}_VERSION) if(_GIT_TAG_COUNT) #ignore if 0 set(_GIT_VERSION_POST "${_GIT_TAG_COUNT}") endif() else() # The first commit after a tag should increase the project version # number in Version.cmake and be "dev1" MATH(EXPR _GIT_VERSION_COUNT "${_GIT_VERSION_COUNT}+1") set(_GIT_VERSION_DEV "${_GIT_VERSION_COUNT}") endif() # save variable in a configuration file in case we have no git directory configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CMake/ProjectSourceVersionVars.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/ProjectSourceVersionVars.cmake" @ONLY) ants-2.2.0/CMake/ProjectSourceVersion.cmake.in000066400000000000000000000011111311104306400211670ustar00rootroot00000000000000macro( _set_if_not_empty var value ) if( NOT "${value}" STREQUAL "" ) set( ${var} "${value}" ) endif() endmacro() set( _GIT_VERSION_MAJOR "@_GIT_VERSION_MAJOR@" ) set( _GIT_VERSION_MINOR "@_GIT_VERSION_MINOR@" ) _set_if_not_empty( _GIT_VERSION_PATCH "@_GIT_VERSION_PATCH@" ) _set_if_not_empty( _GIT_VERSION_TWEAK "@_GIT_VERSION_TWEAK@" ) _set_if_not_empty( _GIT_VERSION_RC "@_GIT_VERSION_RC@" ) _set_if_not_empty( _GIT_VERSION_POST "@_GIT_VERSION_POST@" ) _set_if_not_empty( _GIT_VERSION_DEV "@_GIT_VERSION_DEV@" ) _set_if_not_empty( _GIT_VERSION_HASH "@_GIT_VERSION_HASH@" ) ants-2.2.0/CMake/ProjectSourceVersionVars.cmake.in000066400000000000000000000011111311104306400220230ustar00rootroot00000000000000macro( _set_if_not_empty var value ) if( NOT "${value}" STREQUAL "" ) set( ${var} "${value}" ) endif() endmacro() set( _GIT_VERSION_MAJOR "@_GIT_VERSION_MAJOR@" ) set( _GIT_VERSION_MINOR "@_GIT_VERSION_MINOR@" ) _set_if_not_empty( _GIT_VERSION_PATCH "@_GIT_VERSION_PATCH@" ) _set_if_not_empty( _GIT_VERSION_TWEAK "@_GIT_VERSION_TWEAK@" ) _set_if_not_empty( _GIT_VERSION_RC "@_GIT_VERSION_RC@" ) _set_if_not_empty( _GIT_VERSION_POST "@_GIT_VERSION_POST@" ) _set_if_not_empty( _GIT_VERSION_DEV "@_GIT_VERSION_DEV@" ) _set_if_not_empty( _GIT_VERSION_HASH "@_GIT_VERSION_HASH@" ) ants-2.2.0/CMake/SlicerMacroCheckExternalProjectDependency.cmake000066400000000000000000000062471311104306400246360ustar00rootroot00000000000000################################################################################ # # Program: 3D Slicer # # Copyright (c) Kitware Inc. # # See COPYRIGHT.txt # or http://www.slicer.org/copyright/copyright.txt for details. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc. # and was partially funded by NIH grant 3P41RR013218-12S1 # ################################################################################ if(NOT EXISTS "${EXTERNAL_PROJECT_DIR}") set(EXTERNAL_PROJECT_DIR ${${CMAKE_PROJECT_NAME}_SOURCE_DIR}/SuperBuild) endif() ### macro(ProjectDependancyPush CACHE_LIST VALUE) list(APPEND ${CACHE_LIST} ${VALUE}) #message(STATUS "PUSHING ${VALUE} onto ${CACHE_LIST}: --> ${${CACHE_LIST}}") endmacro() macro(ProjectDependancyPop CACHE_LIST TVAR) list(GET ${CACHE_LIST} -1 ${TVAR}) list(REMOVE_AT ${CACHE_LIST} -1) #message(STATUS "POPING ${${TVAR}} from ${CACHE_LIST}: --> ${${CACHE_LIST}}") endmacro() macro(SlicerMacroCheckExternalProjectDependency proj) # Set indent variable if needed if(NOT DEFINED __indent) set(__indent "") else() set(__indent "${__indent} ") endif() # Sanity checks if(NOT DEFINED ${proj}_DEPENDENCIES) message(FATAL_ERROR "${__indent}${proj}_DEPENDENCIES variable is NOT defined !") endif() # Display dependency of project being processed if("${${proj}_DEPENDENCIES}" STREQUAL "") message(STATUS "SuperBuild - ${__indent}${proj}[OK]") else() set(dependency_str " ") foreach(dep ${${proj}_DEPENDENCIES}) if(External_${dep}_FILE_INCLUDED) set(dependency_str "${dependency_str}${dep}[INCLUDED], ") else() set(dependency_str "${dependency_str}${dep}, ") endif() endforeach() message(STATUS "SuperBuild - ${__indent}${proj} => Requires${dependency_str}") endif() # Include dependencies foreach(dep ${${proj}_DEPENDENCIES}) if(NOT External_${dep}_FILE_INCLUDED) if(EXISTS "${EXTERNAL_PROJECT_DIR}/External_${dep}.cmake") include(${EXTERNAL_PROJECT_DIR}/External_${dep}.cmake) elseif(EXISTS "${Slicer_ADDITIONAL_EXTERNAL_PROJECT_DIR}/External_${dep}.cmake") include(${Slicer_ADDITIONAL_EXTERNAL_PROJECT_DIR}/External_${dep}.cmake) else() message(FATAL_ERROR "Can't find External_${dep}.cmake") endif() endif() endforeach() set(__${proj}_superbuild_message "SuperBuild - ${__indent}${proj}[OK]") set(__${proj}_indent ${__indent}) # If project being process has dependencies, indicates it has also been added. if(NOT "${${proj}_DEPENDENCIES}" STREQUAL "") message(STATUS ${__${proj}_superbuild_message}) endif() # Update indent variable string(LENGTH "${__indent}" __indent_length) math(EXPR __indent_length "${__indent_length}-2") if(NOT ${__indent_length} LESS 0) string(SUBSTRING "${__indent}" 0 ${__indent_length} __indent) endif() endmacro() ants-2.2.0/CMake/SlicerMacroEmptyExternalProject.cmake000066400000000000000000000025161311104306400227130ustar00rootroot00000000000000########################################################################### # # Library: CTK # # Copyright (c) Kitware Inc. # # 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.commontk.org/LICENSE # # 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. # ########################################################################### # See http://github.com/commontk/CTK/blob/master/CMake/ctkMacroEmptyExternalProject.cmake # # Convenient macro allowing to define a "empty" project in case an external one is provided # using for example _DIR. # Doing so allows to keep the external project dependency system happy. # macro(SlicerMacroEmptyExternalProject proj dependencies) ExternalProject_Add(${proj} SOURCE_DIR ${CMAKE_BINARY_DIR}/EMPTY_${proj} BINARY_DIR EMPTY_${proj}-build DOWNLOAD_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS ${dependencies} ) endmacro() ants-2.2.0/CMake/itkCheckSourceTree.cmake000066400000000000000000000011701311104306400201600ustar00rootroot00000000000000# Install a pre-commit hook to bootstrap commit hooks. if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git/config" AND NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit") # Silently ignore the error if the hooks directory is read-only. execute_process( COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/CMake/pre-commit ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit OUTPUT_VARIABLE _output ERROR_VARIABLE _output RESULT_VARIABLE _result ) if(_result AND NOT "${_output}" MATCHES "Error copying file") message("${_output}") endif() endif() ants-2.2.0/CMake/pre-commit000077500000000000000000000003371311104306400154360ustar00rootroot00000000000000#!/bin/sh echo 'Your work tree has not been configured for development. Paste the following commands into a shell: ./Utilities/SetupForDevelopment.sh See http://www.itk.org/Wiki/ITK/Git/Develop for more details.' exit 1 ants-2.2.0/CMakeLists.txt000066400000000000000000000063061311104306400152160ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.9) cmake_policy(VERSION 2.8.9) find_program(CCACHE_PROGRAM ccache) if(CCACHE_PROGRAM) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") endif() set(LIBRARY_SOVERSION_INFO 2) set(LIBRARY_VERSION_INFO 2.1.0) set(LOCAL_PROJECT_NAME ANTS) INCLUDE(InstallRequiredSystemLibraries) ## NOTE THERE SHOULD BE NO PROJECT STATEMENT HERE! ## This file acts as a simple switch to initiate ## two completely independant CMake build environments. #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default # Phase I: ${LOCAL_PROJECT_NAME}_SUPERBUILD is set to ON, and the # supporting packages defined in "SuperBuild.cmake" # are built. The last package in "SuperBuild.cmake" # to be built is a recursive call to this # file with ${LOCAL_PROJECT_NAME}_SUPERBUILD explicitly # set to "OFF" to initiate Phase II # # Phase II: Build the ${LOCAL_PROJECT_NAME}, referencing the support # packages built in Phase I. #----------------------------------------------------------------------------- option(${LOCAL_PROJECT_NAME}_SUPERBUILD "Build ${LOCAL_PROJECT_NAME} and the projects it depends on via SuperBuild.cmake." ON) mark_as_advanced(${LOCAL_PROJECT_NAME}_SUPERBUILD) option(${LOCAL_PROJECT_NAME}_USE_QT "Find and use Qt with VTK to build GUI Tools" OFF) mark_as_advanced(${LOCAL_PROJECT_NAME}_USE_QT) option(${LOCAL_PROJECT_NAME}_INSTALL_DEVELOPMENT "Install development support include and libraries for external packages." OFF) mark_as_advanced(${LOCAL_PROJECT_NAME}_INSTALL_DEVELOPMENT) option(COPY_SCRIPT_FILES_TO_BIN_DIR "Copy the script files to the ANTS bin directory." OFF) mark_as_advanced(COPY_SCRIPT_FILES_TO_BIN_DIR) set(CPACK_PACKAGE_NAME "ANTs") set(CPACK_PACKAGE_VENDOR "CMake.org") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ANTs - Advanced Normalization Tools") set(CPACK_PACKAGE_VERSION 2.1.0) set(CPACK_PACKAGE_VERSION_MAJOR 2) set(CPACK_PACKAGE_VERSION_MINOR 1) set(CPACK_PACKAGE_VERSION_PATCH 0) set(CPACK_PACKAGE_INSTALL_DIRECTORY "ANTS_Install") set(CPACK_BINARY_GENERATORS "DragNDrop TGZ TZ") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.txt") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ANTs - robust image registration, segmentation and more") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/ANTSCopyright.txt") set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.txt") #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(${LOCAL_PROJECT_NAME}_SUPERBUILD) project(SuperBuild_${LOCAL_PROJECT_NAME}) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") include(CPack) # This must always be last! return() else() project(${LOCAL_PROJECT_NAME}) include("${CMAKE_CURRENT_SOURCE_DIR}/${LOCAL_PROJECT_NAME}.cmake") include(CPack) # This must always be last! return() endif() message(FATAL_ERROR "You should never reach this point !") ants-2.2.0/COPYING.txt000066400000000000000000000027071311104306400143300ustar00rootroot00000000000000Copyright (c) 2009-2013 ConsortiumOfANTS All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the consortium nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE CONSORTIUM AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ants-2.2.0/CTestConfig.cmake000066400000000000000000000010431311104306400156210ustar00rootroot00000000000000## 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 "ANTS") set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "testing.psychiatry.uiowa.edu") set(CTEST_DROP_LOCATION "/CDash/submit.php?project=ANTS") set(CTEST_DROP_SITE_CDASH TRUE) ants-2.2.0/CTestCustom.cmake000066400000000000000000000275771311104306400157120ustar00rootroot00000000000000#-- #NOTES from http: // www.cmake.org/Wiki/CMake_Testing_With_CTest #set(CTEST_CUSTOM_MEMCHECK_IGNORE # $ {CTEST_CUSTOM_MEMCHECK_IGNORE} # DummyExcludeMemcheckIgnoreTestSetGet # ) #-- #set(CTEST_CUSTOM_WARNING_MATCH #-- #${CTEST_CUSTOM_WARNING_MATCH} #-- #"{standard input}:[0-9][0-9]*: Warning: " #-- #) # # For further details regarding this file, # see http://www.cmake.org/Wiki/CMake_Testing_With_CTest#Customizing_CTest # # and # http://www.kitware.com/blog/home/post/27 # #---------------------------------------------------------------------- #-- #Reset maximum number of warnings so that they all show up. set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS 1000) set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_ERRORS 1000) # The following tests should not be run under valgrind set(CTEST_CUSTOM_MEMCHECK_IGNORE @CTEST_CUSTOM_MEMCHECK_IGNORE@ ) set(CTEST_EXTRA_COVERAGE_GLOB Source/.*/*.h Source/.*/*.hxx Source/.*/*.cxx ) set(CTEST_CUSTOM_COVERAGE_EXCLUDE ${CTEST_CUSTOM_COVERAGE_EXCLUDE} ".*Temporary/.*" ".*boost.*" # Exclude try_compile sources from coverage results: "/CMakeFiles/CMakeTmp/" # Exclude files generated by the moc pre-compiler ".*/moc_.*" # Exclude files generated by the uic pre-compiler ".*/ui_.*" # Exclude files generated by the resource pre-compiler ".*/qrc_.*" # Exclude files from the Testing directories ".*/Testing/.*" # Exclude generated python files ".*Python.cxx" ".*PythonInit.cxx" # Exclude Qt Designer plugins ".*/DesignerPlugins/.*" # Exclude generated cpp files ".*/generated_cpp/.*" ) set(CTEST_CUSTOM_WARNING_EXCEPTION ${CTEST_CUSTOM_WARNING_EXCEPTION} #"vtkparse.tab.c" "Microsoft SDKs." "VC.include." # Ignore 'detached HEAD' warnings ".*Note: checking out.*" ".*detached.*HEAD.*" ".*warning generated..*" # NIPYPE warnings: ".*NIPYPE-prefix.*warning.*" # PCRE warnings: ".*PCRE-prefix.*warning.*" ".*PCRE.*warning.*" ".*pcre.*has no symbols.*" ".*pcre.*warning*" ".*checking for C compiler warning.*" ".*gmake.*: warning.*" # JPEG warnings: ".*/JPEG.*" ".*warning: unused parameter .*cinfo.*" ".*/TIFF.*" # Swig warnings: ".*Swig-prefix.*warning.*" ".*Note.*SWIG.*" ".*CParse.*warning.*" ".*parser.y.*" ".*maximum warning verbosity.*" ".*Swig.*note.*" ".*Swig/Source.*" ".*warning.*argument unused during compilation.*" # Open-CV warnings: ".*OpenCV-build.*warning.*" ".*OpenCV-install.*warning.*" ".*OpenCV.*" # FFTW warnings: ".*fftw.*" # Numpy warnings "_configtest.*warning" # C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data # C4028: formal parameter 'number' different from declaration # C4996: 'function': was declared deprecated # C4133: '=' : incompatible types - from 'PyArray_Descr *' to 'PyObject *' # C4146: unary minus operator applied to unsigned type, result still unsigned # C4716: function '...' : must return a value # C4723: Potential divide by zero "numpy.(core|numarray|linalg|random).*warning (C4244|C4028|C4996|C4133|C4146|C4716|C4723)" # warning: assignment from incompatible pointer type # warning: ‘...’ defined but not used # warning: ‘...’ may be used uninitialized in this function "numpy.(core).*warning.*(assignment|defined but not used|uninitialized)" "NUMPY.*Warning" # Mac "numpy.core.src.multiarray.descriptor.*was declared here" # Tcl "tcl.tcl.unix.*warning: cast" # warning: '...' is deprecated (declared at ...) "tcl.tcl.unix.*warning:.*is deprecated" "tcl.tcl.unix.*warning:.*ignoring return value of" # Tk "tcl.tk.unix.*warning: cast" "System.Library.Frameworks.Tk.framework.Headers.X11.Xlib.h.*warning: function declaration isn't a prototype" "tcl.tk.unix.*warning:.*ignoring return value of" # incrTcl "generic.itk_(option|archetype).*warning" "generic.itcl_.*warning" # qt suppressions from vtk... # Some Slicer dashboards include building bits of Qt which produce lots of # the following warnings when built with the MS compilers. Qt guys should # fix their code. Until they do, keep the Qt chatter off the Slicer dashboard # results: "include.[Qq]t([Cc]ore|[Gg]ui).*warning C4127: conditional expression is constant" "[Qq]t.*h.*warning.*declaration of .* shadows a member of .this" "[Qq]t.*h.*warning.*(copy constructor|assignment operator) could not be generated" # Tiger / 4.6.2 warning "include.[Qq]t[Cc]ore.qtextcodec.h.*warning.*is already a friend of" "include.QtGui.(qtextformat|qtablewidget).*warning" # Snowleopard / 4.6.2 warning "QtGui.framework.Headers.(qtextformat|qtablewidget).*warning" # Suppress warning caused when QT 'foreach' loops are combined ".*warning: declaration of '_container_' shadows a previous local" # STL - Tiger "include.c.*bits.stl_algo.h.*warning: comparison between signed and unsigned integer expressions" # Make "warning: jobserver unavailable" # Suppressing warnings about GL_GLEXT_LEGACY, the link reported below # report a similar problem with GL_GLEXT_PROTOTYPE. # http://lists.apple.com/archives/mac-opengl/2009/Dec/msg00081.html # That problem could be solved installing a newer version of X11 SDK # See http://xquartz.macosforge.org/trac/changeset/343 ".*warning.*GL_GLEXT_LEGACY.*redefined" # ITK suppressions "[Uu]tilities.gdcm" "[Uu]tilities.vxl" "[Uu]tilities.itktiff" "([Ii]nsight|ITKv3).[Cc]ode.[Cc]ommon" "([Ii]nsight|ITKv3).[Cc]ode.[Nn]umerics" "([Ii]nsight|ITKv3).[Cc]ode.(IO|io)" "([Ii]nsight|ITKv3).[Cc]ode.[Ss]patial[Oo]bject" "([Ii]nsight|ITKv3).[Uu]tilities.[Nn]rrd(IO|io)" "([Ii]nsight|ITKv3).[Uu]tilities.(openjpeg|nifti)" "([Ii]nsight|ITKv3|BRAINSFit).*Informational: catch(...) semantics changed since Visual C\\+\\+ 7.1; structured exceptions (SEH) are no longer caught" # VTK suppressions "vtkfreetype" "Utilities.vtktiff" "VTK.*IO.vtkMySQLQuery.cxx" "VTK.*Utilities.vtkexodus2" "VTK.*Utilities.vtklibproj" "VTK.*Utilities.vtksqlite" "VTK.*Utilities.VPIC.*cxx" "VTK.*warn_unused_result" "VTK.*Filtering.*cxx" "VTK.*IO.*cxx" "VTK.*Infovis.*cxx" "VTK.*vtk.*warning" # exception specific to Mac/Carbon "VTK.Rendering.vtkCarbonRenderWindow.*warning.*(NewRgn|DiffRgn|EqualRgn|DisposeRgn).*is deprecated" # exception specific to Mac/X11 "VTK.Rendering.vtkOpenGL.*warning: this is the location of the previous definition" # CTK - log4qt "logobjectptr.obj : warning LNK4221: no public symbols found; archive member will be inaccessible" "/usr/bin/ranlib: .*libLog4Qt.a.*has no symbols" "log4qt.rollingfileappender.h.*Warning: Property declaration maxFileSize has no READ accessor function." "ld.*has different visibility.*libLog4Qt.a" # CTK - dcmtk ".*dcmdata/dcovlay.h:93: warning: use of old-style cast.*" ".*/usr/bin/ranlib: .*lib(dcmtls|oflog).a.*has no symbols.*" ".*ld.*has different visibility.*(libdcmdata|libdcmnet|libdcmimgle|liboflog|libofstd).a.*" ".*DCMTK.(ofstd|dcmdata|dcmjpls|dcmnet|dcmimage|dcmimgle|dcmpstat|dcmqrdb).(lib|include|apps).*conversion from '(size_t|SOCKET)' to '.*', possible loss of data.*" ".*DCMTK.*warning.*" # Libs/OpenIGTLink "(OpenIGTLink|openigtlink).[Ss]ource.igtl" # Batchmake "BatchMake.Utilities.Zip.(zip|unzipcmd|zipcmd).*warning" # Libs/tclap "tclap.include.tclap.*Arg.h.*warning C4512" # Teem # Mac - teem/src/nrrd/superset.c:433: warning: format '%d' expects type 'int', but argument 6 has type 'ptrdiff_t' "teem.src.nrrd.superset.c.*warning" # Python - Windows "Modules.zlib.gzio" "Modules._ctypes.libffi_msvc.ffi.*warning C4018" "Modules.audioop.c.*warning C4018" "Modules._multiprocessing.*warning" "(Python|Objects|Modules|modules|PC).*conversion from '(Py_uintptr_t|Py_ssize_t|INT_PTR|size_t|__int64)' to '.*', possible loss of data" # Python - Linux "dist.py.*UserWarning.*licence.*distribution option is deprecated" "Objects.unicodeobject.c.*warning:.*differ in signedness" "[Ii]nclude.(string|unicodeobject).h.*note: expected .const char *. but argument is of type .unsigned char *." "Modules.(getpath|signalmodule).c.*warning: ignoring return value of.*declared with attribute warn_unused_result" "Modules.expat.xmlparse.*warning.*discards qualifiers from pointer target type" # Python - Mac "ranlib: file:.*libpython2.6.a.*has no symbols" "python.Modules._cursesmodule.c.*warning.*may be used uninitialized in this function" "python.Mac.Modules.(cf|Nav).*warning: (cast|unused)" "python.Modules._ctypes.*warning: function declaration isn't a prototype" "QuickTime.framework.QuickTime, missing required architecture x86_64 in file" "python.Modules._ssl.*incompatible pointer type" "python.Mac.Modules.carbonevt.*defined but not used" "python.Mac.Modules.qt._Qtmodule.*used uninitialized in this function" "Modules.main.c.*warning: format not a string literal and no format arguments" # About redefinition of symbols "pyconfig.h.*warning:.*redefined" "features.h.*" # curl suppressions "cmcurl.*warning.*conditional expression is constant" "cmcurl.*warning.*conversion from.*possible loss of data" # C4701: potentially uninitialized local variable '...' used # C4057: 'function' : '...' differs in indirection to slightly different base types from '...' # C4245: '=' : conversion from '...' to '...', signed/unsigned mismatch # C4706: assignment within conditional expression # C4232: nonstandard extension used : '...' : address of dllimport '...' is not static, identity not guaranteed "cmcurl.(transfer|ftp|file|cookie|url|telnet|multi|hostip4|formdata|easy).c.*warning (C4244|C4701|C4057|C4245|C4706|C4232)" # C4131: uses old-style declarator # C4244: conversion from '...' to '...', possible loss of data # C4127: conditional expression is constant "y.tab.c.*warning (C4131|C4244|C4127|C4701)" # C4100: unreferenced formal parameter "getdate.y.*warning(C4100|C4127|C4244)" # Mac "curl.mprintf.*warning.*redefined" "usr.include.secure._stdio.h.*warning: this is the location of the previous definition" "ranlib: file:.*bin.libslicerlibcurl.a.*has no symbols" #PythonQt "PythonQt.src.*conversion from 'size_t' to '.*', possible loss of data" #Libarchive "LibArchive.libArchive.*signed/unsigned mismatch" "LibArchive.libArchive.*conversion from 'size_t' to '.*', possible loss of data" # Visual studio spurious warnings... "The following environment variables were not found" # Since NUMPY has test that return build errors, let's add the following exception "WARNING non-zero return value in ctest from" ## HACK: THIS SHOULD NOT BE SUPPRESSED, NEED TO FIX OPENCV!! "BRAINSCutApplyModel.cxx.*warning.*increases required alignment" "ModuleFactory.*warning.*SymbolPointer" ## HACK: THIS SHOULD NOT BE SUPPRESSED, NEED TO FIX IN ANTS "ANTS.*warning" ## External Packages "Note.*SWIG" ".*parser.y.*" "maximum warning verbosity" ".*OpenCV.*" ".*VTK.*" ".*has no symbols." "ITKv4" "SlicerExecutionModel" "SimpleITK" ".*has no symbols" "CMake Warning:" "note: expanded from macro" ": note:" "vrscanl.c.* warning: unused parameter" "/usr/bin/libtool: warning same member name" ) set(CTEST_CUSTOM_WARNING_MATCH ${CTEST_CUSTOM_WARNING_MATCH} #"CMake Warning[ :]" ) if(APPLE) set(CTEST_CUSTOM_WARNING_EXCEPTION ${CTEST_CUSTOM_WARNING_EXCEPTION} "warning -.: directory name .* does not exist" # Suppressing warnings about duplicate libraries in Darwin # At some point this may be addressed by CMake feature request: # http://public.kitware.com/Bug/view.php?id=10179 "ld.*warning.*duplicate dylib.*" ) endif() set(CTEST_CUSTOM_ERROR_MATCH ${CTEST_CUSTOM_ERROR_MATCH} "CMake Error[ :]" ) set(CTEST_CUSTOM_ERROR_EXCEPTION ${CTEST_CUSTOM_ERROR_EXCEPTION} # Numpy errors "NUMPY.*Warning" "NUMPY._configtest.*undefined reference" "_configtest.*error" "collect2: ld returned 1 exit status" #SWIG "Note.*SWIG" ".*parser.y.*" "maximum warning verbosity" ".*OpenCV.*" ".*VTK.*" ".*has no symbols." "ITKv4" "SlicerExecutionModel" "SimpleITK" ) ants-2.2.0/Common.cmake000066400000000000000000000157511311104306400147140ustar00rootroot00000000000000 #----------------------------------------------------------------------------- # Update CMake module path #------------------------------------------------------------------------------ set(CMAKE_MODULE_PATH ${${PROJECT_NAME}_SOURCE_DIR}/CMake ${${PROJECT_NAME}_BINARY_DIR}/CMake ${CMAKE_MODULE_PATH} ) #----------------------------------------------------------------------------- # Sanity checks #------------------------------------------------------------------------------ include(PreventInSourceBuilds) include(PreventInBuildInstalls) #include(itkCheckSourceTree) include(CMakeDependentOption) #----------------------------------------------------------------------------- # Build option(s) #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- option(BUILD_SHARED_LIBS "Build ITK with shared libraries." OFF) set(ANTS_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) ###################################################################################################### # BA - add this stuff to help installation of ANTsR # SET(CMAKE_SKIP_BUILD_RPATH FALSE) # SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") # SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) # IF("${isSystemDir}" STREQUAL "-1") # SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") # ENDIF("${isSystemDir}" STREQUAL "-1") ##################################################################################################### set(USE_ITKv4 ON) set(ITK_VERSION_MAJOR 4 CACHE STRING "Choose the expected ITK major version to build ANTS only version 4 allowed.") # Set the possible values of ITK major version for cmake-gui set_property(CACHE ITK_VERSION_MAJOR PROPERTY STRINGS "4") set(expected_ITK_VERSION_MAJOR ${ITK_VERSION_MAJOR}) if(${ITK_VERSION_MAJOR} VERSION_LESS ${expected_ITK_VERSION_MAJOR}) # Note: Since ITKv3 doesn't include a ITKConfigVersion.cmake file, let's check the version # explicitly instead of passing the version as an argument to find_package() command. message(FATAL_ERROR "Could not find a configuration file for package \"ITK\" that is compatible " "with requested version \"${expected_ITK_VERSION_MAJOR}\".\n" "The following configuration files were considered but not accepted:\n" " ${ITK_CONFIG}, version: ${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}.${ITK_VERSION_PATCH}\n") endif() if(${ITK_VERSION_MAJOR} STREQUAL "3") message(FATAL_ERROR "ITKv3 is no longer supported") endif() #----------------------------------------------------------------------------- # Set a default build type if none was specified 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() #----------------------------------------------------------------------------- # CMake Function(s) and Macro(s) #----------------------------------------------------------------------------- # With CMake 2.8.9 or later, the UPDATE_COMMAND is required for updates to occur. # For earlier versions, we nullify the update state to prevent updates and # undesirable rebuild. if(CMAKE_VERSION VERSION_LESS 2.8.9) set(cmakeversion_external_update UPDATE_COMMAND "") else() set(cmakeversion_external_update LOG_UPDATE 1) endif() if(CMAKE_VERSION VERSION_LESS 2.8.3) include(Pre283CMakeParseArguments) else() include(CMakeParseArguments) endif() #----------------------------------------------------------------------------- # Platform check #----------------------------------------------------------------------------- set(PLATFORM_CHECK true) if(PLATFORM_CHECK) # See CMake/Modules/Platform/Darwin.cmake) # 6.x == Mac OSX 10.2 (Jaguar) # 7.x == Mac OSX 10.3 (Panther) # 8.x == Mac OSX 10.4 (Tiger) # 9.x == Mac OSX 10.5 (Leopard) # 10.x == Mac OSX 10.6 (Snow Leopard) if (DARWIN_MAJOR_VERSION LESS "9") message(FATAL_ERROR "Only Mac OSX >= 10.5 are supported !") endif() endif() #----------------------------------------------------------------------------- if(NOT COMMAND SETIFEMPTY) macro(SETIFEMPTY) set(KEY ${ARGV0}) set(VALUE ${ARGV1}) if(NOT ${KEY}) set(${ARGV}) endif() endmacro() endif() SETIFEMPTY(LIB_INSTALL_DIR lib${LIB_SUFFIX}) SETIFEMPTY(BIN_INSTALL_DIR bin) SETIFEMPTY(SCRIPTS_INSTALL_DIR ${BIN_INSTALL_DIR}) #----------------------------------------------------------------------------- SETIFEMPTY(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) SETIFEMPTY(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) SETIFEMPTY(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) #----------------------------------------------------------------------------- SETIFEMPTY(CMAKE_INSTALL_LIBRARY_DESTINATION ${LIB_INSTALL_DIR}) SETIFEMPTY(CMAKE_INSTALL_ARCHIVE_DESTINATION ${LIB_INSTALL_DIR}) SETIFEMPTY(CMAKE_INSTALL_RUNTIME_DESTINATION ${BIN_INSTALL_DIR}) #------------------------------------------------------------------------- SETIFEMPTY(ANTs_CLI_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) SETIFEMPTY(ANTs_CLI_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}) SETIFEMPTY(ANTs_CLI_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) #------------------------------------------------------------------------- SETIFEMPTY(ANTs_CLI_INSTALL_LIBRARY_DESTINATION ${CMAKE_INSTALL_LIBRARY_DESTINATION}) SETIFEMPTY(ANTs_CLI_INSTALL_ARCHIVE_DESTINATION ${CMAKE_INSTALL_ARCHIVE_DESTINATION}) SETIFEMPTY(ANTs_CLI_INSTALL_RUNTIME_DESTINATION ${CMAKE_INSTALL_RUNTIME_DESTINATION}) #------------------------------------------------------------------------- # Augment compiler flags #------------------------------------------------------------------------- include(ITKSetStandardCompilerFlags) if(ITK_LEGACY_REMOVE) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_DEBUG_DESIRED_FLAGS} " ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_DEBUG_DESIRED_FLAGS} " ) else() # Release, or anything else set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_RELEASE_DESIRED_FLAGS} " ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_RELEASE_DESIRED_FLAGS} " ) endif() endif() #----------------------------------------------------------------------------- # Add needed flag for gnu on linux like enviroments to build static common libs # suitable for linking with shared object libs. if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") if(NOT "${CMAKE_CXX_FLAGS}" MATCHES "-fPIC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") endif() if(NOT "${CMAKE_C_FLAGS}" MATCHES "-fPIC") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") endif() endif() ants-2.2.0/Examples/000077500000000000000000000000001311104306400142275ustar00rootroot00000000000000ants-2.2.0/Examples/ANTS.cxx000066400000000000000000000273751311104306400155360ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include "itkPICSLAdvancedNormalizationToolKit.h" #include "itkANTSImageTransformation.h" #include "itkANTSImageRegistrationOptimizer.h" #include #include namespace ants { void PrintCommandLineHelp( const std::string & progName ) { std::cout << " \n " << std::endl; std::cout << "Example usage: \n " << std::endl; std::cout << progName << " ImageDimension -m MI[fixedimage.nii.gz,movingimage.nii.gz,1,32] -o Outputfname.nii.gz -i 30x20x0 -r Gauss[3,1] -t Elast[3] \n \n " << std::endl; std::cout << " Compulsory arguments:\n " << std::endl; std::cout << " ImageDimension: 2 or 3 (for 2 or 3 Dimensional registration)\n " << std::endl; std::cout << " -m: Type of similarity model used for registration. \n " << std::endl; std::cout << " For intramodal image registration, use: " << std::endl; std::cout << " CC = cross-correlation " << std::endl; std::cout << " MI = mutual information " << std::endl; std::cout << " PR = probability mapping " << std::endl; std::cout << " MSQ = mean square difference " << std::endl; std::cout << " \n " << std::endl; std::cout << " For intermodal image registration, use: " << std::endl; std::cout << " MI = mutual information " << std::endl; std::cout << " PR = probability mapping " << std::endl; std::cout << " \n " << std::endl; std::cout << " -o Outputfname.nii.gz: the name of the resulting image.\n " << std::endl; std::cout << " -i Max-iterations in format: JxKxL, where: " << std::endl; std::cout << " J = max iterations at coarsest resolution (here, reduce by power of 2^2) " << std::endl; std::cout << " K = middle resolution iterations (here,reduce by power of 2) " << std::endl; std::cout << " L = fine resolution iterations (here, full resolution). This level takes much more time per iteration!\n " << std::endl; std::cout << " Adding an extra value before JxKxL (i.e. resulting in IxJxKxL) would add another iteration level.\n " << std::endl; std::cout << " -r Regularization \n" << std::endl; std::cout << " -t Type of transformation model used for registration \n" << std::endl; std::cout << " For elastic image registration, use: " << std::endl; std::cout << " Elast = elastic transformation model (less deformation possible)\n " << std::endl; std::cout << " For diffeomorphic image registration, use: " << std::endl; std::cout << " Syn[GradStep,TimePoints,IntegrationStep] --geodesic 2 = SyN with time with arbitrary number of time points in time discretization " << std::endl; std::cout << " SyN[GradStep,2,IntegrationStep] = SyN with time optimized specifically for 2 time points in the time discretization " << std::endl; std::cout << " SyN[GradStep] = Greedy SyN, typicall GradStep=0.25 " << std::endl; std::cout << " Exp[GradStep,TimePoints] = Exponential " << std::endl; std::cout << " GreedyExp = Diffeomorphic Demons style exponential mapping " << std::endl; std::cout << " \n " << std::endl; std::cout << " Please use the `ANTS -h ` call or refer to the ANTS.pdf manual or antsIntroduction.sh script for additional information and typical values for transformation models\n " << std::endl; } template int ANTSex(int argc, char *argv[]) { typedef itk::PICSLAdvancedNormalizationToolKit RegistrationType; typename RegistrationType::Pointer registration = RegistrationType::New(); registration->ParseCommandLine( argc, argv ); std::cout << " Run Reg " << std::endl; try { registration->RunRegistration(); } catch( std::exception const& e ) { std::cerr << "Exception caught in ANTS: " << std::endl << e.what() << std::endl; return EXIT_FAILURE; } catch( ... ) { std::cerr << "Non-standard exception caught in ANTS. No more information available." << std::endl; return EXIT_FAILURE; } registration->GetTransformationModel()->SetWriteComponentImages(true); registration->GetTransformationModel()->Write(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ANTS( std::vector args, std::ostream* /*out_stream = NULL*/ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ANTS" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 2 ) { std::cerr << " call ANTS -h or ANTS --help " << std::endl; return EXIT_FAILURE; } int dim = 0; if( argc > 1 ) { dim = atoi( argv[1] ); } // if( dim <= 1 || dim > 3 ) // { // std::cerr << " You passed ImageDimension: " << dim // << " . Please use only 2 or 3 (for 2 or 3 Dimensional registration) " << std::endl; // ants::PrintCommandLineHelp(argv[0]); // return EXIT_FAILURE; // } /** * Try the simple case of the call "ANTS fixedImage movingImage" */ if( argc == 3 && ( atoi( argv[1] ) != '-' || atoi( argv[1] ) != 2 || atoi( argv[1] ) != 3 ) ) { itk::ImageIOBase::Pointer fixedImageIO = itk::ImageIOFactory::CreateImageIO( argv[1], itk::ImageIOFactory::ReadMode ); if( fixedImageIO.IsNull() ) { std::cerr << "Invalid fixed image: " << argv[1] << std::endl; return EXIT_FAILURE; } itk::ImageIOBase::Pointer movingImageIO = itk::ImageIOFactory::CreateImageIO( argv[2], itk::ImageIOFactory::ReadMode ); if( movingImageIO.IsNull() ) { std::cerr << "Invalid moving image: " << argv[2] << std::endl; return EXIT_FAILURE; } fixedImageIO->SetFileName( argv[1] ); fixedImageIO->ReadImageInformation(); movingImageIO->SetFileName( argv[2] ); movingImageIO->ReadImageInformation(); const unsigned int fdim = fixedImageIO->GetNumberOfDimensions(); const unsigned int mdim = movingImageIO->GetNumberOfDimensions(); if( fdim != mdim ) { std::cerr << "Fixed image dimension does not equal " << "the moving image dimension (" << fdim << " != " << mdim << ")" << std::endl; return EXIT_FAILURE; } if( fdim != 2 && fdim != 3 ) { std::cerr << "Unsupported image dimension" << std::endl; return EXIT_FAILURE; } /** * After the checking, we can add the default parameters; */ std::string transformation( " -t SyN[1.0] "); std::string regularization( " -r Gauss[3,0.5] "); std::string metric = std::string( " -m PR[" ) + std::string( argv[1] ) + std::string( "," ) + std::string( argv[2] ) + std::string( ",1,3] " ); std::string outputNaming( " -o ANTS.nii.gz " ); long maxSize = vnl_math_min( fixedImageIO->GetDimensions( 0 ), movingImageIO->GetDimensions( 0 ) ); for( unsigned int d = 1; d < fdim; d++ ) { long tmpMax = vnl_math_max( fixedImageIO->GetDimensions( d ), movingImageIO->GetDimensions( d ) ); if( maxSize < tmpMax ) { maxSize = tmpMax; } } unsigned int numberOfLevels = static_cast( std::log( (double)maxSize / 32 ) / std::log( (double) 2 ) ) + 1; std::string iterations( " -i " ); for( int n = numberOfLevels; n > 0; n-- ) { std::ostringstream buf; unsigned long numberOfIterations = ( 5 << (n - 1) ); buf << numberOfIterations << "x"; iterations += buf.str(); } iterations = iterations.substr( 0, iterations.length() - 1 ); std::ostringstream dimBuf; dimBuf << fdim; std::string arguments; arguments.clear(); arguments = std::string( " ANTS " ) + dimBuf.str() + iterations + transformation + regularization + metric + outputNaming; dim = fdim; std::cout << arguments << std::endl; unsigned int my_argc = 0; std::string::size_type pos = 0; while( true ) { std::string::size_type delimPos = arguments.find_first_of( " ", pos ); std::string::size_type tokenPos = arguments.find_first_not_of( " ", pos ); if( std::string::npos != delimPos && std::string::npos != tokenPos && tokenPos < delimPos ) { my_argc++; } else if( std::string::npos == delimPos ) { if( std::string::npos != tokenPos ) { my_argc++; } else { break; } } pos = delimPos + 1; } char * * my_argv = NULL; if( my_argc > 0 ) { unsigned int arg_count = 0; my_argv = new char *[my_argc]; pos = 0; while( true ) { std::string::size_type delimPos = arguments.find_first_of( " ", pos ); std::string::size_type tokenPos = arguments.find_first_not_of( " ", pos ); if( std::string::npos != delimPos && std::string::npos != tokenPos && tokenPos < delimPos ) { std::string arg = arguments.substr( pos, delimPos - pos ); my_argv[arg_count] = new char[arg.size() + 1]; strcpy( my_argv[arg_count], arg.c_str() ); arg_count++; } else if( std::string::npos == delimPos ) { if( std::string::npos != tokenPos ) { std::string arg = arguments.substr( pos ); my_argv[arg_count] = new char[arg.size() + 1]; strcpy( my_argv[arg_count], arg.c_str() ); arg_count++; } else { break; } } pos = delimPos + 1; } } switch( dim ) { case 3: { return ANTSex<3>( my_argc, my_argv ); } break; default: return ANTSex<2>( my_argc, my_argv ); } } else { switch( dim ) { case 3: { return ANTSex<3>( argc, argv ); } break; default: return ANTSex<2>( argc, argv ); } } std::cerr << "Shoudln't have gotten here." << std::endl; return EXIT_FAILURE; } } // namespace ants ants-2.2.0/Examples/ANTSConformalMapping.cxx000066400000000000000000000464671311104306400207160ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include "antsCommandLineParser.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkFEMLinearSystemWrapperItpack.h" #include "itkFEMElement3DC0LinearTriangularLaplaceBeltrami.h" #include "itkFEMElement3DC0LinearTriangularMembrane.h" #include "itkFEMDiscConformalMap.h" #include "vtkCallbackCommand.h" #include "vtkPolyDataWriter.h" #include "vtkPolyDataReader.h" #include #include #include #include #include namespace ants { template class CommandIterationUpdate : public itk::Command { public: typedef CommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); protected: CommandIterationUpdate() { }; public: void Execute(itk::Object *caller, const itk::EventObject & event) { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event) { const TFilter * filter = dynamic_cast( object ); if( typeid( event ) != typeid( itk::IterationEvent ) ) { return; } std::cout << "Iteration " << filter->GetElapsedIterations() << " (of " << filter->GetMaximumNumberOfIterations() << "): "; std::cout << filter->GetCurrentConvergenceMeasurement() << " (threshold = " << filter->GetConvergenceThreshold() << ")" << std::endl; } }; void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "Two mesh images are specified as input - 1. defines the label mesh. must have a scalar attached to vertices named 'Label'. 2. defines the feature mesh. scalar name is 'Feature'. we put the 2nd mesh's values into the flat space." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "input-mesh" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "[InputMesh1.vtk,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Display the mesh." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "display-mesh" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "[InputMesh1.vtk]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Inflation --- two params : \n 1. BandPass (smaller increases smoothing) -- e.g. 0.001. \n " ) + std::string( "2. number of iterations --- higher increases smoothing. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "inflate" ); option->SetShortName( 'f' ); option->SetUsageOption( 0, "[,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "SegmentationCost --- 3 params : \n 1. Float-Max-Cost : controls the size of the output region. \n " ) + std::string( "2. Float-Weight for distance cost = edge_length*W_d \n " ) + std::string( "3. Float-Weight for label cost = H(fabs( desiredLabel - localLabel ))*W_l*MaxCost \n where H is the heaviside function." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "segmentation-cost" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "e.g. [40,1,0]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The output consists of one (or more) meshes ... 1. the flattened mesh with features mapped. 2. the extracted extrinisic mesh. 3. inflated mesh. "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "[MyFlatMesh.vtk,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Map to a canonical domain : pass square or circle "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "canonical-domain" ); option->SetShortName( 'c' ); option->SetUsageOption( 0, "[domain]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Parameterize the boundary while searching for the boundary (a bit slow and not guaranteed to be doable). " ) + std::string( "If false, we try to parameterize after the searching is done. " ) + std::string( "This option is meaningless if you pass the boundary in as an option. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "param-while-searching" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "0 / 1 " ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Which label (unsigned integer) to flatten. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "label-to-flatten" ); option->SetShortName( 'l' ); option->SetUsageOption( 0, "1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The name of a boundary parameterization file (not implemented)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "boundary-param" ); option->SetShortName( 'b' ); option->SetUsageOption( 0, "[filename.vtk]" ); option->SetDescription( description ); parser->AddOption( option ); } } template int ANTSConformalMapping( itk::ants::CommandLineParser *parser ) { typedef float PixelType; typedef float RealType; typedef itk::Image ImageType; // we define the options in the InitializeCommandLineOptions function // and then use them here ... typedef vtkPolyData MeshType; vtkSmartPointer labelmesh = NULL; vtkSmartPointer featuremesh = NULL; vtkSmartPointer inflatedmesh = NULL; typedef itk::FEMDiscConformalMap ParamType; typename ParamType::Pointer flattener = ParamType::New(); flattener->SetDebug(false); flattener->SetSigma(1); // flattener->SetSurfaceMesh(vtkmesh); // first find out if the user wants to inflate the mesh ... unsigned int inflate_iterations = 0; float inflate_param = 0; typename itk::ants::CommandLineParser::OptionType::Pointer infOption = parser->GetOption( "inflate" ); if( infOption && infOption->GetNumberOfFunctions() ) { if( infOption->GetFunction( 0 )->GetNumberOfParameters() == 2 ) { inflate_param = parser->Convert(infOption->GetParameter( 0 ) ); inflate_iterations = parser->Convert(infOption->GetParameter( 1 ) ); std::cout << " you will inflate before flattening with params " << inflate_param << " applied over " << inflate_iterations << " iterations. " << std::endl; } else { std::cerr << " wrong params for inflation. ignoring. " << std::endl; std::cerr << " " << infOption->GetDescription() << std::endl; return EXIT_FAILURE; } } float maxCost = 40, distCostW = 1, labelCostW = 0; typename itk::ants::CommandLineParser::OptionType::Pointer costOption = parser->GetOption( "segmentation-cost" ); if( costOption && costOption->GetNumberOfFunctions() ) { if( costOption->GetFunction( 0 )->GetNumberOfParameters() == 3 ) { maxCost = parser->Convert(costOption->GetParameter( 0 ) ); distCostW = parser->Convert(costOption->GetParameter( 1 ) ); labelCostW = parser->Convert(costOption->GetParameter( 2 ) ); } else { std::cerr << " wrong params for cost weights. " << std::endl; std::cerr << " " << costOption->GetDescription() << std::endl; return EXIT_FAILURE; } } typename itk::ants::CommandLineParser::OptionType::Pointer displayOption = parser->GetOption( "display-mesh" ); if( displayOption && displayOption->GetNumberOfFunctions() ) { if( displayOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { std::string dispm = displayOption->GetParameter( 0 ); std::cout << " render " << dispm << std::endl; // read the vtk file ... vtkPolyDataReader *fltReader = vtkPolyDataReader::New(); fltReader->SetFileName(dispm.c_str() ); fltReader->Update(); vtkSmartPointer normalGenerator = vtkSmartPointer::New(); normalGenerator->SetInput(fltReader->GetOutput() ); normalGenerator->Update(); vtkRenderer* ren1 = vtkRenderer::New(); vtkRenderWindow* renWin = vtkRenderWindow::New(); renWin->AddRenderer(ren1); vtkRenderWindowInteractor* inter = vtkRenderWindowInteractor::New(); inter->SetRenderWindow(renWin); vtkCallbackCommand *cbc = vtkCallbackCommand::New(); ren1->AddObserver(vtkCommand::KeyPressEvent, cbc); vtkDataSetMapper* mapper = vtkDataSetMapper::New(); mapper->SetInput( normalGenerator->GetOutput() ); mapper->SetScalarRange(0, 255); vtkActor* actor = vtkActor::New(); actor->SetMapper(mapper); ren1->SetViewport(0.0, 0.0, 1.0, 1.0); ren1->AddActor(actor); renWin->Render(); inter->Start(); mapper->Delete(); actor->Delete(); ren1->Delete(); renWin->Delete(); inter->Delete(); fltReader->Delete(); return 0; } } typename itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); /** * Initialization */ typename itk::ants::CommandLineParser::OptionType::Pointer inOption = parser->GetOption( "input-mesh" ); if( inOption && inOption->GetFunction( 0 )->GetNumberOfParameters() == 2 ) { std::string innm = inOption->GetParameter( 0 ); vtkSmartPointer labReader = vtkSmartPointer::New(); labReader->SetFileName(innm.c_str() ); labReader->Update(); labelmesh = vtkSmartPointer( labReader->GetOutput() ); vtkDataArray* labels = labelmesh->GetPointData()->GetArray("Label"); if( !labels ) { std::cerr << " Cannot find vtk Array named 'Label' in " << innm << std::endl; std::cerr << " This could cause problems " << std::endl; // std::cout <<" exiting " << std::endl; // throw std::exception(); } innm = inOption->GetParameter( 1 ); vtkSmartPointer fltReader = vtkSmartPointer::New(); fltReader->SetFileName(innm.c_str() ); fltReader->Update(); featuremesh = vtkSmartPointer( fltReader->GetOutput() ); vtkDataArray* feats = featuremesh->GetPointData()->GetArray("Feature"); if( !feats ) { std::cerr << " Cannot find vtk Array named 'Feature' in " << innm << std::endl; std::cerr << " continuing " << std::endl; } /** inflation */ if( inflate_iterations > 0 ) { vtkSmartPointer smoother = vtkSmartPointer::New(); smoother->SetInput(labelmesh); smoother->SetNumberOfIterations( (int) inflate_iterations ); smoother->BoundarySmoothingOn(); smoother->FeatureEdgeSmoothingOff(); smoother->SetFeatureAngle(180.0); smoother->SetEdgeAngle(180.0); smoother->SetPassBand( inflate_param ); // smaller values increase smoothing smoother->NonManifoldSmoothingOn(); smoother->NormalizeCoordinatesOff(); smoother->Update(); inflatedmesh = vtkSmartPointer(smoother->GetOutput() ); std::cout << " done smoothing " << std::endl; flattener->SetSurfaceMesh(inflatedmesh); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { for( unsigned int p = 0; p < outputOption->GetFunction( 0 )->GetNumberOfParameters(); p++ ) { if( p == 2 && inflatedmesh ) { vtkPolyDataWriter *writer = vtkPolyDataWriter::New(); writer->SetInput(inflatedmesh); std::string outnm = outputOption->GetParameter( 2 ); std::cout << " writing " << outnm << std::endl; writer->SetFileName(outnm.c_str() ); writer->SetFileTypeToBinary(); writer->Update(); } } } } else { flattener->SetSurfaceMesh(labelmesh); } flattener->SetSurfaceFeatureMesh(featuremesh); } bool paramws = parser->template Convert( parser->GetOption( "param-while-searching" )->GetFunction() ); flattener->SetParamWhileSearching(paramws); unsigned int labeltoflatten = parser->template Convert( parser->GetOption( "label-to-flatten" )->GetFunction() ); flattener->SetLabelToFlatten(labeltoflatten); std::string canonicaldomain = parser->GetOption( "canonical-domain" )->GetFunction(); // canonicaldomain=ConvertToLowerCase( canonicaldomain ); std::cout << " you will map label " << labeltoflatten << " to a " << canonicaldomain << std::endl; if( canonicaldomain == std::string("circle") ) { flattener->SetMapToCircle(); } else if( canonicaldomain == std::string("square") ) { flattener->SetMapToSquare(); } else { std::cerr << " that domain is not an option -- exiting. " << std::endl; return EXIT_FAILURE; } // do stuff -- but not implemented yet // flattener->SetDiscBoundaryList(NULL); std::cout << " you will flatten " << labeltoflatten << ". param while searching? " << paramws << std::endl; flattener->SetSigma(1); flattener->SetMaxCost(maxCost); flattener->SetDistanceCostWeight(distCostW); flattener->SetLabelCostWeight(labelCostW); std::cout << " MC " << maxCost << " DW " << distCostW << " LW " << labelCostW << std::endl; flattener->ExtractSurfaceDisc(); std::cout << " begin conformal mapping "; flattener->ConformalMap(); /** * output */ if( outputOption && outputOption->GetNumberOfFunctions() ) { if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { for( unsigned int p = 0; p < outputOption->GetFunction( 0 )->GetNumberOfParameters(); p++ ) { if( p == 0 ) { vtkPolyDataWriter *writer = vtkPolyDataWriter::New(); writer->SetInput(flattener->m_DiskSurfaceMesh); std::string outnm = outputOption->GetParameter( p ); std::cout << " writing " << outnm << std::endl; writer->SetFileName(outnm.c_str() ); writer->SetFileTypeToBinary(); if( flattener->m_DiskSurfaceMesh ) { writer->Update(); } } if( p == 1 ) { vtkPolyDataWriter *writer = vtkPolyDataWriter::New(); writer->SetInput(flattener->m_ExtractedSurfaceMesh); std::string outnm = outputOption->GetParameter( 1 ); std::cout << " writing " << outnm << std::endl; writer->SetFileName(outnm.c_str() ); writer->SetFileTypeToBinary(); if( flattener->m_ExtractedSurfaceMesh ) { writer->Update(); } } if( p == 2 && inflatedmesh ) { vtkPolyDataWriter *writer = vtkPolyDataWriter::New(); writer->SetInput(inflatedmesh); std::string outnm = outputOption->GetParameter( 2 ); std::cout << " writing " << outnm << std::endl; writer->SetFileName(outnm.c_str() ); writer->SetFileTypeToBinary(); writer->Update(); } } } } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ANTSConformalMapping( std::vector args, std::ostream* out_stream = NULL ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ANTSConformalMapping" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "A tool for conformal mapping to various canonical coordinate systems: disc, square " ) + std::string( " operates on 3D vtk triangulated meshes.") + std::string( " Open problems include computation of, consistent orientation of and parameterization of the boundary-condition defining loop. Should we use curve matching ? Knot points? Min distortion? " ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc < 2 || parser->Convert( parser->GetOption( "help" )->GetFunction() ) ) { parser->PrintMenu( std::cout, 5, false ); if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } else if( parser->Convert( parser->GetOption( 'h' )->GetFunction() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } ANTSConformalMapping<3>( parser ); } } // namespace ants ants-2.2.0/Examples/ANTSIntegrateVectorField.cxx000066400000000000000000000400411311104306400215110ustar00rootroot00000000000000 #include "antsUtilities.h" #include "antsAllocImage.h" #include #include "itkVectorIndexSelectionCastImageFilter.h" #include "itkImageRegionIteratorWithIndex.h" #include "vnl/algo/vnl_determinant.h" #include "itkWarpImageFilter.h" #include "itkImageFileWriter.h" #include "itkRescaleIntensityImageFilter.h" #include "vnl/algo/vnl_determinant.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkVectorCurvatureAnisotropicDiffusionImageFilter.h" #include "itkLaplacianRecursiveGaussianImageFilter.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "ReadWriteData.h" namespace ants { template typename TImage::Pointer GetVectorComponent(typename TField::Pointer field, unsigned int index) { // Initialize the Moving to the displacement field typedef TImage ImageType; typename ImageType::Pointer sfield = AllocImage(field); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( field, field->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { typename TField::PixelType v1 = vfIter.Get(); sfield->SetPixel(vfIter.GetIndex(), v1[index]); } return sfield; } template typename TImage::Pointer SmoothImage(typename TImage::Pointer image, float sig) { // find min value typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter(image, image->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { typename TImage::PixelType v1 = vfIter.Get(); if( vnl_math_isnan(v1) ) { vfIter.Set(0); } } typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(sig); filter->SetUseImageSpacingOn(); filter->SetMaximumError(.01f); filter->SetInput(image); filter->Update(); typename TImage::Pointer out = filter->GetOutput(); return out; } template void SmoothDeformation(typename TImage::Pointer vectorimage, float sig) { typedef itk::Vector VectorType; typedef itk::Image ImageType; typename ImageType::Pointer subimgx = GetVectorComponent(vectorimage, 0); subimgx = SmoothImage(subimgx, sig); typename ImageType::Pointer subimgy = GetVectorComponent(vectorimage, 1); subimgy = SmoothImage(subimgy, sig); typename ImageType::Pointer subimgz = GetVectorComponent(vectorimage, 2); subimgz = SmoothImage(subimgz, sig); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType Iterator( vectorimage, vectorimage->GetLargestPossibleRegion().GetSize() ); Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { VectorType vec; vec[0] = subimgx->GetPixel(Iterator.GetIndex() ); vec[1] = subimgy->GetPixel(Iterator.GetIndex() ); vec[2] = subimgz->GetPixel(Iterator.GetIndex() ); Iterator.Set(vec); ++Iterator; } return; } template float IntegrateLength( typename TImage::Pointer gmsurf, typename TImage::Pointer /* thickimage */, typename TImage::IndexType velind, typename TField::Pointer lapgrad, float itime, float starttime, const float deltaTime, typename TInterp::Pointer vinterp, typename TImage::SpacingType spacing, float vecsign, float timesign, float gradsign ) { typedef typename TField::PixelType VectorType; typedef typename TField::PointType DPointType; typedef itk::VectorLinearInterpolateImageFunction DefaultInterpolatorType; VectorType zero; zero.Fill(0); VectorType disp; disp.Fill(0); unsigned int ct = 0; DPointType pointIn1; DPointType pointIn2; typename DefaultInterpolatorType::ContinuousIndexType vcontind; DPointType pointIn3; enum { ImageDimension = TImage::ImageDimension }; typedef typename TImage::IndexType IndexType; unsigned int m_NumberOfTimePoints = 2; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { pointIn1[jj] = velind[jj] * lapgrad->GetSpacing()[jj]; } itime = starttime; bool timedone = false; float totalmag = 0; while( !timedone ) { float scale = 1; // *m_DT[timeind]/m_DS[timeind]; // std::cout << " scale " << scale << std::endl; double itimetn1 = itime - timesign * deltaTime * scale; double itimetn1h = itime - timesign * deltaTime * 0.5 * scale; if( itimetn1h < 0 ) { itimetn1h = 0; } if( itimetn1h > m_NumberOfTimePoints - 1 ) { itimetn1h = m_NumberOfTimePoints - 1; } if( itimetn1 < 0 ) { itimetn1 = 0; } if( itimetn1 > m_NumberOfTimePoints - 1 ) { itimetn1 = m_NumberOfTimePoints - 1; } // first get current position of particle for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { pointIn1[jj] = velind[jj] * lapgrad->GetSpacing()[jj]; } // std::cout << " ind " << index << std::endl; // now index the time varying field at that position. typename DefaultInterpolatorType::OutputType f1; f1.Fill(0); typename DefaultInterpolatorType::OutputType f2; f2.Fill(0); typename DefaultInterpolatorType::OutputType f3; f3.Fill(0); typename DefaultInterpolatorType::OutputType f4; f4.Fill(0); typename DefaultInterpolatorType::ContinuousIndexType Y1; typename DefaultInterpolatorType::ContinuousIndexType Y2; typename DefaultInterpolatorType::ContinuousIndexType Y3; typename DefaultInterpolatorType::ContinuousIndexType Y4; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { pointIn2[jj] = disp[jj] + pointIn1[jj]; vcontind[jj] = pointIn2[jj] / lapgrad->GetSpacing()[jj]; Y1[jj] = vcontind[jj]; Y2[jj] = vcontind[jj]; Y3[jj] = vcontind[jj]; Y4[jj] = vcontind[jj]; } // Y1[ImageDimension]=itimetn1; // Y2[ImageDimension]=itimetn1h; // Y3[ImageDimension]=itimetn1h; // Y4[ImageDimension]=itime; f1 = vinterp->EvaluateAtContinuousIndex( Y1 ); for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { Y2[jj] += f1[jj] * deltaTime * 0.5; } bool isinside = true; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { if( Y2[jj] < 1 || Y2[jj] > lapgrad->GetLargestPossibleRegion().GetSize()[jj] - 2 ) { isinside = false; } } if( isinside ) { f2 = vinterp->EvaluateAtContinuousIndex( Y2 ); } for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { Y3[jj] += f2[jj] * deltaTime * 0.5; } isinside = true; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { if( Y3[jj] < 1 || Y3[jj] > lapgrad->GetLargestPossibleRegion().GetSize()[jj] - 2 ) { isinside = false; } } if( isinside ) { f3 = vinterp->EvaluateAtContinuousIndex( Y3 ); } for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { Y4[jj] += f3[jj] * deltaTime; } isinside = true; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { if( Y4[jj] < 1 || Y4[jj] > lapgrad->GetLargestPossibleRegion().GetSize()[jj] - 2 ) { isinside = false; } } if( isinside ) { f4 = vinterp->EvaluateAtContinuousIndex( Y4 ); } for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { pointIn3[jj] = pointIn2[jj] + gradsign * vecsign * deltaTime / 6.0 * ( f1[jj] + 2.0 * f2[jj] + 2.0 * f3[jj] + f4[jj] ); } VectorType out; float mag = 0, dmag = 0, voxmag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { out[jj] = pointIn3[jj] - pointIn1[jj]; mag += (pointIn3[jj] - pointIn2[jj]) * (pointIn3[jj] - pointIn2[jj]); voxmag += (pointIn3[jj] - pointIn2[jj]) / spacing[jj] * (pointIn3[jj] - pointIn2[jj]) / spacing[jj]; dmag += (pointIn3[jj] - pointIn1[jj]) * (pointIn3[jj] - pointIn1[jj]); disp[jj] = out[jj]; } voxmag = sqrt(voxmag); dmag = sqrt(dmag); totalmag += sqrt(mag); ct++; // if (!propagate) //thislength=dmag;// // thislength += totalmag; itime = itime + deltaTime * timesign; IndexType myind; for( unsigned int qq = 0; qq < ImageDimension; qq++ ) { myind[qq] = (unsigned long)(pointIn3[qq] / spacing[qq] + 0.5); } if( gmsurf->GetPixel(myind) < 1 ) { timedone = true; } if( ct > 1000 ) { std::cout << " stopping b/c exceed 1000 points " << voxmag << std::endl; timedone = true; } if( voxmag < 0.1 ) { timedone = true; } } return totalmag; } template int IntegrateVectorField(int argc, char *argv[]) { typedef float PixelType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::Image ImageType; typedef typename ImageType::SpacingType SpacingType; const float deltaTime = 0.001; float gradstep = 1. / deltaTime; // atof(argv[3])*(-1.0); std::string vectorfn = std::string(argv[1]); std::string roifn = std::string(argv[2]); int argct = 3; argct++; std::string lenoutname = std::string(""); if( argc > argct ) { lenoutname = std::string(argv[argct]); } argct++; if( argc > argct ) { gradstep *= atof(argv[argct]); } argct++; typename ImageType::Pointer ROIimage; ReadImage(ROIimage, roifn.c_str() ); typename ImageType::Pointer thickimage; ReadImage(thickimage, roifn.c_str() ); thickimage->FillBuffer(0); typename DisplacementFieldType::Pointer VECimage; ReadImage(VECimage, vectorfn.c_str() ); SpacingType spacing = ROIimage->GetSpacing(); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType Iterator( ROIimage, ROIimage->GetLargestPossibleRegion().GetSize() ); double timezero = 0; // 1 double timeone = 1; // (s[ImageDimension]-1-timezero); float starttime = timezero; // timezero; float finishtime = timeone; // s[ImageDimension]-1;//timeone; typename DisplacementFieldType::IndexType velind; float timesign = 1.0; if( starttime > finishtime ) { timesign = -1.0; } typedef DisplacementFieldType TimeVaryingVelocityFieldType; typedef typename DisplacementFieldType::PointType DPointType; typedef itk::VectorLinearInterpolateImageFunction DefaultInterpolatorType; typename DefaultInterpolatorType::Pointer vinterp = DefaultInterpolatorType::New(); typedef itk::LinearInterpolateImageFunction ScalarInterpolatorType; VectorType zero; zero.Fill(0); DPointType pointIn1; DPointType pointIn2; typename DefaultInterpolatorType::ContinuousIndexType vcontind; DPointType pointIn3; typedef itk::ImageRegionIteratorWithIndex VIteratorType; VIteratorType VIterator( VECimage, VECimage->GetLargestPossibleRegion().GetSize() ); VIterator.GoToBegin(); while( !VIterator.IsAtEnd() ) { VectorType vec = VIterator.Get(); float mag = 0; for( unsigned int qq = 0; qq < ImageDimension; qq++ ) { mag += vec[qq] * vec[qq]; } mag = sqrt(mag); if( mag > 0 ) { vec = vec / mag; } VIterator.Set(vec * gradstep); ++VIterator; } Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { velind = Iterator.GetIndex(); float itime = starttime; VectorType disp; disp.Fill(0.0); if( ROIimage->GetPixel(velind) == 2 ) { vinterp->SetInputImage(VECimage); float gradsign = -1.0; double vecsign = -1.0; float len1 = IntegrateLength (ROIimage, thickimage, velind, VECimage, itime, starttime, deltaTime, vinterp, spacing, vecsign, gradsign, timesign); gradsign = 1.0; vecsign = 1; const float len2 = IntegrateLength (ROIimage, thickimage, velind, VECimage, itime, starttime, deltaTime, vinterp, spacing, vecsign, gradsign, timesign ); float totalength = len1 + len2; thickimage->SetPixel(velind, totalength); if( (totalength) > 0 ) { std::cout << " len1 " << len1 << " len2 " << len2 << " ind " << velind << std::endl; } } ++Iterator; } WriteImage(thickimage, lenoutname.c_str() ); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ANTSIntegrateVectorField( std::vector args, std::ostream* /*out_stream = NULL*/ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ANTSIntegrateVectorField" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Usage: " << argv[0] << " VecImageIN.nii.gz ROIMaskIN.nii.gz FibersOUT.vtk LengthImageOUT.nii.gz " << std::endl; std::cout << " The vector field should have vectors as voxels , the ROI is an integer image, fibers out will be vtk text files .... " << std::endl; std::cout << " ROI-Mask controls where the integration is performed and the start point region ... " << std::endl; std::cout << " e.g. the brain will have value 1 , the ROI has value 2 , then all starting seed points " << std::endl; std::cout << " for the integration will start in the region labeled 2 and be constrained to the region labeled 1. " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } std::string ifn = std::string(argv[1]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(ifn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(ifn.c_str() ); imageIO->ReadImageInformation(); unsigned int dim = imageIO->GetNumberOfDimensions(); switch( dim ) { case 2: { IntegrateVectorField<2>(argc, argv); } break; case 3: { IntegrateVectorField<3>(argc, argv); } break; case 4: { IntegrateVectorField<4>(argc, argv); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ANTSIntegrateVelocityField.cxx000066400000000000000000000135301311104306400220500ustar00rootroot00000000000000 #include "antsUtilities.h" #include "antsAllocImage.h" #include #include "itkVectorIndexSelectionCastImageFilter.h" #include "itkImageRegionIteratorWithIndex.h" #include "vnl/algo/vnl_determinant.h" #include "itkANTSImageRegistrationOptimizer.h" #include "itkTimeVaryingVelocityFieldIntegrationImageFilter.h" #include "itkWarpImageFilter.h" #include "itkTimeVaryingVelocityFieldTransform.h" #include "itkImageFileWriter.h" #include "itkRescaleIntensityImageFilter.h" #include "vnl/algo/vnl_determinant.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkVectorCurvatureAnisotropicDiffusionImageFilter.h" #include "itkLaplacianRecursiveGaussianImageFilter.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "ReadWriteData.h" namespace ants { template int IntegrateVelocityField(int argc, char *argv[]) { int argct = 1; std::string imgfn = std::string(argv[argct]); argct++; std::string vectorfn = std::string(argv[argct]); argct++; std::string outname = std::string(argv[argct]); argct++; typedef float PixelType; PixelType timezero = 0; PixelType timeone = 1; PixelType dT = 0.01; if( argc > argct ) { timezero = atof(argv[argct]); } argct++; if( argc > argct ) { timeone = atof(argv[argct]); } argct++; if( argc > argct ) { dT = atof(argv[argct]); } argct++; std::cout << " time-0 " << timezero << " dt " << dT << " time-1 " << timeone << std::endl; PixelType starttime = timezero; PixelType finishtime = timeone; typedef float PixelType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::Image TimeVaryingVelocityFieldType; typedef itk::Image ImageType; typename ImageType::Pointer image; ReadImage(image, imgfn.c_str() ); typedef TimeVaryingVelocityFieldType tvt; typename tvt::Pointer timeVaryingVelocity; ReadImage(timeVaryingVelocity, vectorfn.c_str() ); VectorType zero; zero.Fill(0); typename DisplacementFieldType::Pointer deformation = AllocImage(image, zero); if( !timeVaryingVelocity ) { std::cerr << " No TV Field " << std::endl; return EXIT_FAILURE; } if( starttime < 0 ) { starttime = 0; } if( starttime > 1 ) { starttime = 1; } if( finishtime < 0 ) { finishtime = 0; } if( finishtime > 1 ) { finishtime = 1; } typedef itk::TimeVaryingVelocityFieldIntegrationImageFilter IntegratorType; typename IntegratorType::Pointer integrator = IntegratorType::New(); integrator->SetInput( timeVaryingVelocity ); integrator->SetLowerTimeBound( starttime ); integrator->SetUpperTimeBound( finishtime ); integrator->SetNumberOfIntegrationSteps( (unsigned int ) 1 / dT ); integrator->Update(); WriteImage( integrator->GetOutput(), outname.c_str() ); return 0; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ANTSIntegrateVelocityField( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ANTSIntegrateVelocityField" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cerr << "Usage: " << argv[0] << " reference_image VelocityIn.mhd DeformationOut.nii.gz time0 time1 dT " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } std::cout << " start " << std::endl; std::string ifn = std::string(argv[1]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(ifn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(ifn.c_str() ); imageIO->ReadImageInformation(); unsigned int dim = imageIO->GetNumberOfDimensions(); std::cout << " dim " << dim << std::endl; switch( dim ) { case 2: { IntegrateVelocityField<2>(argc, argv); } break; case 3: { IntegrateVelocityField<3>(argc, argv); } break; case 4: { IntegrateVelocityField<4>(argc, argv); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ANTSJacobian.cxx000066400000000000000000000444411311104306400171560ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include #include #include #include #include #include #include "itkVectorIndexSelectionCastImageFilter.h" #include "itkImageRegionIteratorWithIndex.h" #include "vnl/algo/vnl_determinant.h" #include "ReadWriteData.h" #include "vnl/algo/vnl_determinant.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkVectorCurvatureAnisotropicDiffusionImageFilter.h" #include "itkMatrixOffsetTransformBase.h" #include "itkWarpImageMultiTransformFilter.h" namespace ants { template typename TImage::Pointer VectorAniDiff(typename TImage::Pointer img, unsigned int iters) { double timeStep = 0.065; typedef TImage VectorImageType; typedef itk::VectorCurvatureAnisotropicDiffusionImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( img ); filter->SetNumberOfIterations( iters ); filter->SetTimeStep( timeStep ); filter->SetConductanceParameter(1.0); filter->Update(); // Software Guide : EndCodeSnippet return filter->GetOutput(); } template typename TImage::Pointer GenerateGridImage(TImage* img, unsigned int gridsize) { typedef TImage ImageType; enum { ImageDimension = TImage::ImageDimension }; itk::ImageRegionIteratorWithIndex wimIter( img, img->GetLargestPossibleRegion() ); wimIter.GoToBegin(); for( ; !wimIter.IsAtEnd(); ++wimIter ) { wimIter.Set(2); } wimIter.GoToBegin(); for( ; !wimIter.IsAtEnd(); ++wimIter ) { typename ImageType::IndexType ind = wimIter.GetIndex(); for( int i = 0; i < 2; i++ ) { if( ind[i] % gridsize == 0 ) { wimIter.Set(0); } // if (ind[i] % (gridsize+1) == 0) wimIter.Set(0);// tartan } } wimIter.GoToBegin(); for( ; !wimIter.IsAtEnd(); ++wimIter ) { typename ImageType::IndexType ind = wimIter.GetIndex(); typename ImageType::IndexType ind2 = wimIter.GetIndex(); for( int i = 0; i < 2; i++ ) { ind2[i] = ind[i] - 1; if( ind2[i] < 0 ) { ind2[i] = 0; } } for( int i = 0; i < 2; i++ ) { // this creates a 3-d effect // if (ind[i] % gridsize == 0) img->SetPixel(ind2,2); // this gives double thickness if( ind[i] % gridsize == 0 ) { img->SetPixel(ind2, 0); } } } return img; } template typename ImageType::Pointer ReadAnImage(char* fn) { // Read the image files begin typedef itk::ImageFileReader FileSourceType; typename FileSourceType::Pointer reffilter = FileSourceType::New(); reffilter->SetFileName( fn ); try { reffilter->Update(); } catch( ... ) { return ITK_NULLPTR; } return reffilter->GetOutput(); } template typename TDisplacementField::PixelType TransformVector(TDisplacementField* field, typename TImage::IndexType index ) { enum { ImageDimension = TImage::ImageDimension }; typename TDisplacementField::PixelType vec = field->GetPixel(index); /* buggy code from before */ typename TDisplacementField::PixelType newvec; newvec.Fill(0); for( unsigned int row = 0; row < ImageDimension; row++ ) { for( unsigned int col = 0; col < ImageDimension; col++ ) { newvec[row] += vec[col] * field->GetDirection()[row][col]; } } return newvec; } template typename TDisplacementField::PixelType ProjectVector(typename TDisplacementField::PixelType invec, typename TDisplacementField::PixelType projvec ) { enum { ImageDimension = TImage::ImageDimension }; typename TDisplacementField::PixelType newvec; double ip = 0; for( unsigned int i = 0; i < ImageDimension; i++ ) { ip += invec[i] * projvec[i]; } for( unsigned int i = 0; i < ImageDimension; i++ ) { newvec[i] = ip * projvec[i]; } return newvec; } void antsjacobiansplit(const std::string& s, char c, std::vector& v) { std::string::size_type i = 0; std::string::size_type j = s.find(c); while( j != std::string::npos ) { v.push_back(s.substr(i, j - i) ); i = ++j; j = s.find(c, j); if( j == std::string::npos ) { v.push_back(s.substr(i, s.length() ) ); } } } template void ComputeJacobian(TDisplacementField* field, char* fnm, char* maskfn, bool uselog = false, bool norm = false, std::string projvec = "") { std::vector v; if( projvec.length() > 2 ) { antsjacobiansplit(projvec, 'x', v); for( std::vector::size_type i = 0; i < v.size(); ++i ) { std::cout << v[i] << '\n'; } } typedef TImage ImageType; typedef TDisplacementField FieldType; enum { ImageDimension = TImage::ImageDimension }; typedef itk::Image FloatImageType; typename FloatImageType::RegionType m_JacobianRegion; typename FloatImageType::Pointer mask = ITK_NULLPTR; typename FieldType::PixelType pvec; if( !v.empty() ) { for( unsigned int i = 0; i < ImageDimension; i++ ) { pvec[i] = atof(v[i].c_str() ); } pvec = pvec / pvec.GetNorm(); std::cout << " using projection vector " << pvec << std::endl; } mask = ReadAnImage(maskfn); if( !field ) { return; } typename TImage::SizeType s = field->GetLargestPossibleRegion().GetSize(); typename TImage::SpacingType sp = field->GetSpacing(); typename FloatImageType::Pointer m_FloatImage = AllocImage(field, 0); if( false ) { typedef itk::MatrixOffsetTransformBase TransformType; typedef itk::WarpImageMultiTransformFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); warper->SetInput(ITK_NULLPTR); warper->SetEdgePaddingValue( 0); warper->SetSmoothScale(1); warper->PushBackDisplacementFieldTransform(field); warper->SetOutputParametersFromImage(field ); warper->Update(); // grid=warper->GetOutput(); typedef itk::ImageFileWriter writertype; typename writertype::Pointer writer = writertype::New(); std::string fng = std::string(fnm) + "grid.nii.gz"; writer->SetFileName(fng.c_str() ); writer->SetInput(ITK_NULLPTR); writer->Write(); std::cout << " Grid done "; } typename FloatImageType::SizeType m_FieldSize = field->GetLargestPossibleRegion().GetSize(); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator wimIter( m_FloatImage, m_FloatImage->GetLargestPossibleRegion() ); wimIter.GoToBegin(); for( ; !wimIter.IsAtEnd(); ++wimIter ) { wimIter.Set(1.0); } typedef vnl_matrix MatrixType; MatrixType jMatrix, idMatrix, avgMatrix; jMatrix.set_size(ImageDimension, ImageDimension); avgMatrix.set_size(ImageDimension, ImageDimension); avgMatrix.fill(0); itk::ImageRegionIteratorWithIndex m_FieldIter( field, field->GetLargestPossibleRegion() ); typename TImage::IndexType rindex; typename TImage::IndexType ddrindex; typename TImage::IndexType ddlindex; typename TImage::IndexType difIndex[ImageDimension][2]; double det = 0.0; unsigned int posoff = 1; float space = 1.0; typename FieldType::PixelType dPix; typename FieldType::PixelType lpix; typename FieldType::PixelType llpix; typename FieldType::PixelType rpix; typename FieldType::PixelType rrpix; typename FieldType::PixelType cpix; // double totaljac=0.0; // /the finite difference equations // float wC, wLL, wL, wR, wRR; // 3rd deriv - 4th order // wC = 0.0; // wLL = 1.; wL = -2.0; wR = 2.0; wRR = -1.0; // 4th deriv - 4th order // wC = -6.0; // wLL = 1.; wL = -4.0; wR = -4.0; wRR = 1.0; // 2nd deriv - 4th order // wC = 30.0; // wLL = -1.0; wL = 16.0; wR = 16.0; wRR = -1.0; unsigned long ct = 0; for( m_FieldIter.GoToBegin(); !m_FieldIter.IsAtEnd(); ++m_FieldIter ) { rindex = m_FieldIter.GetIndex(); float mindist = 1.0; bool oktosample = true; float dist = 100.0; for( unsigned int row = 0; row < ImageDimension; row++ ) { dist = fabs( (float)rindex[row]); if( dist < mindist ) { oktosample = false; } dist = fabs( (float)s[row] - (float)rindex[row]); if( dist < mindist ) { oktosample = false; } } if( oktosample ) { ct++; cpix = TransformVector(field, rindex); if( !v.empty() ) { cpix = ProjectVector(cpix, pvec); } for( unsigned int row = 0; row < ImageDimension; row++ ) { difIndex[row][0] = rindex; difIndex[row][1] = rindex; ddrindex = rindex; ddlindex = rindex; if( (unsigned int) rindex[row] < (unsigned int) m_FieldSize[row] - 2 ) { difIndex[row][0][row] = rindex[row] + posoff; ddrindex[row] = rindex[row] + posoff * 2; } if( rindex[row] > 1 ) { difIndex[row][1][row] = rindex[row] - 1; ddlindex[row] = rindex[row] - 2; } float h = 1; space = 1.0; // should use image spacing here? rpix = TransformVector(field, difIndex[row][1]); if( !v.empty() ) { rpix = ProjectVector(rpix, pvec); } rpix = rpix * h + cpix * (1. - h); lpix = TransformVector(field, difIndex[row][0]); if( !v.empty() ) { lpix = ProjectVector(lpix, pvec); } lpix = lpix * h + cpix * (1. - h); // dPix = ( rpix - lpix)*(1.0)/(2.0); // rrpix = TransformVector(field,ddrindex); // rrpix = rrpix*h+rpix*(1.-h); // llpix = TransformVector(field,ddlindex); // llpix = llpix*h+lpix*(1.-h); // dPix=( rrpix*(-1.0) + rpix*8.0 - lpix*8.0 + lpix )*(-1.0)*space/(12.0*h); //4th order centered // difference dPix = ( lpix - rpix ) * (1.0) * space / (2.0 * h); // 4th order centered difference for( unsigned int col = 0; col < ImageDimension; col++ ) { float val; if( row == col ) { val = dPix[col] / sp[col] + 1.0; } else { val = dPix[col] / sp[col]; } // std::cout << " row " << row << " col " << col << " val " << val << std::endl; jMatrix.put(col, row, val); avgMatrix.put(col, row, avgMatrix.get(col, row) + val); } } // the determinant of the jacobian matrix // std::cout << " get det " << std::endl; det = vnl_determinant(jMatrix); // float prodval = m_FloatImage->GetPixel(rindex); if( det < 0.0 ) { det = 0; } m_FloatImage->SetPixel(rindex, det ); // totaljac+=det; } // oktosample if } if( norm && mask ) { std::cout << " using mask && normalizing " << std::endl; /* typedef itk::DiscreteGaussianImageFilter dgf; float sig=2.0; typename FloatImageType::Pointer temp; { typename dgf::Pointer filter = dgf::New(); filter->SetVariance(sig); filter->SetUseImageSpacingOff(); filter->SetMaximumError(.01f); filter->SetInput(m_FloatImage); filter->Update(); // m_FloatImage=filter->GetOutput(); temp=filter->GetOutput(); } { typename dgf::Pointer filter = dgf::New(); filter->SetVariance(sig); filter->SetUseImageSpacingOff(); filter->SetMaximumError(.01f); filter->SetInput(temp); filter->Update(); // m_FloatImage=filter->GetOutput(); temp=filter->GetOutput(); } */ double total = 0.0; unsigned long _ct = 0; for( m_FieldIter.GoToBegin(); !m_FieldIter.IsAtEnd(); ++m_FieldIter ) { rindex = m_FieldIter.GetIndex(); if( mask->GetPixel(rindex) > 0 ) { total += m_FloatImage->GetPixel(rindex); _ct++; } else { m_FloatImage->SetPixel(rindex, 0); } } total /= (double) _ct; for( m_FieldIter.GoToBegin(); !m_FieldIter.IsAtEnd(); ++m_FieldIter ) { rindex = m_FieldIter.GetIndex(); double val = m_FloatImage->GetPixel(rindex) / total; if( mask->GetPixel(rindex) > 0 ) { m_FloatImage->SetPixel(rindex, val); } else { m_FloatImage->SetPixel(rindex, 0); } } } for( m_FieldIter.GoToBegin(); !m_FieldIter.IsAtEnd(); ++m_FieldIter ) { rindex = m_FieldIter.GetIndex(); double val = m_FloatImage->GetPixel(rindex); if( uselog && val > 0 ) { val = log(val); } else if( uselog && val < 0 ) { val = log(0.01); } if( uselog ) { m_FloatImage->SetPixel(rindex, val); } } typedef itk::ImageFileWriter writertype; typename writertype::Pointer writer = writertype::New(); std::string fn = std::string(fnm) + "jacobian.nii.gz"; if( uselog ) { fn = std::string(fnm) + "logjacobian.nii.gz"; } writer->SetFileName(fn.c_str() ); writer->SetInput(m_FloatImage); writer->Write(); return; } template int Jacobian(int argc, char *argv[]) { // std::cout << " enter " << ImageDimension << std::endl; if( argc < 3 ) { std::cout << "Usage: Jacobian gWarp outfile uselog maskfn normbytotalbool VectorToProjectWarpAgainst " << std::endl; std::cout << " VectorToProjectWarpAgainst should be in the form 1.0x0.0x0.0 where x separates vector components " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } typedef float PixelType; typedef itk::Vector VectorType; typedef itk::Image FieldType; typedef itk::Image ImageType; typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[1] ); reader->Update(); typename FieldType::Pointer gWarp = reader->GetOutput(); bool uselog = false; if( argc > 3 ) { uselog = (bool)atoi(argv[3]); } bool norm = false; if( argc > 5 ) { norm = (bool)atoi(argv[5]); } std::string projvec; if( argc > 6 ) { projvec = std::string(argv[6]); } std::string maskfn = std::string( "ThugLifeIsDeadToMe---2pac" ); if( argc > 4 ) { maskfn = std::string( argv[4] ); } // std::cout << " name "<< argv[2] << " mask " << argv[4] << " norm " << norm << " Log " << uselog << std::endl; ComputeJacobian(gWarp, argv[2], const_cast( maskfn.c_str() ), uselog, norm, projvec); // DiffeomorphicJacobian(gWarp,1,argv[2]); // if (argc > 3) DiffeomorphicMetric(gWarp,argv[2]); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ANTSJacobian( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ANTSJacobian" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); std::cout << "WARNING! " << argv[0] << " may not be working correctly, see CreateJacobianDeterminantImage for an alternative method " << std::endl; // std::cout << "Please use CreateJacobianDeterminantImage " << std::endl; // return EXIT_SUCCESS; if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " ImageDim gWarp outfile uselog maskfn normbytotalbool projectionvector " << std::endl; std::cout << " for example " << std::endl << " ANTSJacobian 3 myWarp.nii Output 1 templatebrainmask.nii 1 1x0 " << std::endl; std::cout << " the last 1 normalizes the jacobian by the total in the mask. use this to adjust for head size. 1x0 will project the warp along direction 1,0 --- don't add this option if you dont want to do this " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { Jacobian<2>(argc - 1, argv + 1); } break; case 3: { Jacobian<3>(argc - 1, argv + 1); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ANTSUseDeformationFieldToGetAffineTransform.cxx000066400000000000000000000406261311104306400253110ustar00rootroot00000000000000/** ANTS Landmarks used to initialize an affine transform ... */ #include "antsUtilities.h" #include #include #include #include "itkLandmarkBasedTransformInitializer.h" #include "itkImage.h" #include "itkImageIOBase.h" #include "itkImageIOFactory.h" #include #include #include "ReadWriteData.h" #include "itkTransformFileWriter.h" #include #include "vnl/algo/vnl_qr.h" #include namespace ants { template void WriteAffineTransformFile(typename TransformType::Pointer & transform, const std::string & filename) { itk::TransformFileWriter::Pointer transform_writer; transform_writer = itk::TransformFileWriter::New(); transform_writer->SetFileName(filename); transform_writer->SetInput(transform); try { transform_writer->Update(); } catch( itk::ExceptionObject & err ) { std::cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl << "Exception in writing tranform file: " << std::endl << filename << std::endl; return; } return; } // ////////////////////////////////////////////////////////////////////// // Stripped from ANTS_affine_registration2.h template inline void PostConversionInAffine(const typename RunningAffineTransformType::Pointer & transform_running, typename AffineTransformType::Pointer & transform) { transform->SetCenter(*(reinterpret_cast (const_cast(&(transform_running-> GetCenter() ) ) ) ) ); transform->SetTranslation(*(reinterpret_cast (const_cast(&(transform_running ->GetTranslation() ) ) ) ) ); transform->SetMatrix(*(reinterpret_cast (const_cast(&(transform_running->GetMatrix() ) ) ) ) ); // std::cout << "transform_running" << transform_running << std::endl; // std::cout << "transform" << transform << std::endl; } template void DumpTransformForANTS3D(const typename TransformA::Pointer & transform, const std::string & ANTS_prefix) { const int ImageDimension = 3; // ANTS transform file type typedef itk::MatrixOffsetTransformBase AffineTransformType; AffineTransformType::Pointer transform_ANTS = AffineTransformType::New(); // typedef TransformAPointer::ObjectType TransformA; // std::cout << " writing " << ANTS_prefix << " affine " << std::endl; // std::string ANTS_affine_filename = ANTS_prefix + std::string( "Affine.txt" ); std::string ANTS_affine_filename = ANTS_prefix; std::cout << " writing ANTS affine file:" << ANTS_affine_filename << std::endl; PostConversionInAffine(transform, transform_ANTS); WriteAffineTransformFile(transform_ANTS, ANTS_affine_filename); } // //////// // x: fixedLandmarks // y: movingLandmarks // (A,t,c) : affine transform, A:3*3, t: 3*1 c: 3*1 (c is the center of all points in x) // y-c = A*(x-c) + t; // steps: // 1. c = average of points of x // 2. let y1 = y-c; x1 = x - c; x11 = [x1; 1 ... 1] // extend x11 // 3. minimize (y1-A1*x11)^2, A1 is a 3*4 matrix // 4. A = A1(1:3, 1:3), t = A1(1:3, 4); // step 3: // A11 = (y1*x11')*(x11*x11')^(-1) // type info: // assume PointContainerType is std::vector // assume TrnasformPointerType is MatrixOffsetTransformBase template void GetAffineTransformFromTwoPointSets3D(PointContainerType & fixedLandmarks, PointContainerType & movingLandmarks, typename TransformType::Pointer & transform) { const int Dim = 3; int n = fixedLandmarks.size(); vnl_matrix y(Dim, n), x(Dim, n); for( int i = 0; i < n; i++ ) { for( int j = 0; j < Dim; j++ ) { x(j, i) = fixedLandmarks[i][j]; y(j, i) = movingLandmarks[i][j]; } } vnl_vector c(Dim); for( int j = 0; j < Dim; j++ ) { c[j] = x.get_row(j).mean(); } vnl_matrix y1(Dim, n), x11(Dim + 1, n); for( int i = 0; i < n; i++ ) { y1.set_column(i, y.get_column(i) - c); vnl_vector x_tmp(Dim), x1_tmp(Dim + 1); x_tmp = x.get_column(i) - c; for( int j = 0; j < Dim; j++ ) { x1_tmp[j] = x_tmp[j]; } x1_tmp[Dim] = 1; x11.set_column(i, x1_tmp); } vnl_matrix A11(Dim, Dim + 1); vnl_matrix x11t = x11.transpose(); // vnl_matrix_inverse tmp(x11 * x11t); // BA -- removed this -- not used? vnl_svd qr( x11t ); // can use vnl_qr A11 = qr.inverse() * (y1.transpose() ); A11 = A11.transpose(); vnl_matrix A(Dim, Dim); A = A11.extract(Dim, Dim, 0, 0); // std::cout << "y=" << y << std::endl; // std::cout << "x=" << x << std::endl; // // std::cout << "y1=" << y1 << std::endl; // std::cout << "x11=" << x11 << std::endl; std::cout << "A11=" << A11 << std::endl; vnl_vector t = A11.get_column(Dim); typedef typename TransformType::InputPointType PointType; typedef typename TransformType::OutputVectorType VectorType; typedef typename TransformType::MatrixType MatrixType; PointType center; for( int i = 0; i < Dim; i++ ) { center[i] = c[i]; } VectorType translation; for( int i = 0; i < Dim; i++ ) { translation[i] = t[i]; } MatrixType matrix(A); transform->SetCenter(center); transform->SetTranslation(translation); transform->SetMatrix(matrix); return; } template void GetRigidTransformFromTwoPointSets3D(PointContainerType & fixedLandmarks, PointContainerType & movingLandmarks, typename TTransform::Pointer & aff) { // Set the transform type.. typedef itk::VersorRigid3DTransform TransformType; TransformType::Pointer transform = TransformType::New(); typedef float PixelType; const unsigned int Dimension = 3; typedef itk::Image FixedImageType; typedef itk::Image MovingImageType; typedef itk::LandmarkBasedTransformInitializer TransformInitializerType; TransformInitializerType::Pointer initializer = TransformInitializerType::New(); initializer->SetFixedLandmarks(fixedLandmarks); initializer->SetMovingLandmarks(movingLandmarks); initializer->SetTransform( transform ); initializer->InitializeTransform(); std::cout << "rigid: " << transform << std::endl; // ANTS transform file type // typedef itk::MatrixOffsetTransformBase< double, Dimension, Dimension > AffineTransformType; // typename AffineTransformType::Pointer aff = AffineTransformType::New(); PostConversionInAffine(transform, aff); } template void FetchLandmarkMappingFromDisplacementField(const std::string& deformation_field_file_name, float load_ratio, PointContainerType & fixedLandmarks, PointContainerType & movingLandmarks, typename itk::Image::Pointer maskimg) { const unsigned int ImageDimension = 3; typedef typename PointContainerType::value_type PointType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::ImageFileReader FieldReaderType; typename FieldReaderType::Pointer field_reader = FieldReaderType::New(); field_reader->SetFileName( deformation_field_file_name ); field_reader->Update(); typename DisplacementFieldType::Pointer field = field_reader->GetOutput(); fixedLandmarks.clear(); movingLandmarks.clear(); unsigned int nb_voxels = 1; itk::Size field_size = field->GetLargestPossibleRegion().GetSize(); for( unsigned int i = 0; i < ImageDimension; i++ ) { nb_voxels *= field_size[i]; } // float load_ratio = 0.01; unsigned int nb_try_to_load = (unsigned int) ( (float) nb_voxels * load_ratio); std::cout << "trying to load " << nb_try_to_load << " from " << nb_voxels << " points." << std::endl; fixedLandmarks.reserve(nb_try_to_load ); movingLandmarks.reserve(nb_try_to_load ); typedef itk::ImageRegionIteratorWithIndex FieldIteratorType; FieldIteratorType it(field, field->GetLargestPossibleRegion() ); srand( time(ITK_NULLPTR) ); it.GoToBegin(); unsigned int cnt = 0; for( ; (!it.IsAtEnd() ) & (cnt < nb_try_to_load); ++it ) { bool getpoint = true; if( maskimg ) { if( maskimg->GetPixel( it.GetIndex() ) < 0.5 ) { getpoint = false; } } if( getpoint ) { if( rand() % 32767 > load_ratio * 32767 ) { continue; } PointType point1, point2; // get the output image index typename DisplacementFieldType::IndexType index = it.GetIndex(); field->TransformIndexToPhysicalPoint(index, point1 ); VectorType displacement = field->GetPixel(index); for( unsigned int j = 0; j < ImageDimension; j++ ) { point2[j] = point1[j] + displacement[j]; } fixedLandmarks.push_back(point1); movingLandmarks.push_back(point2); ++cnt; } } std::cout << "total " << cnt << " points loaded from " << deformation_field_file_name << "." << std::endl; std::cout << fixedLandmarks.size() << std::endl; std::cout << movingLandmarks.size() << std::endl; return; } // // The test specifies a bunch of fixed and moving landmarks and test if the // fixed landmarks after transform by the computed transform coincides // with the moving landmarks.... int DisplacementFieldBasedTransformInitializer3D(int argc, char * argv[]) { const unsigned int Dim = 3; typedef itk::Point PointType; typedef itk::Image ImageType; typedef std::vector PointContainerType; const char *deformation_field_file_name = argv[1]; float load_ratio = atof(argv[2]); bool bRigid = (strcmp(argv[3], "rigid") == 0); std::string ANTS_prefix(argv[4]); std::string maskfn = std::string(""); if( argc > 5 ) { maskfn = std::string(argv[5]); } std::cout << " mask " << maskfn << std::endl; // input PointContainerType fixedLandmarks, movingLandmarks; // output typedef itk::MatrixOffsetTransformBase AffineTransformType; AffineTransformType::Pointer aff = AffineTransformType::New(); ImageType::Pointer maskimg = ITK_NULLPTR; if( maskfn.length() > 4 ) { ReadImage(maskimg, maskfn.c_str() ); } FetchLandmarkMappingFromDisplacementField(deformation_field_file_name, load_ratio, fixedLandmarks, movingLandmarks, maskimg); if( bRigid ) { GetRigidTransformFromTwoPointSets3D(fixedLandmarks, movingLandmarks, aff); } else { GetAffineTransformFromTwoPointSets3D(fixedLandmarks, movingLandmarks, aff); } std::cout << "affine:" << aff; DumpTransformForANTS3D(aff, ANTS_prefix); return EXIT_SUCCESS; // initializer->SetFixedLandmarks(fixedLandmarks); // initializer->SetMovingLandmarks(movingLandmarks); // initializer->SetTransform( transform ); // initializer->InitializeTransform(); // // transform->Print(std::cout); // // // transform the transform to ANTS format // std::string ANTS_prefix(argv[4]); // // // typedef itk::MatrixOffsetTransformBase< double, 3, 3> AffineTransformType; // AffineTransformType::Pointer aff = AffineTransformType::New(); // GetAffineTransformFromTwoPointSets3D(fixedLandmarks, movingLandmarks, aff); // std::cout << "affine:" << aff; // // // if (bRigid) // DumpTransformForANTS3D(transform, ANTS_prefix); // else // DumpTransformForANTS3D(aff, ANTS_prefix); // // // return EXIT_SUCCESS; } // ////////////////////////////////////////////////////////////////////// // Stripped from ANTS_affine_registration2.h int DisplacementFieldBasedTransformInitializer2D(int, char * []) { std::cout << " not implemented " << std::endl; return 1; /* typedef float PixelType; const unsigned int Dimension = 2; typedef itk::Image< PixelType, Dimension > FixedImageType; typedef itk::Image< PixelType, Dimension > MovingImageType; typedef itk::Image< PixelType, Dimension > ImageType; typename FixedImageType::Pointer fixedimage; typename MovingImageType::Pointer movingimage; ReadImage(fixedimage,argv[1]); ReadImage(movingimage,argv[2]); // Set the transform type.. typedef itk::Rigid2DTransform< double > TransformType; */ return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ANTSUseDeformationFieldToGetAffineTransform( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ANTSUseDeformationFieldToGetAffineTransform" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " zzzWarp.nii.gz load_ratio(ex: 0.01) [rigid | affine] OutAffine.txt [mask.nii.gz]" << std::endl; std::cout << " we expect the input deformation field in the same physical space as the images you want to " << std::endl; std::cout << "load_ratio: ratio of points to be loaded from deformation field (to save memory) " << std::endl; std::cout << " the mask gives the region from which points will be selected ... " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension // std::string fn = std::string(argv[1]) + "xvec.nii.gz"; // itk::ImageIOBase::Pointer imageIO = // itk::ImageIOFactory::CreateImageIO(fn.c_str(), itk::ImageIOFactory::ReadMode); // imageIO->SetFileName(fn.c_str()); // imageIO->ReadImageInformation(); int dim = 3; // switch ( imageIO->GetNumberOfDimensions() ) switch( dim ) { case 2: { DisplacementFieldBasedTransformInitializer2D(argc, argv); } break; case 3: { DisplacementFieldBasedTransformInitializer3D(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ANTSUseLandmarkImagesToGetAffineTransform.cxx000066400000000000000000000366441311104306400247620ustar00rootroot00000000000000/** ANTS Landmarks used to initialize an affine transform ... */ #include "antsUtilities.h" #include #include "itkLandmarkBasedTransformInitializer.h" #include "itkImage.h" #include "itkImageIOBase.h" #include "itkImageIOFactory.h" #include #include #include "ReadWriteData.h" #include "itkTransformFileWriter.h" #include #include "vnl/algo/vnl_qr.h" namespace ants { // ////////////////////////////////////////////////////////////////////// // Stripped from ANTS_affine_registration2.h template void WriteAffineTransformFile(typename TransformType::Pointer & transform, const std::string & filename) { itk::TransformFileWriter::Pointer transform_writer; transform_writer = itk::TransformFileWriter::New(); transform_writer->SetFileName(filename); transform_writer->SetInput(transform); try { transform_writer->Update(); } catch( itk::ExceptionObject & err ) { std::cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl << "Exception in writing tranform file: " << std::endl << filename << std::endl; return; } return; } // ////////////////////////////////////////////////////////////////////// // Stripped from ANTS_affine_registration2.h template inline void PostConversionInAffine(RunningAffineTransformPointerType& transform_running, AffineTransformPointerType & transform) { typedef typename RunningAffineTransformPointerType::ObjectType RunningAffineTransformType; typedef typename AffineTransformPointerType::ObjectType AffineTransformType; transform->SetCenter(*(reinterpret_cast (const_cast(&(transform_running-> GetCenter() ) ) ) ) ); transform->SetTranslation(*(reinterpret_cast (const_cast(&(transform_running ->GetTranslation() ) ) ) ) ); transform->SetMatrix(*(reinterpret_cast (const_cast(&(transform_running->GetMatrix() ) ) ) ) ); // std::cout << "transform_running" << transform_running << std::endl; // std::cout << "transform" << transform << std::endl; } template void DumpTransformForANTS3D(typename TransformA::Pointer & transform, const std::string & ANTS_prefix) { // ANTS transform file type typedef itk::AffineTransform AffineTransformType; typename AffineTransformType::Pointer transform_ANTS = AffineTransformType::New(); std::string ANTS_affine_filename = ANTS_prefix; PostConversionInAffine(transform, transform_ANTS); WriteAffineTransformFile(transform_ANTS, ANTS_affine_filename); } // //////// // x: fixedLandmarks // y: movingLandmarks // (A,t,c) : affine transform, A:3*3, t: 3*1 c: 3*1 (c is the center of all points in x) // y-c = A*(x-c) + t; // steps: // 1. c = average of points of x // 2. let y1 = y-c; x1 = x - c; x11 = [x1; 1 ... 1] // extend x11 // 3. minimize (y1-A1*x11)^2, A1 is a 3*4 matrix // 4. A = A1(1:3, 1:3), t = A1(1:3, 4); // step 3: // A11 = (y1*x11')*(x11*x11')^(-1) // type info: // assume PointContainerType is std::vector // assume TrnasformPointerType is MatrixOffsetTransformBase template void GetAffineTransformFromTwoPointSets(PointContainerType & fixedLandmarks, PointContainerType & movingLandmarks, typename TransformType::Pointer & transform) { int n = fixedLandmarks.size(); vnl_matrix y(Dim, n), x(Dim, n); for( int i = 0; i < n; i++ ) { for( unsigned int j = 0; j < Dim; j++ ) { x(j, i) = fixedLandmarks[i][j]; y(j, i) = movingLandmarks[i][j]; } } vnl_vector c(Dim); for( unsigned int j = 0; j < Dim; j++ ) { c[j] = x.get_row(j).mean(); } vnl_matrix y1(Dim, n), x11(Dim + 1, n); for( int i = 0; i < n; i++ ) { y1.set_column(i, y.get_column(i) - c); vnl_vector x_tmp(Dim), x1_tmp(Dim + 1); x_tmp = x.get_column(i) - c; for( unsigned int j = 0; j < Dim; j++ ) { x1_tmp[j] = x_tmp[j]; } x1_tmp[Dim] = 1; x11.set_column(i, x1_tmp); } vnl_matrix A11(Dim, Dim + 1); vnl_matrix x11t = x11.transpose(); vnl_svd qr( x11t ); // can use vnl_qr A11 = qr.inverse() * (y1.transpose() ); A11 = A11.transpose(); vnl_matrix A(Dim, Dim); A = A11.extract(Dim, Dim, 0, 0); std::cout << "y=" << y << std::endl; std::cout << "x=" << x << std::endl; std::cout << "y1=" << y1 << std::endl; std::cout << "x11=" << x11 << std::endl; std::cout << "A11=" << A11 << std::endl; vnl_vector t = A11.get_column(Dim); typedef typename TransformType::InputPointType PointType; typedef typename TransformType::OutputVectorType VectorType; typedef typename TransformType::MatrixType MatrixType; PointType center; for( unsigned int i = 0; i < Dim; i++ ) { center[i] = c[i]; } VectorType translation; for( unsigned int i = 0; i < Dim; i++ ) { translation[i] = t[i]; } MatrixType matrix(A); transform->SetCenter(center); transform->SetTranslation(translation); transform->SetMatrix(matrix); return; } // // The test specifies a bunch of fixed and moving landmarks and test if the // fixed landmarks after transform by the computed transform coincides // with the moving landmarks.... template int LandmarkBasedTransformInitializerBA(int, char * argv[]) { typedef float PixelType; typedef itk::Image FixedImageType; typedef itk::Image MovingImageType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; typename ImageType::Pointer fixedimage; typename ImageType::Pointer movingimage; ReadImage(fixedimage, argv[1]); ReadImage(movingimage, argv[2]); bool bRigid = (strcmp(argv[3], "rigid") == 0); /** get all of the relevant labels in the fixed image and moving image */ typedef std::vector LabelSetType; LabelSetType myFixLabelSet; LabelSetType myMovLabelSet; /** count the labels in the image */ Iterator It( fixedimage, fixedimage->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( fabs(label) > 0 ) { if( find( myFixLabelSet.begin(), myFixLabelSet.end(), label ) == myFixLabelSet.end() ) { // std::cout <<" f-label " << label << std::endl; myFixLabelSet.push_back( label ); } } } Iterator ItM( movingimage, movingimage->GetLargestPossibleRegion() ); for( ItM.GoToBegin(); !ItM.IsAtEnd(); ++ItM ) { PixelType label = ItM.Get(); if( fabs(label) > 0 ) { if( find( myMovLabelSet.begin(), myMovLabelSet.end(), label ) == myMovLabelSet.end() ) { // std::cout <<" m-label " << label << std::endl; myMovLabelSet.push_back( label ); } } } std::sort(myFixLabelSet.begin(), myFixLabelSet.end() ); std::sort(myMovLabelSet.begin(), myMovLabelSet.end() ); LabelSetType::const_iterator fit; LabelSetType::const_iterator mit = myMovLabelSet.begin(); for( fit = myFixLabelSet.begin(); fit != myFixLabelSet.end(); ++fit ) { float fixlabel = *fit; float movlabel = *mit; std::cout << " fix-label " << fixlabel << " movlabel " << movlabel << std::endl; if( movlabel != fixlabel ) { std::cout << " labels do not match -- exiting " << std::endl; exit(1); } ++mit; } // Set the transform type.. typedef itk::AffineTransform TransformType; typename TransformType::Pointer transform = TransformType::New(); typedef itk::LandmarkBasedTransformInitializer TransformInitializerType; typename TransformInitializerType::Pointer initializer = TransformInitializerType::New(); // Set fixed and moving landmarks typedef typename TransformInitializerType::LandmarkPointContainer PointsContainerType; PointsContainerType fixedLandmarks; PointsContainerType movingLandmarks; // compute the CoM's of all the landmarks typename ImageType::SpacingType spacing = fixedimage->GetSpacing(); for( fit = myFixLabelSet.begin(); fit != myFixLabelSet.end(); ++fit ) { float currentlabel = *fit; float totalct = 0; typename TransformInitializerType::LandmarkPointType myCenterOfMass; myCenterOfMass.Fill(0); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( fabs( label - currentlabel ) < 0.001 ) { totalct++; // compute center of mass typename ImageType::PointType point; fixedimage->TransformIndexToPhysicalPoint(It.GetIndex(), point); for( unsigned int i = 0; i < spacing.Size(); i++ ) { myCenterOfMass[i] += point[i]; } std::cout << " point " << point << std::endl; } } for( unsigned int i = 0; i < spacing.Size(); i++ ) { myCenterOfMass[i] /= (float)totalct; } // std::cout << " pushing-fix " << myCenterOfMass << std::endl; fixedLandmarks.push_back( myCenterOfMass ); } // compute the CoM's of all the landmarks spacing = movingimage->GetSpacing(); for( mit = myMovLabelSet.begin(); mit != myMovLabelSet.end(); ++mit ) { float currentlabel = *mit; float totalct = 0; typename TransformInitializerType::LandmarkPointType myCenterOfMass; myCenterOfMass.Fill(0); for( ItM.GoToBegin(); !ItM.IsAtEnd(); ++ItM ) { PixelType label = ItM.Get(); if( label == currentlabel ) { totalct++; // compute center of mass typename ImageType::PointType point; movingimage->TransformIndexToPhysicalPoint(ItM.GetIndex(), point); for( unsigned int i = 0; i < spacing.Size(); i++ ) { myCenterOfMass[i] += point[i]; } } } for( unsigned int i = 0; i < spacing.Size(); i++ ) { myCenterOfMass[i] /= (float)totalct; } // std::cout << " pushing-mov " << myCenterOfMass << std::endl; movingLandmarks.push_back( myCenterOfMass ); } typename TransformInitializerType::PointsContainerConstIterator fitr = fixedLandmarks.begin(); typename TransformInitializerType::PointsContainerConstIterator mitr = movingLandmarks.begin(); while( mitr != movingLandmarks.end() ) { std::cout << " Fixed Landmark: " << *fitr << " Moving landmark " << *mitr << std::endl; ++fitr; ++mitr; } initializer->SetFixedLandmarks(fixedLandmarks); initializer->SetMovingLandmarks(movingLandmarks); // initializer->SetFixedImage( fixedimage ); // initializer->SetMovingImage( movingimage ); initializer->SetTransform( transform ); initializer->InitializeTransform(); transform->Print(std::cout); // to transform a point // transform->TransformPoint( *fitr ) << std::endl; // transform the transform to ANTS format std::string ANTS_prefix(argv[4]); typedef itk::AffineTransform AffineTransformType; typename AffineTransformType::Pointer aff = AffineTransformType::New(); GetAffineTransformFromTwoPointSets(fixedLandmarks, movingLandmarks, aff); std::cout << "affine:" << aff; if( bRigid ) { DumpTransformForANTS3D(transform, ANTS_prefix); } else { DumpTransformForANTS3D(aff, ANTS_prefix); } return EXIT_SUCCESS; } int ANTSUseLandmarkImagesToGetAffineTransform( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ANTSUseLandmarkImagesToGetAffineTransform" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " FixedImageWithLabeledLandmarks.nii.gz MovingImageWithLabeledLandmarks.nii.gz [rigid | affine] OutAffine.txt " << std::endl; std::cout << " we expect the input images to be (1) N-ary (2) in the same physical space as the images you want to " << std::endl; std::cout << " register and (3 ) to have the same landmark points defined within them ... " << std::endl; std::cout << " landmarks will be defined from the center of mass of the labels in the input images . " << std::endl; std::cout << " You can use ITK-snap to generate the label images. " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension std::string fn = std::string(argv[1]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); switch( imageIO->GetNumberOfDimensions() ) { case 2: { LandmarkBasedTransformInitializerBA<2>(argc, argv); } break; case 3: { LandmarkBasedTransformInitializerBA<3>(argc, argv); } break; default: std::cerr << "Unsupported dimension" << std::endl; exit( EXIT_FAILURE ); } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ANTSUseLandmarkImagesToGetBSplineDisplacementField.cxx000066400000000000000000000446751311104306400265320ustar00rootroot00000000000000/** ANTS Landmarks used to initialize an b-spline displacement field ... */ #include "antsUtilities.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkContinuousIndex.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkImportImageFilter.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkPointSet.h" #include #include namespace ants { template int LandmarkBasedDisplacementFieldTransformInitializer( int argc, char *argv[] ) { typedef float RealType; typedef unsigned int LabelType; typedef itk::Image LabelImageType; typedef itk::ImageFileReader ImageReaderType; typename ImageReaderType::Pointer fixedReader = ImageReaderType::New(); fixedReader->SetFileName( argv[1] ); fixedReader->Update(); typename LabelImageType::Pointer fixedImage = fixedReader->GetOutput(); typename LabelImageType::DirectionType fixedDirection = fixedImage->GetDirection(); typename LabelImageType::DirectionType fixedDirectionInverse( fixedDirection.GetInverse() ); typename LabelImageType::DirectionType identityDirection; identityDirection.SetIdentity(); const typename LabelImageType::RegionType & bufferedRegion = fixedImage->GetBufferedRegion(); const itk::SizeValueType numberOfPixels = bufferedRegion.GetNumberOfPixels(); const bool filterHandlesMemory = false; typedef itk::ImportImageFilter ImporterType; typename ImporterType::Pointer importer = ImporterType::New(); importer->SetImportPointer( const_cast( fixedImage->GetBufferPointer() ), numberOfPixels, filterHandlesMemory ); importer->SetRegion( fixedImage->GetBufferedRegion() ); importer->SetOrigin( fixedImage->GetOrigin() ); importer->SetSpacing( fixedImage->GetSpacing() ); importer->SetDirection( identityDirection ); importer->Update(); const typename ImporterType::OutputImageType * parametricInputImage = importer->GetOutput(); typename ImageReaderType::Pointer movingReader = ImageReaderType::New(); movingReader->SetFileName( argv[2] ); movingReader->Update(); typename LabelImageType::Pointer movingImage = movingReader->GetOutput(); /////////////////////////////////////////////////////////////////////////////////////////// typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::PointSet PointSetType; typename PointSetType::Pointer fixedPoints = PointSetType::New(); fixedPoints->Initialize(); std::vector fixedLabels; itk::ImageRegionIteratorWithIndex ItF( fixedImage, fixedImage->GetLargestPossibleRegion() ); unsigned int fixedCount = 0; for( ItF.GoToBegin(); !ItF.IsAtEnd(); ++ItF ) { if( ItF.Get() != 0 ) { if( std::find( fixedLabels.begin(), fixedLabels.end(), ItF.Get() ) == fixedLabels.end() ) { fixedLabels.push_back( ItF.Get() ); } typename PointSetType::PointType fixedPoint; fixedImage->TransformIndexToPhysicalPoint( ItF.GetIndex(), fixedPoint ); fixedPoints->SetPointData( fixedCount, ItF.Get() ); fixedPoints->SetPoint( fixedCount++, fixedPoint ); } } std::sort( fixedLabels.begin(), fixedLabels.end() ); typename PointSetType::Pointer movingPoints = PointSetType::New(); movingPoints->Initialize(); std::vector movingLabels; itk::ImageRegionIteratorWithIndex ItM( movingImage, movingImage->GetLargestPossibleRegion() ); unsigned int movingCount = 0; for( ItM.GoToBegin(); !ItM.IsAtEnd(); ++ItM ) { if( ItM.Get() != 0 ) { if( std::find( movingLabels.begin(), movingLabels.end(), ItM.Get() ) == movingLabels.end() ) { movingLabels.push_back( ItM.Get() ); } typename PointSetType::PointType movingPoint; movingImage->TransformIndexToPhysicalPoint( ItM.GetIndex(), movingPoint ); movingPoints->SetPointData( movingCount, ItM.Get() ); movingPoints->SetPoint( movingCount++, movingPoint ); } } std::sort( movingLabels.begin(), movingLabels.end() ); // Get moving center points typename PointSetType::Pointer movingCenters = PointSetType::New(); movingCenters->Initialize(); for( unsigned int n = 0; n < movingLabels.size(); n++ ) { LabelType currentLabel = movingLabels[n]; typename PointSetType::PointType center; center.Fill( 0 ); float N = 0; typename PointSetType::PointsContainerConstIterator ItP = movingPoints->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator ItD = movingPoints->GetPointData()->Begin(); while( ItP != movingPoints->GetPoints()->End() ) { if( ItD.Value() == currentLabel ) { typename PointSetType::PointType point = ItP.Value(); for( unsigned int d = 0; d < ImageDimension; d++ ) { center[d] += point[d]; } N += 1.0; } ++ItP; ++ItD; } for( unsigned int d = 0; d < ImageDimension; d++ ) { center[d] /= N; } movingCenters->SetPoint( n, center ); movingCenters->SetPointData( n, currentLabel ); } // Get fixed center points typename PointSetType::Pointer fixedCenters = PointSetType::New(); fixedCenters->Initialize(); for( unsigned int n = 0; n < fixedLabels.size(); n++ ) { LabelType currentLabel = fixedLabels[n]; typename PointSetType::PointType center; center.Fill( 0 ); float N = 0; typename PointSetType::PointsContainerConstIterator ItP = fixedPoints->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator ItD = fixedPoints->GetPointData()->Begin(); while( ItP != fixedPoints->GetPoints()->End() ) { if( ItD.Value() == currentLabel ) { typename PointSetType::PointType point = ItP.Value(); for( unsigned int d = 0; d < ImageDimension; d++ ) { center[d] += point[d]; } N += 1.0; } ++ItP; ++ItD; } for( unsigned int d = 0; d < ImageDimension; d++ ) { center[d] /= N; } fixedCenters->SetPoint( n, center ); fixedCenters->SetPointData( n, currentLabel ); } if( fixedCenters->GetNumberOfPoints() != movingCenters->GetNumberOfPoints() ) { std::cerr << "The number of fixed points and moving points must be the same." << std::endl; return EXIT_FAILURE; } // Read in the optional label weights std::vector labelWeights; std::vector userLabels; bool useWeights = false; unsigned int labelCount = 0; if( argc > 8 ) { useWeights = true; std::fstream labelStr( argv[8] ); if( labelStr.is_open() ) { while( !labelStr.eof() ) { char line[256]; labelStr.getline( line, 256 ); std::string lineString = std::string( line ); std::size_t pos = lineString.find( ',' ); RealType value; if( pos == std::string::npos ) { std::istringstream iss( lineString ); iss >> value; labelWeights.push_back( value ); userLabels.push_back( movingLabels[labelCount++] ); } else { unsigned int localLabel; std::string element = lineString.substr( 0, pos ); std::istringstream iss( element ); iss >> localLabel; userLabels.push_back( localLabel ); element = lineString.substr( pos + 1, lineString.length() ); std::istringstream iss2( element ); iss2 >> value; labelWeights.push_back( value ); } } labelStr.close(); } else { std::cerr << "File " << argv[8] << " cannot be opened." << std::endl; return EXIT_FAILURE; } } // Now match up the center points typedef itk::PointSet DisplacementFieldPointSetType; typedef itk::BSplineScatteredDataPointSetToImageFilter BSplineFilterType; typedef typename BSplineFilterType::WeightsContainerType WeightsContainerType; typename WeightsContainerType::Pointer weights = WeightsContainerType::New(); weights->Initialize(); const typename WeightsContainerType::Element boundaryWeight = 1.0e10; const typename WeightsContainerType::Element weight = 1.0; typename DisplacementFieldPointSetType::Pointer fieldPoints = DisplacementFieldPointSetType::New(); fieldPoints->Initialize(); unsigned long count = 0; typename PointSetType::PointsContainerConstIterator mIt = movingCenters->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator mItD = movingCenters->GetPointData()->Begin(); while( mItD != movingCenters->GetPointData()->End() ) { typename PointSetType::PointsContainerConstIterator fIt = fixedCenters->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator fItD = fixedCenters->GetPointData()->Begin(); while( fItD != fixedCenters->GetPointData()->End() ) { if( fItD.Value() == mItD.Value() ) { typename PointSetType::PointType fpoint = fIt.Value(); typename PointSetType::PointType mpoint = mIt.Value(); VectorType vector; typename LabelImageType::PointType fixedPhysicalPoint; for( unsigned int i = 0; i < ImageDimension; i++ ) { fixedPhysicalPoint[i] = fpoint[i]; vector[i] = mpoint[i] - fpoint[i]; } itk::ContinuousIndex fixedCidx; fixedImage->TransformPhysicalPointToContinuousIndex( fixedPhysicalPoint, fixedCidx ); typename DisplacementFieldType::PointType fieldPoint; parametricInputImage->TransformContinuousIndexToPhysicalPoint( fixedCidx, fieldPoint ); fieldPoints->SetPoint( count, fieldPoint ); fieldPoints->SetPointData( count, vector ); if( useWeights ) { std::vector::const_iterator it = std::find( userLabels.begin(), userLabels.end(), mItD.Value() ); if( it != userLabels.end() ) { weights->InsertElement( count, labelWeights[it - userLabels.begin()] ); } else { std::cerr << "Unspecified label " << mItD.Value() << " in specified user label weights." << std::endl; return EXIT_FAILURE; } } else { weights->InsertElement( count, weight ); } count++; break; } ++fItD; ++fIt; } ++mItD; ++mIt; } bool enforceStationaryBoundary = true; if( argc > 7 ) { enforceStationaryBoundary = static_cast( atoi( argv[7] ) ); } if( enforceStationaryBoundary ) { typename LabelImageType::IndexType startIndex2 = fixedImage->GetLargestPossibleRegion().GetIndex(); typename LabelImageType::SizeType inputSize2 = fixedImage->GetLargestPossibleRegion().GetSize(); for( ItF.GoToBegin(); !ItF.IsAtEnd(); ++ItF ) { typename LabelImageType::IndexType index = ItF.GetIndex(); bool isOnStationaryBoundary = false; for( unsigned int d = 0; d < ImageDimension; d++ ) { if( index[d] == startIndex2[d] || index[d] == startIndex2[d] + static_cast( inputSize2[d] ) - 1 ) { isOnStationaryBoundary = true; break; } } if( isOnStationaryBoundary ) { VectorType vector; vector.Fill( 0.0 ); typename PointSetType::PointType fixedPoint; parametricInputImage->TransformIndexToPhysicalPoint( index, fixedPoint ); fieldPoints->SetPoint( count, fixedPoint ); fieldPoints->SetPointData( count, vector ); weights->InsertElement( count, boundaryWeight ); count++; } } } typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); unsigned int numberOfLevels = atoi( argv[5] ); unsigned int splineOrder = 3; if( argc > 6 ) { splineOrder = atoi( argv[6] ); } std::vector meshSize = ConvertVector( std::string( argv[4] ) ); typename BSplineFilterType::ArrayType ncps; ncps.Fill( 0 ); if( meshSize.size() == 1 ) { ncps.Fill( meshSize[0] + splineOrder ); } else if( meshSize.size() == ImageDimension ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { ncps[d] = meshSize[d] + splineOrder; } } else { std::cerr << "Invalid meshSize format." << std::endl; } // std::cout << ncps << std::endl; // // bspliner->DebugOn(); bspliner->SetOrigin( fixedImage->GetOrigin() ); bspliner->SetSpacing( fixedImage->GetSpacing() ); bspliner->SetSize( fixedImage->GetLargestPossibleRegion().GetSize() ); bspliner->SetDirection( fixedImage->GetDirection() ); bspliner->SetGenerateOutputImage( true ); bspliner->SetNumberOfLevels( numberOfLevels ); bspliner->SetSplineOrder( splineOrder ); bspliner->SetNumberOfControlPoints( ncps ); bspliner->SetInput( fieldPoints ); bspliner->SetPointWeights( weights ); bspliner->Update(); typedef itk::VectorLinearInterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); interpolator->SetInputImage( bspliner->GetOutput() ); std::cout << "Distance errors:" << std::endl; mIt = movingCenters->GetPoints()->Begin(); mItD = movingCenters->GetPointData()->Begin(); while( mItD != movingCenters->GetPointData()->End() ) { typename PointSetType::PointsContainerConstIterator fIt = fixedCenters->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator fItD = fixedCenters->GetPointData()->Begin(); while( fItD != fixedCenters->GetPointData()->End() ) { if( fItD.Value() == mItD.Value() ) { typename PointSetType::PointType fpoint = fIt.Value(); typename PointSetType::PointType mpoint = mIt.Value(); VectorType displacement = ( mpoint - fpoint ); typename InterpolatorType::PointType point; for( unsigned int i = 0; i < ImageDimension; i++ ) { point[i] = fpoint[i]; } VectorType vector = interpolator->Evaluate( point ); RealType error = ( vector - displacement ).GetNorm(); std::cout << " " << fItD.Value() << ": " << error << std::endl; break; } ++fItD; ++fIt; } ++mItD; ++mIt; } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( argv[3] ); writer->SetInput( bspliner->GetOutput() ); writer->Update(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ANTSUseLandmarkImagesToGetBSplineDisplacementField( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ANTSUseLandmarkImagesToGetBSplineDisplacementField" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cerr << "Usage: " << argv[0] << " fixedImageWithLabeledLandmarks movingImageWithLabeledLandmarks outputDisplacementField " << "meshSize[0]xmeshSize[1]x... numberOfLevels [order=3] [enforceStationaryBoundaries=1] [landmarkWeights]" << std::endl; std::cerr << " we expect the input images to be (1) N-ary (2) in the same physical space as the images you want to " << std::endl; std::cerr << " register and (3 ) to have the same landmark points defined within them ... " << std::endl; std::cerr << " landmarks will be defined from the center of mass of the labels in the input images . " << std::endl; std::cerr << " You can use ITK-snap to generate the label images. " << std::endl; std::cerr << " The optional landmarks weights are read from a text file where each row is either:" << std::endl; std::cerr << " \"label,labelWeight\" or " << std::endl; std::cerr << " \"labelWeight\" or " << std::endl; std::cerr << " If the latter format is used, the label weights are assumed to be arranged in ascending order by label." << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension std::string fn = std::string(argv[1]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); switch( imageIO->GetNumberOfDimensions() ) { case 2: { LandmarkBasedDisplacementFieldTransformInitializer<2>(argc, argv); } break; case 3: { LandmarkBasedDisplacementFieldTransformInitializer<3>(argc, argv); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ANTsVersion.cxx000066400000000000000000000036151311104306400171330ustar00rootroot00000000000000/*========================================================================= * Modified from ANTs reference * Copyright Insight Software Consortium * * 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 "ANTsVersion.h" #include "ANTsVersionConfig.h" #include // std::cout, std::ios #include // std::ostringstream namespace { std::string MakeExtendedVersionString() { std::ostringstream v; v << "ANTs Version: " << ANTs::Version::VersionString() << std::endl << "Compiled: " << ANTs::Version::BuildDate() << std::endl; return v.str(); } static const std::string extendedVersionString = MakeExtendedVersionString(); } namespace ANTs { unsigned int Version::MajorVersion() { return ANTS_VERSION_MAJOR; } unsigned int Version::MinorVersion() { return ANTS_VERSION_MINOR; } unsigned int Version::PatchVersion() { return ANTS_VERSION_PATCH; } unsigned int Version::TweakVersion() { return 0; } const std::string &Version::VersionString() { static const std::string v( ANTS_VERSION ); return v; } const std::string &Version::BuildDate() { static const std::string v( __DATE__ " " __TIME__ ); return v; } const std::string &Version::ExtendedVersionString() { return extendedVersionString; } } ants-2.2.0/Examples/ANTsVersion.h000066400000000000000000000026051311104306400165560ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __ANTsVersion_h #define __ANTsVersion_h //NOTE: Windows magic needed here #define ANTsCommon_EXPORT #include namespace ANTs { /** \class Version * \brief Version info for ANTs */ class ANTsCommon_EXPORT Version { public: static unsigned int MajorVersion(); static unsigned int MinorVersion(); static unsigned int PatchVersion(); static unsigned int TweakVersion(); static const std::string &VersionString(); static const std::string &BuildDate(); static const std::string &ExtendedVersionString(); std::string ToString() { return Version::ExtendedVersionString(); } }; } #endif ants-2.2.0/Examples/Atropos.cxx000066400000000000000000002150771311104306400164160ustar00rootroot00000000000000#include #include #include #include #include "antsUtilities.h" #include "ReadWriteData.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIterator.h" #include "itkMaskImageFilter.h" #include "itkNumericSeriesFileNames.h" #include "itkSymmetricSecondRankTensor.h" #include "itkVectorImage.h" #include "itkVectorIndexSelectionCastImageFilter.h" #include "antsAtroposSegmentationImageFilter.h" #include "antsBoxPlotQuantileListSampleFilter.h" #include "antsCommandLineOption.h" #include "antsCommandLineParser.h" #include "antsGaussianListSampleFunction.h" #include "antsLogEuclideanGaussianListSampleFunction.h" #include "antsGrubbsRosnerListSampleFilter.h" #include "antsHistogramParzenWindowsListSampleFunction.h" #include "antsJointHistogramParzenShapeAndOrientationListSampleFunction.h" #include "antsListSampleToListSampleFilter.h" #include "antsManifoldParzenWindowsListSampleFunction.h" #include "antsPassThroughListSampleFilter.h" #include "antsPartialVolumeGaussianListSampleFunction.h" #include "itkTimeProbe.h" namespace ants { template class CommandIterationUpdate : public itk::Command { public: typedef CommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); protected: CommandIterationUpdate() { }; public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { const TFilter * filter = dynamic_cast( object ); if( typeid( event ) != typeid( itk::IterationEvent ) ) { return; } std::cout << " Iteration " << filter->GetElapsedIterations() << " (of " << filter->GetMaximumNumberOfIterations() << "): "; std::cout << "posterior probability = " << filter->GetCurrentPosteriorProbability(); typedef typename TFilter::RealType RealType; RealType annealingTemperature = filter->GetInitialAnnealingTemperature() * std::pow( filter->GetAnnealingRate(), static_cast( filter->GetElapsedIterations() ) ); annealingTemperature = vnl_math_max( annealingTemperature, filter->GetMinimumAnnealingTemperature() ); std::cout << " (annealing temperature = " << annealingTemperature << ")" << std::endl; } }; template int AtroposSegmentation( itk::ants::CommandLineParser *parser ) { typedef float PixelType; typedef float RealType; typedef itk::Image InputImageType; typedef unsigned int LabelType; typedef itk::Image LabelImageType; bool verbose = false; typename itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } if( verbose ) { std::cout << std::endl << "Running Atropos for " << ImageDimension << "-dimensional images." << std::endl; } typedef itk::ants::AtroposSegmentationImageFilter SegmentationFilterType; typename SegmentationFilterType::Pointer segmenter = SegmentationFilterType::New(); if( verbose ) { typedef CommandIterationUpdate CommandType; typename CommandType::Pointer observer = CommandType::New(); segmenter->AddObserver( itk::IterationEvent(), observer ); } /** * memory-usage -- need to set before setting the prior probability images. */ typename itk::ants::CommandLineParser::OptionType::Pointer memoryOption = parser->GetOption( "minimize-memory-usage" ); if( memoryOption && memoryOption->GetNumberOfFunctions() ) { segmenter->SetMinimizeMemoryUsage( parser->Convert( memoryOption->GetFunction( 0 )->GetName() ) ); } /** * Initialization */ typename itk::ants::CommandLineParser::OptionType::Pointer initializationOption = parser->GetOption( "initialization" ); if( initializationOption && initializationOption->GetNumberOfFunctions() && initializationOption->GetFunction( 0 )->GetNumberOfParameters() < 1 ) { if( verbose ) { std::cerr << "Incorrect initialization option specification." << std::endl; std::cerr << " " << initializationOption->GetDescription() << std::endl; } return EXIT_FAILURE; } else { segmenter->SetNumberOfTissueClasses( parser->Convert( initializationOption->GetFunction( 0 )->GetParameter( 0 ) ) ); std::string initializationStrategy = initializationOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( initializationStrategy ); if( !initializationStrategy.compare( std::string( "random" ) ) ) { segmenter->SetInitializationStrategy( SegmentationFilterType::Random ); } else if( !initializationStrategy.compare( std::string( "otsu" ) ) ) { segmenter->SetInitializationStrategy( SegmentationFilterType::Otsu ); } else if( !initializationStrategy.compare( std::string( "kmeans" ) ) ) { segmenter->SetInitializationStrategy( SegmentationFilterType::KMeans ); if( initializationOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { std::vector clusterCenters = parser->ConvertVector( initializationOption->GetFunction( 0 )->GetParameter( 1 ) ); if( clusterCenters.size() != segmenter->GetNumberOfTissueClasses() ) { if( verbose ) { std::cerr << "The cluster center vector size does not equal the " << "specified number of classes." << std::endl; } return EXIT_FAILURE; } else { typename SegmentationFilterType::ParametersType parameters; parameters.SetSize( segmenter->GetNumberOfTissueClasses() ); for( unsigned int n = 0; n < parameters.GetSize(); n++ ) { parameters[n] = clusterCenters[n]; } segmenter->SetInitialKMeansParameters( parameters ); } } } else if( !initializationStrategy.compare( std::string( "priorprobabilityimages" ) ) ) { segmenter->SetInitializationStrategy( SegmentationFilterType::PriorProbabilityImages ); if( initializationOption->GetFunction( 0 )->GetNumberOfParameters() < 3 ) { if( verbose ) { std::cerr << "Incorrect initialization option specification." << std::endl; std::cerr << " " << initializationOption->GetDescription() << std::endl; } return EXIT_FAILURE; } segmenter->SetPriorProbabilityWeight( parser->Convert( initializationOption->GetFunction( 0 )->GetParameter( 2 ) ) ); if( initializationOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { segmenter->SetProbabilityThreshold( parser->Convert( initializationOption->GetFunction( 0 )->GetParameter( 3 ) ) ); } std::string filename = initializationOption->GetFunction( 0 )->GetParameter( 1 ); if( filename.find( std::string( "%" ) ) != std::string::npos ) { itk::NumericSeriesFileNames::Pointer fileNamesCreator = itk::NumericSeriesFileNames::New(); fileNamesCreator->SetStartIndex( 1 ); fileNamesCreator->SetEndIndex( segmenter->GetNumberOfTissueClasses() ); fileNamesCreator->SetSeriesFormat( filename.c_str() ); const std::vector & imageNames = fileNamesCreator->GetFileNames(); for( unsigned int k = 0; k < imageNames.size(); k++ ) { typename InputImageType::Pointer image; ReadImage( image, imageNames[k].c_str() ); segmenter->SetPriorProbabilityImage( k + 1, image ); } } else { typedef itk::VectorImage VectorImageType; typename VectorImageType::Pointer image; ReadImage( image, filename.c_str() ); if( image->GetNumberOfComponentsPerPixel() != segmenter->GetNumberOfTissueClasses() ) { if( verbose ) { std::cerr << "The number of components does not match the number of " << "classes." << std::endl; } return EXIT_FAILURE; } typedef itk::VectorIndexSelectionCastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput( image ); for( unsigned int k = 0; k < segmenter->GetNumberOfTissueClasses(); k++ ) { caster->SetIndex( k ); caster->Update(); segmenter->SetPriorProbabilityImage( k + 1, caster->GetOutput() ); } } if( initializationOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { segmenter->SetProbabilityThreshold( parser->Convert( initializationOption->GetFunction( 0 )->GetParameter( 3 ) ) ); } } else if( !initializationStrategy.compare( std::string( "priorlabelimage" ) ) ) { segmenter->SetInitializationStrategy( SegmentationFilterType::PriorLabelImage ); if( initializationOption->GetFunction( 0 )->GetNumberOfParameters() < 3 ) { if( verbose ) { std::cerr << "Incorrect initialization option specification." << std::endl; std::cerr << " " << initializationOption->GetDescription() << std::endl; } return EXIT_FAILURE; } segmenter->SetPriorProbabilityWeight( parser->Convert( initializationOption->GetFunction( 0 )->GetParameter( 2 ) ) ); std::string filename = initializationOption->GetFunction( 0 )->GetParameter( 1 ); typename LabelImageType::Pointer image; ReadImage( image, filename.c_str() ); segmenter->SetPriorLabelImage( image ); } else { if( verbose ) { std::cerr << "Unrecognized initialization strategy request." << std::endl; } return EXIT_FAILURE; } } /** * Posterior probability formulation */ typename itk::ants::CommandLineParser::OptionType::Pointer posteriorOption = parser->GetOption( "posterior-formulation" ); if( posteriorOption && posteriorOption->GetNumberOfFunctions() ) { if( posteriorOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { segmenter->SetUseMixtureModelProportions( parser->Convert( posteriorOption->GetFunction( 0 )->GetParameter( 0 ) ) ); RealType annealingTemperature = 1.0; if( posteriorOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { annealingTemperature = parser->Convert( posteriorOption->GetFunction( 0 )->GetParameter( 1 ) ); if( annealingTemperature <= 0.0 ) { if( verbose ) { std::cerr << "Annealing temperature must be positive." << std::endl; } return EXIT_FAILURE; } } segmenter->SetInitialAnnealingTemperature( annealingTemperature ); RealType annealingRate = 1.0; if( posteriorOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { annealingRate = parser->Convert( posteriorOption->GetFunction( 0 )->GetParameter( 2 ) ); if( annealingRate < 0.0 || annealingRate > 1.0 ) { if( verbose ) { std::cerr << "Annealing rate must be in the range [0, 1]." << std::endl; } return EXIT_FAILURE; } } segmenter->SetAnnealingRate( annealingRate ); if( posteriorOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { RealType minimumAnnealingTemperature = parser->Convert( posteriorOption->GetFunction( 0 )->GetParameter( 3 ) ); segmenter->SetMinimumAnnealingTemperature( minimumAnnealingTemperature ); } } std::string posteriorStrategy = posteriorOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( posteriorStrategy ); if( !posteriorStrategy.compare( std::string( "socrates" ) ) ) { segmenter->SetPosteriorProbabilityFormulation( SegmentationFilterType::Socrates ); } else if( !posteriorStrategy.compare( std::string( "plato" ) ) ) { segmenter->SetPosteriorProbabilityFormulation( SegmentationFilterType::Plato ); } else if( !posteriorStrategy.compare( std::string( "aristotle" ) ) ) { segmenter->SetPosteriorProbabilityFormulation( SegmentationFilterType::Aristotle ); } else if( !posteriorStrategy.compare( std::string( "sigmoid" ) ) ) { segmenter->SetPosteriorProbabilityFormulation( SegmentationFilterType::Sigmoid ); } } /** * convergence options */ typename itk::ants::CommandLineParser::OptionType::Pointer convergenceOption = parser->GetOption( "convergence" ); if( convergenceOption && convergenceOption->GetNumberOfFunctions() ) { if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { segmenter->SetMaximumNumberOfIterations( parser->Convert( convergenceOption->GetFunction( 0 )->GetName() ) ); } if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { segmenter->SetMaximumNumberOfIterations( parser->Convert( convergenceOption->GetFunction( 0 )->GetParameter( 0 ) ) ); } if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { segmenter->SetConvergenceThreshold( parser->Convert( convergenceOption->GetFunction( 0 )->GetParameter( 1 ) ) ); } } /** * mask image */ typename itk::ants::CommandLineParser::OptionType::Pointer maskOption = parser->GetOption( "mask-image" ); if( maskOption && maskOption->GetNumberOfFunctions() ) { try { typename LabelImageType::Pointer image; ReadImage( image, maskOption->GetFunction( 0 )->GetName().c_str() ); segmenter->SetMaskImage( image ); // Check to see that the labels in the prior label image or the non-zero // probability voxels in the prior probability images encompass the entire // mask region. if( segmenter->GetInitializationStrategy() == SegmentationFilterType::PriorLabelImage ) { itk::ImageRegionConstIterator ItM( segmenter->GetMaskImage(), segmenter->GetMaskImage()->GetLargestPossibleRegion() ); itk::ImageRegionConstIterator ItP( segmenter->GetPriorLabelImage(), segmenter->GetPriorLabelImage()->GetLargestPossibleRegion() ); for( ItM.GoToBegin(), ItP.GoToBegin(); !ItM.IsAtEnd(); ++ItM, ++ItP ) { if( ItM.Get() != itk::NumericTraits::ZeroValue() && ItP.Get() == 0 ) { if( verbose ) { std::cout << std::endl; std::cout << "Warning: the labels in the the prior label image do " << "not encompass the entire mask region. As a result each unlabeled voxel will be " << "initially assigned a random label. The user might want to consider " << "various alternative strategies like assigning an additional " << "\"background\" label to the unlabeled voxels or propagating " << "the labels within the mask region." << std::endl; std::cout << std::endl; } break; } } } else if( segmenter->GetInitializationStrategy() == SegmentationFilterType::PriorProbabilityImages ) { itk::ImageRegionConstIteratorWithIndex ItM( segmenter->GetMaskImage(), segmenter->GetMaskImage()->GetLargestPossibleRegion() ); for( ItM.GoToBegin(); !ItM.IsAtEnd(); ++ItM ) { if( ItM.Get() != itk::NumericTraits::ZeroValue() ) { RealType sumPriorProbability = 0.0; for( unsigned int n = 0; n < segmenter->GetNumberOfTissueClasses(); n++ ) { sumPriorProbability += segmenter->GetPriorProbabilityImage( n + 1 )->GetPixel( ItM.GetIndex() ); } if( sumPriorProbability < segmenter->GetProbabilityThreshold() ) { if( verbose ) { std::cout << std::endl; std::cout << "Warning: the sum of the priors from the the prior probability images are " << "less than the probability threshold within the mask region. As a result " << "each zero probability voxel will be " << "initially assigned a random label. The user might want to consider " << "various alternative strategies like assigning an additional " << "\"background\" label to the zero probability voxels or propagating " << "the probabilities within the mask region." << std::endl; std::cout << std::endl; } break; } } } } } catch( ... ) { } } else { if( verbose ) { std::cerr << "An image mask is required. Specify a mask image" << " with the -x option." << std::endl; } return EXIT_FAILURE; } /** * BSpline options */ typename itk::ants::CommandLineParser::OptionType::Pointer bsplineOption = parser->GetOption( "bspline" ); if( bsplineOption && bsplineOption->GetNumberOfFunctions() ) { if( bsplineOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { std::vector numLevels = parser->ConvertVector( bsplineOption->GetFunction( 0 )->GetParameter( 0 ) ); typename SegmentationFilterType::ArrayType numberOfFittingLevels; if( numLevels.size() == 1 ) { numberOfFittingLevels.Fill( numLevels[0] ); } else if( numLevels.size() == ImageDimension ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { numberOfFittingLevels[d] = numLevels[d]; } } else { if( verbose ) { std::cerr << "Incorrect number of levels" << std::endl; } return EXIT_FAILURE; } segmenter->SetNumberOfLevels( numberOfFittingLevels ); } if( bsplineOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { segmenter->SetSplineOrder( parser->Convert( bsplineOption->GetFunction( 0 )->GetParameter( 2 ) ) ); } if( bsplineOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { std::vector array = parser->ConvertVector( bsplineOption->GetFunction( 0 )->GetParameter( 1 ) ); typename SegmentationFilterType::ArrayType numberOfControlPoints; if( array.size() == 1 ) { numberOfControlPoints.Fill( array[0] + segmenter->GetSplineOrder() ); } else if( array.size() == ImageDimension ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { numberOfControlPoints[d] = array[d] + segmenter->GetSplineOrder(); } } else { if( verbose ) { std::cerr << "Incorrect mesh resolution" << std::endl; } return EXIT_FAILURE; } segmenter->SetNumberOfControlPoints( numberOfControlPoints ); } } /** * labels */ typename itk::ants::CommandLineParser::OptionType::Pointer labelOption = parser->GetOption( "label-propagation" ); if( labelOption && labelOption->GetNumberOfFunctions() ) { if( labelOption->GetNumberOfFunctions() == 1 && ( labelOption->GetFunction( 0 )->GetName() ).empty() ) { typename SegmentationFilterType::LabelParameterMapType labelMap; float labelLambda = parser->Convert( labelOption->GetFunction( 0 )->GetParameter( 0 ) ); float labelBoundaryProbability = 1.0; if( labelOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { labelBoundaryProbability = parser->Convert( labelOption->GetFunction( 0 )->GetParameter( 1 ) ); if( labelBoundaryProbability < 0.0 ) { labelBoundaryProbability = 0.0; } if( labelBoundaryProbability > 1.0 ) { labelBoundaryProbability = 1.0; } } for( unsigned int n = 1; n <= segmenter->GetNumberOfTissueClasses(); n++ ) { typename SegmentationFilterType::LabelParametersType labelPair; labelPair.first = labelLambda; labelPair.second = labelBoundaryProbability; labelMap[n] = labelPair; } segmenter->SetPriorLabelParameterMap( labelMap ); } else { typename SegmentationFilterType::LabelParameterMapType labelMap; for( unsigned int n = 0; n < labelOption->GetNumberOfFunctions(); n++ ) { typename SegmentationFilterType::LabelParametersType labelPair; float labelLambda = parser->Convert( labelOption->GetFunction( n )->GetParameter( 0 ) ); float labelBoundaryProbability = 1.0; if( labelOption->GetFunction( n )->GetNumberOfParameters() > 1 ) { labelBoundaryProbability = parser->Convert( labelOption->GetFunction( n )->GetParameter( 1 ) ); if( labelBoundaryProbability < 0.0 ) { labelBoundaryProbability = 0.0; } if( labelBoundaryProbability > 1.0 ) { labelBoundaryProbability = 1.0; } } labelPair.first = labelLambda; labelPair.second = labelBoundaryProbability; unsigned int whichClass = parser->Convert( labelOption->GetFunction( n )->GetName() ); labelMap[whichClass] = labelPair; } segmenter->SetPriorLabelParameterMap( labelMap ); } } /** * intensity images */ typename itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "intensity-image" ); if( imageOption && imageOption->GetNumberOfFunctions() ) { unsigned int count = 0; for( int n = imageOption->GetNumberOfFunctions() - 1; n >= 0; n-- ) { std::string imagename; if( imageOption->GetFunction( n )->GetNumberOfParameters() > 0 ) { imagename = imageOption->GetFunction( n )->GetParameter( 0 ); } else { imagename = imageOption->GetFunction( n )->GetName(); } typename InputImageType::Pointer image; ReadImage( image, imagename.c_str() ); segmenter->SetIntensityImage( count, image ); if( imageOption->GetFunction( count )->GetNumberOfParameters() > 1 ) { segmenter->SetAdaptiveSmoothingWeight( count, parser->Convert( imageOption->GetFunction( count )->GetParameter( 1 ) ) ); } else { segmenter->SetAdaptiveSmoothingWeight( count, 0.0 ); } count++; } } else { if( verbose ) { std::cerr << "No input images were specified. Specify an input image" << " with the -a option." << std::endl; } return EXIT_FAILURE; } /** * MRF options */ typename itk::ants::CommandLineParser::OptionType::Pointer mrfOption = parser->GetOption( "mrf" ); if( mrfOption && mrfOption->GetNumberOfFunctions() ) { if( mrfOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { try { typedef typename SegmentationFilterType::RealImageType MRFCoefficientImageType; typedef itk::ImageFileReader MRFNeighborhoodImageReaderType; typename MRFNeighborhoodImageReaderType::Pointer mrfNeighborhoodReader = MRFNeighborhoodImageReaderType::New(); mrfNeighborhoodReader->SetFileName( mrfOption->GetFunction( 0 )->GetParameter( 0 ) ); typename MRFCoefficientImageType::Pointer mrfCoefficientImage = mrfNeighborhoodReader->GetOutput(); mrfCoefficientImage->Update(); mrfCoefficientImage->DisconnectPipeline(); segmenter->SetMRFCoefficientImage( mrfCoefficientImage ); } catch( ... ) { segmenter->SetMRFSmoothingFactor( parser->Convert( mrfOption->GetFunction( 0 )->GetParameter( 0 ) ) ); } } if( mrfOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { std::vector array = parser->ConvertVector( mrfOption->GetFunction( 0 )->GetParameter( 1 ) ); typename SegmentationFilterType::ArrayType radius; if( array.size() == 1 ) { radius.Fill( array[0] ); } else if( array.size() == ImageDimension ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { radius[d] = array[d]; } } else { if( verbose ) { std::cerr << "MRF radius size needs to be equal to the image dimension." << std::endl; } return EXIT_FAILURE; } segmenter->SetMRFRadius( radius ); } } /** * ICM options */ typename itk::ants::CommandLineParser::OptionType::Pointer icmOption = parser->GetOption( "icm" ); if( icmOption && icmOption->GetNumberOfFunctions() == 1 ) { segmenter->SetUseAsynchronousUpdating( parser->Convert( icmOption->GetFunction( 0 )->GetName() ) ); } if( icmOption && icmOption->GetNumberOfFunctions() ) { if( icmOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { segmenter->SetUseAsynchronousUpdating( parser->Convert( icmOption->GetFunction( 0 )->GetParameter( 0 ) ) ); } if( icmOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { segmenter->SetMaximumNumberOfICMIterations( parser->Convert( icmOption->GetFunction( 0 )-> GetParameter( 1 ) ) ); } } /** * random seed */ typename itk::ants::CommandLineParser::OptionType::Pointer seedOption = parser->GetOption( "use-random-seed" ); if( seedOption && seedOption->GetNumberOfFunctions() ) { bool useRandomSeed = parser->Convert( seedOption->GetFunction( 0 )->GetName() ); if( !useRandomSeed ) { // assign seed from itkMersenneTwisterRandomVariateGenerator.h (line 347) segmenter->SetRandomizerInitializationSeed( 19650218UL ); } } /** * euclidean distance */ typename itk::ants::CommandLineParser::OptionType::Pointer distanceOption = parser->GetOption( "use-euclidean-distance" ); if( distanceOption && distanceOption->GetNumberOfFunctions() ) { segmenter->SetUseEuclideanDistanceForPriorLabels( parser->Convert( distanceOption->GetFunction( 0 )->GetName() ) ); } /** * likelihood */ typename itk::ants::CommandLineParser::OptionType::Pointer likelihoodOption = parser->GetOption( "likelihood-model" ); if( likelihoodOption && likelihoodOption->GetNumberOfFunctions() ) { std::string likelihoodModel = likelihoodOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( likelihoodModel ); if( !likelihoodModel.compare( std::string( "gaussian" ) ) ) { typedef typename SegmentationFilterType::SampleType SampleType; typedef itk::ants::Statistics::GaussianListSampleFunction LikelihoodType; for( unsigned int n = 0; n < segmenter->GetNumberOfTissueClasses(); n++ ) { typename LikelihoodType::Pointer gaussianLikelihood = LikelihoodType::New(); segmenter->SetLikelihoodFunction( n, gaussianLikelihood ); } } else if( !likelihoodModel.compare( std::string( "manifoldparzenwindows" ) ) ) { typedef typename SegmentationFilterType::SampleType SampleType; typedef itk::ants::Statistics::ManifoldParzenWindowsListSampleFunction LikelihoodType; float regularizationSigma = 1.0; if( likelihoodOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { regularizationSigma = parser->Convert( likelihoodOption->GetFunction( 0 )->GetParameter( 0 ) ); } unsigned int evalNeighborhood = 50; if( likelihoodOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { evalNeighborhood = parser->Convert( likelihoodOption->GetFunction( 0 )->GetParameter( 1 ) ); } unsigned int covNeighborhood = 0; if( likelihoodOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { covNeighborhood = parser->Convert( likelihoodOption->GetFunction( 0 )->GetParameter( 2 ) ); } float covSigma = 1.0; if( likelihoodOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { covSigma = parser->Convert( likelihoodOption->GetFunction( 0 )->GetParameter( 3 ) ); } for( unsigned int n = 0; n < segmenter->GetNumberOfTissueClasses(); n++ ) { typename LikelihoodType::Pointer mpwLikelihood = LikelihoodType::New(); mpwLikelihood->SetRegularizationSigma( regularizationSigma ); mpwLikelihood->SetEvaluationKNeighborhood( evalNeighborhood ); mpwLikelihood->SetCovarianceKNeighborhood( covNeighborhood ); mpwLikelihood->SetKernelSigma( covSigma ); segmenter->SetLikelihoodFunction( n, mpwLikelihood ); } } else if( !likelihoodModel.compare( std::string( "histogramparzenwindows" ) ) ) { typedef typename SegmentationFilterType::SampleType SampleType; typedef itk::ants::Statistics::HistogramParzenWindowsListSampleFunction LikelihoodType; float sigma = 1.0; if( likelihoodOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { sigma = parser->Convert( likelihoodOption->GetFunction( 0 )->GetParameter( 0 ) ); } unsigned int numberOfBins = 32; if( likelihoodOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { numberOfBins = parser->Convert( likelihoodOption->GetFunction( 0 )->GetParameter( 1 ) ); } for( unsigned int n = 0; n < segmenter->GetNumberOfTissueClasses(); n++ ) { typename LikelihoodType::Pointer hpwLikelihood = LikelihoodType::New(); hpwLikelihood->SetSigma( sigma ); hpwLikelihood->SetNumberOfHistogramBins( numberOfBins ); segmenter->SetLikelihoodFunction( n, hpwLikelihood ); } } else if( !likelihoodModel.compare( std::string( "jointshapeandorientationprobability" ) ) ) { if( segmenter->GetNumberOfIntensityImages() != static_cast( ImageDimension * ( ImageDimension + 1 ) / 2 ) ) { if( verbose ) { std::cerr << " Expect images in upper triangular order " << std::endl; std::cerr << " xx xy xz yy yz zz " << std::endl; std::cerr << "Incorrect number of intensity images specified." << std::endl; } return EXIT_FAILURE; } typedef typename SegmentationFilterType::SampleType SampleType; typedef itk::ants::Statistics:: JointHistogramParzenShapeAndOrientationListSampleFunction LikelihoodType; float shapeSigma = 2.0; if( likelihoodOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { shapeSigma = parser->Convert( likelihoodOption->GetFunction( 0 )->GetParameter( 0 ) ); } unsigned int numberOfShapeBins = 64; if( likelihoodOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { numberOfShapeBins = parser->Convert( likelihoodOption->GetFunction( 0 )->GetParameter( 1 ) ); } float orientationSigma = 1.0; if( likelihoodOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { orientationSigma = parser->Convert( likelihoodOption->GetFunction( 0 )->GetParameter( 2 ) ); } unsigned int numberOfOrientationBins = 32; if( likelihoodOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { numberOfOrientationBins = parser->Convert( likelihoodOption->GetFunction( 0 )->GetParameter( 3 ) ); } for( unsigned int n = 0; n < segmenter->GetNumberOfTissueClasses(); n++ ) { typename LikelihoodType::Pointer hpwLikelihood = LikelihoodType::New(); hpwLikelihood->SetShapeSigma( shapeSigma ); hpwLikelihood->SetOrientationSigma( orientationSigma); hpwLikelihood->SetNumberOfShapeJointHistogramBins( numberOfShapeBins ); hpwLikelihood->SetNumberOfOrientationJointHistogramBins( numberOfOrientationBins); segmenter->SetLikelihoodFunction( n, hpwLikelihood ); } } else if( !likelihoodModel.compare( std::string( "logeuclideangaussian" ) ) ) { if( segmenter->GetNumberOfIntensityImages() != static_cast( ImageDimension * ( ImageDimension + 1 ) / 2 ) ) { if( verbose ) { std::cerr << " Expect images in upper triangular order " << std::endl; std::cerr << " xx xy xz yy yz zz " << std::endl; std::cerr << "Incorrect number of intensity images specified." << std::endl; } return EXIT_FAILURE; } typedef typename SegmentationFilterType::SampleType SampleType; typedef itk::ants::Statistics::LogEuclideanGaussianListSampleFunction LikelihoodType; for( unsigned int n = 0; n < segmenter->GetNumberOfTissueClasses(); n++ ) { typename LikelihoodType::Pointer gaussianLikelihood = LikelihoodType::New(); segmenter->SetLikelihoodFunction( n, gaussianLikelihood ); } } else { if( verbose ) { std::cerr << "Unrecognized likelihood model request." << std::endl; } return EXIT_FAILURE; } } /** * partial volume */ typename itk::ants::CommandLineParser::OptionType::Pointer pvOption = parser->GetOption( "partial-volume-label-set" ); if( pvOption && pvOption->GetNumberOfFunctions() ) { unsigned int labelSetCount = 0; for( int n = pvOption->GetNumberOfFunctions() - 1; n >= 0; n-- ) { typename SegmentationFilterType::PartialVolumeLabelSetType labelSet = parser->ConvertVector( pvOption->GetFunction( n )->GetName() ); if( labelSet.size() != 2 ) { if( verbose ) { std::cerr << "Error: Currently Atropos only supports partial " << "volume label sets of size equal to 2." << std::endl; } return EXIT_FAILURE; } segmenter->AddPartialVolumeLabelSet( labelSet ); typedef typename SegmentationFilterType::SampleType SampleType; typedef itk::ants::Statistics::PartialVolumeGaussianListSampleFunction LikelihoodType; typename LikelihoodType::Pointer partialVolumeLikelihood = LikelihoodType::New(); segmenter->SetLikelihoodFunction( labelSetCount + segmenter->GetNumberOfTissueClasses(), partialVolumeLikelihood ); labelSetCount++; } typename itk::ants::CommandLineParser::OptionType::Pointer pvlOption = parser->GetOption( "use-partial-volume-likelihoods" ); bool useLikelihoods = false; if( pvlOption && pvlOption->GetNumberOfFunctions() ) { std::string value = pvlOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( value ); if( !value.compare( "true" ) || !value.compare( "1" ) ) { useLikelihoods = true; } else { useLikelihoods = false; } } segmenter->SetUsePartialVolumeLikelihoods( useLikelihoods ); } /** * outliers handling */ typename itk::ants::CommandLineParser::OptionType::Pointer outlierOption = parser->GetOption( "winsorize-outliers" ); if( outlierOption && outlierOption->GetNumberOfFunctions() ) { std::string outlierStrategy = outlierOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( outlierStrategy ); if( !outlierStrategy.compare( std::string( "boxplot" ) ) ) { typedef typename SegmentationFilterType::SampleType SampleType; typedef itk::ants::Statistics::BoxPlotQuantileListSampleFilter SampleFilterType; typename SampleFilterType::Pointer boxplotFilter = SampleFilterType::New(); if( outlierOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { boxplotFilter->SetLowerPercentile( parser->Convert( outlierOption->GetFunction( 0 )->GetParameter( 0 ) ) ); } if( outlierOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { boxplotFilter->SetUpperPercentile( parser->Convert( outlierOption->GetFunction( 0 )->GetParameter( 1 ) ) ); } if( outlierOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { boxplotFilter->SetWhiskerScalingFactor( parser->Convert( outlierOption->GetFunction( 0 )->GetParameter( 2 ) ) ); } segmenter->SetOutlierHandlingFilter( boxplotFilter ); } else if( !outlierStrategy.compare( std::string( "grubbsrosner" ) ) ) { typedef typename SegmentationFilterType::SampleType SampleType; typedef itk::ants::Statistics::GrubbsRosnerListSampleFilter SampleFilterType; typename SampleFilterType::Pointer grubbsFilter = SampleFilterType::New(); if( outlierOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { grubbsFilter->SetSignificanceLevel( parser->Convert( outlierOption->GetFunction( 0 )->GetParameter( 0 ) ) ); } if( outlierOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { grubbsFilter->SetWinsorizingLevel( parser->Convert( outlierOption->GetFunction( 0 )->GetParameter( 1 ) ) ); } segmenter->SetOutlierHandlingFilter( grubbsFilter ); } else { if( verbose ) { std::cerr << "Unrecognized outlier handling strategy request." << std::endl; } return EXIT_FAILURE; } } itk::TimeProbe timer; timer.Start(); try { if( verbose ) { std::cout << std::endl << "Progress: " << std::endl; } // segmenter->DebugOn(); segmenter->Update(); } catch( itk::ExceptionObject & exp ) { if( verbose ) { std::cerr << exp << std::endl; } return EXIT_FAILURE; } timer.Stop(); /** * output */ if( icmOption && icmOption->GetNumberOfFunctions() && icmOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { if( segmenter->GetUseAsynchronousUpdating() && segmenter->GetICMCodeImage() ) { typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( segmenter->GetICMCodeImage() ); writer->SetFileName( ( icmOption->GetFunction( 0 )->GetParameter( 2 ) ).c_str() ); writer->Update(); } } if( verbose ) { std::cout << std::endl << "Writing output:" << std::endl; } typename itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { if( outputOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { WriteImage(segmenter->GetOutput(), ( outputOption->GetFunction( 0 )->GetName() ).c_str() ); } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { WriteImage(segmenter->GetOutput(), ( outputOption->GetFunction( 0 )->GetParameter( 0 ) ).c_str() ); } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { std::string filename = outputOption->GetFunction( 0 )->GetParameter( 1 ); itk::NumericSeriesFileNames::Pointer fileNamesCreator = itk::NumericSeriesFileNames::New(); fileNamesCreator->SetStartIndex( 1 ); fileNamesCreator->SetEndIndex( segmenter->GetNumberOfTissueClasses() ); fileNamesCreator->SetSeriesFormat( filename.c_str() ); const std::vector & imageNames = fileNamesCreator->GetFileNames(); for( unsigned int i = 0; i < imageNames.size(); i++ ) { if( verbose ) { std::cout << " Writing posterior image (class " << i + 1 << ")" << std::endl; } typename InputImageType::Pointer probabilityImage = segmenter->GetPosteriorProbabilityImage( i + 1 ); if( segmenter->GetMaskImage() ) { typedef itk::MaskImageFilter MaskerType; typename MaskerType::Pointer masker = MaskerType::New(); masker->SetInput1( probabilityImage ); masker->SetInput2( segmenter->GetMaskImage() ); masker->SetOutsideValue( 0 ); masker->Update(); probabilityImage = masker->GetOutput(); } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( probabilityImage ); writer->SetFileName( imageNames[i].c_str() ); writer->Update(); } } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { std::string filename = outputOption->GetFunction( 0 )->GetParameter( 2 ); itk::NumericSeriesFileNames::Pointer fileNamesCreator = itk::NumericSeriesFileNames::New(); fileNamesCreator->SetStartIndex( 1 ); fileNamesCreator->SetEndIndex( segmenter->GetNumberOfTissueClasses() ); fileNamesCreator->SetSeriesFormat( filename.c_str() ); const std::vector & imageNames = fileNamesCreator->GetFileNames(); for( unsigned int i = 0; i < segmenter->GetNumberOfTissueClasses(); i++ ) { if( verbose ) { std::cout << " Writing likelihood image (class " << i + 1 << ")" << std::endl; } typename InputImageType::Pointer likelihoodImage = segmenter->GetLikelihoodImage( i + 1 ); typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( likelihoodImage ); writer->SetFileName( imageNames[i].c_str() ); writer->Update(); } } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { std::string filename = outputOption->GetFunction( 0 )->GetParameter( 3 ); itk::NumericSeriesFileNames::Pointer fileNamesCreator = itk::NumericSeriesFileNames::New(); fileNamesCreator->SetStartIndex( 1 ); fileNamesCreator->SetEndIndex( segmenter->GetNumberOfTissueClasses() ); fileNamesCreator->SetSeriesFormat( filename.c_str() ); const std::vector & imageNames = fileNamesCreator->GetFileNames(); for( unsigned int i = 0; i < segmenter->GetNumberOfTissueClasses(); i++ ) { if( segmenter->GetPriorProbabilityImage( i + 1 ) || segmenter->GetPriorLabelImage() ) { if( verbose ) { std::cout << " Writing distance image (class " << i + 1 << ")" << std::endl; } typename InputImageType::Pointer distanceImage = segmenter->GetDistancePriorProbabilityImage( i + 1 ); typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( distanceImage ); writer->SetFileName( imageNames[i].c_str() ); writer->Update(); } } } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 4 ) { std::string filename = outputOption->GetFunction( 0 )->GetParameter( 4 ); itk::NumericSeriesFileNames::Pointer fileNamesCreator = itk::NumericSeriesFileNames::New(); fileNamesCreator->SetStartIndex( 1 ); fileNamesCreator->SetEndIndex( segmenter->GetNumberOfTissueClasses() ); fileNamesCreator->SetSeriesFormat( filename.c_str() ); const std::vector & imageNames = fileNamesCreator->GetFileNames(); if( segmenter->GetAdaptiveSmoothingWeight( 0 ) > 0.0 ) { for( unsigned int i = 0; i < segmenter->GetNumberOfTissueClasses(); i++ ) { if( segmenter->GetPriorProbabilityImage( i + 1 ) || segmenter->GetPriorLabelImage() ) { if( verbose ) { std::cout << " Writing B-spline image (class " << i + 1 << ")" << std::endl; } typename InputImageType::Pointer bsplineImage = segmenter->GetSmoothIntensityImageFromPriorImage( 0, i + 1 ); typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( bsplineImage ); writer->SetFileName( imageNames[i].c_str() ); writer->Update(); } } } } } if( verbose ) { std::cout << std::endl; segmenter->Print( std::cout, 2 ); std::cout << "Elapsed time: " << timer.GetMean() << std::endl; } return EXIT_SUCCESS; } void AtroposInitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, Atropos tries to " ) + std::string( "infer the dimensionality from the first input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "image-dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3/4" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "One or more scalar images is specified for segmentation " ) + std::string( "using the -a/--intensity-image option. For segmentation " ) + std::string( "scenarios with no prior information, the first scalar " ) + std::string( "image encountered on the command line is used to order " ) + std::string( "labelings such that the class with the smallest intensity " ) + std::string( "signature is class \'1\' through class \'N\' which represents " ) + std::string( "the voxels with the largest intensity values. The " ) + std::string( "optional adaptive smoothing weight parameter is applicable " ) + std::string( "only when using prior label or probability images. This " ) + std::string( "scalar parameter is to be specified between [0,1] which " ) + std::string( "smooths each labeled region separately and modulates the " ) + std::string( "intensity measurement at each voxel in each intensity image " ) + std::string( "between the original intensity and its smoothed " ) + std::string( "counterpart. The smoothness parameters are governed by the " ) + std::string( "-b/--bspline option." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "intensity-image" ); option->SetShortName( 'a' ); option->SetUsageOption( 0, "[intensityImage,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "If the adaptive smoothing weights are > 0, the intensity " ) + std::string( "images are smoothed in calculating the likelihood values. " ) + std::string( "This is to account for subtle intensity differences " ) + std::string( "across the same tissue regions." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "bspline" ); option->SetShortName( 'b' ); option->SetUsageOption( 0, "[,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "To initialize the FMM parameters, one of the following " ) + std::string( "options must be specified. If one does not have " ) + std::string( "prior label or probability images we recommend " ) + std::string( "using kmeans as it is typically faster than otsu and can " ) + std::string( "be used with multivariate initialization. However, since a " ) + std::string( "Euclidean distance on the inter cluster distances is used, one " ) + std::string( "might have to appropriately scale the additional input images. " ) + std::string( "Random initialization is meant purely for intellectual " ) + std::string( "curiosity. The prior weighting (specified in the range " ) + std::string( "[0,1]) is used to modulate the calculation of the " ) + std::string( "posterior probabilities between the likelihood*mrfprior " ) + std::string( "and the likelihood*mrfprior*prior. For specifying many " ) + std::string( "prior probability images for a multi-label segmentation, " ) + std::string( "we offer a minimize usage option (see -m). With that option " ) + std::string( "one can specify a prior probability threshold in which only " ) + std::string( "those pixels exceeding that threshold are stored in memory. "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "initialization" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "Random[numberOfClasses]" ); option->SetUsageOption( 1, "Otsu[numberOfTissueClasses]" ); option->SetUsageOption( 2, "KMeans[numberOfTissueClasses,]" ); option->SetUsageOption( 3, "PriorProbabilityImages[numberOfTissueClasses,fileSeriesFormat(index=1 to numberOfClasses) or vectorImage,priorWeighting,]" ); option->SetUsageOption( 4, "PriorLabelImage[numberOfTissueClasses,labelImage,priorWeighting]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The partial volume estimation option allows one to model" ) + std::string( "mixtures of classes within single voxels. Atropos " ) + std::string( "currently allows the user to model two class mixtures " ) + std::string( "per partial volume class. The user specifies a set of " ) + std::string( "class labels per partial volume class requested. For " ) + std::string( "example, suppose the user was performing a classic 3-" ) + std::string( "tissue segmentation (csf, gm, wm) using kmeans " ) + std::string( "initialization. Suppose the user also wanted to model the " ) + std::string( "partial voluming effects between csf/gm and gm/wm. " ) + std::string( "The user would specify it using -i kmeans[3] " ) + std::string( "and -s 1x2 -s 2x3. So, for this example, there would be 3 " ) + std::string( "tissue classes and 2 partial volume classes. Optionally," ) + std::string( "the user can limit partial volume handling to mrf considerations " ) + std::string( "only whereby the output would only be the three tissues." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "partial-volume-label-set" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "label1xlabel2" ); option->SetUsageOption( 0, "label1xlabel2xlabel3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The user can specify whether or not to use the partial " ) + std::string( "volume likelihoods, in which case the partial volume class " ) + std::string( "is considered separate from the tissue classes. " ) + std::string( "Alternatively, one can use the MRF only to handle partial " ) + std::string( "volume in which case, partial volume voxels are not " ) + std::string( "considered as separate classes." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-partial-volume-likelihoods" ); option->SetUsageOption( 0, "1/(0)" ); option->SetUsageOption( 1, "true/(false)" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Different posterior probability formulations are possible as ") + std::string( "are different update options. To guarantee theoretical " ) + std::string( "convergence properties, a proper formulation of the well-known " ) + std::string( "iterated conditional modes (ICM) uses an asynchronous update step " ) + std::string( "modulated by a specified annealing temperature. If one sets " ) + std::string( "the AnnealingTemperature > 1 in the posterior formulation " ) + std::string( "a traditional code set for a proper ICM update will be created. ") + std::string( "Otherwise, a synchronous update step will take place at each iteration. ") + std::string( "The annealing temperature, T, converts the posteriorProbability " ) + std::string( "to posteriorProbability^(1/T) over the course of optimization. "); std::string( "Options include the following: " ) + std::string( " Socrates: posteriorProbability = (spatialPrior)^priorWeight" ) + std::string( "*(likelihood*mrfPrior)^(1-priorWeight), " ) + std::string( " Plato: posteriorProbability = 1.0, " ) + std::string( " Aristotle: posteriorProbability = 1.0, " ) + std::string( " Sigmoid: posteriorProbability = 1.0, " )/* + std::string( " Zeno: posteriorProbability = 1.0\n" ) + std::string( " Diogenes: posteriorProbability = 1.0\n" ) + std::string( " Thales: posteriorProbability = 1.0\n" ) + std::string( " Democritus: posteriorProbability = 1.0.\n" ) */; OptionType::Pointer option = OptionType::New(); option->SetLongName( "posterior-formulation" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "Socrates[,,,]" ); option->SetUsageOption( 1, "Plato[,,,]" ); option->SetUsageOption( 2, "Aristotle[,,,]" ); option->SetUsageOption( 3, "Sigmoid[,,,]]" ); // option->SetUsageOption( 5, "Thales[]" ); // option->SetUsageOption( 6, "Democritus" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The image mask (which is required) defines the region which " ) + std::string( "is to be labeled by the Atropos algorithm." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask-image" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "maskImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Convergence is determined by calculating the mean maximum " ) + std::string( "posterior probability over the region of interest at " ) + std::string( "each iteration. When this value decreases or increases " ) + std::string( "less than the specified threshold from the previous " ) + std::string( "iteration or the maximum number of iterations is exceeded " ) + std::string( "the program terminates."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "convergence" ); option->SetShortName( 'c' ); option->SetUsageOption( 0, "numberOfIterations" ); option->SetUsageOption( 1, "[,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Both parametric and non-parametric options exist in Atropos. " ) + std::string( "The Gaussian parametric option is commonly used " ) + std::string( "(e.g. SPM & FAST) where the mean and standard deviation " ) + std::string( "for the Gaussian of each class is calculated at each " ) + std::string( "iteration. Other groups use non-parametric approaches " ) + std::string( "exemplified by option 2. We recommend using options 1 " ) + std::string( "or 2 as they are fairly standard and the " ) + std::string( "default parameters work adequately." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "likelihood-model" ); option->SetShortName( 'k' ); option->SetUsageOption( 0, "Gaussian" ); option->SetUsageOption( 1, "HistogramParzenWindows[,]" ); option->SetUsageOption( 2, "ManifoldParzenWindows[,,,]" ); option->SetUsageOption( 3, "JointShapeAndOrientationProbability[,, , ]" ); option->SetUsageOption( 4, "LogEuclideanGaussian" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Markov random field (MRF) theory provides a general " ) + std::string( "framework for enforcing spatially contextual constraints " ) + std::string( "on the segmentation solution. The default smoothing " ) + std::string( "factor of 0.3 provides a moderate amount of smoothing. " ) + std::string( "Increasing this number causes more smoothing whereas " ) + std::string( "decreasing the number lessens the smoothing. The radius " ) + std::string( "parameter specifies the mrf neighborhood. Different " ) + std::string( "update schemes are possible but only the asynchronous " ) + std::string( "updating has theoretical convergence properties. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mrf" ); option->SetShortName( 'm' ); option->SetUsageOption( 0, "[,]" ); option->SetUsageOption( 1, "[,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Asynchronous updating requires the construction of an " ) + std::string( "ICM code image which is a label image (with labels in the " ) + std::string( "range {1,..,MaximumICMCode}) constructed such that no MRF " ) + std::string( "neighborhood has duplicate ICM code labels. Thus, to update " ) + std::string( "the voxel class labels we iterate through the code labels " ) + std::string( "and, for each code label, we iterate through the image " ) + std::string( "and update the voxel class label that has the corresponding " ) + std::string( "ICM code label. One can print out the ICM code image by " ) + std::string( "specifying an ITK-compatible image filename." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "icm" ); option->SetShortName( 'g' ); option->SetUsageOption( 0, "[,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Initialize internal random number generator with a random seed. " ) + std::string( "Otherwise, initialize with a constant seed number." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-random-seed" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "0/(1)" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The output consists of a labeled image where each voxel " ) + std::string( "in the masked region is assigned a label from 1, 2, " ) + std::string( "..., N. Optionally, one can also output the posterior " ) + std::string( "probability images specified in the same format as the " ) + std::string( "prior probability images, e.g. posterior%02d.nii.gz " ) + std::string( "(C-style file name formatting)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "[classifiedImage,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "By default, memory usage is not minimized, however, if " ) + std::string( "this is needed, the various probability and distance " ) + std::string( "images are calculated on the fly instead of being " ) + std::string( "stored in memory at each iteration. Also, if prior " ) + std::string( "probability images are used, only the non-negligible " ) + std::string( "pixel values are stored in memory. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "minimize-memory-usage" ); option->SetShortName( 'u' ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "To remove the effects of outliers in calculating the " ) + std::string( "weighted mean and weighted covariance, the user can " ) + std::string( "opt to remove the outliers through the options " ) + std::string( "specified below." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "winsorize-outliers" ); option->SetShortName( 'w' ); option->SetUsageOption( 0, "BoxPlot[,,]" ); option->SetUsageOption( 1, "GrubbsRosner[,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Given prior label or probability images, the labels are " ) + std::string( "propagated throughout the masked region so that every " ) + std::string( "voxel in the mask is labeled. Propagation is done " ) + std::string( "by using a signed distance transform of the label. " ) + std::string( "Alternatively, propagation of the labels with the " ) + std::string( "fast marching filter respects the distance along the " ) + std::string( "shape of the mask (e.g. the sinuous sulci and gyri " ) + std::string( "of the cortex." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-euclidean-distance" ); option->SetShortName( 'e' ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "The propagation of each prior label can be controlled " ) + std::string( "by the lambda and boundary probability parameters. The " ) + std::string( "latter parameter is the probability (in the range " ) + std::string( "[0,1]) of the label on the boundary which increases linearly " ) + std::string( "to a maximum value of 1.0 in the interior of the labeled " ) + std::string( "region. The former parameter dictates the exponential " ) + std::string( "decay of probability propagation outside the labeled " ) + std::string( "region from the boundary probability, i.e. " ) + std::string( "boundaryProbability*exp( -lambda * distance )." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "label-propagation" ); option->SetShortName( 'l' ); option->SetUsageOption( 0, "whichLabel[lambda=0.0,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int Atropos( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "Atropos" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "A finite mixture modeling (FMM) segmentation approach " ) + std::string( "with possibilities for specifying prior constraints. " ) + std::string( "These prior constraints include the specification " ) + std::string( "of a prior label image, prior probability images " ) + std::string( "(one for each class), and/or an MRF prior to " ) + std::string( "enforce spatial smoothing of the labels. Similar algorithms " ) + std::string( "include FAST and SPM. Reference: Avants BB, Tustison NJ, Wu " ) + std::string( "J, Cook PA, Gee JC. An open source multivariate framework for " ) + std::string( "n-tissue segmentation with evaluation on public data. " ) + std::string( "Neuroinformatics. 2011 Dec;9(4):381-400." ); parser->SetCommandDescription( commandDescription ); AtroposInitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc == 1 ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Get dimensionality unsigned int dimension = 3; itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "image-dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { // Read in the first intensity image to get the image dimension. std::string filename; itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "intensity-image" ); if( imageOption && imageOption->GetNumberOfFunctions() ) { if( imageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { filename = imageOption->GetFunction( 0 )->GetParameter( 0 ); } else { filename = imageOption->GetFunction( 0 )->GetName(); } } else { std::cerr << "No input images were specified. Specify an input image" << " with the -a option" << std::endl; return EXIT_FAILURE; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); dimension = imageIO->GetNumberOfDimensions(); } switch( dimension ) { case 2: return AtroposSegmentation<2>( parser ); break; case 3: return AtroposSegmentation<3>( parser ); break; case 4: return AtroposSegmentation<4>( parser ); break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/AverageAffineTransform.cxx000066400000000000000000000256541311104306400213460ustar00rootroot00000000000000// compute the average of a list of affine transform #include "antsUtilities.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMatrixOffsetTransformBase.h" #include "itkTransformFactory.h" #include "itkAverageAffineTransformFunction.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" namespace ants { static bool AverageAffineTransform_ParseInput(int argc, char * *argv, char *& output_transform_filename, char *& reference_transform_filename, TRAN_OPT_QUEUE & opt_queue) { opt_queue.clear(); opt_queue.reserve(argc); output_transform_filename = argv[0]; reference_transform_filename = ITK_NULLPTR; int ind = 1; while( ind < argc ) { if( strcmp(argv[ind], "-R") == 0 ) { ind++; if( ind >= argc ) { return false; } reference_transform_filename = argv[ind]; } else if( strcmp(argv[ind], "-i") == 0 ) { ind++; if( ind >= argc ) { return false; } TRAN_OPT opt; opt.filename = argv[ind]; if( CheckFileType(opt.filename) != AFFINE_FILE ) { std::cerr << "file: " << opt.filename << " is not an affine .txt file. Invalid to use '-i' " << std::endl; return EXIT_FAILURE; } opt.file_type = AFFINE_FILE; opt.do_affine_inv = true; opt.weight = 1.0; // default value if( ind < argc - 1 ) // test if still has extra parameters { double weight; if( get_a_double_number(argv[ind + 1], weight) ) { ind++; opt.weight = weight; } } opt_queue.push_back(opt); } else { TRAN_OPT opt; opt.filename = argv[ind]; if( CheckFileType(opt.filename) != AFFINE_FILE ) { std::cerr << "file: " << opt.filename << " is not an affine .txt file." << std::endl; return EXIT_FAILURE; } opt.file_type = CheckFileType(opt.filename); opt.do_affine_inv = false; opt.weight = 1.0; // default value if( ind < argc - 1 ) // test if still has extra parameters { double weight; if( get_a_double_number(argv[ind + 1], weight) ) { ind++; opt.weight = weight; } } opt_queue.push_back(opt); } ind++; } // if (reference_image_filename == NULL) { // std::cout << "the reference image file (-R) must be given!!!" // << std::endl; // return false; // } return true; } template void AverageAffineTransform(char *output_affine_txt, char *reference_affine_txt, TRAN_OPT_QUEUE & opt_queue) { // typedef itk::Image ImageType; // typedef itk::Vector VectorType; // typedef itk::Image DisplacementFieldType; typedef itk::MatrixOffsetTransformBase AffineTransformType; // typedef itk::WarpImageMultiTransformFilter WarperType; typedef itk::AverageAffineTransformFunction WarperType; itk::TransformFactory::RegisterTransform(); // typedef itk::ImageFileReader ImageFileReaderType; // typename ImageFileReaderType::Pointer reader_img = ImageFileReaderType::New(); // typename ImageType::Pointer img_ref = ImageType::New(); // typename ImageFileReaderType::Pointer reader_img_ref = ImageFileReaderType::New(); WarperType average_func; // warper->SetInput(img_mov); // warper->SetEdgePaddingValue( 0); // VectorType pad; // pad.Fill(0); // warper->SetEdgePaddingValue(pad); typedef itk::TransformFileReader TranReaderType; // typedef itk::ImageFileReader FieldReaderType; int cnt_affine = 0; const int kOptQueueSize = opt_queue.size(); for( int i = 0; i < kOptQueueSize; i++ ) { const TRAN_OPT & opt = opt_queue[i]; switch( opt.file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); typename AffineTransformType::Pointer aff = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); if( opt_queue[i].do_affine_inv ) { aff->GetInverse(aff); } // std::cout << aff << std::endl; double weight = opt.weight; average_func.PushBackAffineTransform(aff, weight); cnt_affine++; break; } case DEFORMATION_FILE: { std::cout << "Average affine only files: ignore " << opt.filename << std::endl; } break; default: std::cerr << "Unknown file type!" << std::endl; } } typedef typename WarperType::PointType PointType; PointType aff_center; typename AffineTransformType::Pointer aff_ref_tmp; if( reference_affine_txt ) { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(reference_affine_txt); tran_reader->Update(); aff_ref_tmp = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); } else { if( cnt_affine > 0 ) { std::cout << "the reference affine file for center is selected as the first affine!" << std::endl; aff_ref_tmp = average_func.GetTransformList().begin()->aff; } else { std::cerr << "No affine input is given. nothing to do ......" << std::endl; return; } } aff_center = aff_ref_tmp->GetCenter(); std::cout << "new center is : " << aff_center << std::endl; // warper->PrintTransformList(); // typename AffineTransformType::Pointer aff_output = warper->ComposeAffineOnlySequence(aff_center); typename AffineTransformType::Pointer aff_output = AffineTransformType::New(); average_func.AverageMultipleAffineTransform(aff_center, aff_output); typedef itk::TransformFileWriter TranWriterType; typename TranWriterType::Pointer tran_writer = TranWriterType::New(); tran_writer->SetFileName(output_affine_txt); tran_writer->SetInput(aff_output); tran_writer->Update(); std::cout << "wrote file to : " << output_affine_txt << std::endl; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int AverageAffineTransform( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "AverageAffineTransform" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc <= 3 ) { std::cerr << "AverageAffineTransform ImageDimension output_affine_transform [-R reference_affine_transform] " << "{[-i] affine_transform_txt [weight(=1)] ]}" << std::endl << std::endl << " Usage: Compute weighted average of input affine transforms. " << std::endl << "For 2D and 3D transform, the affine transform is first decomposed into " "scale x shearing x rotation. Then these parameters are averaged, using the weights if they provided. " "For 3D transform, the rotation component is the quaternion. After averaging, the quaternion will also " "be normalized to have unit norm. For 2D transform, the rotation component is the rotation angle. " "The weight for each transform is a non-negative number. The sum of all weights will be normalized to 1 " "before averaging. The default value for each weight is 1.0. " << std::endl << std::endl << "All affine transforms is a \"centerd\" transform, following ITK convention. A reference_affine_transform" " defines the center for the output transform. The first provided transform is the default reference " "transform" << std::endl << "Output affine transform is a MatrixOffsetBaseTransform." << std::endl << " -i option takes the inverse of the affine mapping." << std::endl << " For example: " << std::endl << " 2 output_affine.txt -R A.txt A1.txt 1.0 -i A2.txt 2.0 A3.txt A4.txt 6.0 A5.txt" << std::endl << "This computes: (1*A1 + 2*(A2)^-1 + A3 + A4*6 + A5 ) / (1+2+1+6+5)" << std::endl; return EXIT_SUCCESS; } TRAN_OPT_QUEUE opt_queue; char *output_transform_filename = ITK_NULLPTR; char *reference_transform_filename = ITK_NULLPTR; int kImageDim = atoi(argv[1]); const bool is_parsing_ok = AverageAffineTransform_ParseInput(argc - 2, argv + 2, output_transform_filename, reference_transform_filename, opt_queue); if( is_parsing_ok ) { std::cout << "output_transform_filename: " << output_transform_filename << std::endl; std::cout << "reference_transform_filename: "; if( reference_transform_filename ) { std::cout << reference_transform_filename << std::endl; } else { std::cout << "NULL" << std::endl; } DisplayOptQueue(opt_queue); switch( kImageDim ) { case 2: { AverageAffineTransform<2>(output_transform_filename, reference_transform_filename, opt_queue); } break; case 3: { AverageAffineTransform<3>(output_transform_filename, reference_transform_filename, opt_queue); } break; } } else { std::cerr << "Input error!" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/AverageAffineTransformNoRigid.cxx000066400000000000000000000260061311104306400226120ustar00rootroot00000000000000// compute the average of a list of affine transform #include "antsUtilities.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMatrixOffsetTransformBase.h" #include "itkTransformFactory.h" #include "itkAverageAffineTransformNoRigidFunction.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" namespace ants { static bool AverageAffineTransformNoRigid_ParseInput(int argc, char * *argv, char *& output_transform_filename, char *& reference_transform_filename, TRAN_OPT_QUEUE & opt_queue) { opt_queue.clear(); opt_queue.reserve(argc); output_transform_filename = argv[0]; reference_transform_filename = ITK_NULLPTR; int ind = 1; while( ind < argc ) { if( strcmp(argv[ind], "-R") == 0 ) { ind++; if( ind >= argc ) { return false; } reference_transform_filename = argv[ind]; } else if( strcmp(argv[ind], "-i") == 0 ) { ind++; if( ind >= argc ) { return false; } TRAN_OPT opt; opt.filename = argv[ind]; if( CheckFileType(opt.filename) != AFFINE_FILE ) { std::cerr << "file: " << opt.filename << " is not an affine .txt file. Invalid to use '-i' " << std::endl; return false; } opt.file_type = AFFINE_FILE; opt.do_affine_inv = true; opt.weight = 1.0; // default value if( ind < argc - 1 ) // test if still has extra parameters { double weight; if( get_a_double_number(argv[ind + 1], weight) ) { ind++; opt.weight = weight; } } opt_queue.push_back(opt); } else { TRAN_OPT opt; opt.filename = argv[ind]; if( CheckFileType(opt.filename) != AFFINE_FILE ) { std::cerr << "file: " << opt.filename << " is not an affine .txt file." << std::endl; return false; } opt.file_type = CheckFileType(opt.filename); opt.do_affine_inv = false; opt.weight = 1.0; // default value if( ind < argc - 1 ) // test if still has extra parameters { double weight; if( get_a_double_number(argv[ind + 1], weight) ) { ind++; opt.weight = weight; } } opt_queue.push_back(opt); } ind++; } // if (reference_image_filename == NULL) { // std::cout << "the reference image file (-R) must be given!!!" // << std::endl; // return false; // } return true; } template void AverageAffineTransformNoRigid(char *output_affine_txt, char *reference_affine_txt, TRAN_OPT_QUEUE & opt_queue) { // typedef itk::Image ImageType; // typedef itk::Vector VectorType; // typedef itk::Image DisplacementFieldType; typedef itk::MatrixOffsetTransformBase AffineTransformType; // typedef itk::WarpImageMultiTransformFilter WarperType; typedef itk::AverageAffineTransformNoRigidFunction WarperType; itk::TransformFactory::RegisterTransform(); // typedef itk::ImageFileReader ImageFileReaderType; // typename ImageFileReaderType::Pointer reader_img = ImageFileReaderType::New(); // typename ImageType::Pointer img_ref = ImageType::New(); // typename ImageFileReaderType::Pointer reader_img_ref = ImageFileReaderType::New(); WarperType average_func; // warper->SetInput(img_mov); // warper->SetEdgePaddingValue( 0); // VectorType pad; // pad.Fill(0); // warper->SetEdgePaddingValue(pad); typedef itk::TransformFileReader TranReaderType; // typedef itk::ImageFileReader FieldReaderType; int cnt_affine = 0; const int kOptQueueSize = opt_queue.size(); for( int i = 0; i < kOptQueueSize; i++ ) { const TRAN_OPT & opt = opt_queue[i]; switch( opt.file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); typename AffineTransformType::Pointer aff = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); if( opt_queue[i].do_affine_inv ) { aff->GetInverse(aff); } // std::cout << aff << std::endl; double weight = opt.weight; average_func.PushBackAffineTransform(aff, weight); cnt_affine++; break; } case DEFORMATION_FILE: { std::cout << "Average affine only files: ignore " << opt.filename << std::endl; } break; default: std::cout << "Unknown file type!" << std::endl; } } typedef typename WarperType::PointType PointType; PointType aff_center; typename AffineTransformType::Pointer aff_ref_tmp; if( reference_affine_txt ) { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(reference_affine_txt); tran_reader->Update(); aff_ref_tmp = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); } else { if( cnt_affine > 0 ) { std::cout << "the reference affine file for center is selected as the first affine!" << std::endl; aff_ref_tmp = average_func.GetTransformList().begin()->aff; } else { std::cout << "No affine input is given. nothing to do ......" << std::endl; return; } } aff_center = aff_ref_tmp->GetCenter(); std::cout << "new center is : " << aff_center << std::endl; // warper->PrintTransformList(); // typename AffineTransformType::Pointer aff_output = warper->ComposeAffineOnlySequence(aff_center); typename AffineTransformType::Pointer aff_output = AffineTransformType::New(); average_func.AverageMultipleAffineTransform(aff_center, aff_output); typedef itk::TransformFileWriter TranWriterType; typename TranWriterType::Pointer tran_writer = TranWriterType::New(); tran_writer->SetFileName(output_affine_txt); tran_writer->SetInput(aff_output); tran_writer->Update(); std::cout << "wrote file to : " << output_affine_txt << std::endl; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int AverageAffineTransformNoRigid( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "AverageAffineTransformNoRigid" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc <= 3 ) { std::cerr << "AverageAffineTransformNoRigid ImageDimension output_affine_transform [-R reference_affine_transform] " << "{[-i] affine_transform_txt [weight(=1)] ]}" << std::endl << std::endl << " Usage: Compute weighted average of input affine transforms. " << std::endl << "For 2D and 3D transform, the affine transform is first decomposed into " "scale x shearing x rotation. Then these parameters are averaged, using the weights if they provided. " "For 3D transform, the rotation component is the quaternion. After averaging, the quaternion will also " "be normalized to have unit norm. For 2D transform, the rotation component is the rotation angle. " "The weight for each transform is a non-negative number. The sum of all weights will be normalized to 1 " "before averaging. The default value for each weight is 1.0. " << std::endl << std::endl << "All affine transforms is a \"centerd\" transform, following ITK convention. A reference_affine_transform" " defines the center for the output transform. The first provided transform is the default reference " "transform" << std::endl << "Output affine transform is a MatrixOffsetBaseTransform." << std::endl << " -i option takes the inverse of the affine mapping." << std::endl << " For example: " << std::endl << " 2 output_affine.txt -R A.txt A1.txt 1.0 -i A2.txt 2.0 A3.txt A4.txt 6.0 A5.txt" << std::endl << "This computes: (1*A1 + 2*(A2)^-1 + A3 + A4*6 + A5 ) / (1+2+1+6+5)" << std::endl; return EXIT_SUCCESS; } TRAN_OPT_QUEUE opt_queue; char *output_transform_filename = ITK_NULLPTR; char *reference_transform_filename = ITK_NULLPTR; int kImageDim = atoi(argv[1]); const bool is_parsing_ok = AverageAffineTransformNoRigid_ParseInput(argc - 2, argv + 2, output_transform_filename, reference_transform_filename, opt_queue); if( is_parsing_ok ) { std::cout << "output_transform_filename: " << output_transform_filename << std::endl; std::cout << "reference_transform_filename: "; if( reference_transform_filename ) { std::cout << reference_transform_filename << std::endl; } else { std::cout << "NULL" << std::endl; } DisplayOptQueue(opt_queue); switch( kImageDim ) { case 2: { AverageAffineTransformNoRigid<2>(output_transform_filename, reference_transform_filename, opt_queue); } break; case 3: { AverageAffineTransformNoRigid<3>(output_transform_filename, reference_transform_filename, opt_queue); } break; } } else { std::cerr << "Input error!" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/AverageImages.cxx000066400000000000000000000331641311104306400174620ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ // We divide the 2nd input image by its mean and add it to the first // input image with weight 1/n. // The output overwrites the 1st img with the sum. // Note: could easily add variance computation // http://people.revoledu.com/kardi/tutorial/RecursiveStatistic/Time-Variance.htm #include "antsUtilities.h" #include "itkArray.h" #include "itkVariableLengthVector.h" #include "itkImage.h" #include "itkImageRegionConstIterator.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkOptimalSharpeningImageFilter.h" #include "itkLaplacianSharpeningImageFilter.h" #include "itkResampleImageFilter.h" #include namespace ants { template int AverageImages1(unsigned int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::ImageFileReader ImageFileReader; typedef itk::ImageFileWriter writertype; { const std::string temp(argv[1]); if( !( ( temp == "2" ) || ( temp == "3" ) || ( temp == "4" ) ) ) { std::cerr << "ERROR: Dimension option must be 2 or 3 or 4, " << temp << "given" << std::endl; return EXIT_FAILURE; } } { const std::string temp(argv[3]); if( !( ( temp == "0" ) || ( temp == "1" ) ) ) { std::cerr << "ERROR: Normalize option must be 0 or 1, " << temp << "given" << std::endl; return EXIT_FAILURE; } } const bool normalizei = atoi(argv[3]); const float numberofimages = (float)argc - 4.; typename ImageType::SizeType maxSize; maxSize.Fill( 0 ); unsigned int bigimage = 0; for( unsigned int j = 4; j < argc; j++ ) { // Get the image dimension const std::string fn = std::string(argv[j]); typename itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); for( unsigned int i = 0; i < ImageType::ImageDimension; i++ ) { itk::SizeValueType currentDimensionSize = imageIO->GetDimensions( i ); if( currentDimensionSize > maxSize[i] ) { maxSize[i] = currentDimensionSize; bigimage = j; } } } std::cout << " bigimage " << bigimage << " maxSize " << maxSize << std::endl; typename ImageFileReader::Pointer reader = ImageFileReader::New(); reader->SetFileName(argv[bigimage]); reader->Update(); typename ImageType::Pointer averageimage = reader->GetOutput(); std::cout << " Setting physcal space of output average image based on largest image " << std::endl; unsigned int vectorlength = reader->GetImageIO()->GetNumberOfComponents(); std::cout << " Averaging " << numberofimages << " images with dim = " << ImageDimension << " vector components " << vectorlength << std::endl; PixelType meanval = 0; averageimage->FillBuffer(meanval); // Reset all images to a mean of zero on the accumulator buffer. for( unsigned int j = 4; j < argc; j++ ) { std::cout << " reading " << std::string(argv[j]) << std::endl; typename ImageFileReader::Pointer rdr = ImageFileReader::New(); rdr->SetFileName(argv[j]); rdr->Update(); typedef itk::ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); // default to identity resampler->SetTransform( transform ); // default to linearinterp resampler->SetInterpolator( interpolator ); resampler->SetInput( rdr->GetOutput() ); resampler->SetOutputParametersFromImage( averageimage ); resampler->Update(); typename ImageType::Pointer image2 = resampler->GetOutput(); Iterator vfIter2( image2, image2->GetLargestPossibleRegion() ); unsigned long ct = 0; if( normalizei ) { meanval = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { const PixelType & localp = image2->GetPixel( vfIter2.GetIndex() ); meanval = meanval + localp; ct++; } if( ct > 0 ) { meanval = meanval / (float)ct; } if( meanval <= 0 ) { meanval = (1); } } for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { PixelType val = vfIter2.Get(); if( normalizei ) { val /= meanval; } val = val / (float)numberofimages; const PixelType & oldval = averageimage->GetPixel(vfIter2.GetIndex() ); averageimage->SetPixel(vfIter2.GetIndex(), val + oldval ); } } // typedef itk::OptimalSharpeningImageFilter sharpeningFilter; typedef itk::LaplacianSharpeningImageFilter sharpeningFilter; typename sharpeningFilter::Pointer shFilter = sharpeningFilter::New(); if( normalizei && argc > 3 && vectorlength == 1 ) { shFilter->SetInput( averageimage ); // shFilter->SetSValue(0.5); averageimage = shFilter->GetOutput(); } std::cout << " writing output "; { typename writertype::Pointer writer = writertype::New(); writer->SetFileName(argv[2]); writer->SetInput( averageimage ); writer->Update(); } return EXIT_SUCCESS; } template int AverageImages(unsigned int argc, char *argv[]) { typedef itk::Vector PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::ImageFileReader ImageFileReader; typedef itk::ImageFileWriter writertype; // bool normalizei = atoi(argv[3]); float numberofimages = (float)argc - 4.; typename ImageType::Pointer averageimage = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::SizeType size; size.Fill( 0 ); typename ImageType::SizeType maxSize; maxSize.Fill( 0 ); unsigned int bigimage = 4; for( unsigned int j = 4; j < argc; j++ ) { // Get the image dimension std::string fn = std::string(argv[j]); std::cout << " fn " << fn << " " << ImageDimension << " " << NVectorComponents << std::endl; typename itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName( fn.c_str() ); imageIO->ReadImageInformation(); for( unsigned int i = 0; i < imageIO->GetNumberOfDimensions(); i++ ) { size[i] = imageIO->GetDimensions( i ); } for( unsigned int i = 0; i < imageIO->GetNumberOfDimensions(); i++ ) { if( size[i] > maxSize[i] ) { maxSize[i] = size[i]; bigimage = j; std::cout << " bigimage " << j << " size " << size << std::endl; } } } std::cout << " largest image " << size << std::endl; typename ImageFileReader::Pointer reader = ImageFileReader::New(); reader->SetFileName(argv[bigimage]); reader->Update(); averageimage = reader->GetOutput(); unsigned int vectorlength = reader->GetImageIO()->GetNumberOfComponents(); std::cout << " Averaging " << numberofimages << " images with dim = " << ImageDimension << " vector components " << vectorlength << std::endl; typename ImageType::IndexType zindex; zindex.Fill(0); PixelType meanval = reader->GetOutput()->GetPixel(zindex); meanval.Fill(0); averageimage->FillBuffer(meanval); for( unsigned int j = 4; j < argc; j++ ) { std::cout << " reading " << std::string(argv[j]) << " for average " << std::endl; typename ImageFileReader::Pointer rdr = ImageFileReader::New(); rdr->SetFileName(argv[j]); rdr->Update(); image2 = rdr->GetOutput(); Iterator vfIter2( image2, image2->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { PixelType val = vfIter2.Get(); double valnorm = val.GetNorm(); if( !vnl_math_isnan( valnorm ) && !vnl_math_isinf( valnorm ) ) { val = val / (float)numberofimages; PixelType oldval = averageimage->GetPixel( vfIter2.GetIndex() ); averageimage->SetPixel(vfIter2.GetIndex(), val + oldval ); } } } { typename writertype::Pointer writer = writertype::New(); writer->SetFileName(argv[2]); writer->SetInput( averageimage ); writer->Update(); } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int AverageImages( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "AverageImages" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 5 ) { std::cout << "\n" << std::endl; std::cout << "Usage: \n" << std::endl; std::cout << argv[0] << " ImageDimension Outputfname.nii.gz Normalize \n" << std::endl; std::cout << " Compulsory arguments: \n" << std::endl; std::cout << " ImageDimension: 2 or 3 (for 2 or 3 dimensional input).\n " << std::endl; std::cout << " Outputfname.nii.gz: the name of the resulting image.\n" << std::endl; std::cout << " Normalize: 0 (false) or 1 (true); if true, the 2nd image is divided by its mean. This will select the largest image to average into.\n" << std::endl; std::cout << " Example Usage:\n" << std::endl; std::cout << argv[0] << " 3 average.nii.gz 1 *.nii.gz \n" << std::endl; std::cout << " \n" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } const int dim = atoi( argv[1] ); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(argv[4], itk::ImageIOFactory::ReadMode); imageIO->SetFileName(argv[4]); imageIO->ReadImageInformation(); unsigned int ncomponents = imageIO->GetNumberOfComponents(); // Get the image dimension switch( dim ) { case 2: { switch( ncomponents ) { case 2: { return AverageImages<2, 2>(argc, argv); } break; default: { return AverageImages1<2, 1>(argc, argv); } break; } } break; case 3: { switch( ncomponents ) { case 7: { return AverageImages<3, 7>(argc, argv); } break; case 6: { return AverageImages<3, 6>(argc, argv); } break; case 3: { return AverageImages<3, 3>(argc, argv); } break; case 2: { return AverageImages<3, 2>(argc, argv); } break; default: { return AverageImages1<3, 1>(argc, argv); } break; } } break; case 4: { switch( ncomponents ) { case 7: { return AverageImages<4, 7>(argc, argv); } break; case 6: { return AverageImages<4, 6>(argc, argv); } break; case 4: { return AverageImages<4, 4>(argc, argv); } break; case 3: { return AverageImages<4, 3>(argc, argv); } break; default: { return AverageImages1<4, 1>(argc, argv); } break; } } break; default: std::cout << " You passed ImageDimension: " << dim << " . Please use only image domains of 2, 3 or 4 " << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/AverageTensorImages.cxx000066400000000000000000000124361311104306400206540ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include #include "stdio.h" #include "itkImage.h" #include "itkImageRegionIteratorWithIndex.h" #include "ReadWriteData.h" #include "TensorFunctions.h" namespace ants { template int AverageTensorImages(unsigned int argc, char *argv[]) { // typedef itk::Vector TensorType; typedef itk::SymmetricSecondRankTensor TensorType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex IteratorType; char * outputName = argv[2]; int mathtype = atoi(argv[3]); float numberofimages = (float)argc - 4.0; std::cout << "Averaging " << numberofimages << " images " << std::endl; typename ImageType::Pointer averageimage = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::SizeType size; size.Fill(0); unsigned int bigimage = 0; for( unsigned int j = 4; j < argc; j++ ) { // Get the image dimension std::string fn = std::string(argv[j]); std::cout << " fn " << fn << std::endl; typename itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); for( unsigned int i = 0; i < imageIO->GetNumberOfDimensions(); i++ ) { if( imageIO->GetDimensions(i) > size[i] ) { size[i] = imageIO->GetDimensions(i); bigimage = j; std::cout << " bigimage " << j << " size " << size << std::endl; } } } std::cout << " largest image " << size << std::endl; bool logeuc = true; if( mathtype == 1 ) { logeuc = false; } TensorType nullTensor; nullTensor[0] = nullTensor[1] = nullTensor[2] = nullTensor[3] = nullTensor[4] = nullTensor[5] = 0; ReadTensorImage(averageimage, argv[bigimage], logeuc); averageimage->FillBuffer(nullTensor); for( unsigned int j = 4; j < argc; j++ ) { std::string fn = std::string(argv[j]); ReadTensorImage(image2, fn.c_str(), logeuc); IteratorType vfIter( image2, image2->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { TensorType val = vfIter.Get() / numberofimages; averageimage->SetPixel(vfIter.GetIndex(), val + averageimage->GetPixel(vfIter.GetIndex() ) ); } } WriteTensorImage(averageimage, outputName, logeuc); return EXIT_SUCCESS; } // Main Program // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int AverageTensorImages( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "AverageTensorImages" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); try { if( argc - 4 < 1 ) { std::cerr << "Basic useage ex: " << std::endl; std::cerr << argv[0] << " ImageDimension average.nii mathtype list-of-files-via-wildcard " << std::endl; std::cerr << " e.g. \n AverageTensorImages 3 average.nii 1 *registered.nii " << std::endl; std::cerr << " mathtype=[0=log-euclidean, 1=euclidean] " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } int dim = atoi(argv[1]); // char * outputName = argv[2]; // int mathtype = atoi(argv[3]); // int numberofimages = argc - 4; // Get the image dimension switch( dim ) { case 2: { return AverageTensorImages<2>(argc, argv); } break; case 3: { return AverageTensorImages<3>(argc, argv); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } catch( itk::ExceptionObject & err ) { std::cerr << "ExceptionObject caught !" << std::endl; std::cerr << err << std::endl; return EXIT_FAILURE; } } } // namespace ants ants-2.2.0/Examples/CMakeLists.txt000066400000000000000000000170041311104306400167710ustar00rootroot00000000000000## Note that the antsUtilities can always be built static. It will then be linked ## Directly into the other libraries. add_library(antsUtilities STATIC antsUtilities.cxx antsRegistrationTemplateHeader.cxx antsRegistration2DDouble.cxx antsRegistration2DFloat.cxx antsRegistration3DDouble.cxx antsRegistration3DFloat.cxx antsRegistration4DDouble.cxx antsRegistration4DFloat.cxx ../Utilities/ReadWriteData.cxx ../Utilities/antsCommandLineOption.cxx ../Utilities/antsCommandLineParser.cxx ANTsVersion.cxx ImageMathHelper.cxx ImageMathHelper2D.cxx ImageMathHelper3D.cxx ImageMathHelper4D.cxx ) target_link_libraries(antsUtilities ${ITK_LIBRARIES} ) install(TARGETS antsUtilities RUNTIME DESTINATION ${BIN_INSTALL_DIR} COMPONENT RUNTIME_antsUtilities LIBRARY DESTINATION ${LIB_INSTALL_DIR} COMPONENT RUNTIME_antsUtilities ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT DEVELOPMENT_antsUtilities ) macro(STANDARD_ANTS_BUILD ANTS_FUNCTION_NAME EXTRA_LIBS) set( ANTS_FUNCTION_NAME ${ANTS_FUNCTION_NAME} ) add_library(l_${ANTS_FUNCTION_NAME} ${ANTS_FUNCTION_NAME}.cxx) SET_TARGET_PROPERTIES(l_${ANTS_FUNCTION_NAME} PROPERTIES SOVERSION ${LIBRARY_SOVERSION_INFO} VERSION ${LIBRARY_VERSION_INFO}) message(STATUS "${ANTS_FUNCTION_NAME} ${EXTRA_LIBS}") target_link_libraries(l_${ANTS_FUNCTION_NAME} antsUtilities ${EXTRA_LIBS} ) configure_file( template_for_executables.cxx.in cli_${ANTS_FUNCTION_NAME}.cxx ) add_executable( ${ANTS_FUNCTION_NAME} cli_${ANTS_FUNCTION_NAME}.cxx ) target_link_libraries( ${ANTS_FUNCTION_NAME} l_${ANTS_FUNCTION_NAME} ) install(TARGETS l_${ANTS_FUNCTION_NAME} ${ANTS_FUNCTION_NAME} RUNTIME DESTINATION ${BIN_INSTALL_DIR} COMPONENT RUNTIME_${ANTS_FUNCTION_NAME} LIBRARY DESTINATION ${LIB_INSTALL_DIR} COMPONENT RUNTIME_${ANTS_FUNCTION_NAME} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT DEVELOPMENT_${ANTS_FUNCTION_NAME} ) endmacro() #set(PROG_WITH_NO_FLAG simpleSynRegistration) #add_executable(${PROG_WITH_NO_FLAG} ${PROG_WITH_NO_FLAG}.cxx) #target_link_libraries(${PROG_WITH_NO_FLAG} antsUtilities ${ITK_LIBRARIES}) set(CORE_ANTS_APPS antsAffineInitializer antsJointFusion SurfaceBasedSmoothing CreateJacobianDeterminantImage ThresholdImage ResampleImage sccan N4BiasFieldCorrection N3BiasFieldCorrection KellyKapowski antsRegistration antsMotionCorrStats antsMotionCorr antsApplyTransforms LabelGeometryMeasures LabelClustersUniquely Atropos antsApplyTransformsToPoints ) set(MORE_ANTS_APPS antsAI antsJointTensorFusion ImageMath iMath ANTS ANTSJacobian PrintHeader ResetDirection ANTSUseLandmarkImagesToGetAffineTransform ANTSUseLandmarkImagesToGetBSplineDisplacementField ANTSUseDeformationFieldToGetAffineTransform antsLandmarkBasedTransformInitializer LaplacianThickness SetOrigin SetSpacing SetDirectionByMatrix SurfaceCurvature ConvertScalarImageToRGB CreateWarpedGridImage MeasureImageSimilarity ConvertToJpg ConvertImage ConvertImagePixelType ConvertInputImagePixelTypeToFloat FitBSplineToPoints AverageTensorImages ImageSetStatistics MultiplyImages SmoothDisplacementField SmoothImage ClusterImageStatistics LabelOverlapMeasures LesionFilling MeasureMinMaxMean WarpImageMultiTransform ComposeMultiTransform MemoryTest PermuteFlipImageOrientationAxes ImageCompare ResampleImageBySpacing CopyImageHeaderInformation WarpTimeSeriesImageMultiTransform ExtractSliceFromImage ExtractRegionFromImage ExtractRegionFromImageByMask PasteImageIntoImage TileImages CreateTiledMosaic CreateImage DenoiseImage NonLocalSuperResolution WarpTensorImageMultiTransform ReorientTensorImage RebaseTensorImage KellySlater CreateDTICohort antsAlignOrigin antsMotionCorrDiffusionDirection antsSliceRegularizedRegistration ANTSIntegrateVectorField ANTSIntegrateVelocityField antsTransformInfo antsUtilitiesTesting AverageAffineTransform AverageAffineTransformNoRigid AverageImages simpleSynRegistration CompositeTransformUtil CreateDisplacementField ConvertTransformFile compareTwoTransforms SuperResolution TimeSCCAN TextureCooccurrenceFeatures TextureRunLengthFeatures ImageIntensityStatistics GetConnectedComponentsFeatureImages DeNrrd StackSlices ) foreach(ANTS_APP ${CORE_ANTS_APPS}) STANDARD_ANTS_BUILD(${ANTS_APP} "") endforeach() if (BUILD_ALL_ANTS_APPS) foreach(ANTS_APP ${MORE_ANTS_APPS}) STANDARD_ANTS_BUILD(${ANTS_APP} "") endforeach() else(BUILD_ALL_ANTS_APPS) foreach(ANTS_APP ${MORE_ANTS_APPS}) if(ANTS_BUILD_${ANTS_APP}) STANDARD_ANTS_BUILD(${ANTS_APP} "") endif() endforeach() endif(BUILD_ALL_ANTS_APPS) if(USE_VTK) set(VTK_ANTS_APPS # ConvertVectorFieldToVTK antsSurf antsVol GetMeshAndTopology CheckTopology WarpVTKPolyDataMultiTransform # FLATFEM # ConformalMapping # ANTSConformalMapping ) foreach(ANTS_APP ${VTK_ANTS_APPS}) STANDARD_ANTS_BUILD(${ANTS_APP} "${VTK_LIBRARIES}") endforeach() endif(USE_VTK) install(PROGRAMS ../Scripts/ANTSpexec.sh ../Scripts/antsASLProcessing.sh ../Scripts/antsAtroposN4.sh ../Scripts/antsBOLDNetworkAnalysis.R ../Scripts/antsBrainExtraction.sh ../Scripts/antsCorticalThickness.sh ../Scripts/antsIntermodalityIntrasubject.sh ../Scripts/antsIntroduction.sh ../Scripts/antsLaplacianBoundaryCondition.R ../Scripts/antsLongitudinalCorticalThickness.sh ../Scripts/antsJointLabelFusion.sh ../Scripts/antsMultivariateTemplateConstruction.sh ../Scripts/antsMultivariateTemplateConstruction2.sh ../Scripts/antsNetworkAnalysis.R ../Scripts/antsNeuroimagingBattery ../Scripts/antsRegistrationSyN.sh ../Scripts/antsRegistrationSyNQuick.sh ../Scripts/waitForPBSQJobs.pl ../Scripts/waitForSGEQJobs.pl ../Scripts/waitForXGridJobs.pl ../Scripts/waitForSlurmJobs.pl DESTINATION ${SCRIPTS_INSTALL_DIR} PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE CONFIGURATIONS Release COMPONENT SCRIPTS ) if( COPY_SCRIPT_FILES_TO_BIN_DIR ) message( "-- Copy scripts." ) file( GLOB SCRIPT_FILES "../Scripts/*.sh" ) file( GLOB PSCRIPT_FILES "../Scripts/*.pl" ) foreach( SCRIPT_FILE ${SCRIPT_FILES} ) message( " Copying ${SCRIPT_FILE} to ${CMAKE_CURRENT_BINARY_DIR}/../../bin/" ) CONFIGURE_FILE( ${SCRIPT_FILE} ${CMAKE_CURRENT_BINARY_DIR}/../../bin/ COPYONLY ) endforeach( SCRIPT_FILE ) foreach( SCRIPT_FILE ${PSCRIPT_FILES} ) message( " Copying ${SCRIPT_FILE} to ${CMAKE_CURRENT_BINARY_DIR}/../../bin/" ) CONFIGURE_FILE( ${SCRIPT_FILE} ${CMAKE_CURRENT_BINARY_DIR}/../../bin/ COPYONLY ) endforeach( SCRIPT_FILE ) endif( COPY_SCRIPT_FILES_TO_BIN_DIR ) ## Build test option if(BUILD_TESTING) add_subdirectory(TestSuite) endif(BUILD_TESTING) set(CPACK_PACKAGE_NAME "ANTs") set(CPACK_PACKAGE_VENDOR "CMake.org") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ANTs - Advanced Normalization Tools") set(CPACK_PACKAGE_VERSION 2.1.0) set(CPACK_PACKAGE_VERSION_MAJOR 2) set(CPACK_PACKAGE_VERSION_MINOR 1) set(CPACK_PACKAGE_VERSION_PATCH 0) set(CPACK_PACKAGE_INSTALL_DIRECTORY "ANTS") set(CPACK_BINARY_GENERATORS "DragNDrop TGZ TZ") # This must always be last! include(CPack) ants-2.2.0/Examples/CheckTopology.cxx000066400000000000000000000277431311104306400175420ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Copyright (c) 2002 Insight 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. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include #include #include #include #include #include "itkDiscreteGaussianImageFilter.h" #include "itkImage.h" #include "itkExceptionObject.h" #include "ReadWriteData.h" #include "itkRandomImageSource.h" #include "itkImageRandomConstIteratorWithIndex.h" #include "itkImageLinearIteratorWithIndex.h" #include "itkShapedNeighborhoodIterator.h" #include "BinaryImageToMeshFilter.h" #include "vtkCallbackCommand.h" #include "vtkPointPicker.h" #include "vtkCellPicker.h" #include "vtkExtractEdges.h" #include "itkMinimumMaximumImageFilter.h" #include "itkConnectedComponentImageFilter.h" #include "itkRelabelComponentImageFilter.h" #include "itkLabelStatisticsImageFilter.h" namespace ants { float random_range(float lowest_number, float highest_number) { float range = highest_number - lowest_number; return lowest_number + (float)(range * (float)rand() / (float)(RAND_MAX) ); } float ComputeGenus(vtkPolyData* pd1) { vtkExtractEdges* edgeex = vtkExtractEdges::New(); edgeex->SetInputData(pd1); edgeex->Update(); vtkPolyData* edg1 = edgeex->GetOutput(); vtkIdType nedg = edg1->GetNumberOfCells(); vtkIdType vers = pd1->GetNumberOfPoints(); int nfac = pd1->GetNumberOfPolys(); float g = 0.5 * (2.0 - vers + nedg - nfac); std::cout << " Genus " << g << std::endl; std::cout << " face " << nfac << " edg " << nedg << " vert " << vers << std::endl; // edg1->Delete(); //caused malloc err edgeex->Delete(); // should be deleted b/c of New() above !! return g; } float vtkComputeTopology(vtkPolyData* pd) { vtkPolyDataConnectivityFilter* con = vtkPolyDataConnectivityFilter::New(); con->SetExtractionModeToLargestRegion(); con->SetInputData(pd); float g = ComputeGenus(con->GetOutput() ); con->Delete(); // should be deleted b/c of New() above !! return g; } template float GetImageTopology(typename TImage::Pointer image) { typedef TImage ImageType; double aaParm = 0.024; typedef BinaryImageToMeshFilter FilterType; typename FilterType::Pointer fltMesh = FilterType::New(); fltMesh->SetInput(image); fltMesh->SetAntiAliasMaxRMSError(aaParm); fltMesh->SetAntiAliasMaxRMSError( -1000.0 ); // to do nothing fltMesh->SetSmoothingIterations(0); fltMesh->Update(); vtkPolyData* vtkmesh = fltMesh->GetMesh(); std::cout << " start topo " << std::endl; float genus = vtkComputeTopology(vtkmesh); std::cout << " Genus " << genus << std::endl; // vtkmesh->Delete(); return genus; } template void NormalizeImage(typename TImage::Pointer image) { typedef itk::ImageRegionIteratorWithIndex Iterator; float max = 0; Iterator vfIter2( image, image->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { if( vfIter2.Get() > max ) { max = vfIter2.Get(); } } if( max == 0 ) { max = 1; } for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { vfIter2.Set( vfIter2.Get() / max); } } template typename TImage::Pointer SmoothImage( typename TImage::Pointer image, float sig ) { typedef TImage ImageType; enum { ImageDimension = ImageType::ImageDimension }; typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(sig); filter->SetUseImageSpacingOff(); filter->SetMaximumError(.01f); filter->SetInput(image); filter->Update(); return filter->GetOutput(); } template // std::vector typename TImage::Pointer GetLargestComponent(typename TImage::Pointer image) { enum { ImageDimension = TImage::ImageDimension }; typedef int InternalPixelType; typedef itk::Image InternalImageType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::ConnectedComponentImageFilter FilterType; typedef itk::RelabelComponentImageFilter RelabelType; typename ThresholdFilterType::Pointer threshold = ThresholdFilterType::New(); typename FilterType::Pointer filter = FilterType::New(); typename RelabelType::Pointer relabel = RelabelType::New(); threshold->SetInput(image); threshold->SetInsideValue(itk::NumericTraits::OneValue()); threshold->SetOutsideValue(itk::NumericTraits::ZeroValue()); threshold->SetLowerThreshold(0.499); threshold->SetUpperThreshold(1.001); threshold->Update(); filter->SetInput(threshold->GetOutput() ); // if (argc > 5) { int fullyConnected = 1; // atoi( argv[5] ); filter->SetFullyConnected( fullyConnected ); } relabel->SetInput( filter->GetOutput() ); unsigned int minSize = 50; std::cout << " min Size " << minSize << std::endl; relabel->SetMinimumObjectSize( minSize ); // relabel->SetUseHistograms(true); try { relabel->Update(); } catch( itk::ExceptionObject & excep ) { std::cerr << "Relabel: exception caught !" << std::endl; std::cerr << excep << std::endl; } typename TImage::Pointer Clusters = AllocImage(image, 0); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); float maximum = relabel->GetNumberOfObjects(); float maxtstat = 0; std::vector histogram( (int)maximum + 1); std::vector clustersum( (int)maximum + 1); for( int i = 0; i <= maximum; i++ ) { histogram[i] = 0; clustersum[i] = 0; } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 0 ) { float vox = image->GetPixel(vfIter.GetIndex() ); histogram[(unsigned int)vfIter.Get()] = histogram[(unsigned int)vfIter.Get()] + 1; clustersum[(unsigned int)vfIter.Get()] += vox; if( vox > maxtstat ) { maxtstat = vox; } } } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 0 ) { Clusters->SetPixel( vfIter.GetIndex(), histogram[(unsigned int)vfIter.Get()] ); // if ( Clusters->GetPixel( vfIter.GetIndex() ) > maximgval ) // maximgval=Clusters->GetPixel( vfIter.GetIndex()); } else { Clusters->SetPixel(vfIter.GetIndex(), 0); } } float maximgval = 0; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( Clusters->GetPixel( vfIter.GetIndex() ) > maximgval ) { maximgval = Clusters->GetPixel( vfIter.GetIndex() ); } } std::cout << " max size " << maximgval << std::endl; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( Clusters->GetPixel( vfIter.GetIndex() ) >= maximgval ) { Clusters->SetPixel( vfIter.GetIndex(), 1); } else { Clusters->SetPixel( vfIter.GetIndex(), 0); } } // for (int i=0; i<=maximum; i++) // std::cout << " label " << i << " ct is: " << histogram[i] << std::endl; return Clusters; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int CheckTopology( std::vector args, std::ostream* ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "CheckTopology" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 2 ) { std::cerr << "Parameter missing" << std::endl; std::cerr << std::endl; std::cerr << "Usage:" << argv[0] << " image.nii {g0image.nii} {threshold}" << std::endl; std::cerr << " If you put an arg for g0image then image will be smoothed and thresholded \n until it has genus zero or the smoothing kernel gets too large " << std::endl; return EXIT_FAILURE; } float thresh = -1; // 0.0001; if( argc > 3 ) { thresh = atof(argv[3]); } typedef float PixelType; const unsigned int ImageDimension = 3; // AvantsImageDimension; typedef itk::Image ImageType; ImageType::Pointer image = ImageType::New(); ReadImage(image, argv[1]); image = BinaryThreshold(0.5, 1.e9, 1, image); float initG = GetImageTopology(image); if( initG < 0 && argc > 2 ) { std::cout << "smoothing into a Genus Zero image with thresh " << thresh << std::endl; float G = 1; float smooth = 1; ImageType::Pointer simage; while( G != 0 && smooth < 20 ) { simage = SmoothImage(image, smooth); NormalizeImage(simage); simage = BinaryThreshold(thresh, 1.e9, 1, simage); ImageType::Pointer bigimage = GetLargestComponent(simage); G = GetImageTopology(bigimage); smooth = smooth + 1; simage = bigimage; std::cout << " G " << G << " at smoothing " << smooth << std::endl; } std::cout << " Final G " << G << " at smoothing " << smooth << std::endl; float G2 = 0; unsigned int mct = 0; float err = 1.e9; float lasterr = 1.e10; float derr = lasterr - err; while( G2 == 0 && derr > 0 ) { lasterr = err; err = 0; ImageType::Pointer out = ants::Morphological(simage, 3, 0, 1); ImageType::Pointer bigimage = GetLargestComponent(out); G2 = GetImageTopology(bigimage); typedef itk::ImageRegionIteratorWithIndex ImageIteratorType; ImageIteratorType iter( bigimage, bigimage->GetLargestPossibleRegion() ); iter.GoToBegin(); while( !iter.IsAtEnd() ) { err += fabs(iter.Get() - image->GetPixel(iter.GetIndex() ) ); ++iter; } mct++; derr = lasterr - err; std::cout << " G2 " << G2 << " at morph " << mct << " err " << err << std::endl; if( G2 == 0 && derr > 0 ) { simage = GetLargestComponent(out); } } WriteImage(simage, argv[2]); } else if( argc > 2 ) { WriteImage(image, argv[2]); } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ClusterImageStatistics.cxx000066400000000000000000000324721311104306400214220ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include "itkDiscreteGaussianImageFilter.h" // RecursiveAverageImages img1 img2 weightonimg2 outputname // We divide the 2nd input image by its mean and add it to the first // input image with weight 1/n. // The output overwrites the 1st img with the sum. #include #include #include #include #include "vnl/vnl_vector.h" #include "itkMinimumMaximumImageFilter.h" #include "itkConnectedComponentImageFilter.h" #include "itkRelabelComponentImageFilter.h" #include "itkLabelStatisticsImageFilter.h" #include "ReadWriteData.h" namespace ants { template int ClusterStatistics(unsigned int argc, char *argv[]) { typedef float PixelType; // const unsigned int ImageDimension = AvantsImageDimension; typedef itk::Image ImageType; // typedef itk::ImageRegionIteratorWithIndex Iterator; typedef unsigned long ULPixelType; typedef itk::Image labelimagetype; typedef itk::ConnectedComponentImageFilter FilterType; typedef itk::RelabelComponentImageFilter RelabelType; // want the average value in each cluster as defined by the mask and the value thresh and the clust thresh std::string roimaskfn = std::string(argv[2]); std::string labelimagefn = std::string(argv[3]); std::string outname = std::string(argv[4]); float clusterthresh = atof(argv[5]); float minSize = clusterthresh; float valuethresh = atof(argv[6]); // std::cout << " Cth " << clusterthresh << " Vth " << valuethresh << std::endl; typename ImageType::Pointer valimage = ITK_NULLPTR; typename ImageType::Pointer roiimage = ITK_NULLPTR; typename ImageType::Pointer labelimage = ITK_NULLPTR; ReadImage(roiimage, roimaskfn.c_str() ); ReadImage(labelimage, labelimagefn.c_str() ); typedef itk::MinimumMaximumImageFilter MinMaxFilterType; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetInput( labelimage ); minMaxFilter->Update(); double min = minMaxFilter->GetMinimum(); double max = minMaxFilter->GetMaximum(); double range = max - min; for( unsigned int filecount = 7; filecount < argc; filecount++ ) { // std::cout <<" doing " << std::string(argv[filecount]) << std::endl; ReadImage(valimage, argv[filecount]); // first, threshold the value image then get the clusters of min size typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer threshold = ThresholdFilterType::New(); threshold->SetInput(valimage); threshold->SetInsideValue(1); threshold->SetOutsideValue(0); threshold->SetLowerThreshold(valuethresh); threshold->SetUpperThreshold(1.e9); threshold->Update(); typename ImageType::Pointer thresh = threshold->GetOutput(); typedef itk::ImageRegionIteratorWithIndex fIterator; typedef itk::ImageRegionIteratorWithIndex Iterator; fIterator tIter( thresh, thresh->GetLargestPossibleRegion() ); for( tIter.GoToBegin(); !tIter.IsAtEnd(); ++tIter ) { if( roiimage->GetPixel(tIter.GetIndex() ) < 0.5 ) { tIter.Set(0); } } // typename typename FilterType::Pointer filter = FilterType::New(); // typename typename RelabelType::Pointer relabel = RelabelType::New(); filter->SetInput( thresh ); int fullyConnected = 0; // atoi( argv[5] ); filter->SetFullyConnected( fullyConnected ); relabel->SetInput( filter->GetOutput() ); relabel->SetMinimumObjectSize( (unsigned int) minSize ); try { relabel->Update(); } catch( itk::ExceptionObject & excep ) { std::cerr << "Relabel: exception caught !" << std::endl; std::cerr << excep << std::endl; } typename ImageType::Pointer Clusters = MakeNewImage(valimage, 0); typename ImageType::Pointer Values = MakeNewImage(valimage, 0); typename ImageType::Pointer Labels = MakeNewImage(valimage, 0); Iterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); float maximum = relabel->GetNumberOfObjects(); // std::cout << " #object " << maximum << std::endl; // float maxtstat=0; std::vector histogram( (int)maximum + 1); std::vector maxlabel( (int)maximum + 1); std::vector suminlabel( (unsigned long) range + 1); std::vector countinlabel( (unsigned long) range + 1); std::vector sumofvalues( (int)maximum + 1); std::vector maxvalue( (int)maximum + 1); for( int i = 0; i <= maximum; i++ ) { histogram[i] = 0; sumofvalues[i] = 0; maxvalue[i] = 0; maxlabel[i] = 0; } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 0 ) { float vox = valimage->GetPixel(vfIter.GetIndex() ); if( vox >= valuethresh ) { histogram[(unsigned long)vfIter.Get()] = histogram[(unsigned long)vfIter.Get()] + 1; sumofvalues[(unsigned long)vfIter.Get()] = sumofvalues[(unsigned long)vfIter.Get()] + vox; if( maxvalue[(unsigned long)vfIter.Get()] < vox ) { maxvalue[(unsigned long)vfIter.Get()] = vox; maxlabel[(unsigned long)vfIter.Get()] = (long int)labelimage->GetPixel(vfIter.GetIndex() ); } suminlabel[(unsigned long)(labelimage->GetPixel(vfIter.GetIndex() ) - min)] += vox; countinlabel[(unsigned long)(labelimage->GetPixel(vfIter.GetIndex() ) - min)] += 1; } } } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 0 ) { Clusters->SetPixel( vfIter.GetIndex(), histogram[(unsigned long)vfIter.Get()] ); Values->SetPixel( vfIter.GetIndex(), sumofvalues[(unsigned long)vfIter.Get()] / (float)histogram[(unsigned int)vfIter.Get()] ); Labels->SetPixel( vfIter.GetIndex(), labelimage->GetPixel(vfIter.GetIndex() ) ); } else { Clusters->SetPixel(vfIter.GetIndex(), 0); Labels->SetPixel(vfIter.GetIndex(), 0); Values->SetPixel(vfIter.GetIndex(), 0); } } // WriteImage(Values,std::string("temp.nii.gz").c_str()); // WriteImage(Clusters,std::string("temp2.nii.gz").c_str()); float maximgval = 0; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( Clusters->GetPixel( vfIter.GetIndex() ) > maximgval ) { maximgval = Clusters->GetPixel( vfIter.GetIndex() ); } } // std::cout << " max size " << maximgval << std::endl; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( Clusters->GetPixel( vfIter.GetIndex() ) < minSize ) { Clusters->SetPixel( vfIter.GetIndex(), 0); Values->SetPixel( vfIter.GetIndex(), 0); Labels->SetPixel( vfIter.GetIndex(), 0); } } // WriteImage(Values,(outname+"values.nii.gz").c_str()); // WriteImage(Labels,(outname+"labels.nii.gz").c_str()); WriteImage(Clusters, (outname + "sizes.nii.gz").c_str() ); // now begin output // std::cout << " Writing Text File " << outname << std::endl; std::string outname2 = outname + std::string("average.csv"); std::string outname3 = outname + std::string("volume.csv"); std::ofstream outf( (outname2).c_str(), std::ofstream::out); std::ofstream outf2( (outname3).c_str(), std::ofstream::out); if( outf.good() ) { // outf << std::string(argv[filecount]) << std::endl; for( int i = 0; i < maximum + 1; i++ ) { if( histogram[i] >= minSize ) { // outf << " Cluster " << i << " size " << histogram[i] << " average " << // sumofvalues[i]/(float)histogram[i] << " max " << maxvalue[i] << " label " << maxlabel[i] << std::endl; std::cout << " Cluster " << i << " size " << histogram[i] << " average " << sumofvalues[i] / (float)histogram[i] << " max " << maxvalue[i] << " label " << maxlabel[i] << std::endl; } } for( unsigned int i = 0; i <= range; i++ ) { // if ( countinlabel[i] > 0) { if( countinlabel[i] == 0 ) { countinlabel[i] = 1; } // outf << " Label " << i+min << " average " << suminlabel[i]/(float)countinlabel[i] << std::endl; std::cout << " Label " << i + min << " average " << suminlabel[i] / (float)countinlabel[i] << std::endl; if( i < range ) { outf << suminlabel[i] / (float)countinlabel[i] << ","; } else { outf << suminlabel[i] / (float)countinlabel[i] << std::endl; } } } } else { std::cout << " File No Good! " << outname << std::endl; } outf.close(); if( outf2.good() ) { for( unsigned int i = 0; i <= range; i++ ) { if( countinlabel[i] == 0 ) { countinlabel[i] = 1; } if( i < range ) { outf2 << (float)countinlabel[i] << ","; } else { outf2 << (float)countinlabel[i] << std::endl; } } } else { std::cout << " File No Good! " << outname << std::endl; } outf2.close(); } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ClusterImageStatistics( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ClusterImageStatistics" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << " Given an ROI and Label Image, find the max and average value \n in a value image where the value > some user-defined threshold \n and the cluster size is larger than some min size. \n " << std::endl; std::cout << "Usage: \n " << std::endl; std::cout << argv[0] << " ImageDimension ROIMask.ext LabelImage.ext OutPrefix MinimumClusterSize ValueImageThreshold Image1WithValuesOfInterest.ext ... ImageNWithValuesOfInterest.ext \n \n " << std::endl; std::cout << " ROIMask.ext -- overall region of interest \n \n LabelImage.ext -- labels for the sub-regions, e.g. Brodmann or just unique labels (see LabelClustersUniquely ) \n \n OutputPrefix -- all output has this prefix \n \n MinimumClusterSize -- the minimum size of clusters of interest \n \n ValueImageThreshold -- minimum value of interest \n \n Image*WithValuesOfInterest.ext --- image(s) that define the values you want to measure \n "; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi(argv[1]) ) { case 2: { ClusterStatistics<2>(argc, argv); } break; case 3: { ClusterStatistics<3>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ComposeMultiTransform.cxx000066400000000000000000000360761311104306400213030ustar00rootroot00000000000000 #include "antsUtilities.h" #include "antsUtilities.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMatrixOffsetTransformBase.h" #include "itkTransformFactory.h" #include "itkDisplacementFieldFromMultiTransformFilter.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" namespace ants { static bool ComposeMultiTransform_ParseInput(int argc, char * *argv, char *& output_image_filename, char *& reference_image_filename, TRAN_OPT_QUEUE & opt_queue) { opt_queue.clear(); opt_queue.reserve(argc - 2); output_image_filename = argv[0]; reference_image_filename = ITK_NULLPTR; int ind = 1; while( ind < argc ) { if( strcmp(argv[ind], "-R") == 0 ) { ind++; if( ind >= argc ) { return false; } reference_image_filename = argv[ind]; } else if( strcmp(argv[ind], "-i") == 0 ) { ind++; if( ind >= argc ) { return false; } TRAN_OPT opt; opt.filename = argv[ind]; if( CheckFileType(opt.filename) != AFFINE_FILE ) { std::cout << "file: " << opt.filename << " is not an affine .txt file. Invalid to use '-i' " << std::endl; return false; } opt.file_type = AFFINE_FILE; opt.do_affine_inv = true; opt_queue.push_back(opt); } else { TRAN_OPT opt; opt.filename = argv[ind]; opt.file_type = CheckFileType(opt.filename); opt.do_affine_inv = false; opt_queue.push_back(opt); } ind++; } // if (reference_image_filename == NULL) { // std::cout << "the reference image file (-R) must be given!!!" // << std::endl; // return false; // } return true; } template void ComposeMultiTransform(char *output_image_filename, char *reference_image_filename, TRAN_OPT_QUEUE & opt_queue) { typedef itk::Image ImageType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::MatrixOffsetTransformBase AffineTransformType; // typedef itk::WarpImageMultiTransformFilter // WarperType; typedef itk::DisplacementFieldFromMultiTransformFilter WarperType; itk::TransformFactory::RegisterTransform(); typedef itk::ImageFileReader ImageFileReaderType; typename ImageFileReaderType::Pointer reader_img = ImageFileReaderType::New(); typename ImageType::Pointer img_ref; typename ImageFileReaderType::Pointer reader_img_ref = ImageFileReaderType::New(); if( reference_image_filename ) { reader_img_ref->SetFileName(reference_image_filename); reader_img_ref->Update(); img_ref = reader_img_ref->GetOutput(); } else { std::cout << "the reference image file (-R) must be given!!!" << std::endl; return; } typename WarperType::Pointer warper = WarperType::New(); // warper->SetInput(img_mov); // warper->SetEdgePaddingValue( 0); VectorType pad; pad.Fill(0); // warper->SetEdgePaddingValue(pad); typedef itk::TransformFileReader TranReaderType; typedef itk::ImageFileReader FieldReaderType; const int kOptQueueSize = opt_queue.size(); for( int i = 0; i < kOptQueueSize; i++ ) { const TRAN_OPT & opt = opt_queue[i]; switch( opt_queue[i].file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); typename AffineTransformType::Pointer aff = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); if( opt_queue[i].do_affine_inv ) { aff->GetInverse(aff); } // std::cout << aff << std::endl; warper->PushBackAffineTransform(aff); } break; case DEFORMATION_FILE: { typename FieldReaderType::Pointer field_reader = FieldReaderType::New(); field_reader->SetFileName(opt.filename); field_reader->Update(); typename DisplacementFieldType::Pointer field = field_reader->GetOutput(); // std::cout << field << std::endl; warper->PushBackDisplacementFieldTransform(field); } break; default: std::cout << "Unknown file type!" << std::endl; } } warper->SetOutputParametersFromImage( img_ref ); std::cout << "output size: " << warper->GetOutputSize() << std::endl; std::cout << "output spacing: " << warper->GetOutputSpacing() << std::endl; // warper->PrintTransformList(); warper->DetermineFirstDeformNoInterp(); warper->Update(); typename DisplacementFieldType::Pointer field_output = DisplacementFieldType::New(); field_output = warper->GetOutput(); std::string filePrefix = output_image_filename; std::string::size_type pos = filePrefix.rfind("."); std::string extension = std::string(filePrefix, pos, filePrefix.length() - 1); filePrefix = std::string(filePrefix, 0, pos); std::cout << "output extension is: " << extension << std::endl; { typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName(output_image_filename); writer->SetInput(field_output); writer->Update(); } } template void ComposeMultiAffine(char *output_affine_txt, char *reference_affine_txt, TRAN_OPT_QUEUE & opt_queue) { typedef itk::Image ImageType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::MatrixOffsetTransformBase AffineTransformType; // MatrixOffsetTransformBase is not usually registered, so register it here. // MatrixOffsetTransformBase should NOT be a valid transform type for writting, // but it is needed for historical reading purposes. itk::TransformFactory::RegisterTransform(); typedef itk::TransformFileReader TranReaderType; typedef itk::WarpImageMultiTransformFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); bool has_affine_tranform = false; const int kOptQueueSize = opt_queue.size(); for( int i = 0; i < kOptQueueSize; i++ ) { const TRAN_OPT & opt = opt_queue[i]; switch( opt_queue[i].file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); typename AffineTransformType::Pointer aff = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); if( opt_queue[i].do_affine_inv ) { aff->GetInverse(aff); } warper->PushBackAffineTransform(aff); has_affine_tranform = true; } break; case DEFORMATION_FILE: { std::cout << "Compose affine only files: ignore " << opt.filename << std::endl; } break; default: { std::cout << "Unknown file type!" << std::endl; } } } typename AffineTransformType::Pointer aff_ref_tmp; if( reference_affine_txt ) { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(reference_affine_txt); tran_reader->Update(); aff_ref_tmp = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); } else { if( has_affine_tranform == true ) { std::cout << "the reference affine file for center is selected as the first affine!" << std::endl; aff_ref_tmp = ( (warper->GetTransformList() ).begin() )->second.aex.aff; } else { std::cout << "No affine input is given. nothing to do ......" << std::endl; return; } } { typedef typename AffineTransformType::CenterType PointType; const PointType aff_center = aff_ref_tmp->GetCenter(); std::cout << "new center is : " << aff_center << std::endl; { typename AffineTransformType::Pointer aff_output = AffineTransformType::New(); warper->ComposeAffineOnlySequence(aff_center, aff_output); typedef itk::TransformFileWriter TranWriterType; typename TranWriterType::Pointer tran_writer = TranWriterType::New(); tran_writer->SetFileName(output_affine_txt); tran_writer->SetInput(aff_output); tran_writer->Update(); } } std::cout << "wrote file to : " << output_affine_txt << std::endl; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ComposeMultiTransform( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ComposeMultiTransform" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc <= 3 ) { std::cout << "ComposeMultiTransform ImageDimension output_field [-R reference_image] " << "{[deformation_field | [-i] affine_transform_txt ]}" << std::endl; std::cout << " Usage has the same form as WarpImageMultiTransform " << std::endl; std::cout << " For Example: " << std::endl; std::cout << std::endl; std::cout << argv[0] << " Dimension outwarp.nii -R template.nii ExistingWarp.nii ExistingAffine.nii " << std::endl; std::cout << " or for an inverse mapping : " << std::endl; std::cout << argv[0] << " Dimension outwarp.nii -R template.nii -i ExistingAffine.nii ExistingInverseWarp.nii " << std::endl; std::cout << " recalling that the -i option takes the inverse of the affine mapping " << std::endl; std::cout << std::endl; std::cout << "Or: to compose multiple affine text file into one: " << std::endl; std::cout << "ComposeMultiTransform ImageDimension output_affine_txt [-R reference_affine_txt] " << "{[-i] affine_transform_txt}" << std::endl << "This will be evoked if a text file is given as the second parameter. In this case " << "reference_affine_txt is used to define the center of the output affine. " << "The default reference is the first given affine text file. " << "This ignores all non-txt files among the following parameters." << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } TRAN_OPT_QUEUE opt_queue; // char *moving_image_filename = NULL; char *output_image_filename = ITK_NULLPTR; char *reference_image_filename = ITK_NULLPTR; int kImageDim = atoi(argv[1]); const bool is_parsing_ok = ComposeMultiTransform_ParseInput(argc - 2, argv + 2, output_image_filename, reference_image_filename, opt_queue); if( is_parsing_ok ) { switch( CheckFileType(output_image_filename) ) { case DEFORMATION_FILE: { if( reference_image_filename == ITK_NULLPTR ) { std::cout << "the reference image file (-R) must be given!!!" << std::endl; return false; } std::cout << "output_image_filename: " << output_image_filename << std::endl; std::cout << "reference_image_filename: "; if( reference_image_filename ) { std::cout << reference_image_filename << std::endl; } else { std::cout << "NULL" << std::endl; } DisplayOptQueue(opt_queue); switch( kImageDim ) { case 2: { ComposeMultiTransform<2>(output_image_filename, reference_image_filename, opt_queue); } break; case 3: { ComposeMultiTransform<3>(output_image_filename, reference_image_filename, opt_queue); } break; } } break; case AFFINE_FILE: { std::cout << "output_affine_txt: " << output_image_filename << std::endl; std::cout << "reference_affine_txt: "; if( reference_image_filename ) { std::cout << reference_image_filename << std::endl; } else { std::cout << "NULL" << std::endl; } DisplayOptQueue(opt_queue); switch( kImageDim ) { case 2: { ComposeMultiAffine<2>(output_image_filename, reference_image_filename, opt_queue); } break; case 3: { ComposeMultiAffine<3>(output_image_filename, reference_image_filename, opt_queue); } break; } } break; default: { std::cout << "Unknow output file format: " << output_image_filename << std::endl; } break; } return EXIT_SUCCESS; } else { std::cout << "Input error!" << std::endl; return EXIT_FAILURE; } } } // namespace ants ants-2.2.0/Examples/CompositeTransformUtil.cxx000066400000000000000000000213701311104306400214520ustar00rootroot00000000000000#include #include #include #include #include "antsUtilities.h" #include #include "itkTransform.h" #include "itkCompositeTransform.h" #include "itkDisplacementFieldTransform.h" #include "antsCommandLineParser.h" #include "itkantsReadWriteTransform.h" #include "itkTransformFactory.h" namespace ants { static bool MatOffRegistered[2] = { false, false }; template void RegisterMatOff() { if( !MatOffRegistered[VImageDimension - 2] ) { MatOffRegistered[VImageDimension - 2] = true; // Register the matrix offset transform base class to the // transform factory for compatibility with the current ANTs. typedef itk::MatrixOffsetTransformBase MatrixOffsetTransformType; itk::TransformFactory::RegisterTransform(); } } /** * print the usage and exit */ static void PrintGenericUsageStatement() { std::cout << "Usage: CompositeTransformUtil --disassemble " << " " << std::endl << "or" << std::endl << "CompositeTransformUtil --assemble " << " ... " << std::endl; } /** * given a composite transform, write out all its component * transforms. */ template int Disassemble(itk::TransformBaseTemplate *transform, const std::string & transformName, const std::string & prefix) { typedef itk::CompositeTransform CompositeTransformType; typedef typename CompositeTransformType::TransformTypePointer TransformPointer; typedef typename itk::DisplacementFieldTransform DisplacementFieldTransformType; CompositeTransformType *composite = dynamic_cast(transform); if( composite == ITK_NULLPTR ) { std::cout << "Transform File " << transformName << " is a " << transform->GetNameOfClass() << " not a Composite Transform." << std::endl; return EXIT_FAILURE; } const unsigned int numTransforms = composite->GetNumberOfTransforms(); for( unsigned int i = 0; i < numTransforms; ++i ) { TransformPointer curXfrm = composite->GetNthTransform(i); DisplacementFieldTransformType *dispXfrm = dynamic_cast(curXfrm.GetPointer() ); std::stringstream fname; fname << std::setfill('0') << std::setw(2) << i << "_" << prefix << "_" << curXfrm->GetNameOfClass(); if( dispXfrm != ITK_NULLPTR ) { fname << ".nii.gz"; // if it's a displacement field transform } else { fname << ".mat"; // .txt does not have enough precision! } itk::ants::WriteTransform(curXfrm, fname.str() ); } return EXIT_SUCCESS; } static int Disassemble(const std::string & CompositeName, const std::string & Prefix) { itk::TransformBaseTemplate::Pointer transform = itk::ants::ReadTransform(CompositeName).GetPointer(); if( transform.IsNull() ) { transform = itk::ants::ReadTransform(CompositeName).GetPointer(); if( transform.IsNull() ) { return EXIT_FAILURE; // ReadTransform prints error messages on // failure. } } const unsigned int inDim(transform->GetInputSpaceDimension() ); const unsigned int outDim(transform->GetOutputSpaceDimension() ); if( inDim != outDim ) { std::cout << "Can't handle mixed input & output dimension: input(" << inDim << ") output (" << outDim << ")" << std::endl; return EXIT_FAILURE; } switch( inDim ) { case 2: return Disassemble<2>(transform, CompositeName, Prefix); case 3: return Disassemble<3>(transform, CompositeName, Prefix); default: std::cout << "Unknown dimension " << inDim << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } template int Assemble(const std::string & CompositeName, const std::vector & transformNames, const typename itk::Transform::Pointer & firstTransform) { typedef itk::CompositeTransform CompositeTransformType; typedef typename CompositeTransformType::TransformType TransformType; typename CompositeTransformType::Pointer composite = CompositeTransformType::New(); composite->AddTransform(firstTransform); for( unsigned int i = 1; i < transformNames.size(); ++i ) { typename TransformType::Pointer curXfrm = itk::ants::ReadTransform(transformNames[i]); if( curXfrm.IsNull() ) { return EXIT_FAILURE; // ReadTransform will complain if anything goes wrong. } composite->AddTransform(curXfrm); } typename TransformType::Pointer genericXfrmPtr = composite.GetPointer(); return itk::ants::WriteTransform(genericXfrmPtr, CompositeName); } static int Assemble(const std::string & CompositeName, const std::vector & transformNames) { { itk::Transform::Pointer FirstTransform = itk::ants::ReadTransform(transformNames[0]); if( FirstTransform.IsNotNull() ) { return Assemble<2>(CompositeName, transformNames, FirstTransform); } } { itk::Transform::Pointer FirstTransform = itk::ants::ReadTransform(transformNames[0]); if( FirstTransform.IsNotNull() ) { return Assemble<3>(CompositeName, transformNames, FirstTransform); } } return EXIT_FAILURE; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int CompositeTransformUtil( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "CompositeTransformUtil" ); unsigned int argc = args.size(); char* * argv = new char *[argc + 1]; for( unsigned int i = 0; i < argc; ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { PrintGenericUsageStatement(); return EXIT_SUCCESS; } if( argc < 2 ) { PrintGenericUsageStatement(); return EXIT_FAILURE; } ++argv; --argc; std::string action(*argv); ++argv; --argc; if( argc == 0 ) { std::cout << "Missing CompositeTransformName" << std::endl; PrintGenericUsageStatement(); return EXIT_FAILURE; } RegisterMatOff<2>(); RegisterMatOff<3>(); std::string CompositeName(*argv); ++argv; --argc; if( action == "--disassemble" ) { if( argc == 0 ) { std::cout << "Missing output transforms prefix" << std::endl; PrintGenericUsageStatement(); return EXIT_FAILURE; } std::string Prefix(*argv); return Disassemble(CompositeName, Prefix); } else if( action != "--assemble" ) { std::cout << "Unknown action " << action << std::endl; PrintGenericUsageStatement(); return EXIT_FAILURE; } std::vector transformNames; do { std::string transformName(*argv); ++argv; --argc; transformNames.push_back(transformName); } while( argc != 0 ); if( transformNames.size() < 1 ) { std::cout << "Missing transform names to " << "assemble into a composite transform" << std::endl; PrintGenericUsageStatement(); return EXIT_FAILURE; } return Assemble(CompositeName, transformNames); } } // namespace ants ants-2.2.0/Examples/ComputeSimilarityMetric.cxx000066400000000000000000000223751311104306400216130ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt for 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include "ReadWriteData.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkAvantsMutualInformationRegistrationFunction.h" #include "itkProbabilisticRegistrationFunction.h" #include "itkCrossCorrelationRegistrationFunction.h" namespace ants { template int ComputeSimilarityMetric(int argc, char *argv[]) { typedef float PixelType; typedef itk::Vector VectorType; typedef itk::Image FieldType; typedef itk::Image ImageType; typedef itk::ImageFileWriter writertype; typedef typename ImageType::IndexType IndexType; typedef typename ImageType::SizeType SizeType; typedef typename ImageType::SpacingType SpacingType; typedef itk::AffineTransform AffineTransformType; typedef itk::LinearInterpolateImageFunction InterpolatorType1; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::Image JointHistType; typedef itk::ImageFileWriter jhwritertype; // get command line params unsigned int argct = 2; unsigned int whichmetric = atoi(argv[argct]); argct++; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = std::string(argv[argct]); argct++; std::string logfilename = ""; if( argc > argct ) { logfilename = std::string(argv[argct]); } argct++; std::string imgfilename = ""; if( argc > argct ) { imgfilename = std::string(argv[argct]); } argct++; typename ImageType::Pointer image1 = NULL; ReadImage(image1, fn1.c_str() ); typename ImageType::Pointer image2 = NULL; ReadImage(image2, fn2.c_str() ); /* typedef itk::ImageRegionIteratorWithIndex VIterator; typename FieldType::Pointer field=FieldType::New(); field->SetLargestPossibleRegion( image1->GetLargestPossibleRegion() ); field->SetBufferedRegion( image1->GetLargestPossibleRegion() ); field->SetLargestPossibleRegion( image1->GetLargestPossibleRegion() ); field->Allocate(); field->SetSpacing(image1->GetSpacing()); field->SetOrigin(image1->GetOrigin()); VectorType zero; zero.Fill(0); VIterator vfIter2( field, field->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { IndexType index=vfIter2.GetIndex(); vfIter2.Set(zero); } */ typedef ImageType FixedImageType; typedef ImageType MovingImageType; typedef FieldType DisplacementFieldType; // Choose the similarity metric typedef itk::AvantsMutualInformationRegistrationFunction MIMetricType; typedef itk::CrossCorrelationRegistrationFunction CCMetricType; // typedef itk::LandmarkCrossCorrelationRegistrationFunction // MetricType; // typename typename MIMetricType::Pointer mimet = MIMetricType::New(); typename CCMetricType::Pointer ccmet = CCMetricType::New(); typename CCMetricType::RadiusType hradius; typename CCMetricType::RadiusType ccradius; ccradius.Fill(4); typename MIMetricType::RadiusType miradius; miradius.Fill(0); // mimet->SetDisplacementField(field); mimet->SetFixedImage(image1); mimet->SetMovingImage(image2); mimet->SetRadius(miradius); mimet->SetGradientStep(1.e2); mimet->SetNormalizeGradient(false); // ccmet->SetDisplacementField(field); ccmet->SetFixedImage(image1); ccmet->SetMovingImage(image2); ccmet->SetRadius(ccradius); ccmet->SetGradientStep(1.e2); ccmet->SetNormalizeGradient(false); double metricvalue = 0; std::string metricname = ""; // ORIENTATION ALERT -- the original code here // set the region, spacing, and origin without setting directions. typename ImageType::Pointer metricimg = AllocImage(image1, 0); if( whichmetric == 0 ) { hradius = miradius; unsigned long ct = 0; Iterator iter( metricimg, metricimg->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { IndexType index = iter.GetIndex(); double fval = image1->GetPixel(index); double mval = image2->GetPixel(index); metricvalue += fabs(fval - mval); ct++; } metricvalue /= (float)ct; metricname = "MSQ "; } else if( whichmetric == 1 ) // imagedifference { hradius = ccradius; ccmet->InitializeIteration(); metricvalue = ccmet->ComputeCrossCorrelation(); metricname = "CC "; } else { hradius = miradius; mimet->InitializeIteration(); metricvalue = mimet->ComputeMutualInformation(); metricname = "MI "; } std::cout << fn1 << " : " << fn2 << " => " << metricname << metricvalue << std::endl; if( logfilename.length() > 3 ) { std::ofstream logfile; logfile.open(logfilename.c_str(), std::ofstream::app); if( logfile.good() ) { logfile << fn1 << " : " << fn2 << " => " << metricname << metricvalue << std::endl; } else { std::cout << " cant open file "; } logfile.close(); } if( imgfilename.length() > 3 ) { /* typename MetricType::NeighborhoodType asamIt( hradius, field,field->GetLargestPossibleRegion()); unsigned long ct = 0; double totval = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { IndexType index=vfIter2.GetIndex(); double val=0; asamIt.SetLocation(index); // met->ComputeUpdate( asamIt, gd); met->ComputeMetricAtPairB(index, zero); metricimg->SetPixel(index, val); //if (ct % 10000 == 0) // std::cout << val << " index " << index << std::endl; // asamIt.SetLocation(index); // totval+=met->localProbabilistic; ct++; } std::cout << " AvantsMI : " << totval/(double)ct << " E " << met->GetEnergy() << std::endl; std::cout << " write begin " << std::endl; typedef itk::ImageFileWriter writertype; writertype::Pointer w= writertype::New(); w->SetInput(metricimg); w->SetFileName(outname.c_str()); w->Write(); // met->WriteImages(); std::cout << " write end " << std::endl; */ } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ComputeSimilarityMetric( std::vector args, std::ostream* out_stream = NULL ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ComputeSimilarityMetric" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Basic useage ex: " << std::endl; std::cout << argv[0] << " ImageDimension whichmetric image1.ext image2.ext {logfile} {outimage.ext} " << std::endl; std::cout << " outimage and logfile are optional " << std::endl; return EXIT_FAILURE; } // Get the image dimension switch( atoi(argv[1]) ) { case 2: { ComputeSimilarityMetric<2>(argc, argv); } break; case 3: { ComputeSimilarityMetric<3>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ConformalMapping.cxx000066400000000000000000001074211311104306400202140ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Copyright (c) 2002 Insight 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. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include #include #include #include #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIterator.h" #include "itkMesh.h" #include "itkSphereMeshSource.h" #include "itkBinaryMask3DMeshSource.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkFEMConformalMap.h" #include "itkFEMDiscConformalMap.h" #include "BinaryImageToMeshFilter.h" #include "vtkCallbackCommand.h" #include "vtkPointPicker.h" #include "vtkCellPicker.h" #include "vtkPolyDataWriter.h" #include "vtkPolyDataReader.h" #include "ReadWriteData.h" #include "itkRescaleIntensityImageFilter.h" #include "itkSurfaceImageCurvature.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkImageRegionIterator.h" #include "itkPointSet.h" #include #include namespace ants { /* OPEN QUESTIONS: LOCAL FROM GLOBAL FOR TRIANGLE IN 3D (MARCELO) USEFUL FOR INTERPOLATING STEREOGRAPHIC COORDS INTO IMAGE. CURRENTLY JUST DENSELY INTERPOLATE ACROSS EACH TRIANGLE. -- Not necessary for triangle TRIANGLE AREA IN 3D (D0NE : USING CROSS PRODUCT ) - need to insure we use the hypotneuse as the AB edge : so theta is <= 1.0; - try to apply bcs to even out the map - what is the effect of material param E? */ void Display(vtkUnstructuredGrid* vtkgrid, bool secondwin = false, bool delinter = true) { // Create the renderer and window stuff std::cout << " second win " << secondwin << std::endl; vtkRenderer* ren1 = vtkRenderer::New(); vtkRenderer* ren2 = vtkRenderer::New(); vtkRenderWindow* renWin = vtkRenderWindow::New(); renWin->AddRenderer(ren1); if( secondwin ) { renWin->AddRenderer(ren2); } vtkRenderWindowInteractor* inter = vtkRenderWindowInteractor::New(); inter->SetRenderWindow(renWin); // vtkCellPicker* cpicker = vtkCellPicker::New(); vtkCallbackCommand *cbc = vtkCallbackCommand::New(); /*------------------------------------------------------------------------- void vtkCompositeManagerExitInteractor(vtkObject *vtkNotUsed(o), unsigned long vtkNotUsed(event), void *clientData, void *) { vtkCompositeManager *self = (vtkCompositeManager *)clientData; self->ExitInteractor(); }*/ // cbc=vectraEventHandler; //-> SetCallback(vectraEventHandler); ren1->AddObserver(vtkCommand::KeyPressEvent, cbc); vtkDataSetMapper* mapper = vtkDataSetMapper::New(); mapper->SetInput(vtkgrid); mapper->SetScalarRange(0, 255); vtkActor* actor = vtkActor::New(); actor->SetMapper(mapper); vtkDataSetMapper* mapper2 = vtkDataSetMapper::New(); if( secondwin ) { mapper2->SetInput(vtkgrid); } if( secondwin ) { mapper2->SetScalarRange(0, 255); } vtkActor* actor2 = vtkActor::New(); if( secondwin ) { actor2->SetMapper(mapper2); } if( secondwin ) { ren1->SetViewport(0.0, 0.0, 0.5, 1.0); ren2->SetViewport(0.5, 0.0, 1.0, 1.0); } else { ren1->SetViewport(0.0, 0.0, 1.0, 1.0); } if( secondwin ) { ren2->AddActor(actor2); } // add the actor and start the render loop ren1->AddActor(actor); // this->InteractionPicker->Pick(x, y, 0.0, this->CurrentRenderer); // this->InteractionPicker->GetPickPosition(this->DownPt); renWin->Render(); inter->Start(); /* int X, Y; char keypressed = *(inter -> GetKeySym()); inter -> GetMousePosition(&X, &Y); std::cout <<" X Y " << X << " " << Y << std::endl; renWin->Render(); inter->Start();*/ /* vtkPointPicker* picker=vtkPointPicker::New(); // vtkPoints *GetPickedPositions() {return this->PickedPositions;}; float x = inter->GetEventPosition()[0]; float y = inter->GetEventPosition()[1]; float z = 0.0; picker->Pick(x,y,z,ren1); std::cout <<" picked " << (*picker->GetPickedPositions()) << std::endl;*/ mapper->Delete(); actor->Delete(); ren1->Delete(); mapper2->Delete(); actor2->Delete(); ren2->Delete(); renWin->Delete(); if( delinter ) { inter->Delete(); } } template void MapToSphere(typename TImage::Pointer image, int fixdir, float e) { typedef TImage ImageType; typedef unsigned char PixelType; typedef itk::Mesh MeshType; typedef itk::BinaryMask3DMeshSource MeshSourceType; PixelType internalValue = 1; typename MeshSourceType::Pointer meshSource = MeshSourceType::New(); meshSource->SetBinaryImage( image ); meshSource->SetObjectValue( internalValue ); try { meshSource->Update(); } catch( itk::ExceptionObject & exp ) { std::cout << "Exception thrown during Update() " << std::endl; std::cout << exp << std::endl; return; } meshSource->GetOutput(); std::cout << meshSource->GetNumberOfNodes() << std::endl; std::cout << meshSource->GetNumberOfCells() << std::endl; typedef itk::FEMConformalMap ParamType; typename ParamType::Pointer Parameterizer = ParamType::New(); Parameterizer->SetDebug(false); // Parameterizer->SetDebug(true); Parameterizer->SetReadFromFile(false); Parameterizer->SetParameterFileName(""); Parameterizer->SetImage(image); std::cout << " fixdir " << fixdir << " e " << e << " best e ~ 3.e-3 " << std::endl; Parameterizer->SetNorthPole(fixdir); Parameterizer->SetSigma(e); Parameterizer->SetSurfaceMesh(meshSource->GetOutput() ); // Parameterizer->GenerateSystemFromSurfaceMesh(); std::cout << std::endl; Parameterizer->ConformalMap(); Parameterizer->ComputeStereographicCoordinates(); if( Parameterizer->GetImage() ) { std::cout << " writing param images " << std::endl; { Parameterizer->MapCheckerboardToImage(0.05); typename itk::ImageFileWriter::Pointer writer; writer = itk::ImageFileWriter::New(); std::string fn = "checker.nii"; writer->SetFileName(fn.c_str() ); writer->SetInput(Parameterizer->GetImage() ); writer->Write(); } } } template typename TImage::Pointer SmoothImage( typename TImage::Pointer input, double var) { typedef TImage ImageType; typedef typename ImageType::PixelType PixelType; enum { ImageDimension = ImageType::ImageDimension }; typedef itk::Image realImageType; typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(var); filter->SetMaximumError(.01f); filter->SetUseImageSpacingOn(); filter->SetInput(input); filter->Update(); typedef itk::CastImageFilter CasterType1; typename CasterType1::Pointer caster1 = CasterType1::New(); caster1->SetInput(filter->GetOutput() ); caster1->Update(); return caster1->GetOutput(); } float ComputeGenus(vtkPolyData* pd1) { vtkExtractEdges* edgeex = vtkExtractEdges::New(); edgeex->SetInput(pd1); edgeex->Update(); vtkPolyData* edg1 = edgeex->GetOutput(); vtkIdType nedg = edg1->GetNumberOfCells(); vtkIdType vers = pd1->GetNumberOfPoints(); int nfac = pd1->GetNumberOfPolys(); float g = 0.5 * (2.0 - vers + nedg - nfac); std::cout << " Genus " << g << std::endl; std::cout << " face " << nfac << " edg " << nedg << " vert " << vers << std::endl; return g; } float vtkComputeTopology(vtkPolyData* pd) { // Marching cubes // std::cout << " Marching Cubes "; // vtkMarchingCubes *marchingCubes = vtkMarchingCubes::New(); // vtkContourFilter *marchingCubes = vtkContourFilter::New(); // vtkKitwareContourFilter *marchingCubes = vtkKitwareContourFilter::New(); // marchingCubes->SetInput((vtkDataSet*) vds); // marchingCubes->SetValue(0, hithresh); // int nc; // std::cout << " Input #conts "; std::cin >> nc; // marchingCubes->SetNumberOfContours(2); // marchingCubes->SetComputeScalars(false); // marchingCubes->SetComputeGradients(false); // marchingCubes->SetComputeNormals(false); vtkPolyDataConnectivityFilter* con = vtkPolyDataConnectivityFilter::New(); con->SetExtractionModeToLargestRegion(); // con->SetInput(marchingCubes->GetOutput()); con->SetInput(pd); // vtkUnstructuredGridToPolyDataFilter* gp = vtkUnstructuredGridToPolyDataFilter::New(); // gp->SetInput(con->GetOutput()); float g = ComputeGenus(con->GetOutput() ); // marchingCubes->Delete(); return g; } template void GetMeshAndCurvature(typename TImage::Pointer image, float e, const char* filename) { typedef TImage ImageType; typedef vtkPolyData MeshType; double aaParm = 0.024; typedef BinaryImageToMeshFilter FilterType; typename FilterType::Pointer fltMesh = FilterType::New(); fltMesh->SetInput(image); fltMesh->SetAntiAliasMaxRMSError(aaParm); fltMesh->SetAntiAliasMaxRMSError( -1000.0 ); // to do nothing fltMesh->Update(); vtkPolyData* vtkmesh = fltMesh->GetMesh(); // assign scalars to the original surface mesh // Display((vtkUnstructuredGrid*)vtkmesh); std::cout << " Genus " << vtkComputeTopology(vtkmesh) << std::endl; typedef itk::SurfaceImageCurvature surfktype; typename surfktype::Pointer surfk = surfktype::New(); // kappa=; typedef itk::Image FloatImageType; surfk->SetInput(image); // SmoothImage(image,1.0)); surfk->SetNeighborhoodRadius( 1.5 ); surfk->SetSigma(1.); surfk->SetUseLabel(false); surfk->SetUseGeodesicNeighborhood(false); surfk->SetkSign(1.0); surfk->ComputeFrameOverDomain( 3 ); typename FloatImageType::Pointer kappa = surfk->GetFunctionImage(); typedef itk::Image itype; typedef itk::RescaleIntensityImageFilter< FloatImageType, itype> CastFilterType; typename CastFilterType::Pointer caster = CastFilterType::New(); caster->SetInput( image ); caster->SetOutputMinimum( 0 ); caster->SetOutputMaximum( 255 ); std::string fn = std::string(filename); fn = fn.substr(0, fn.length() - 4) + "kappa.nii"; typedef itk::ImageFileWriter writertype; typename writertype::Pointer w = writertype::New(); typename itype::Pointer kimg = caster->GetOutput(); w->SetInput(kimg); w->SetFileName(fn.c_str() ); w->Write(); typename itype::SpacingType spacing = image->GetSpacing(); vtkPoints* vtkpoints = vtkmesh->GetPoints(); int numPoints = vtkpoints->GetNumberOfPoints(); float mx = 0, mn = 9.e9, meank = 0; for( int i = 0; i < numPoints; i++ ) { typename ImageType::IndexType index; for( int j = 0; j < 3; j++ ) { index[j] = (int)(vtkpoints->GetPoint(i)[j] / spacing[j] + 0.5); } float temp = kimg->GetPixel(index); // float temp=image->GetPixel(index); if( fabs(temp) > mx ) { mx = fabs(temp); } if( fabs(temp) < mn && mn > 0 ) { mn = fabs(temp); } meank += fabs(temp); } std::cout << " max kap " << mx << " mn k " << mn << std::endl; meank /= numPoints; // mx=1.3; // mx=2.0; // bool done=false; // while (!done) { vtkFloatArray * param = vtkFloatArray::New(); param->SetName("angle"); float dif = (mx - mn) * 0.25; const float mx2 = meank + dif; const float mn2 = meank - dif; dif = mx2 - mn2; for( int i = 0; i < numPoints; i++ ) { typename ImageType::IndexType index; for( int j = 0; j < 3; j++ ) { index[j] = (int)(vtkpoints->GetPoint(i)[j] / spacing[j] + 0.5); } float temp = kimg->GetPixel(index); // float temp=surfk->CurvatureAtIndex(index); if( i % 1000 == 0 ) { std::cout << " kappa " << temp << std::endl; } // =fabs(manifoldIntegrator->GetGraphNode(i)->GetTotalCost()); temp = fabs(temp); param->InsertNextValue( (temp - mn2) * 255. / dif); } vtkmesh->GetPointData()->SetScalars(param); // Display((vtkUnstructuredGrid*)vtkmesh); // std::cout<<"DOne? "; std::cin >> done; } std::cout << " done with curvature map "; vtkPolyDataWriter *writer = vtkPolyDataWriter::New(); writer->SetInput(vtkmesh); std::string outnm = std::string(filename); outnm = outnm.substr(0, outnm.length() - 4) + ".vtk"; std::cout << " writing " << outnm << std::endl; // outnm="C:\\temp\\mesh.vtk"; writer->SetFileName(outnm.c_str() ); writer->SetFileTypeToBinary(); writer->Update(); std::cout << " done writing "; return; } template float GetImageTopology(typename TImage::Pointer image, float e, const char* filename) { typedef TImage ImageType; typedef vtkPolyData MeshType; double aaParm = 0.024; typedef BinaryImageToMeshFilter FilterType; typename FilterType::Pointer fltMesh = FilterType::New(); fltMesh->SetInput(image); fltMesh->SetAntiAliasMaxRMSError(aaParm); fltMesh->SetAntiAliasMaxRMSError( -1000.0 ); // to do nothing fltMesh->Update(); vtkPolyData* vtkmesh = fltMesh->GetMesh(); // assign scalars to the original surface mesh // Display((vtkUnstructuredGrid*)vtkmesh); float genus = vtkComputeTopology(vtkmesh); std::cout << " Genus " << genus << std::endl; return genus; } template void MapToDisc(vtkPolyData* vtkmesh, float e, std::string outfn) { typedef TImage ImageType; typedef vtkPolyData MeshType; typedef itk::FEMDiscConformalMap ParamType; typename ParamType::Pointer Parameterizer = ParamType::New(); Parameterizer->SetDebug(false); // Parameterizer->SetDebug(true); Parameterizer->SetReadFromFile(false); Parameterizer->SetParameterFileName(""); Parameterizer->SetSigma(e); Parameterizer->SetSurfaceMesh(vtkmesh); ComputeGenus( (vtkPolyData *)Parameterizer->GetSurfaceMesh() ); Parameterizer->ExtractSurfaceDisc(); ComputeGenus( (vtkPolyData *)Parameterizer->GetSurfaceMesh() ); // Display((vtkUnstructuredGrid*)Parameterizer->GetSurfaceMesh()); std::cout << " begin conformal mapping "; Parameterizer->ConformalMap(); std::cout << " display patch "; // ComputeGenus((vtkPolyData*)Parameterizer->m_ExtractedSurfaceMesh); // Display((vtkUnstructuredGrid*)Parameterizer->m_ExtractedSurfaceMesh); // std::cout << " display flattened patch "; // float step = 0.1; // float maxt=0.0; // for (float tt = 0.0; tt<=maxt; tt=tt+step) { // std::cout <<" Building at : " << tt << std::endl; // / Parameterizer->BuildOutputMeshes(tt); // if (tt == 0.) { vtkPolyDataWriter *writer = vtkPolyDataWriter::New(); writer->SetInput(Parameterizer->m_ExtractedSurfaceMesh); std::string outnm; // =std::string(filename); // outnm=outnm.substr(0,outnm.length()-4)+".vtk"; outnm = outfn + "mapspace.vtk"; std::cout << " writing " << outnm << std::endl; writer->SetFileName(outnm.c_str() ); writer->SetFileTypeToBinary(); writer->Update(); } { vtkPolyDataWriter *writer = vtkPolyDataWriter::New(); writer->SetInput(Parameterizer->m_DiskSurfaceMesh); std::string outnm; outnm = outfn + "mapflat.vtk"; std::cout << " writing " << outnm << std::endl; writer->SetFileName(outnm.c_str() ); writer->SetFileTypeToBinary(); writer->Update(); } } /* for (float sig=0.4; sig<=1.0; sig=sig+0.1) { Parameterizer->SetSmooth(sig); Parameterizer->ConjugateHarmonic(); // ComputeGenus((vtkPolyData*)Parameterizer->m_DiskSurfaceMesh); Display((vtkUnstructuredGrid*)Parameterizer->m_DiskSurfaceMesh); } */ typedef typename ParamType::FlatImageType imtype; typename itk::ImageFileWriter::Pointer writer; writer = itk::ImageFileWriter::New(); std::string fn = outfn + "flat.nii"; writer->SetFileName(fn.c_str() ); writer->SetInput(Parameterizer->m_FlatImage); if( Parameterizer->m_FlatImage ) { writer->Write(); } std::cout << " done writing "; } /* template void MeshToImage(vtkPolyData* vtkmesh, int imagesize, std::string outfn) { const unsigned int Dimension = 2; const int splineOrder = 3; const int component = 0; const char* dataname = "scalars"; typedef TImage ImageType; typedef vtkPolyData MeshType; typedef typename ImageType::PixelType PixelType; typedef typename itk::Vector FunctionalDataType; typedef typename itk::Image FunctionalImageType; int numVertices = vtkmesh->GetNumberOfPoints(); vtkmesh->GetPointData()->SetActiveScalars( dataname ); std::cout << "#of vertices " << numVertices << " Fitting variable " << vtkmesh->GetPointData()->GetScalars()->GetName( ) << std::endl; typedef itk::PointSet FunctionalMapType; typedef itk::BSplineScatteredDataPointSetToImageFilter BSplineFilterType; typename FunctionalMapType::Pointer funcdataPoints = FunctionalMapType::New(); typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); FunctionalDataType data, data1; typename FunctionalMapType::PointType point; double bounds[6]; vtkPoints *meshpoints = vtkmesh->GetPoints(); meshpoints->ComputeBounds(); meshpoints->GetBounds( bounds ); for (int ID=0; ID < numVertices; ++ID) { data[0] = vtkmesh->GetPointData()->GetScalars()->GetComponent(ID, component); for (int dir=0; dir < Dimension; ++dir) point[dir] = meshpoints->GetPoint(ID)[dir]; //Debug //data[0] = 1*point[0] + 0*point[1]; funcdataPoints->SetPointData( ID, data); funcdataPoints->SetPoint( ID, point); //std::cout << ID << " " << point[0] << " " << point[1] << " " << data[0] << std::endl; } std::cout << "Read all mesh data" << std::endl; typename FunctionalImageType::PointType origin; typename FunctionalImageType::SpacingType spacing; typename FunctionalImageType::SizeType size; float maxX, minX, maxY, minY; maxX = static_cast(bounds[1]); minX = static_cast(bounds[0]); maxY = static_cast(bounds[3]); minY = static_cast(bounds[2]); std::cout << "minX " << minX << " maxX " << maxX << " minY " << minY << " maxY " << maxY << std::endl; size[0] = imagesize; size[1] = imagesize; origin[0] = (minX + maxX)/2; origin[1] = (minY + maxY)/2; origin[0] = minX - (maxX-minX)*0.1; origin[1] = minY - (maxY-minY)*0.1; origin[0] = minX; origin[1] = minY; spacing[0] = 1.1*((((maxX-minX)>(maxY-minY))?(maxX-minX):(maxY-minY)))/imagesize; spacing[1] = spacing[0]; std::cout << "size " << size << " origin " << origin << " spacing " << spacing << std::endl; typename BSplineFilterType::ArrayType ncps; ncps.Fill( splineOrder + 1 ); //const int numLevels = round((1/static_cast(Dimension))*log(static_cast(numVertices))/log(2.0)); const int numLevels = 13; typename BSplineFilterType::ArrayType close; close.Fill( false ); std::cout << "No. of levels " << numLevels << " splineOrder " << splineOrder << " #control points " << ncps << std::endl; bspliner->SetOrigin( origin ); bspliner->SetSpacing( spacing ); bspliner->SetSize( size ); bspliner->SetGenerateOutputImage( true ); bspliner->SetNumberOfLevels( numLevels ); bspliner->SetSplineOrder( splineOrder ); bspliner->SetNumberOfControlPoints( ncps ); bspliner->SetCloseDimension( close ); bspliner->SetInput( funcdataPoints ); bspliner->DebugOn(); std::cout << "Entering BSpline" << std::endl; bspliner->Update(); std::cout << "BSpline fitting done" << std::endl; // ORIENTATION ALERT -- the original code here // set the region, spacing, and origin without setting directions. typename ImageType::Pointer outimage = AllocImage(bspliner->GetOutput()); typename itk::ImageRegionIterator ItO( outimage, outimage->GetRequestedRegion() ); typename itk::ImageRegionConstIterator ItB( bspliner->GetOutput(), bspliner->GetOutput()->GetRequestedRegion() ); for ( ItB.GoToBegin(), ItO.GoToBegin(); !ItB.IsAtEnd(); ++ItB, ++ItO ) { ItO.Set( static_cast( ItB.Get()[0] ) ); } typename itk::ImageFileWriter::Pointer writer; writer = itk::ImageFileWriter::New(); std::string fn=outfn+"flat.nii"; writer->SetFileName(fn.c_str()); writer->SetInput( outimage ); writer->Write(); */ /*** Evaluate quality of fit ***/ /* typedef typename itk::LinearInterpolateImageFunction< ImageType, float > InterpolatorType; typename InterpolatorType::PointType testpoint; typename InterpolatorType::Pointer interp = InterpolatorType::New(); interp->SetInputImage( outimage ); for (int ID=0; ID < numVertices; ++ID) { data[0] = vtkmesh->GetPointData()->GetScalars()->GetComponent(ID, component); for (int dir=0; dir < Dimension; ++dir) { point[dir] = meshpoints->GetPoint(ID)[dir]; testpoint[dir] = point[dir] - origin[dir]; } vtkmesh->GetPointData()->GetScalars()->SetComponent(ID, component, static_cast(data[0]) - static_cast(interp->Evaluate( testpoint ))); //vtkmesh->GetPointData()->GetScalars()->SetComponent(ID, component, interp->Evaluate( testpoint )); data1[0] = vtkmesh->GetPointData()->GetScalars()->GetComponent(ID, component); cout << "error " << data1[0] << " original " << data[0] << " at " << point << " interpolated " << interp->Evaluate( testpoint ) << " at " << testpoint << endl; } vtkPolyDataWriter *vtkwriter = vtkPolyDataWriter::New(); vtkwriter->SetInput( vtkmesh ); vtkwriter->SetFileName( "newmesh3.vtk" ); vtkwriter->Write(); */ // } template typename TImage::Pointer RemoveNaNs(typename TImage::Pointer image, float replaceval ) { typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter(image, image->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { typename TImage::PixelType v1 = vfIter.Get(); if( vnl_math_isnan(v1) ) { vfIter.Set(replaceval); } } return image; } /* template void ImageToMesh(vtkPolyData* vtkmesh, typename TImage::Pointer image, std::string outfn) { const unsigned int Dimension = 2; const int splineOrder = 3; const int imagesize = 512; const int component = 0; const char* dataname = "points"; typedef TImage ImageType; typedef vtkPolyData MeshType; typedef typename ImageType::PixelType PixelType; typedef typename itk::Vector FunctionalDataType; typedef typename itk::Image FunctionalImageType; float replaceval = 5; image = RemoveNaNs(image, replaceval); int numVertices = vtkmesh->GetNumberOfPoints(); vtkmesh->GetPointData()->SetActiveScalars( dataname ); std::cout << "#of vertices " << numVertices << " Fitting variable " << vtkmesh->GetPointData()->GetScalars()->GetName( ) << std::endl; typedef itk::PointSet FunctionalMapType; typedef itk::BSplineScatteredDataPointSetToImageFilter BSplineFilterType; typename FunctionalMapType::Pointer funcdataPoints = FunctionalMapType::New(); typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); FunctionalDataType data; typename FunctionalMapType::PointType point; double bounds[6]; vtkPoints *meshpoints = vtkmesh->GetPoints(); meshpoints->ComputeBounds(); meshpoints->GetBounds( bounds ); for (int ID=0; ID < numVertices; ++ID) { data[0] = vtkmesh->GetPointData()->GetScalars()->GetComponent(ID, component); for (int dir=0; dir < Dimension; ++dir) point[dir] = meshpoints->GetPoint(ID)[dir]; //Debug //data[0] = 1*point[0] + 0*point[1]; funcdataPoints->SetPointData( ID, data); funcdataPoints->SetPoint( ID, point); //std::cout << ID << " " << point[0] << " " << point[1] << " " << data[0] << std::endl; } std::cout << "Read all mesh data" << std::endl; // Create new data array vtkFloatArray *newdata; newdata = vtkFloatArray::New(); newdata->SetName("atlas"); typename FunctionalImageType::PointType origin; typename FunctionalImageType::SpacingType spacing; typename FunctionalImageType::SizeType size; float maxX, minX, maxY, minY; maxX = static_cast(bounds[1]); minX = static_cast(bounds[0]); maxY = static_cast(bounds[3]); minY = static_cast(bounds[2]); std::cout << "minX " << minX << " maxX " << maxX << " minY " << minY << " maxY " << maxY << std::endl; size[0] = imagesize; size[1] = imagesize; origin[0] = (minX + maxX)/2; origin[1] = (minY + maxY)/2; origin[0] = minX - (maxX-minX)*0.1; origin[1] = minY - (maxY-minY)*0.1; origin[0] = minX; origin[1] = minY; spacing[0] = 1.1*((((maxX-minX)>(maxY-minY))?(maxX-minX):(maxY-minY)))/imagesize; spacing[1] = spacing[0]; std::cout << "size " << size << " origin " << origin << " spacing " << spacing << std::endl; */ /* bspline code -- not doing b-spline now typename BSplineFilterType::ArrayType ncps; ncps.Fill( splineOrder + 1 ); //const int numLevels = round((1/static_cast(Dimension))*log(static_cast(numVertices))/log(2.0)); const int numLevels = 13; typename BSplineFilterType::ArrayType close; close.Fill( false ); std::cout << "No. of levels " << numLevels << " splineOrder " << splineOrder << " #control points " << ncps << std::endl; bspliner->SetOrigin( origin ); bspliner->SetSpacing( spacing ); bspliner->SetSize( size ); bspliner->SetGenerateOutputImage( true ); bspliner->SetNumberOfLevels( numLevels ); bspliner->SetSplineOrder( splineOrder ); bspliner->SetNumberOfControlPoints( ncps ); bspliner->SetCloseDimension( close ); bspliner->SetInput( funcdataPoints ); bspliner->DebugOn(); std::cout << "Entering BSpline" << std::endl; bspliner->Update(); std::cout << "BSpline fitting done" << std::endl; typename ImageType::Pointer outimage = ImageType::New(); outimage->SetSpacing( bspliner->GetOutput()->GetSpacing() ); outimage->SetOrigin( bspliner->GetOutput()->GetOrigin() ); outimage->SetRegions( bspliner->GetOutput()->GetLargestPossibleRegion() ); outimage->Allocate(); */ /* typename itk::ImageRegionIterator ItO( image, image->GetRequestedRegion() ); // Evaluate quality of fit //typedef typename itk::LinearInterpolateImageFunction< ImageType, float > InterpolatorType; typedef typename itk::NearestNeighborInterpolateImageFunction< ImageType, float > InterpolatorType; typename InterpolatorType::PointType testpoint; typedef typename itk::ContinuousIndex< float, Dimension > ContinuousIndexType; typename InterpolatorType::Pointer interp = InterpolatorType::New(); interp->SetInputImage( image ); ContinuousIndexType contind; for (int ID=0; ID < numVertices; ++ID) { for (int dir=0; dir < Dimension; ++dir) testpoint[dir] = meshpoints->GetPoint(ID)[dir] - origin[dir]; newdata->InsertNextValue( interp->Evaluate( testpoint ) ); //vtkmesh->GetPointData()->GetScalars()->SetComponent(ID, component, (interp->Evaluate( testpoint ))); //vtkmesh->GetPointData()->GetScalars()->SetComponent(ID, component, interp->Evaluate( testpoint )); // cout << vtkmesh->GetPointData()->GetScalars()->GetComponent(ID, component) << " " << data[0] << " " << interp->Evaluate( testpoint ) << endl; } vtkmesh->GetPointData()->AddArray( newdata ); vtkPolyDataWriter *vtkwriter = vtkPolyDataWriter::New(); vtkwriter->SetInput( vtkmesh ); std::string fn=outfn+".vtk"; vtkwriter->SetFileName(fn.c_str()); vtkwriter->Write(); } */ // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ConformalMapping( std::vector args, std::ostream* out_stream = NULL ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ConformalMapping" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); // Define the dimension of the images const unsigned int Dimension = 3; typedef float PixelType; // Declare the types of the output images typedef itk::Image ImageType; typedef itk::Image Image2DType; // Declare the type of the index,size and region to initialize images typedef itk::Index IndexType; typedef itk::Size SizeType; typedef itk::ImageRegion RegionType; typedef itk::ImageRegionIterator IteratorType; // Declare the type of the Mesh char* filename; PixelType loth = 1; PixelType hith = 256; int fixdir = 1; float param = 2.e-4; std::string outfn; std::cout << "to get mesh: ConformalMapping image.nii 1 255 1.e-3 0 outname " << std::endl; std::cout << "to flatten mesh: ConformalMapping mesh.vtk 1 2 3 4 outname " << std::endl; std::cout << "to view mesh: ConformalMapping mesh.vtk 1 2 3 1 outname " << std::endl; std::cout << "to get image topology: ConformalMapping image.nii 1 255 1.e-3 5 outname " << std::endl; std::cout << "to convert flattened mesh to image : ConformalMapping mesh.vtk 1 2 3 6 outname " << std::endl; std::cout << "to interpolate data in flattened image domain to original mesh: ConformalMapping image.nii 1 2 3 7 outname originalflatmesh.vtk" << std::endl; std::cout << " to smooth a mesh --- ConformalMapping mesh.vtk 1 2 NumSmoothIts 8 outname " << std::endl; if( argc >= 2 ) { filename = argv[1]; loth = atof(argv[2]); hith = atof(argv[3]); param = atof(argv[4]); fixdir = atoi(argv[5]); outfn = std::string(argv[6]); } else { filename = (char *)"image.nii"; return 0; } if( fixdir == 0 ) { typedef itk::ImageFileReader FileSourceType; typedef ImageType::PixelType PixType; FileSourceType::Pointer readfilter = FileSourceType::New(); readfilter->SetFileName( filename ); try { readfilter->Update(); } catch( itk::ExceptionObject & e ) { std::cout << "Exception caught during reference file reading " << std::endl; std::cout << e << std::endl; return -1; } ImageType::Pointer image = readfilter->GetOutput(); ImageType::Pointer thresh = BinaryThreshold(loth, hith, hith, image ); // Save the mesh GetMeshAndCurvature(thresh, param, filename); // MapToSphere(thresh,fixdir,e); } else if( fixdir == 5 ) { typedef itk::ImageFileReader FileSourceType; typedef ImageType::PixelType PixType; FileSourceType::Pointer readfilter = FileSourceType::New(); readfilter->SetFileName( filename ); try { readfilter->Update(); } catch( itk::ExceptionObject & e ) { std::cout << "Exception caught during reference file reading " << std::endl; std::cout << e << std::endl; return -1; } ImageType::Pointer image = readfilter->GetOutput(); ImageType::Pointer thresh = BinaryThreshold(loth, hith, hith, image ); // Save the mesh GetImageTopology(thresh, param, filename); // MapToSphere(thresh,fixdir,e); } /*************** flat mesh to image ****************/ else if( fixdir == 6 ) { vtkPolyDataReader *fltReader = vtkPolyDataReader::New(); fltReader->SetFileName(filename); fltReader->Update(); // MeshToImage(fltReader->GetOutput(), 512, outfn); } /*************** flat mesh to image ****************/ else if( fixdir == 7 ) /*************** flat map to mesh *****************/ { char * const refmeshname = argv[7]; vtkPolyDataReader *fltReader = vtkPolyDataReader::New(); fltReader->SetFileName(refmeshname); fltReader->Update(); typedef itk::ImageFileReader FileSourceType; FileSourceType::Pointer readfilter = FileSourceType::New(); readfilter->SetFileName( filename ); readfilter->Update(); Image2DType::Pointer image = readfilter->GetOutput(); // ImageToMesh(fltReader->GetOutput(), image, outfn); } else if( fixdir == 8 ) /*************** flat map to mesh *****************/ { std::cout << " read " << std::string(filename) << " write " << outfn << std::endl; vtkPolyDataReader *fltReader = vtkPolyDataReader::New(); fltReader->SetFileName(filename); fltReader->Update(); vtkSmartPointer smoother = vtkSmartPointer::New(); smoother->SetInput(fltReader->GetOutput() ); smoother->SetNumberOfIterations( (int) param ); smoother->BoundarySmoothingOn(); smoother->FeatureEdgeSmoothingOff(); smoother->SetFeatureAngle(180.0); smoother->SetEdgeAngle(180.0); smoother->SetPassBand(1.e-3); // smaller values increase smoothing smoother->NonManifoldSmoothingOn(); smoother->NormalizeCoordinatesOff(); smoother->Update(); vtkPolyDataWriter *writer = vtkPolyDataWriter::New(); writer->SetInput( smoother->GetOutput() ); writer->SetFileName(outfn.c_str() ); writer->SetFileTypeToBinary(); writer->Update(); std::cout << " done writing "; } /*************** flat map to mesh ****************/ else { vtkPolyDataReader *fltReader = vtkPolyDataReader::New(); fltReader->SetFileName(filename); fltReader->Update(); vtkPolyData* polydata = fltReader->GetOutput(); vtkSmartPointer normalGenerator = vtkSmartPointer::New(); normalGenerator->SetInput(polydata); normalGenerator->Update(); polydata = normalGenerator->GetOutput(); if( fixdir == 1 ) { Display( static_cast(polydata) ); } // if ( fixdir == 1 ) Display((vtkUnstructuredGrid*)fltReader->GetOutput()); std::cout << " m_Smooth " << param << std::endl; MapToDisc(fltReader->GetOutput(), param, outfn); } return 0; } } // namespace ants ants-2.2.0/Examples/ConvertImage.cxx000066400000000000000000000262121311104306400173410ustar00rootroot00000000000000#include "antsUtilities.h" #include "ReadWriteData.h" #include "itkCastImageFilter.h" #include "itkNumericTraits.h" #include "itkRescaleIntensityImageFilter.h" #include "itkVectorIndexSelectionCastImageFilter.h" #include namespace ants { template int ConvertImage( int argc, char *argv[] ) { typedef TPixel OutputPixelType; if( argc > 4 && atoi( argv[4] ) == 9 ) { typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::Image ComponentImageType; typename DisplacementFieldType::Pointer displacementField = DisplacementFieldType::New(); for( unsigned int d = 0; d < ImageDimension; d++ ) { std::string filename = std::string( argv[2] ); if( d == 0 ) { filename += std::string( "xvec.nii.gz" ); } else if( d == 1 ) { filename += std::string( "yvec.nii.gz" ); } else if( d == 2 ) { filename += std::string( "zvec.nii.gz" ); } typename ComponentImageType::Pointer inputImage; ReadImage( inputImage, filename.c_str() ); if( d == 0 ) { displacementField->CopyInformation( inputImage ); displacementField->SetRegions( inputImage->GetRequestedRegion() ); displacementField->Allocate(); VectorType V; V.Fill( 0.0 ); displacementField->FillBuffer( V ); } itk::ImageRegionConstIterator It( inputImage, inputImage->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItD( displacementField, displacementField->GetLargestPossibleRegion() ); for( It.GoToBegin(), ItD.GoToBegin(); !It.IsAtEnd(); ++ItD, ++It ) { VectorType V = ItD.Get(); V[d] = It.Get(); ItD.Set( V ); } } WriteImage( displacementField, argv[3] ); } else if( argc > 4 && atoi( argv[4] ) == 10 ) { typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::Image ComponentImageType; typename DisplacementFieldType::Pointer inputImage; ReadImage( inputImage, argv[2] ); for( unsigned int d = 0; d < ImageDimension; d++ ) { typedef itk::VectorIndexSelectionCastImageFilter SelectorType; typename SelectorType::Pointer selector = SelectorType::New(); selector->SetInput( inputImage ); selector->SetIndex( d ); selector->Update(); std::string filename = std::string( argv[3] ); if( d == 0 ) { filename += std::string( "xvec.nii.gz" ); } else if( d == 1 ) { filename += std::string( "yvec.nii.gz" ); } else if( d == 2 ) { filename += std::string( "zvec.nii.gz" ); } WriteImage( selector->GetOutput(), filename.c_str() ); } } else if( argc > 4 && atoi( argv[4] ) == 11 ) { typedef itk::Vector VectorType; typedef itk::Image VelocityFieldType; typedef itk::Image ComponentImageType; typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[2] ); reader->Update(); typename VelocityFieldType::Pointer inputImage; ReadImage( inputImage, argv[2] ); for( unsigned int d = 0; d < ImageDimension; d++ ) { typedef itk::VectorIndexSelectionCastImageFilter SelectorType; typename SelectorType::Pointer selector = SelectorType::New(); selector->SetInput( reader->GetOutput() ); selector->SetIndex( d ); selector->Update(); std::string filename = std::string( argv[3] ); if( d == 0 ) { filename += std::string( "xvec.nii.gz" ); } else if( d == 1 ) { filename += std::string( "yvec.nii.gz" ); } else if( d == 2 ) { filename += std::string( "zvec.nii.gz" ); } WriteImage( selector->GetOutput(), filename.c_str() ); } } else if( argc > 4 && atoi( argv[4] ) == 12 ) { typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typename DisplacementFieldType::Pointer displacementField; ReadImage( displacementField, argv[2] ); WriteImage( displacementField, argv[3] ); } else if( argc == 4 || ( argc > 4 && atoi( argv[4] ) < 9 ) ) { typedef typename itk::NumericTraits::RealType RealType; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typename InputImageType::Pointer inputImage; ReadImage( inputImage, argv[2] ); std::vector rescaleFileTypes; rescaleFileTypes.push_back( ".png" ); rescaleFileTypes.push_back( ".jpeg" ); rescaleFileTypes.push_back( ".jpg" ); rescaleFileTypes.push_back( ".tiff" ); rescaleFileTypes.push_back( ".tif" ); rescaleFileTypes.push_back( ".bmp" ); bool isRescaleType = false; for( unsigned int i = 0; i < rescaleFileTypes.size(); i++ ) { if( strstr( argv[3], rescaleFileTypes[i].c_str() ) != NULL ) { isRescaleType = true; break; } } if( isRescaleType ) { typedef itk::RescaleIntensityImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( inputImage ); filter->SetOutputMinimum( itk::NumericTraits::min() ); filter->SetOutputMaximum( itk::NumericTraits::max() ); filter->Update(); WriteImage( filter->GetOutput(), argv[3] ); } else { typedef itk::CastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput( inputImage ); caster->Update(); WriteImage( caster->GetOutput(), argv[3] ); } } return EXIT_SUCCESS; } int ConvertImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ConvertImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cerr << "Usage: " << argv[0] << " imageDimension " << "inputImage outputImage " << std::endl; std::cerr << "pixelType: 0 -> float (default)" << std::endl << " 1 -> unsigned char" << std::endl << " 2 -> unsigned short" << std::endl << " 3 -> unsigned int" << std::endl << " 4 -> unsigned long" << std::endl << " 5 -> char" << std::endl << " 6 -> short" << std::endl << " 7 -> int" << std::endl << " 8 -> long" << std::endl << " 9 -> component images to a float vector image" << std::endl << " 10 -> vector image to component images" << std::endl << " 11 -> time-varying velocity field image to component images (ImageDimension is the dimensionality of the displacement vector)" << std::endl << " 12 -> float vector image" << std::endl; return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: if( argc > 4 && atoi( argv[4] ) == 1 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 2 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 3 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 4 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 5 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 6 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 7 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 8 ) { ConvertImage( argc, argv ); } else { ConvertImage( argc, argv ); } break; case 3: if( argc > 4 && atoi( argv[4] ) == 1 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 2 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 3 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 4 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 5 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 6 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 7 ) { ConvertImage( argc, argv ); } else if( argc > 4 && atoi( argv[4] ) == 8 ) { ConvertImage( argc, argv ); } else { ConvertImage( argc, argv ); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ConvertImagePixelType.cxx000066400000000000000000000216221311104306400212050ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include #include #include #include #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "itkCastImageFilter.h" #include "itkRescaleIntensityImageFilter.h" namespace ants { template int ConvertType(int argc, char *argv[], double MINVAL, double MAXVAL) { typedef TPIXELTYPE outPixelType; typedef float floatPixelType; typedef float inPixelType; typedef itk::Image ImageType; typedef itk::Image IntermediateType; typedef itk::Image OutImageType; typedef itk::ImageFileReader readertype; typedef itk::ImageFileWriter writertype; typename readertype::Pointer reader = readertype::New(); if( argc < 2 ) { std::cout << "Missing input filename" << std::endl; throw; } reader->SetFileName(argv[1]); reader->Update(); std::cout << " Updated reader " << std::endl; typedef itk::CastImageFilter castertype; typename castertype::Pointer caster = castertype::New(); caster->SetInput(reader->GetOutput() ); caster->Update(); // Rescale the image intensities so that they fall between 0 and 255 typedef itk::RescaleIntensityImageFilter FilterType; typename FilterType::Pointer fixedrescalefilter = FilterType::New(); fixedrescalefilter->SetInput(caster->GetOutput() ); const double desiredMinimum = MINVAL; double desiredMaximum = MAXVAL; fixedrescalefilter->SetOutputMinimum( desiredMinimum ); fixedrescalefilter->SetOutputMaximum( desiredMaximum ); fixedrescalefilter->UpdateLargestPossibleRegion(); typedef itk::CastImageFilter castertype2; typename castertype2::Pointer caster2 = castertype2::New(); caster2->SetInput(fixedrescalefilter->GetOutput() ); caster2->Update(); typename OutImageType::Pointer outim = caster2->GetOutput(); typename OutImageType::SpacingType spc = outim->GetSpacing(); outim->SetSpacing(spc); std::cout << " Dire in " << reader->GetOutput()->GetDirection() << std::endl; std::cout << " Dire out " << outim->GetDirection() << std::endl; typename writertype::Pointer writer = writertype::New(); if( argc < 3 ) { std::cout << "Missing output filename" << std::endl; throw; } writer->SetFileName(argv[2]); writer->SetInput(outim); writer->Update(); writer->Write(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ConvertImagePixelType( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ConvertImagePixelType" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " infile.nii out.ext TYPE-OPTION " << std::endl; std::cout << " ext is the extension you want, e.g. tif. " << std::endl; std::cout << " TYPE-OPTION : TYPE " << std::endl; std::cout << " 0 : char " << std::endl; std::cout << " 1 : unsigned char " << std::endl; std::cout << " 2 : short " << std::endl; std::cout << " 3 : unsigned short " << std::endl; std::cout << " 4 : int " << std::endl; std::cout << " 5 : unsigned int " << std::endl; std::cout << " Note that some pixel types are not supported by some image formats. e.g. int is not supported by jpg. " << std::endl; std::cout << " You can easily extend this for other pixel types with a few lines of code and adding usage info. " << std::endl; std::cout << " The image intensity will be scaled to the dynamic range of the pixel type. E.g. uchar => 0 (min), 255 (max). " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } unsigned int typeoption = 0; if( argc > 3 ) { typeoption = atoi(argv[3]); } // Get the image dimension std::string fn = std::string(argv[1]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); if( typeoption == 0 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertType<2, char>(argc, argv, SCHAR_MIN, SCHAR_MAX ); } break; case 3: { ConvertType<3, char>(argc, argv, SCHAR_MIN, SCHAR_MAX ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( typeoption == 1 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertType<2, unsigned char>(argc, argv, 0, UCHAR_MAX ); } break; case 3: { ConvertType<3, unsigned char>(argc, argv, 0, UCHAR_MAX ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( typeoption == 2 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertType<2, short>(argc, argv, SHRT_MIN, SHRT_MAX); } break; case 3: { ConvertType<3, short>(argc, argv, SHRT_MIN, SHRT_MAX); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( typeoption == 3 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertType<2, unsigned short>(argc, argv, 0, USHRT_MAX ); } break; case 3: { ConvertType<3, unsigned short>(argc, argv, 0, USHRT_MAX ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( typeoption == 4 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertType<2, int>(argc, argv, INT_MIN, INT_MAX); } break; case 3: { ConvertType<3, int>(argc, argv, INT_MIN, INT_MAX); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( typeoption == 5 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertType<2, unsigned int>(argc, argv, 0, UINT_MAX ); } break; case 3: { ConvertType<3, unsigned int>(argc, argv, 0, UINT_MAX ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ConvertInputImagePixelTypeToFloat.cxx000066400000000000000000000206301311104306400235140ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include #include #include #include #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "itkCastImageFilter.h" #include "itkRescaleIntensityImageFilter.h" namespace ants { template int ConvertTypeToFloat( int argc, char *argv[] ) { typedef TPIXELTYPE inPixelType; typedef float floatPixelType; typedef float outPixelType; typedef itk::Image ImageType; typedef itk::Image IntermediateType; typedef itk::Image OutImageType; typedef itk::ImageFileReader readertype; typedef itk::ImageFileWriter writertype; typename readertype::Pointer reader = readertype::New(); if( argc < 2 ) { std::cout << "Missing input filename" << std::endl; throw; } reader->SetFileName(argv[1]); reader->Update(); typedef itk::CastImageFilter castertype; typename castertype::Pointer caster = castertype::New(); caster->SetInput(reader->GetOutput() ); caster->Update(); typedef itk::CastImageFilter castertype2; typename castertype2::Pointer caster2 = castertype2::New(); caster2->SetInput(caster->GetOutput() ); caster2->Update(); typename OutImageType::Pointer outim = caster2->GetOutput(); typename writertype::Pointer writer = writertype::New(); if( argc < 3 ) { std::cout << "Missing output filename" << std::endl; throw; } writer->SetFileName(argv[2]); writer->SetInput(outim); writer->Update(); writer->Write(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ConvertInputImagePixelTypeToFloat( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ConvertInputImagePixelTypeToFloat" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " infile.nii out.ext TYPE-OPTION " << std::endl; std::cout << " ext is the extension you want, e.g. tif. " << std::endl; std::cout << " TYPE-OPTION : TYPE " << std::endl; std::cout << " 0 : char " << std::endl; std::cout << " 1 : unsigned char " << std::endl; std::cout << " 2 : short " << std::endl; std::cout << " 3 : unsigned short " << std::endl; std::cout << " 4 : int " << std::endl; std::cout << " 5 : unsigned int " << std::endl; std::cout << " Note that some pixel types are not supported by some image formats. e.g. int is not supported by jpg. " << std::endl; std::cout << " You can easily extend this for other pixel types with a few lines of code and adding usage info. " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } unsigned int typeoption = 0; if( argc > 3 ) { typeoption = atoi(argv[3]); } // Get the image dimension std::string fn = std::string(argv[1]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); if( typeoption == 0 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertTypeToFloat<2, char>(argc, argv ); } break; case 3: { ConvertTypeToFloat<3, char>(argc, argv ); } break; case 4: { ConvertTypeToFloat<4, char>(argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( typeoption == 1 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertTypeToFloat<2, unsigned char>(argc, argv ); } break; case 3: { ConvertTypeToFloat<3, unsigned char>(argc, argv ); } break; case 4: { ConvertTypeToFloat<4, unsigned char>(argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( typeoption == 2 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertTypeToFloat<2, short>(argc, argv ); } break; case 3: { ConvertTypeToFloat<3, short>(argc, argv ); } break; case 4: { ConvertTypeToFloat<4, short>(argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( typeoption == 3 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertTypeToFloat<2, unsigned short>(argc, argv ); } break; case 3: { ConvertTypeToFloat<3, unsigned short>(argc, argv ); } break; case 4: { ConvertTypeToFloat<4, unsigned short>(argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( typeoption == 4 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertTypeToFloat<2, int>(argc, argv ); } break; case 3: { ConvertTypeToFloat<3, int>(argc, argv ); } break; case 4: { ConvertTypeToFloat<4, int>(argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( typeoption == 5 ) { switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertTypeToFloat<2, unsigned int>(argc, argv ); } break; case 3: { ConvertTypeToFloat<3, unsigned int>(argc, argv ); } break; case 4: { ConvertTypeToFloat<4, unsigned int>(argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ConvertScalarImageToRGB.cxx000066400000000000000000000301471311104306400213270ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include #include #include #include #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIterator.h" #include "itkRGBPixel.h" #include "itkRGBAPixel.h" #include "itkRedColormapFunction.h" #include "itkGreenColormapFunction.h" #include "itkBlueColormapFunction.h" #include "itkGreyColormapFunction.h" #include "itkHotColormapFunction.h" #include "itkCoolColormapFunction.h" #include "itkSpringColormapFunction.h" #include "itkSummerColormapFunction.h" #include "itkAutumnColormapFunction.h" #include "itkWinterColormapFunction.h" #include "itkCopperColormapFunction.h" #include "itkHSVColormapFunction.h" #include "itkJetColormapFunction.h" #include "itkCustomColormapFunction.h" #include "itkOverUnderColormapFunction.h" #include "itkScalarToRGBColormapImageFilter.h" namespace ants { template int ConvertScalarImageToRGB( int argc, char *argv[] ) { typedef itk::RGBPixel RGBPixelType; // typedef itk::RGBAPixel RGBPixelType; typedef float RealType; typedef itk::Image RealImageType; typedef itk::Image RGBImageType; typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[2] ); reader->Update(); typedef itk::Image MaskImageType; typename MaskImageType::Pointer maskImage = ITK_NULLPTR; typedef itk::ImageFileReader MaskReaderType; typename MaskReaderType::Pointer maskreader = MaskReaderType::New(); maskreader->SetFileName( argv[4] ); try { maskreader->Update(); maskImage = maskreader->GetOutput(); } catch( ... ) { maskImage = ITK_NULLPTR; } ; std::string colormapString( argv[5] ); typedef itk::ScalarToRGBColormapImageFilter RGBFilterType; typename RGBFilterType::Pointer rgbfilter = RGBFilterType::New(); rgbfilter->SetInput( reader->GetOutput() ); if( colormapString == "red" ) { rgbfilter->SetColormap( RGBFilterType::Red ); } else if( colormapString == "green" ) { rgbfilter->SetColormap( RGBFilterType::Green ); } else if( colormapString == "blue" ) { rgbfilter->SetColormap( RGBFilterType::Blue ); } else if( colormapString == "grey" ) { rgbfilter->SetColormap( RGBFilterType::Grey ); } else if( colormapString == "cool" ) { rgbfilter->SetColormap( RGBFilterType::Cool ); } else if( colormapString == "hot" ) { rgbfilter->SetColormap( RGBFilterType::Hot ); } else if( colormapString == "spring" ) { rgbfilter->SetColormap( RGBFilterType::Spring ); } else if( colormapString == "autumn" ) { rgbfilter->SetColormap( RGBFilterType::Autumn ); } else if( colormapString == "winter" ) { rgbfilter->SetColormap( RGBFilterType::Winter ); } else if( colormapString == "copper" ) { rgbfilter->SetColormap( RGBFilterType::Copper ); } else if( colormapString == "summer" ) { rgbfilter->SetColormap( RGBFilterType::Summer ); } else if( colormapString == "jet" ) { rgbfilter->SetColormap( RGBFilterType::Jet ); // typedef itk::Function::JetColormapFunction ColormapType; // typename ColormapType::Pointer colormap = ColormapType::New(); // rgbfilter->SetColormap( colormap ); } else if( colormapString == "hsv" ) { rgbfilter->SetColormap( RGBFilterType::HSV ); // typedef itk::Function::HSVColormapFunction ColormapType; // typename ColormapType::Pointer colormap = ColormapType::New(); // rgbfilter->SetColormap( colormap ); } else if( colormapString == "overunder" ) { rgbfilter->SetColormap( RGBFilterType::OverUnder ); } else if( colormapString == "custom" ) { typedef itk::Function::CustomColormapFunction ColormapType; typename ColormapType::Pointer colormap = ColormapType::New(); std::ifstream str( argv[6] ); std::string line; // Get red values { std::getline( str, line ); std::istringstream iss( line ); float value; typename ColormapType::ChannelType channel; while( iss >> value ) { channel.push_back( value ); } colormap->SetRedChannel( channel ); } // Get green values { std::getline( str, line ); std::istringstream iss( line ); float value; typename ColormapType::ChannelType channel; while( iss >> value ) { channel.push_back( value ); } colormap->SetGreenChannel( channel ); } // Get blue values { std::getline( str, line ); std::istringstream iss( line ); float value; typename ColormapType::ChannelType channel; while( iss >> value ) { channel.push_back( value ); } colormap->SetBlueChannel( channel ); } rgbfilter->SetColormap( colormap ); } RealType minimumValue = itk::NumericTraits::max(); RealType maximumValue = itk::NumericTraits::NonpositiveMin(); itk::ImageRegionIteratorWithIndex ItS( reader->GetOutput(), reader->GetOutput()->GetLargestPossibleRegion() ); for( ItS.GoToBegin(); !ItS.IsAtEnd(); ++ItS ) { if( !maskImage || maskImage->GetPixel( ItS.GetIndex() ) != 0 ) { if( minimumValue >= ItS.Get() ) { minimumValue = ItS.Get(); } if( maximumValue <= ItS.Get() ) { maximumValue = ItS.Get(); } } } rgbfilter->SetUseInputImageExtremaForScaling( false ); rgbfilter->GetModifiableColormap()->SetMinimumInputValue( minimumValue ); rgbfilter->GetModifiableColormap()->SetMaximumInputValue( maximumValue ); rgbfilter->GetModifiableColormap()->SetMinimumRGBComponentValue( ( argc > 9 ) ? static_cast< typename RGBPixelType::ComponentType>( atof( argv[9] ) ) : 0 ); rgbfilter->GetModifiableColormap()->SetMaximumRGBComponentValue( ( argc > 10 ) ? static_cast< typename RGBPixelType::ComponentType>( atof( argv[10] ) ) : 255 ); if( argc > 7 ) { std::string argvString( argv[7] ); if( argvString != "min" && argvString != "minimum" ) { rgbfilter->GetModifiableColormap()->SetMinimumInputValue( static_cast( atof( argv[7] ) ) ); } } if( argc > 8 ) { std::string argvString( argv[8] ); if( argvString != "max" && argvString != "maximum" ) { rgbfilter->GetModifiableColormap()->SetMaximumInputValue( static_cast( atof( argv[8] ) ) ); } } try { rgbfilter->Update(); } catch( ... ) { return EXIT_FAILURE; } if( maskImage ) { itk::ImageRegionIterator ItM( maskImage, maskImage->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItC( rgbfilter->GetOutput(), rgbfilter->GetOutput()->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItS2( reader->GetOutput(), reader->GetOutput()->GetLargestPossibleRegion() ); ItM.GoToBegin(); ItC.GoToBegin(); ItS2.GoToBegin(); while( !ItM.IsAtEnd() ) { if( ItM.Get() == 0 ) { RGBPixelType rgbpixel; // RealType minimumValue = rgbfilter->GetModifiableColormap()->GetMinimumInputValue(); // RealType maximumValue = rgbfilter->GetModifiableColormap()->GetMaximumInputValue(); // // RealType minimumRGBValue // = rgbfilter->GetModifiableColormap()->GetMinimumRGBComponentValue(); // RealType maximumRGBValue // = rgbfilter->GetModifiableColormap()->GetMaximumRGBComponentValue(); // // RealType ratio = ( ItS2.Get() - minimumValue ) / ( maximumValue - minimumValue ); // // rgbpixel.Fill( ratio * ( maximumRGBValue - minimumRGBValue ) // + minimumRGBValue ); rgbpixel.Fill( itk::NumericTraits::ZeroValue() ); ItC.Set( rgbpixel ); } ++ItM; ++ItC; ++ItS2; } } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( rgbfilter->GetOutput() ); writer->SetFileName( argv[3] ); writer->Update(); if( argc > 11 ) { // Let's arbitrarily choose 256 colors to populate the look up table. std::ofstream str( argv[11] ); RealType minimumValue2 = rgbfilter->GetModifiableColormap()->GetMinimumInputValue(); RealType maximumValue2 = rgbfilter->GetModifiableColormap()->GetMaximumInputValue(); RealType deltaValue = ( maximumValue2 - minimumValue2 ) / 255.0; for( unsigned int d = 0; d < 256; d++ ) { RealType value = minimumValue2 + d * deltaValue; RGBPixelType rgbPixel = rgbfilter->GetModifiableColormap()->operator()( value ); str << value << "," << static_cast( rgbPixel[0] ) << "," << static_cast( rgbPixel[1] ) << "," << static_cast( rgbPixel[2] ) << ",1" << std::endl; } str.close(); } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ConvertScalarImageToRGB( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ConvertScalarImageToRGB" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 6 ) { std::cout << "Usage: " << argv[0] << " imageDimension inputImage outputImage " << "mask colormap [customColormapFile] [minimumInput] [maximumInput] " << "[minimumRGBOutput=0] [maximumRGBOutput=255] " << std::endl; std::cout << " Possible colormaps: grey, red, green, blue, copper, jet, hsv, "; std::cout << "spring, summer, autumn, winter, hot, cool, overunder, custom" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { ConvertScalarImageToRGB<2>( argc, argv ); } break; case 3: { ConvertScalarImageToRGB<3>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ConvertToJpg.cxx000066400000000000000000000140411311104306400173370ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include #include #include #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "itkCastImageFilter.h" #include "itkRescaleIntensityImageFilter.h" namespace ants { template int ConvertType(int argc, char *argv[]) { typedef unsigned char outPixelType; typedef float floatPixelType; typedef float inPixelType; typedef itk::Image ImageType; typedef itk::Image IntermediateType; typedef itk::Image OutImageType; typedef itk::ImageFileReader readertype; typedef itk::ImageFileWriter writertype; typename readertype::Pointer reader = readertype::New(); if( argc < 2 ) { std::cout << "Missing input filename" << std::endl; throw; } reader->SetFileName(argv[1]); reader->Update(); std::cout << " Updated reader " << std::endl; typedef itk::CastImageFilter castertype; typename castertype::Pointer caster = castertype::New(); caster->SetInput(reader->GetOutput() ); caster->Update(); // Rescale the image intensities so that they fall between 0 and 255 typedef itk::RescaleIntensityImageFilter FilterType; typename FilterType::Pointer fixedrescalefilter = FilterType::New(); fixedrescalefilter->SetInput(caster->GetOutput() ); const double desiredMinimum = 0.0; const double desiredMaximum = 255.0; fixedrescalefilter->SetOutputMinimum( desiredMinimum ); fixedrescalefilter->SetOutputMaximum( desiredMaximum ); fixedrescalefilter->UpdateLargestPossibleRegion(); typedef itk::CastImageFilter castertype2; typename castertype2::Pointer caster2 = castertype2::New(); caster2->SetInput(fixedrescalefilter->GetOutput() ); caster2->Update(); typename OutImageType::Pointer outim = caster2->GetOutput(); typename OutImageType::SpacingType spc = outim->GetSpacing(); outim->SetSpacing(spc); std::cout << " Dire in " << reader->GetOutput()->GetDirection() << std::endl; std::cout << " Dire out " << outim->GetDirection() << std::endl; typename writertype::Pointer writer = writertype::New(); if( argc < 3 ) { std::cout << "Missing output filename" << std::endl; throw; } writer->SetFileName(argv[2]); writer->SetInput(outim); writer->Update(); writer->Write(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ConvertToJpg( std::vector args, std::ostream* /*out_stream = NULL */ ) { try { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ConvertToJpg" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: ConvertToJpg infile.nii out.jpg " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension std::string fn = std::string(argv[1]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); switch( imageIO->GetNumberOfDimensions() ) { case 2: { ConvertType<2>(argc, argv); } break; case 3: { ConvertType<3>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } catch( const itk::ExceptionObject & e ) { e.Print( std::cerr ); return EXIT_FAILURE; } catch( const std::exception & e ) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch( ... ) { std::cerr << "Unknown exception caught" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ConvertTransformFile.cxx000066400000000000000000000444151311104306400210770ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include #include "itksys/SystemTools.hxx" #include #include #include "itkTransformFactory.h" #include "itkAffineTransform.h" #include "itkTranslationTransform.h" #include "itkIdentityTransform.h" #include "itkantsReadWriteTransform.h" /* Utility to read in a transform file (presumed to be in binary format) and output * it in one of several different formats, defaulting to legacy text format for human reading. * Options are available to instead output only a transform matrix to a text file, * one row per dimension with space-delimited values. This option works only for * transforms of MatrixOffsetTranformBase or derived, Translation and Identity transforms. */ namespace ants { using namespace std; /* * */ bool FileExists(string strFilename) { struct stat stFileInfo; bool blnReturn; int intStat; // Attempt to get the file attributes intStat = stat(strFilename.c_str(), &stFileInfo); if( intStat == 0 ) { // We were able to get the file attributes // so the file obviously exists. blnReturn = true; } else { // We were not able to get the file attributes. // This may mean that we don't have permission to // access the folder which contains this file. If you // need to do that level of checking, lookup the // return values of stat which will give you // more details on why stat failed. blnReturn = false; } return blnReturn; } /* * */ template bool GetMatrix( const typename TTransform::Pointer & transform, typename TTransform::MatrixType & matrix, bool outputRAS ) { const unsigned int ImageDimension = TTransform::InputSpaceDimension; typedef typename TTransform::ScalarType ScalarType; matrix.Fill( itk::NumericTraits::ZeroValue() ); bool done = false; // Matrix-offset derived { typedef itk::MatrixOffsetTransformBase CastTransformType; typename CastTransformType::Pointer castTransform = dynamic_cast(transform.GetPointer() ); if( castTransform.IsNotNull() ) { matrix = castTransform->GetMatrix(); done = true; } } // Translation if( !done ) { typedef itk::TranslationTransform CastTransformType; typename CastTransformType::Pointer castTransform = dynamic_cast(transform.GetPointer() ); if( castTransform.IsNotNull() ) { for( unsigned int i = 0; i < ImageDimension; i++ ) { matrix(i, i) = itk::NumericTraits::OneValue(); } done = true; } } // Identity if( !done ) { typedef itk::IdentityTransform CastTransformType; typename CastTransformType::Pointer castTransform = dynamic_cast(transform.GetPointer() ); if( castTransform.IsNotNull() ) { for( unsigned int i = 0; i < ImageDimension; i++ ) { matrix(i, i) = itk::NumericTraits::OneValue(); } done = true; } } if( !done ) { // Unsupported transform type return false; } if( outputRAS ) { // Convert to RAS coordinate system. ITK uses LPS. // x and y dimensions are flipped. // This code is from c3d app. vnl_vector v_lps_to_ras(ImageDimension, 1.0); v_lps_to_ras[0] = -1.0; if( ImageDimension > 1 ) { v_lps_to_ras[1] = -1.0; } vnl_diag_matrix m_lps_to_ras(v_lps_to_ras); vnl_matrix mold = matrix.GetVnlMatrix(); matrix.GetVnlMatrix().update(m_lps_to_ras * mold * m_lps_to_ras); } return true; } /* * */ template bool GetHomogeneousMatrix( const typename TTransform::Pointer & transform, TMatrix & hMatrix, bool outputRAS ) { const unsigned int ImageDimension = TTransform::InputSpaceDimension; typedef typename TTransform::ScalarType ScalarType; hMatrix.Fill( itk::NumericTraits::ZeroValue() ); bool done = false; // Get the NxN matrix typename TTransform::MatrixType matrix; if( !GetMatrix( transform, matrix, outputRAS ) ) { return false; } for( unsigned int i = 0; i < ImageDimension; i++ ) { for( unsigned int j = 0; j < ImageDimension; j++ ) { hMatrix(i, j) = matrix(i, j); } } // Set the lower-right corner to 1 unsigned int corner = ImageDimension; hMatrix(corner, corner) = itk::NumericTraits::OneValue(); // // Get the offset // // Identity { typedef itk::IdentityTransform CastTransformType; typename CastTransformType::Pointer castTransform = dynamic_cast(transform.GetPointer() ); if( castTransform.IsNotNull() ) { // Nothing more to do here. return true; } } typename TTransform::OutputVectorType offset; offset.Fill( itk::NumericTraits::ZeroValue() ); // Matrix-offset derived { typedef itk::MatrixOffsetTransformBase CastTransformType; typename CastTransformType::Pointer castTransform = dynamic_cast(transform.GetPointer() ); if( castTransform.IsNotNull() ) { offset = castTransform->GetOffset(); done = true; } } // Translation if( !done ) { typedef itk::TranslationTransform CastTransformType; typename CastTransformType::Pointer castTransform = dynamic_cast(transform.GetPointer() ); if( castTransform.IsNotNull() ) { offset = castTransform->GetOffset(); done = true; } } if( !done ) { // Unsupported transform type return false; } if( outputRAS ) { // Convert to RAS coordinate system. ITK uses LPS. // x and y dimensions are flipped. // This code is from c3d app. offset[0] *= -1.0; if( ImageDimension > 1 ) { offset[1] *= -1.0; } } for( unsigned int i = 0; i < ImageDimension; i++ ) { hMatrix(i, ImageDimension) = offset[i]; } return true; } /* * */ template int ConvertTransformFile(int argc, char* argv[]) { int inputFilenamePos = 2; int outFilenamePos = 3; bool outputMatrix = false; bool outputHomogeneousMatrix = false; bool outputAffine = false; bool outputRAS = false; // User options if( argc > 4 ) { for( int n = 4; n < argc; n++ ) { if( strcmp(argv[n], "--matrix") == 0 || strcmp(argv[n], "-m") == 0 ) { // User has requested outputting matrix information only. outputMatrix = true; } else if( strcmp(argv[n], "--homogeneousMatrix") == 0 || strcmp(argv[n], "--hm") == 0 ) { // User has requested outputting homogeneous matrix information only. outputHomogeneousMatrix = true; } else if( strcmp(argv[n], "--convertToAffineType") == 0 ) { outputAffine = true; } else if( strcmp(argv[n], "--RAS") == 0 || strcmp(argv[n], "--ras") == 0 ) { outputRAS = true; } else { std::cout << "Unrecognized option: " << argv[n] << std::endl; return EXIT_FAILURE; } } if( outputRAS && !outputMatrix && !outputHomogeneousMatrix ) { std::cout << " '--RAS' option must be used with either of 'matrix' or 'homongeneousMatrix' options." << std::endl; return EXIT_FAILURE; } if( (outputMatrix && outputHomogeneousMatrix) || (outputMatrix && outputAffine) || (outputHomogeneousMatrix && outputAffine) ) { std::cout << "Only one primary output option allowed at once." << std::endl; return EXIT_FAILURE; } } // Check the filename std::string inputFilename = std::string( argv[inputFilenamePos] ); if( !FileExists(inputFilename) ) { std::cout << " file " << inputFilename << " does not exist . " << std::endl; return EXIT_FAILURE; } // Get the output filename std::string outFilename = std::string( argv[outFilenamePos] ); // Read the transform typedef itk::Transform TransformType; typename TransformType::Pointer transform; typedef itk::MatrixOffsetTransformBase baseTransformType; itk::TransformFactory::RegisterTransform(); transform = itk::ants::ReadTransform( inputFilename ); if( transform.IsNull() ) { std::cout << "Error while reading transform file. Did you specify the correct dimension?" << std::endl; return EXIT_FAILURE; } // // Outputs // if( outputMatrix || outputHomogeneousMatrix ) { std::ofstream outputStream; outputStream.open(outFilename.c_str(), std::ios::out); if( outputStream.fail() ) { outputStream.close(); std::cout << "Failed opening the output file " << outFilename << std::endl; return EXIT_FAILURE; } if( outputMatrix ) { typedef itk::Matrix MatrixType; MatrixType matrix; if( GetMatrix( transform, matrix, outputRAS ) ) { outputStream << matrix; } else { std::cout << "Error. Transform type is unsupported for getting matrix: " << transform->GetNameOfClass() << std::endl; return EXIT_FAILURE; } } else { // Homogeneous matrix typedef itk::Matrix MatrixType; MatrixType hMatrix; if( GetHomogeneousMatrix( transform, hMatrix, outputRAS ) ) { outputStream << hMatrix; } else { std::cout << "Error. Transform type is unsupported for getting matrix: " << transform->GetNameOfClass() << std::endl; return EXIT_FAILURE; } } outputStream.close(); return EXIT_SUCCESS; } if( outputAffine ) { // Convert to Affine and output as binary. // This is done by taking the matrix and offset from the transform // and assigning them to a new affine transform. typedef itk::MatrixOffsetTransformBase CastTransformType; typename CastTransformType::Pointer matrixOffsetTransform = dynamic_cast(transform.GetPointer() ); if( matrixOffsetTransform.IsNull() ) { std::cout << "The transfrom read from file is not derived from MatrixOffsetTransformBase. Cannot convert to Affine." << std::endl; return EXIT_FAILURE; } if( itksys::SystemTools::GetFilenameLastExtension(outFilename) != ".mat" ) { std::cout << "Output filename '" << outFilename << "' must end in '.mat' for binary output." << std::endl; return EXIT_FAILURE; } typedef itk::AffineTransform AffineTransformType; typename AffineTransformType::Pointer newAffineTransform = AffineTransformType::New(); newAffineTransform->SetMatrix( matrixOffsetTransform->GetMatrix() ); newAffineTransform->SetOffset( matrixOffsetTransform->GetOffset() ); transform = dynamic_cast(newAffineTransform.GetPointer() ); if( transform.IsNull() ) { std::cout << "Unexpected error casting from affine transform to transform type." << std::endl; return EXIT_FAILURE; } int result = itk::ants::WriteTransform( transform, outFilename ); if( result == EXIT_FAILURE ) { std::cout << "Failed writing converted transform to binary format." << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } // Default behavior. // Write it out as a text file using the legacy txt transform format if( itksys::SystemTools::GetFilenameLastExtension(outFilename) != ".txt" && itksys::SystemTools::GetFilenameLastExtension(outFilename) != ".tfm" ) { std::cout << "Output filename '" << outFilename << "' must end in '.txt' or '.tfm' for text-format output." << std::endl; return EXIT_FAILURE; } int result = itk::ants::WriteTransform( transform, outFilename ); if( result == EXIT_FAILURE ) { std::cout << "Failed writing transform to text format." << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' /* * */ int ConvertTransformFile( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ConvertTransformFile" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 || ( strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ) ) { std::cout << "USAGE: " << std::endl << " " << argv[0] << " dimensions inputTransfromFile.ext outputTransformFile.ext [OPTIONS]" << std::endl << std::endl; std::cout << "COMMAND: " << std::endl << " Utility to read in a transform file (presumed to be in binary format) " << std::endl << " and output it in various formats. Default output is legacy human-readable" << std::endl << " text format. Without any options, the output filename extension must be " << std::endl << " .txt or .tfm to signify a text-formatted transform file. " << std::endl << std::endl << " OPTIONS: " << std::endl << std::endl << " --matrix, -m " << std::endl << " Output only the transform matrix (from transform::GetMatrix() )" << std::endl << " to a text file, one row per line with space-delimited values. " << std::endl << " Only works for transforms of type identity, translation or " << std::endl << " MatrixOffsetTranformBase and its derived types." << std::endl << " The output filename must end in '.mat'." << std::endl << std::endl << " --homogeneousMatrix, --hm" << std::endl << " Output an N+1 square homogeneous matrix from the transform matrix and offset." << std::endl << " Only works for transforms of type identity, translation or " << std::endl << " MatrixOffsetTranformBase and its derived types." << std::endl << " The output filename must end in '.mat'." << std::endl << std::endl << " --RAS, --ras" << std::endl << " Combined with the 'matrix' or 'homogeneousMatrix' options, this will convert" << std::endl << " the output into the RAS coordinate system (Right, Anterior, Superior)." << std::endl << " Otherwise, the output is in the LPS coordinate system (Left, Posterior," << std::endl << " Superior), which is used by ITK. RAS is used, for example, by Slicer. " << std::endl << std::endl << " --convertToAffineType" << std::endl << " Convert the input transform type to AffineTransform using the transform's " << std::endl << " matrix and offset, and output again as as a binary transform file." << std::endl << " This is useful for using transforms in programs" << std::endl << " that do not register all available Transform factory types." << std::endl << std::endl; if( argc < 4 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } if( argc > 6 ) { std::cout << "Too many arguments." << std::endl; return EXIT_FAILURE; } // Get the image dimension unsigned int dimension = atoi( argv[1] ); switch( dimension ) { case 1: { return ConvertTransformFile<1>(argc, argv); } break; case 2: { return ConvertTransformFile<2>(argc, argv); } break; case 3: { return ConvertTransformFile<3>(argc, argv); } break; case 4: { return ConvertTransformFile<4>(argc, argv); } break; default: std::cout << "Unsupported dimension " << dimension << "." << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ConvertVectorFieldToVTK.cxx000066400000000000000000000125241311104306400214160ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include "itkImageFileReader.h" #include "itkImageRegionIteratorWithIndex.h" #include "vtkUnstructuredGrid.h" #include "vtkUnstructuredGridWriter.h" #include "vtkFloatArray.h" #include "vtkPoints.h" #include "vtkPointData.h" namespace ants { // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ConvertVectorFieldToVTK( std::vector args, std::ostream* out_stream = NULL ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ConvertVectorFieldToVTK" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " inputDisplacementField outputVTKFile maskImage(optional) slice(optional) whichAxis(optional)" << std::endl; return EXIT_FAILURE; } typedef float PixelType; const unsigned int ImageDimension = 3; typedef itk::Image ImageType; typedef itk::Image MaskImageType; typedef double RealType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::ImageFileReader ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[1] ); // reader->SetUseAvantsNamingConvention( true ); reader->Update(); MaskImageType::Pointer mask; if( argc >= 4 ) { typedef itk::ImageFileReader MaskReaderType; MaskReaderType::Pointer maskreader = MaskReaderType::New(); maskreader->SetFileName( argv[3] ); maskreader->Update(); mask = maskreader->GetOutput(); } else { // ORIENTATION ALERT -- the original code here // set the region, spacing, and origin without setting directions. mask = AllocImage(reader->GetOutput(), 1); } int size[ImageDimension]; int totalsize = 1; for( unsigned int i = 0; i < ImageDimension; i++ ) { size[i] = reader->GetOutput()->GetLargestPossibleRegion().GetSize()[i]; if( argc > 4 && atoi( argv[5] ) == (int) i ) { size[i] = 1; } totalsize *= size[i]; } int totalPoints = totalsize; vtkUnstructuredGrid *field = vtkUnstructuredGrid::New(); vtkPoints * points = vtkPoints::New(); points->Allocate( totalPoints ); vtkFloatArray *vectors = vtkFloatArray::New(); vectors->SetNumberOfComponents( 3 ); vectors->SetNumberOfTuples( totalPoints ); float x[3], v[3]; int offset = 0; itk::ImageRegionIteratorWithIndex It ( mask, mask->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { DisplacementFieldType::IndexType idx = It.GetIndex(); if( ( argc > 4 && idx[atoi( argv[5] )] != atoi( argv[4] ) ) || It.Get() == 0 ) { continue; } DisplacementFieldType::PointType point; reader->GetOutput()->TransformIndexToPhysicalPoint( idx, point ); VectorType V = reader->GetOutput()->GetPixel( idx ); for( unsigned int i = 0; i < ImageDimension; i++ ) { x[i] = point[i]; v[i] = V[i]; } // offset = idx[0] + idx[1]*(size[1]+1) + idx[2]*(size[1]+1)*(size[3]+1); points->InsertPoint( offset, x ); vectors->InsertTuple( offset++, v ); } field->SetPoints( points ); field->GetPointData()->SetVectors( vectors ); points->Delete(); vectors->Delete(); vtkUnstructuredGridWriter *writer = vtkUnstructuredGridWriter::New(); writer->SetInput( field ); // writer->SetFileTypeToBinary(); writer->SetFileName( argv[2] ); writer->Write(); return 0; } } // namespace ants ants-2.2.0/Examples/CopyImageHeaderInformation.cxx000066400000000000000000000142221311104306400221500ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include #include #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "itkCastImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "ReadWriteData.h" #include "TensorFunctions.h" namespace ants { template int CopyImageHeaderInformation(int argc, char *argv[]) { typedef float inPixelType; typedef itk::Image ImageType; typedef itk::ImageFileReader readertype; typename readertype::Pointer reader = readertype::New(); reader->SetFileName(argv[1]); reader->Update(); // std::cout << " Spacing " << reader->GetOutput()->GetSpacing() << std::endl; // std::cout << " Origin " << reader->GetOutput()->GetOrigin() << std::endl; // std::cout << " Direction " << std::endl << reader->GetOutput()->GetDirection() << std::endl; // std::cout << " Size " << std::endl << reader->GetOutput()->GetLargestPossibleRegion().GetSize() << std::endl; bool istensor = false; if( argc > 7 ) { if( atoi(argv[7]) ) { istensor = true; } } if( istensor ) { typedef itk::Vector TensorType; typedef itk::Image TensorFieldType; typename TensorFieldType::Pointer timage; ReadTensorImage(timage, argv[2], false); // std::cout<< " tim dir " << timage->GetDirection() << std::endl; if( argc > 6 ) { if( atoi(argv[6]) ) { timage->SetSpacing( reader->GetOutput()->GetSpacing() ); } } if( argc > 5 ) { if( atoi(argv[5]) ) { timage->SetOrigin( reader->GetOutput()->GetOrigin() ); } } if( argc > 4 ) { if( atoi(argv[4]) ) { timage->SetDirection( reader->GetOutput()->GetDirection() ); } } // std::cout<< " tim dir " << timage->GetDirection() << std::endl; WriteTensorImage( timage, argv[3], false); return EXIT_SUCCESS; } typename readertype::Pointer reader2 = readertype::New(); reader2->SetFileName(argv[2]); reader2->Update(); // MakeNewImage(typename TImage::Pointer image1, typename TImage::PixelType initval) typename ImageType::Pointer newimage = MakeNewImage(reader2->GetOutput(), -1); if( argc > 6 ) { if( atoi(argv[6]) ) { newimage->SetSpacing( reader->GetOutput()->GetSpacing() ); } } if( argc > 5 ) { if( atoi(argv[5]) ) { newimage->SetOrigin( reader->GetOutput()->GetOrigin() ); } } if( argc > 4 ) { if( atoi(argv[4]) ) { newimage->SetDirection( reader->GetOutput()->GetDirection() ); } } WriteImage(newimage, argv[3]); return EXIT_FAILURE; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int CopyImageHeaderInformation( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "CopyImageHeaderInformation" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Usage: " << argv[0] << " refimage.ext imagetocopyrefimageinfoto.ext imageout.ext boolcopydirection boolcopyorigin boolcopyspacing {bool-Image2-IsTensor}" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension std::string fn = std::string(argv[1]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); unsigned int dim = imageIO->GetNumberOfDimensions(); switch( dim ) { case 2: { CopyImageHeaderInformation<2>(argc, argv); } break; case 3: { CopyImageHeaderInformation<3>(argc, argv); } break; case 4: { CopyImageHeaderInformation<4>(argc, argv); } break; default: std::cout << "Unsupported dimension : " << dim << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/CreateDTICohort.cxx000066400000000000000000001234171311104306400177060ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include "antsCommandLineParser.h" #include "antsAllocImage.h" #include "itkArray2D.h" #include "itkDecomposeTensorFunction.h" #include "itkDiffusionTensor3D.h" #include "itkImageDuplicator.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIterator.h" #include "itkLabelGeometryImageFilter.h" #include "itkMersenneTwisterRandomVariateGenerator.h" #include "itkNumericSeriesFileNames.h" #include "itkTimeProbe.h" #include "itkVariableSizeMatrix.h" #include #include "vnl/vnl_matrix.h" #include "vnl/vnl_vector.h" #include #include #include #include #include namespace ants { template double CalculateFractionalAnisotropy( TensorType tensor ) { typename TensorType::EigenValuesArrayType eigenvalues; typename TensorType::EigenVectorsMatrixType eigenvectors; tensor.ComputeEigenAnalysis( eigenvalues, eigenvectors ); if( eigenvalues[0] < 0 ) { eigenvalues[0] = eigenvalues[1]; } if( TensorType::Dimension == 3 && eigenvalues[2] < 0 ) { eigenvalues[2] = eigenvalues[1]; } double fa = 0.0; double mean = tensor.GetTrace() / static_cast( TensorType::Dimension ); double numerator = 0.0; double denominator = 0.0; for( unsigned int d = 0; d < TensorType::Dimension; d++ ) { numerator += vnl_math_sqr( eigenvalues[d] - mean ); denominator += vnl_math_sqr( eigenvalues[d] ); } fa = std::sqrt( ( 3.0 * numerator ) / ( 2.0 * denominator ) ); return fa; } template double CalculateMeanDiffusivity( TensorType tensor ) { typename TensorType::EigenValuesArrayType eigenvalues; typename TensorType::EigenVectorsMatrixType eigenvectors; tensor.ComputeEigenAnalysis( eigenvalues, eigenvectors ); if( eigenvalues[0] < 0 ) { eigenvalues[0] = eigenvalues[1]; } if( TensorType::Dimension == 3 && eigenvalues[2] < 0 ) { eigenvalues[2] = eigenvalues[1]; } double mean = tensor.GetTrace() / static_cast( TensorType::Dimension ); return mean; } template int CreateDTICohort( itk::ants::CommandLineParser *parser ) { typedef float RealType; typedef itk::SymmetricSecondRankTensor TensorType; typedef itk::VariableSizeMatrix MatrixType; typedef itk::Image ImageType; typedef unsigned int LabelType; typedef itk::Image MaskImageType; typename MaskImageType::Pointer maskImage = ITK_NULLPTR; typedef itk::Image TensorImageType; typename TensorImageType::Pointer inputAtlas = ITK_NULLPTR; typedef itk::ImageFileReader TensorReaderType; typename TensorReaderType::Pointer reader = TensorReaderType::New(); typedef itk::DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomizerType; typename RandomizerType::Pointer randomizer = RandomizerType::New(); randomizer->Initialize(); // // Get the input DTI atlas // typename itk::ants::CommandLineParser::OptionType::Pointer inputAtlasOption = parser->GetOption( "dti-atlas" ); if( inputAtlasOption && inputAtlasOption->GetNumberOfFunctions() ) { std::string inputFile = inputAtlasOption->GetFunction( 0 )->GetName(); reader->SetFileName( inputFile.c_str() ); inputAtlas = reader->GetOutput(); inputAtlas->Update(); inputAtlas->DisconnectPipeline(); } else { std::cout << "ERROR: Input DTI atlas not specified." << std::endl; return EXIT_FAILURE; } // // Get the number of output images and duplicate the atlas for each cohort. // std::string outputDirectory( "./" ); std::string rootOutputFileName( "outputDWI" ); unsigned int numberOfControls = 10; unsigned int numberOfExperimentals = 10; typename itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { outputDirectory = outputOption->GetFunction( 0 )->GetParameter( 0 ); outputDirectory += std::string( "/" ); } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { rootOutputFileName = outputOption->GetFunction( 0 )->GetParameter( 1 ); } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { numberOfControls = parser->Convert( outputOption->GetFunction( 0 )->GetParameter( 2 ) ); } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { numberOfExperimentals = parser->Convert( outputOption->GetFunction( 0 )->GetParameter( 3 ) ); } } else { std::cout << "ERROR: No output specified." << std::endl; return EXIT_FAILURE; } // // Get the label mask. If not specified, create one from the DTI atlas. // typename itk::ants::CommandLineParser::OptionType::Pointer maskImageOption = parser->GetOption( "label-mask-image" ); RealType lowerThresholdFunction = 0.2; if( maskImageOption && maskImageOption->GetNumberOfFunctions() ) { std::string inputFile = maskImageOption->GetFunction( 0 )->GetName(); typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer maskreader = ReaderType::New(); maskreader->SetFileName( inputFile.c_str() ); try { maskImage = maskreader->GetOutput(); maskImage->Update(); maskImage->DisconnectPipeline(); } catch( ... ) { lowerThresholdFunction = parser->Convert( maskImageOption->GetFunction( 0 )->GetName() ); } } if( maskImage.IsNull() ) { std::cout << "Mask not read. Creating mask by thresholding " << "the FA of the DTI atlas at >= " << lowerThresholdFunction << "." << std::endl << std::endl; typename ImageType::Pointer faImage = AllocImage(inputAtlas, 0.0); itk::ImageRegionIterator ItA( inputAtlas, inputAtlas->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItF( faImage, faImage->GetLargestPossibleRegion() ); for( ItA.GoToBegin(), ItF.GoToBegin(); !ItA.IsAtEnd(); ++ItA, ++ItF ) { ItF.Set( CalculateFractionalAnisotropy( ItA.Get() ) ); } typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( faImage ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); thresholder->SetLowerThreshold( lowerThresholdFunction ); thresholder->SetUpperThreshold( 2.0 ); maskImage = thresholder->GetOutput(); maskImage->Update(); maskImage->DisconnectPipeline(); } // // Get label information for pathology option // typedef itk::LabelGeometryImageFilter LabelGeometryFilterType; typename LabelGeometryFilterType::Pointer labelGeometry = LabelGeometryFilterType::New(); labelGeometry->SetInput( maskImage ); labelGeometry->CalculatePixelIndicesOff(); labelGeometry->CalculateOrientedBoundingBoxOff(); labelGeometry->CalculateOrientedLabelRegionsOff(); labelGeometry->Update(); typename LabelGeometryFilterType::LabelsType labels = labelGeometry->GetLabels(); std::sort( labels.begin(), labels.end() ); unsigned int totalMaskVolume = 0; for( unsigned int n = 0; n < labels.size(); n++ ) { totalMaskVolume += static_cast( labelGeometry->GetVolume( labels[n] ) ); } // Fill in default values per label: // column 1: percentage change in longitudinal eigenvalue // column 2: percentage change in average of transverse eigenvalue(s) // column 3: percentage of affected voxels itk::Array2D pathologyParameters( labels.size(), 3 ); for( unsigned int i = 0; i < labels.size(); i++ ) { pathologyParameters(i, 0) = 0.0; pathologyParameters(i, 1) = 0.0; pathologyParameters(i, 2) = 0.0; } /** * labels */ typename itk::ants::CommandLineParser::OptionType::Pointer pathologyOption = parser->GetOption( "pathology" ); if( pathologyOption && pathologyOption->GetNumberOfFunctions() ) { if( pathologyOption->GetNumberOfFunctions() == 1 && ( pathologyOption->GetFunction( 0 )->GetName() ).empty() ) { float pathologyDeltaEig1 = 0.0; float pathologyDeltaEig2_Eig3 = 0.0; float percentageVoxels = 0.0; if( pathologyOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { pathologyDeltaEig1 = parser->Convert( pathologyOption->GetFunction( 0 )->GetParameter( 0 ) ); } if( pathologyOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { pathologyDeltaEig2_Eig3 = parser->Convert( pathologyOption->GetFunction( 0 )->GetParameter( 1 ) ); } if( pathologyOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { percentageVoxels = parser->Convert( pathologyOption->GetFunction( 0 )->GetParameter( 2 ) ); } for( unsigned int n = 0; n < labels.size(); n++ ) { RealType percentage = percentageVoxels; if( percentage > 1.0 ) { percentage /= static_cast( labelGeometry->GetVolume( labels[n] ) ); } pathologyParameters(n, 0) = pathologyDeltaEig1; pathologyParameters(n, 1) = pathologyDeltaEig2_Eig3; pathologyParameters(n, 2) = percentage; } } else { for( unsigned int n = 0; n < pathologyOption->GetNumberOfFunctions(); n++ ) { LabelType whichClass = parser->Convert( pathologyOption->GetFunction( n )->GetName() ); std::vector::const_iterator it = std::find( labels.begin(), labels.end(), whichClass ); if( it == labels.end() ) { continue; } float pathologyDeltaEig1 = 0.1; float pathologyDeltaEig2_Eig3 = 0.1; float percentageVoxels = 1.0; if( pathologyOption->GetFunction( n )->GetNumberOfParameters() > 0 ) { pathologyDeltaEig1 = parser->Convert( pathologyOption->GetFunction( n )->GetParameter( 0 ) ); } if( pathologyOption->GetFunction( n )->GetNumberOfParameters() > 1 ) { pathologyDeltaEig2_Eig3 = parser->Convert( pathologyOption->GetFunction( n )->GetParameter( 1 ) ); } if( pathologyOption->GetFunction( n )->GetNumberOfParameters() > 2 ) { percentageVoxels = parser->Convert( pathologyOption->GetFunction( n )->GetParameter( 2 ) ); } RealType percentage = percentageVoxels; if( percentage > 1.0 ) { percentage /= static_cast( labelGeometry->GetVolume( *it ) ); } pathologyParameters(it - labels.begin(), 0) = pathologyDeltaEig1; pathologyParameters(it - labels.begin(), 1) = pathologyDeltaEig2_Eig3; pathologyParameters(it - labels.begin(), 2) = percentage; } } } /** * Perform PCA decomposition on input registered population */ bool applyISV = false; typename MatrixType::InternalMatrixType ISV(1, 1); typename itk::ants::CommandLineParser::OptionType::Pointer populationOption = parser->GetOption( "registered-population" ); if( populationOption && populationOption->GetNumberOfFunctions() ) { std::cout << "--- Modeling intersubject variability ---" << std::endl << std::endl; std::vector imageNames; std::string filename = populationOption->GetFunction( 0 )->GetName(); std::string imageFile; std::fstream str( filename.c_str() ); while( str >> imageFile ) { imageNames.push_back( imageFile ); } str.close(); MatrixType M; MatrixType Mt; MatrixType E; MatrixType Lambda; M.SetSize( imageNames.size(), 2 * totalMaskVolume ); M.Fill( 0 ); for( unsigned int k = 0; k < imageNames.size(); k++ ) { std::cout << "Processing " << imageNames[k] << " (" << k + 1 << " of " << imageNames.size() << ")." << std::endl; typename TensorReaderType::Pointer tensorReader = TensorReaderType::New(); tensorReader->SetFileName( imageNames[k].c_str() ); tensorReader->Update(); unsigned int count = 0; itk::ImageRegionIterator It( tensorReader->GetOutput(), tensorReader->GetOutput()->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItM( maskImage, maskImage->GetLargestPossibleRegion() ); for( It.GoToBegin(), ItM.GoToBegin(); !It.IsAtEnd(); ++It, ++ItM ) { if( ItM.Get() != 0 ) { TensorType tensor = It.Get(); typename TensorType::EigenValuesArrayType eigenvalues; typename TensorType::EigenVectorsMatrixType eigenvectors; tensor.ComputeEigenAnalysis( eigenvalues, eigenvectors ); if( eigenvalues[0] < 0 ) { eigenvalues[0] = eigenvalues[1]; } if( ImageDimension == 3 && eigenvalues[2] < 0 ) { eigenvalues[2] = eigenvalues[1]; } if( ImageDimension == 2 ) { M(k, count) = eigenvalues[1]; M(k, totalMaskVolume + count) = eigenvalues[0]; } else { M(k, count) = eigenvalues[2]; M(k, totalMaskVolume + count) = 0.5 * ( eigenvalues[0] + eigenvalues[1] ); } ++count; } } } std::cout << std::endl; // Now that the matrix M has been calculated, we need to subtract out // the longitudinal mean before performing PCA for( unsigned int i = 0; i < M.Cols(); i++ ) { RealType columnAverage = 0.0; for( unsigned int j = 0; j < M.Rows(); j++ ) { columnAverage += M(j, i); } columnAverage /= static_cast( M.Rows() ); for( unsigned int j = 0; j < M.Rows(); j++ ) { M(j, i) -= columnAverage; } } // Perform PCA decomposition MatrixType MMt = M; MMt *= M.GetTranspose(); decomposer->EvaluateSymmetricEigenDecomposition( MMt, Lambda, E ); ISV = ( M.GetTranspose() * E.GetVnlMatrix() ) / std::sqrt( static_cast( imageNames.size() ) ); applyISV = true; } // // Get DWI parameters // typename ImageType::Pointer b0Image = ITK_NULLPTR; unsigned int numberOfDirections = 0; std::vector > directions; std::vector bvalues; vnl_vector direction( ImageDimension ); // Add a B0 value at direction 0 direction.fill( 0.0 ); directions.push_back( direction ); bvalues.push_back( 0 ); typename itk::ants::CommandLineParser::OptionType::Pointer dwiOption = parser->GetOption( "dwi-parameters" ); if( dwiOption && dwiOption->GetNumberOfFunctions() && dwiOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader2 = ReaderType::New(); reader2->SetFileName( dwiOption->GetFunction( 0 )->GetParameter( 0 ) ); reader2->Update(); b0Image = reader2->GetOutput(); b0Image->DisconnectPipeline(); std::string directionsFileName = dwiOption->GetFunction( 0 )->GetParameter( 1 ); std::fstream str( directionsFileName.c_str() ); if( dwiOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { bvalues.push_back( parser->Convert( dwiOption->GetFunction( 0 )->GetParameter( 2 ) ) ); str >> numberOfDirections; } RealType x = 0.0; unsigned int count = 0; while( str >> x ) { direction[count % ImageDimension] = x; ++count; if( count % ImageDimension == 0 ) { directions.push_back( direction ); if( dwiOption->GetFunction( 0 )->GetNumberOfParameters() < 3 ) { str >> x; bvalues.push_back( x ); } else { bvalues.push_back( bvalues[1] ); } } } if( dwiOption->GetFunction( 0 )->GetNumberOfParameters() < 3 ) { if( bvalues.size() != directions.size() ) { std::cout << "ERROR: Number of bvalues does not match the number of directions." << std::endl; return EXIT_FAILURE; } } else { if( numberOfDirections != directions.size() - 1 ) { std::cout << "ERROR: Number of directions does not match the data file." << std::endl; return EXIT_FAILURE; } } } else { std::cout << "ERROR: No DWI parameters specified." << std::endl; return EXIT_FAILURE; } // // Get Rician noise parameter // RealType noiseSigma = 0; typename itk::ants::CommandLineParser::OptionType::Pointer noiseOption = parser->GetOption( "noise-sigma" ); if( noiseOption && noiseOption->GetNumberOfFunctions() ) { noiseSigma = parser->Convert( noiseOption->GetFunction()->GetName() ); } // // Create the simulated diffusion-weighted images. For each image, we // perform the following steps: // 1. Copy the atlas // 2. Construct new DTI // 2a. Apply pathology (only for the experimentals). // 2b. Introduce subject intervariability // 3. For each direction, write new DWI // 3a. Use DTI from 2 to reconstruct DWI in current direction // 3b. Add Rician noise // itksys::SystemTools::MakeDirectory( outputDirectory.c_str() ); itk::Array2D meanFAandMD( labels.size(), 5 ); meanFAandMD.Fill( 0.0 ); for( unsigned n = 0; n <= numberOfControls + numberOfExperimentals; n++ ) { if( n == 0 ) { std::cout << "--- Calculating regional average FA and MD values (original and " << "pathology + intersubject variability) ---" << std::endl << std::endl; } else if( n <= numberOfControls ) { if( n == 1 ) { std::cout << std::endl << "--- Writing images ---" << std::endl << std::endl; } std::cout << "Writing control " << n << " (of " << numberOfControls << ") DWI images." << std::endl; } else { std::cout << "Writing experimental " << n - numberOfControls << " (of " << numberOfExperimentals << ") DWI images." << std::endl; } // copy atlas typedef itk::ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( inputAtlas ); duplicator->Update(); typename TensorImageType::Pointer dti = duplicator->GetModifiableOutput(); dti->DisconnectPipeline(); // If we are to apply intersubject variability, we calculate random // projection. vnl_vector eigenISVProjection( 1 ); if( applyISV ) { vnl_vector R( ISV.cols() ); for( unsigned int d = 0; d < R.size(); d++ ) { R[d] = randomizer->GetNormalVariate( 0.0, 1.0 ); } eigenISVProjection = ISV * R; } // // Iterate through the image to apply pathology and inter-subject variability // unsigned long count = 0; itk::ImageRegionIterator It( dti, dti->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItM( maskImage, maskImage->GetLargestPossibleRegion() ); for( It.GoToBegin(), ItM.GoToBegin(); !It.IsAtEnd(); ++It, ++ItM ) { LabelType label = ItM.Get(); TensorType tensor = It.Get(); typename TensorType::EigenValuesArrayType eigenvalues; typename TensorType::EigenVectorsMatrixType eigenvectors; tensor.ComputeEigenAnalysis( eigenvalues, eigenvectors ); if( eigenvalues[0] < 0 ) { eigenvalues[0] = eigenvalues[1]; } if( ImageDimension == 3 && eigenvalues[2] < 0 ) { eigenvalues[2] = eigenvalues[1]; } std::vector::const_iterator it = std::find( labels.begin(), labels.end(), label ); if( it == labels.end() ) { std::cout << "ERROR: unknown label." << std::endl; } unsigned int labelIndex = it - labels.begin(); typename TensorType::EigenValuesArrayType newEigenvalues; // // Only apply pathology to a certain fraction of the voxels for a // particular label. We "throw the dice" to determine whether or not // to apply to the current voxel. // RealType pathologyLongitudinalChange = 0.0; RealType pathologyTransverseChange = 0.0; if( ( n == 0 || n > numberOfControls ) && randomizer->GetUniformVariate( 0.0, 1.0 ) <= pathologyParameters(labelIndex, 2) ) { pathologyLongitudinalChange = pathologyParameters(labelIndex, 0); pathologyTransverseChange = pathologyParameters(labelIndex, 1); } // // Apply intersubject variability // RealType isvLongitudinalProjection = 0.0; RealType isvTransverseProjection = 0.0; if( label != 0 && applyISV ) { isvLongitudinalProjection = eigenISVProjection(count); isvTransverseProjection = eigenISVProjection(totalMaskVolume + count); count++; } // // Reconstruct the tensor // if( ImageDimension == 2 ) { newEigenvalues[1] = eigenvalues[1] + eigenvalues[1] * pathologyLongitudinalChange + isvLongitudinalProjection; newEigenvalues[0] = eigenvalues[0] * ( 1.0 + eigenvalues[0] ) * pathologyTransverseChange + isvTransverseProjection; if( newEigenvalues[0] >= newEigenvalues[1] ) { newEigenvalues[0] = newEigenvalues[1] - 1.0e-6; } } else { newEigenvalues[2] = eigenvalues[2] + eigenvalues[2] * pathologyLongitudinalChange + isvLongitudinalProjection; RealType eigenAverage = 0.5 * ( eigenvalues[1] + eigenvalues[0] ); newEigenvalues[1] = ( 2.0 * eigenAverage * ( 1.0 + pathologyTransverseChange ) + isvTransverseProjection ) / ( eigenvalues[0] / eigenvalues[1] + 1.0 ); if( newEigenvalues[1] >= newEigenvalues[2] ) { newEigenvalues[1] = newEigenvalues[2] - 1.0e-6; } newEigenvalues[0] = ( eigenvalues[0] / eigenvalues[1] ) * newEigenvalues[1]; } for( unsigned int d = 0; d < ImageDimension; d++ ) { if( vnl_math_isnan( newEigenvalues[d] ) ) { newEigenvalues[d] = 0.0; } } if( newEigenvalues[0] < 0 ) { newEigenvalues[0] = newEigenvalues[1]; } if( ImageDimension == 3 && newEigenvalues[2] < 0 ) { newEigenvalues[2] = newEigenvalues[1]; } typename TensorType::MatrixType eigenvalueMatrix; eigenvalueMatrix.Fill( 0.0 ); for( unsigned int d = 0; d < ImageDimension; d++ ) { eigenvalueMatrix(d, d) = newEigenvalues[d]; } typename TensorType::MatrixType D( eigenvectors.GetTranspose() ); D *= eigenvalueMatrix; D *= eigenvectors; TensorType newTensor; for( unsigned int i = 0; i < ImageDimension; i++ ) { for( unsigned int j = i; j < ImageDimension; j++ ) { newTensor(i, j) = D(i, j); } } if( label != 0 && n == 0 ) { meanFAandMD(labelIndex, 0) += CalculateFractionalAnisotropy( tensor ); meanFAandMD(labelIndex, 1) += CalculateMeanDiffusivity( tensor ); meanFAandMD(labelIndex, 2) += CalculateFractionalAnisotropy( newTensor ); meanFAandMD(labelIndex, 3) += CalculateMeanDiffusivity( newTensor ); meanFAandMD(labelIndex, 4)++; } else if( n != 0 ) { It.Set( newTensor ); } } if( n == 0 ) { std::cout << " " << std::left << std::setw( 7 ) << "Region" << std::left << std::setw( 15 ) << "FA (original)" << std::left << std::setw( 15 ) << "FA (path+isv)" << std::left << std::setw( 15 ) << "FA (% change)" << std::left << std::setw( 15 ) << "MD (original)" << std::left << std::setw( 15 ) << "MD (path+isv)" << std::left << std::setw( 15 ) << "MD (% change)" << std::endl; for( unsigned int l = 1; l < labels.size(); l++ ) { std::cout << " " << std::left << std::setw( 7 ) << labels[l] << std::left << std::setw( 15 ) << meanFAandMD(l, 0) / meanFAandMD(l, 4) << std::left << std::setw( 15 ) << meanFAandMD(l, 2) / meanFAandMD(l, 4) << std::left << std::setw( 15 ) << ( meanFAandMD(l, 2) - meanFAandMD(l, 0) ) / meanFAandMD(l, 0) << std::left << std::setw( 15 ) << meanFAandMD(l, 1) / meanFAandMD(l, 4) << std::left << std::setw( 15 ) << meanFAandMD(l, 3) / meanFAandMD(l, 4) << std::left << std::setw( 15 ) << ( meanFAandMD(l, 3) - meanFAandMD(l, 1) ) / meanFAandMD(l, 1) << std::endl; } } else { std::string which; if( n <= numberOfControls ) { which = std::string( "Control" ); } else { which = std::string( "Experimental" ); } std::stringstream istream; if( n <= numberOfControls ) { istream << n; } else { istream << ( n - numberOfControls ); } std::string dwiSeriesFileNames = outputDirectory + which + istream.str() + rootOutputFileName + std::string( "Direction%03d.nii.gz" ); itk::NumericSeriesFileNames::Pointer dwiFileNamesCreator = itk::NumericSeriesFileNames::New(); dwiFileNamesCreator->SetStartIndex( 0 ); dwiFileNamesCreator->SetEndIndex( directions.size() - 1 ); dwiFileNamesCreator->SetSeriesFormat( dwiSeriesFileNames.c_str() ); std::vector dwiImageNames = dwiFileNamesCreator->GetFileNames(); for( unsigned int d = 0; d < directions.size(); d++ ) { vnl_vector bk = directions[d]; RealType bvalue = bvalues[d]; std::cout << " Applying direction " << d << " (of " << directions.size() - 1 << "): [" << bk << "]" << ", bvalue = " << bvalue << std::endl; typename ImageType::Pointer dwi = AllocImage(dti, 0); itk::ImageRegionConstIterator ItB( b0Image, b0Image->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItD( dwi, dwi->GetLargestPossibleRegion() ); for( It.GoToBegin(), ItB.GoToBegin(), ItD.GoToBegin(); !It.IsAtEnd(); ++It, ++ItB, ++ItD ) { TensorType tensor = It.Get(); for( unsigned int i = 0; i < tensor.GetNumberOfComponents(); i++ ) { if( vnl_math_isnan( tensor[i] ) ) { tensor[i] = 0.0; } } vnl_matrix D(ImageDimension, ImageDimension); for( unsigned int i = 0; i < ImageDimension; i++ ) { for( unsigned int j = 0; j < ImageDimension; j++ ) { D(i, j) = tensor(i, j); } } vnl_vector bkD = bk * D; RealType signal = ItB.Get() * std::exp( -bvalue * inner_product( bkD, bk ) ); // Add Rician noise RealType realNoise = 0.0; RealType imagNoise = 0.0; if( noiseSigma > 0.0 ) { realNoise = randomizer->GetNormalVariate( 0.0, vnl_math_sqr( noiseSigma ) ); imagNoise = randomizer->GetNormalVariate( 0.0, vnl_math_sqr( noiseSigma ) ); } RealType realSignal = signal + realNoise; RealType imagSignal = imagNoise; std::complex noisySignal( realSignal, imagSignal ); RealType finalSignal = std::sqrt( std::norm( noisySignal ) ); if( signal <= ItB.Get() ) { ItD.Set( finalSignal ); } } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( dwiImageNames[d].c_str() ); writer->SetInput( dwi ); writer->Update(); } } } return EXIT_SUCCESS; } void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, the program tries to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "image-dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "A diffusion tensor atlas image is required input for " ) + std::string( "creating the cohort. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "dti-atlas" ); option->SetShortName( 'a' ); option->SetUsageOption( 0, "inputDTIAtlasFileName" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "A mask image can be specified which determines the region(s). " ) + std::string( "to which the simulated pathology operations are applied. " ) + std::string( "See also the option '--pathology'. If no mask is specified " ) + std::string( "one is created by thresholding the atlas FA map at 0.2 unless " ) + std::string( "a lower threshold is specified." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "label-mask-image" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "maskImageFileName" ); option->SetUsageOption( 1, "lowerThresholdFunction" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "This parameter characterizes the Rician noise in the original DWI" ) + std::string( "images. Van Hecke uses the noise-estimation method of Sijbers et " ) + std::string( "al. \"Automatic estimation of the noise variance from the " ) + std::string( "histogram of a magnetic resonance image\", Phys. Med. Biol. " ) + std::string( "52:1335-1348, 2007." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "noise-sigma" ); option->SetShortName( 'n' ); option->SetUsageOption( 0, "" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The user can specify the simulated pathology in a given " ) + std::string( "area using a label mask. If no label is prepended to " ) + std::string( "parameters, the specified parameters are applied to all labels." ) + std::string( "Pathology is simulated by changing the eigenvalues. Typically " ) + std::string( "this involves a decrease in the largest eigenvalue and an " ) + std::string( "increase in the average of the remaining eigenvalues. " ) + std::string( "Change is specified as a percentage of the current eigenvalues. " ) + std::string( "However, care is taken " ) + std::string( "to ensure that diffusion direction does not change. " ) + std::string( "Additionally, one can specify the number of voxels affected " ) + std::string( "in each region or one can specify the percentage of voxels " ) + std::string( "affected. Default is to change all voxels. Note that the " ) + std::string( "percentages must be specified in the range [0,1]. For " ) + std::string( "dimension=3 where the average transverse diffusion eigenvalues " ) + std::string( "are altered, this change is propagated to the distinct eigenvalues " ) + std::string( "by forcing the ratio to be the same before the change. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "pathology" ); option->SetUsageOption( 0, "label[,,]" ); option->SetShortName( 'p' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "This option specifies the parameters of the output " ) + std::string( "diffusion-weighted images including the directions and " ) + std::string( "b-values. The directions are specified using a direction " ) + std::string( "file which has as its first line the number of directions." ) + std::string( "Each successive three lines contains the x, y, and z " ) + std::string( "directions, respectively, and a single b-value. " ) + std::string( "Note that several direction files of this format are " ) + std::string( "distributed with the Camino DTI toolkit " ) + std::string( "(http://web4.cs.ucl.ac.uk/research/medic/camino/pmwiki/pmwiki.php). " ) + std::string( "Alternatively, one can specify a scheme file where each direction " ) + std::string( "is specified followed by a b-value for that direction, i.e. " ) + std::string( " ... ." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "dwi-parameters" ); option->SetShortName( 'w' ); option->SetUsageOption( 0, "[B0Image,directionFile,bvalue]" ); option->SetUsageOption( 1, "[B0Image,schemeFile]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "If one wants to introduce inter-subject variability" ) + std::string( "a registered DTI population to the DTI atlas is " ) + std::string( "required. This variability is modeled by a PCA " ) + std::string( "decomposition on a combination of the first eigenvalue " ) + std::string( "image and the average of the second and third eigenvalues." ) + std::string( "The registered image file names are specified using " ) + std::string( "a text file " ) + std::string( "where each line is the name of an individual DTI." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "registered-population" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "textFileWithFileNames.txt" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The output consists of a set of diffusion-weighted images " ) + std::string( "for each subject. Each file name is prepended with the " ) + std::string( "word 'Control' or 'Experimental'. The number of control " ) + std::string( "and experimental subjects can be also be specified on the " ) + std::string( "command line. Default is 10 for each group." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "[outputDirectory,fileNameSeriesRootName,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int CreateDTICohort( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "CreateDTICohort" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "CreateDTICohort implements the work of Van Hecke et al. (" ) + std::string( "On the construction of a ground truth framework for " ) + std::string( "evaluating voxl-based diffusion tensor MRI analysis " ) + std::string( "methods, Neuroimage 46:692-707, 2009) to create " ) + std::string( "simulated DTI data sets. The only " ) + std::string( "difference is that all registrations (both for the input " ) + std::string( "population and for the output population) are assumed to " ) + std::string( "take place outside of this program." ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc < 2 || parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } else if( parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Get dimensionality unsigned int dimension = 3; itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "image-dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction()->GetName() ); } else { // Read in the first intensity image to get the image dimension. std::string filename; itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "dti-atlas" ); if( imageOption && imageOption->GetNumberOfFunctions() ) { if( imageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { filename = imageOption->GetFunction( 0 )->GetParameter( 0 ); } else { filename = imageOption->GetFunction( 0 )->GetName(); } } else { std::cout << "No input atlas was specified. Specify a dti atlas" << " with the -a option" << std::endl; return EXIT_FAILURE; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); dimension = imageIO->GetNumberOfDimensions(); } std::cout << std::endl << "Creating DTI cohort for " << dimension << "-dimensional images." << std::endl << std::endl; switch( dimension ) { case 2: { return CreateDTICohort<2>( parser ); } break; case 3: { return CreateDTICohort<3>( parser ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/CreateDisplacementField.cxx000066400000000000000000000220511311104306400214530ustar00rootroot00000000000000 #include "antsUtilities.h" #include "antsAllocImage.h" #include #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" namespace ants { /* See usage output below */ template void CreateDisplacementField( int argc, char *argv[] ) { typedef float ValueType; typedef itk::Image ComponentImageType; typedef itk::Vector VectorPixelType; typedef itk::Image VectorImageType; typedef itk::ImageFileReader FileReaderType; typename FileReaderType::Pointer reader = FileReaderType::New(); typename ComponentImageType::Pointer componentImage; // EnforceZeroBoundaryFlag bool enforceZeroBoundaryFlag = static_cast( atoi( argv[2] ) ); // Get image info reader->SetFileName( argv[3] ); reader->Update(); componentImage = reader->GetOutput(); typedef typename ComponentImageType::RegionType RegionType; RegionType regionOfFirstComponent = componentImage->GetLargestPossibleRegion(); // Create output vector image typename VectorImageType::Pointer vectorImage = AllocImage(componentImage.GetPointer() ); vectorImage->SetRegions( componentImage->GetLargestPossibleRegion() ); itk::ImageRegionIteratorWithIndex comIt( componentImage, regionOfFirstComponent ); typedef typename itk::ImageRegionIteratorWithIndex VecItType; typedef typename VecItType::IndexValueType VecItIndexValueType; VecItType vecIt( vectorImage, regionOfFirstComponent ); for( itk::SizeValueType n = 0; n < NumberOfComponents; n++ ) { reader->SetFileName( argv[3 + n] ); reader->Update(); componentImage = reader->GetOutput(); if( componentImage->GetLargestPossibleRegion() != regionOfFirstComponent ) { std::cout << "LargestPossibleRegion of component " << n << " does not match 1st component image." << std::endl; throw std::exception(); } // Walk the images comIt.GoToBegin(); vecIt.GoToBegin(); for( ; !comIt.IsAtEnd(); ++comIt, ++vecIt ) { VectorPixelType vec = vecIt.Get(); vec[n] = comIt.Get(); vecIt.Set(vec); } } // Set zero boundary vectors if( enforceZeroBoundaryFlag ) { VectorPixelType zeros( ImageDimension ); zeros.Fill( itk::NumericTraits::ZeroValue() ); vecIt.GoToBegin(); while( !vecIt.IsAtEnd() ) { bool isBoundary = false; for( itk::SizeValueType dim = 0; dim < ImageDimension; dim++ ) { if( vecIt.GetIndex()[dim] == 0 || vecIt.GetIndex()[dim] == static_cast(regionOfFirstComponent.GetSize()[dim] - 1) ) { isBoundary = true; break; } } if( isBoundary ) { vecIt.Set( zeros ); } ++vecIt; } } // Write out the output typename itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); writer->SetFileName( argv[argc - 1] ); writer->SetInput( vectorImage ); writer->Update(); // debug #if 0 itk::ImageRegionIteratorWithIndex it( vectorImage, regionOfFirstComponent ); it.GoToBegin(); itk::OffsetValueType col = regionOfFirstComponent.GetSize()[0] / 2; itk::OffsetValueType row = regionOfFirstComponent.GetSize()[1] / 2; typename VectorImageType::IndexType index; index[0] = col; index[1] = 0; std::cout << "Debug output of field: " << std::endl; std::cout << "Middle column: " << std::endl; while( it.GetIndex()[1] < row * 2 ) { it.SetIndex( index ); std::cout << it.Get() << " "; index[1]++; } std::cout << std::endl; index[0] = 0; index[1] = row; std::cout << "Middle row: " << std::endl; while( it.GetIndex()[0] < col * 2 ) { it.SetIndex( index ); std::cout << it.Get() << " "; index[0]++; } std::cout << std::endl; #endif // debug } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int CreateDisplacementField( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "CreateDisplacementField" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Create an itkImage of itkVector pixels (NOT an itkVectorImage), using each scalar input component image for each vector component. An itkImage of itkVectors is the standard type for displacement fields in ITK. All component images (up to 8) are assumed to have the same size, offset, origin, and spacing. The 'EnforceZeroBoundaryFlag' option will create zero-valued vectors along the borders when enabled (pass 1), and is recommended for better displacement field behavior." << std::endl; std::cout << "Usage: " << argv[0] << " ImageDimension EnforceZeroBoundaryFlag{0/1} ComponentImage1 [ ComponentImage2 [...ComponentImageN] ] OutputImage " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } itk::SizeValueType imageDimension = atoi( argv[1] ); itk::SizeValueType numberOfComponents = argc - 4; switch( imageDimension ) { case 2: { switch( numberOfComponents ) { case 1: { CreateDisplacementField<2, 1>( argc, argv ); } break; case 2: { CreateDisplacementField<2, 2>( argc, argv ); } break; case 3: { CreateDisplacementField<2, 3>( argc, argv ); } break; case 4: { CreateDisplacementField<2, 4>( argc, argv ); } break; case 5: { CreateDisplacementField<2, 5>( argc, argv ); } break; case 6: { CreateDisplacementField<2, 6>( argc, argv ); } break; case 7: { CreateDisplacementField<2, 7>( argc, argv ); } break; case 8: { CreateDisplacementField<2, 8>( argc, argv ); } break; default: std::cout << "Unsupported number of components: " << numberOfComponents << std::endl; return EXIT_FAILURE; } } break; case 3: { switch( numberOfComponents ) { case 1: { CreateDisplacementField<3, 1>( argc, argv ); } break; case 2: { CreateDisplacementField<3, 2>( argc, argv ); } break; case 3: { CreateDisplacementField<3, 3>( argc, argv ); } break; case 4: { CreateDisplacementField<3, 4>( argc, argv ); } break; case 5: { CreateDisplacementField<3, 5>( argc, argv ); } break; case 6: { CreateDisplacementField<3, 6>( argc, argv ); } break; case 7: { CreateDisplacementField<3, 7>( argc, argv ); } break; case 8: { CreateDisplacementField<3, 8>( argc, argv ); } break; default: std::cout << "Unsupported number of components: " << numberOfComponents << std::endl; return EXIT_FAILURE; } } break; default: std::cout << "Unsupported number of dimensions: " << imageDimension << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/CreateImage.cxx000066400000000000000000000241411311104306400171230ustar00rootroot00000000000000 #include "antsUtilities.h" #include "antsAllocImage.h" #include #include #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkMersenneTwisterRandomVariateGenerator.h" #include #include namespace ants { template int CreateZeroImage( int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; typedef typename itk::Statistics::MersenneTwisterRandomVariateGenerator GeneratorType; typename GeneratorType::Pointer generator = GeneratorType::New(); generator->Initialize(); generator->SetSeed(); std::string which = std::string( argv[3] ); typename std::string::size_type pos = which.find( "." ); typename std::string::size_type pos3 = std::string::npos; if( argc > 6 ) { std::string pixelValues = std::string( argv[6] ); pos3 = pixelValues.find( "x" ); } typename ImageType::Pointer image; if( pos3 != std::string::npos ) { std::vector og = ConvertVector( std::string( argv[3] ) ); std::vector sp = ConvertVector( std::string( argv[4] ) ); std::vector sz = ConvertVector( std::string( argv[5] ) ); std::vector values = ConvertVector( std::string( argv[6] ) ); unsigned long numberOfPixels = 1; for( unsigned int d = 0; d < ImageDimension; d++ ) { numberOfPixels *= sz[d]; } if( values.size() > numberOfPixels ) { std::cout << "Number of specified pixel values is greater than " << "the size of the image." << std::endl; return EXIT_FAILURE; } if( og.size() != ImageDimension ) { std::cout << "Invalid origin size." << std::endl; return EXIT_FAILURE; } if( sp.size() != ImageDimension ) { std::cout << "Invalid spacing size." << std::endl; return EXIT_FAILURE; } if( sz.size() != ImageDimension ) { std::cout << "Invalid Size size." << std::endl; return EXIT_FAILURE; } typename ImageType::PointType origin; typename ImageType::SpacingType spacing; typename ImageType::SizeType size; for( unsigned int d = 0; d < ImageDimension; d++ ) { origin[d] = og[d]; spacing[d] = sp[d]; size[d] = sz[d]; } typename ImageType::RegionType region; region.SetSize(size); typename ImageType::DirectionType direction; direction.SetIdentity(); image = AllocImage(region, spacing, origin, direction, 0.0); unsigned long count = 0; itk::ImageRegionIterator It( image, image->GetRequestedRegion() ); It.GoToBegin(); while( !It.IsAtEnd() && count < values.size() ) { It.Set( values[count] ); ++It; ++count; } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( argv[2] ); writer->SetInput( image ); writer->Update(); } else if( pos != std::string::npos ) { typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[2] ); reader->Update(); // ORIENTATION ALERT -- the original code here // set the region, spacing, and origin without setting directions. image = AllocImage(reader->GetOutput(), atof(argv[4]) ); if( argc > 5 ) { switch( atoi( argv[5] ) ) { case 1: default: { itk::ImageRegionIterator It( image, image->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { It.Set( static_cast( generator->GetIntegerVariate( static_cast( It.Get() ) ) ) ); } break; } // case 2: // { // itk::ImageRegionIteratorWithIndex ItI( image, // image->GetLargestPossibleRegion() ); // for( ItI.GoToBegin(); !ItI.IsAtEnd(); ++ItI ) // { // ItI.Set( constant - ItI.GetIndex()[d] ); // } // break; // } // default: // std::cout << "Incorrect choice" << std::endl; // return EXIT_FAILURE; // break; } } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( argv[3] ); writer->SetInput( image ); writer->Update(); } else { std::vector og = ConvertVector( std::string( argv[3] ) ); std::vector sp = ConvertVector( std::string( argv[4] ) ); std::vector sz = ConvertVector( std::string( argv[5] ) ); if( og.size() != ImageDimension ) { std::cout << "Invalid origin size." << std::endl; return EXIT_FAILURE; } if( sp.size() != ImageDimension ) { std::cout << "Invalid spacing size." << std::endl; return EXIT_FAILURE; } if( sz.size() != ImageDimension ) { std::cout << "Invalid Size size." << std::endl; return EXIT_FAILURE; } typename ImageType::PointType origin; typename ImageType::SpacingType spacing; typename ImageType::SizeType size; typename ImageType::DirectionType direction; direction.SetIdentity(); for( unsigned int d = 0; d < ImageDimension; d++ ) { origin[d] = og[d]; spacing[d] = sp[d]; size[d] = sz[d]; } typename ImageType::RegionType region; region.SetSize(size); image = AllocImage(region, spacing, origin, direction, atof( argv[6] ) ); if( argc > 7 ) { switch( atoi( argv[7] ) ) { case 1: default: { itk::ImageRegionIterator It( image, image->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { It.Set( static_cast( generator->GetIntegerVariate( static_cast( It.Get() ) ) ) ); } break; } // case 2: // { // itk::ImageRegionIteratorWithIndex ItI( image, // image->GetLargestPossibleRegion() ); // for( ItI.GoToBegin(); !ItI.IsAtEnd(); ++ItI ) // { // ItI.Set( constant - ItI.GetIndex()[d] ); // } // break; // } // default: // { // std::cout << "Incorrect choice" << std::endl; // return EXIT_FAILURE; // break; // } } } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( argv[2] ); writer->SetInput( image ); writer->Update(); } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int CreateImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "CreateImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 5 ) { std::cout << "Usage 1: " << argv[0] << " imageDimension referenceImage outputImage constant [random?]" << std::endl; std::cout << "Usage 2: " << argv[0] << " imageDimension outputImage origin spacing size constant [random?]" << std::endl; std::cout << "Usage 3: " << argv[0] << " imageDimension outputImage origin spacing size pixelValues" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 1: { return CreateZeroImage<1>( argc, argv ); } break; case 2: { return CreateZeroImage<2>( argc, argv ); } break; case 3: { return CreateZeroImage<3>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/CreateJacobianDeterminantImage.cxx000066400000000000000000000133521311104306400227470ustar00rootroot00000000000000#include "antsUtilities.h" #include #include "ReadWriteData.h" #include "itkDeformationFieldGradientTensorImageFilter.h" #include "itkDeterminantTensorImageFilter.h" #include "itkGeometricJacobianDeterminantImageFilter.h" #include "itkLogImageFilter.h" #include "itkMaximumImageFilter.h" namespace ants { template int CreateJacobianDeterminantImage( int argc, char *argv[] ) { typedef double RealType; typedef itk::Image ImageType; typedef itk::Vector VectorType; typedef itk::Image VectorImageType; /** * Read in vector field */ typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[2] ); reader->Update(); typename ImageType::Pointer jacobian = ITK_NULLPTR; typename ImageType::Pointer minimumConstantImage = ImageType::New(); minimumConstantImage->CopyInformation( reader->GetOutput() ); minimumConstantImage->SetRegions( reader->GetOutput()->GetRequestedRegion() ); minimumConstantImage->Allocate(); minimumConstantImage->FillBuffer( 0.001 ); bool calculateLogJacobian = false; if ( argc > 4 ) { calculateLogJacobian = static_cast( atoi( argv[4] ) ); } bool calculateGeometricJacobian = false; if ( argc > 5 ) { calculateGeometricJacobian = static_cast( atoi( argv[5] ) ); } if( calculateGeometricJacobian ) { typedef itk::GeometricJacobianDeterminantImageFilter JacobianFilterType; typename JacobianFilterType::Pointer jacobianFilter = JacobianFilterType::New(); jacobianFilter->SetInput( reader->GetOutput() ); jacobian = jacobianFilter->GetOutput(); jacobian->Update(); jacobian->DisconnectPipeline(); } else { typedef itk::DeformationFieldGradientTensorImageFilter JacobianFilterType; typename JacobianFilterType::Pointer jacobianFilter = JacobianFilterType::New(); jacobianFilter->SetInput( reader->GetOutput() ); jacobianFilter->SetCalculateJacobian( true ); jacobianFilter->SetUseImageSpacing( true ); jacobianFilter->SetOrder( 2 ); jacobianFilter->SetUseCenteredDifference( true ); typedef itk::DeterminantTensorImageFilter DeterminantFilterType; typename DeterminantFilterType::Pointer determinantFilter = DeterminantFilterType::New(); determinantFilter->SetInput( jacobianFilter->GetOutput() ); determinantFilter->Update(); minimumConstantImage->FillBuffer( 0.0 ); typedef itk::MaximumImageFilter MaxFilterType; typename MaxFilterType::Pointer maxFilter = MaxFilterType::New(); maxFilter->SetInput1( determinantFilter->GetOutput() ); maxFilter->SetInput2( minimumConstantImage ); jacobian = maxFilter->GetOutput(); jacobian->Update(); jacobian->DisconnectPipeline(); } if( calculateLogJacobian ) { minimumConstantImage->FillBuffer( 0.001 ); typedef itk::MaximumImageFilter MaxFilterType; typename MaxFilterType::Pointer maxFilter = MaxFilterType::New(); maxFilter->SetInput1( jacobian ); maxFilter->SetInput2( minimumConstantImage ); typedef itk::LogImageFilter LogFilterType; typename LogFilterType::Pointer logFilter = LogFilterType::New(); logFilter->SetInput( maxFilter->GetOutput() ); logFilter->Update(); WriteImage(logFilter->GetOutput(), argv[3] ); } else { WriteImage(jacobian, argv[3] ); } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int CreateJacobianDeterminantImage( std::vector args, std::ostream* itkNotUsed( out_stream ) ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "CreateJacobianDeterminantImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " imageDimension deformationField outputImage [doLogJacobian=0] [useGeometric=0]" << std::endl; return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { return CreateJacobianDeterminantImage<2>( argc, argv ); } break; case 3: { return CreateJacobianDeterminantImage<3>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/CreateTiledMosaic.cxx000066400000000000000000001340461311104306400203040ustar00rootroot00000000000000#include "antsCommandLineParser.h" #include "antsUtilities.h" #include "ReadWriteData.h" #include #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkCastImageFilter.h" #include "itkConstantPadImageFilter.h" #include "itkExtractImageFilter.h" #include "itkFlipImageFilter.h" #include "itkImageDuplicator.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkLabelStatisticsImageFilter.h" #include "itkPermuteAxesImageFilter.h" #include "itkStatisticsImageFilter.h" #include "itkTileImageFilter.h" namespace ants { int CreateMosaic( itk::ants::CommandLineParser *parser ) { const unsigned int ImageDimension = 3; typedef float RealType; typedef RealType PixelType; typedef unsigned char RgbComponentType; typedef itk::RGBPixel RgbPixelType; typedef itk::Image ImageType; typedef itk::Image SliceType; typedef itk::Image RgbSliceType; typedef itk::Image RgbImageType; // Read in input image ImageType::Pointer inputImage = ITK_NULLPTR; itk::ants::CommandLineParser::OptionType::Pointer inputImageOption = parser->GetOption( "input-image" ); if( inputImageOption && inputImageOption->GetNumberOfFunctions() ) { std::string inputFile = inputImageOption->GetFunction( 0 )->GetName(); ReadImage( inputImage, inputFile.c_str() ); } else { std::cout << "Input image not specified." << std::endl; return EXIT_FAILURE; } ImageType::SizeType size = inputImage->GetRequestedRegion().GetSize(); // Read in optional mask image ImageType::Pointer maskImage = ITK_NULLPTR; ImageType::RegionType maskRegion; itk::ants::CommandLineParser::OptionType::Pointer maskImageOption = parser->GetOption( "mask-image" ); if( maskImageOption && maskImageOption->GetNumberOfFunctions() ) { std::string maskFile = maskImageOption->GetFunction( 0 )->GetName(); ReadImage( maskImage, maskFile.c_str() ); typedef itk::Image ShortImageType; typedef itk::CastImageFilter CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput( maskImage ); caster->Update(); typedef itk::LabelStatisticsImageFilter StatsFilterType; StatsFilterType::Pointer stats = StatsFilterType::New(); stats->SetLabelInput( caster->GetOutput() ); stats->SetInput( caster->GetOutput() ); stats->Update(); maskRegion = stats->GetRegion( 1 ); } // Read in optional Rgb image RgbImageType::Pointer rgbImage = ITK_NULLPTR; itk::ants::CommandLineParser::OptionType::Pointer rgbImageOption = parser->GetOption( "rgb-image" ); if( rgbImageOption && rgbImageOption->GetNumberOfFunctions() ) { std::string rgbFile = rgbImageOption->GetFunction( 0 )->GetName(); ReadImage( rgbImage, rgbFile.c_str() ); } RealType minIntensityValue = 0.0; RealType maxIntensityValue = 1.0; if( inputImage ) { typedef itk::StatisticsImageFilter StatisticsImageFilterType; StatisticsImageFilterType::Pointer statisticsImageFilter = StatisticsImageFilterType::New(); statisticsImageFilter->SetInput( inputImage ); statisticsImageFilter->Update(); minIntensityValue = statisticsImageFilter->GetMinimum(); maxIntensityValue = statisticsImageFilter->GetMaximum(); } RealType alpha = 1.0; itk::ants::CommandLineParser::OptionType::Pointer alphaOption = parser->GetOption( "alpha" ); if( alphaOption && alphaOption->GetNumberOfFunctions() ) { alpha = parser->Convert( alphaOption->GetFunction( 0 )->GetName() ); if( alpha < 0 || alpha > 1.0 ) { std::cerr << "The alpha parameter must be between 0 and 1." << std::endl; return EXIT_FAILURE; } } // Add the functional overlays std::vector functionalRgbImages; std::vector functionalMaskImages; std::vector functionalAlphaValues; if( rgbImage ) { functionalRgbImages.push_back( rgbImage ); functionalAlphaValues.push_back( alpha ); if( maskImage.IsNull() ) { maskImage = ImageType::New(); maskImage->CopyInformation( rgbImage ); maskImage->SetRegions( rgbImage->GetRequestedRegion() ); maskImage->Allocate(); maskImage->FillBuffer( 1 ); typedef itk::Image ShortImageType; typedef itk::CastImageFilter CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput( maskImage ); caster->Update(); typedef itk::LabelStatisticsImageFilter StatsFilterType; StatsFilterType::Pointer stats = StatsFilterType::New(); stats->SetLabelInput( caster->GetOutput() ); stats->SetInput( caster->GetOutput() ); stats->Update(); maskRegion = stats->GetRegion( 1 ); } functionalMaskImages.push_back( maskImage ); } itk::ants::CommandLineParser::OptionType::Pointer functionalOverlayOption = parser->GetOption( "functional-overlay" ); if( functionalOverlayOption && functionalOverlayOption->GetNumberOfFunctions() ) { for( unsigned int n = 0; n < functionalOverlayOption->GetNumberOfFunctions(); n++ ) { if( functionalOverlayOption->GetFunction( n )->GetNumberOfParameters() < 2 ) { std::cerr << "Error: each functional overlay must have an RGB image and mask." << "See help menu." << std::endl; return EXIT_FAILURE; } // read RGB image std::string rgbFileName = functionalOverlayOption->GetFunction( n )->GetParameter( 0 ); typedef itk::ImageFileReader RgbReaderType; RgbReaderType::Pointer rgbReader = RgbReaderType::New(); rgbReader->SetFileName( rgbFileName.c_str() ); try { rgbReader->Update(); } catch( ... ) { std::cerr << "Error reading RGB file " << rgbFileName << std::endl; return EXIT_FAILURE; } functionalRgbImages.push_back( rgbReader->GetOutput() ); // read mask std::string maskFileName = functionalOverlayOption->GetFunction( n )->GetParameter( 1 ); typedef itk::ImageFileReader MaskReaderType; MaskReaderType::Pointer maskReader = MaskReaderType::New(); maskReader->SetFileName( maskFileName.c_str() ); try { maskReader->Update(); } catch( ... ) { std::cerr << "Error reading mask file " << maskFileName << std::endl; return EXIT_FAILURE; } functionalMaskImages.push_back( maskReader->GetOutput() ); if( functionalOverlayOption->GetFunction( n )->GetNumberOfParameters() > 2 ) { RealType localAlpha = parser->Convert( functionalOverlayOption->GetFunction( n )->GetParameter( 2 ) ); functionalAlphaValues.push_back( localAlpha ); } else { functionalAlphaValues.push_back( 1.0 ); } } } // Get direction. If not specified, pick direction with coarsest spacing. unsigned int direction = 0; itk::ants::CommandLineParser::OptionType::Pointer directionOption = parser->GetOption( "direction" ); if( directionOption && directionOption->GetNumberOfFunctions() ) { std::string directionString = directionOption->GetFunction( 0 )->GetName(); int physicalCoordinateComponent = -1; if( std::strcmp( directionString.c_str(), "0" ) == 0 ) { direction = 0; } else if( std::strcmp( directionString.c_str(), "1" ) == 0 ) { direction = 1; } else if( std::strcmp( directionString.c_str(), "2" ) == 0 ) { direction = 2; } else if( std::strcmp( directionString.c_str(), "x" ) == 0 ) { physicalCoordinateComponent = 0; } else if( std::strcmp( directionString.c_str(), "y" ) == 0 ) { physicalCoordinateComponent = 1; } else if( std::strcmp( directionString.c_str(), "z" ) == 0 ) { physicalCoordinateComponent = 2; } else { std::cerr << "Unrecognized direction option. See help menu" << std::endl; return EXIT_FAILURE; } if( physicalCoordinateComponent >= 0 ) { float maxComponentValue = 0.0; ImageType::IndexType index; index.Fill( 0 ); ImageType::PointType pointOrigin; inputImage->TransformIndexToPhysicalPoint( index, pointOrigin ); for( unsigned int d = 0; d < ImageDimension; d++ ) { ImageType::PointType point; index.Fill( 0 ); index[d] = 1; inputImage->TransformIndexToPhysicalPoint( index, point ); ImageType::PointType::VectorType directionalVector = point - pointOrigin; if( vnl_math_abs( directionalVector[physicalCoordinateComponent] ) > maxComponentValue ) { direction = d; } } } } // Get padding/cropping options. int paddingType = 0; RealType padValue = 0; unsigned long lowerBound[ImageDimension]; unsigned long upperBound[ImageDimension]; SliceType::RegionType croppedSliceRegion; SliceType::RegionType::SizeType croppedSliceSize; SliceType::RegionType::IndexType croppedSliceIndex; itk::ants::CommandLineParser::OptionType::Pointer paddingOption = parser->GetOption( "pad-or-crop" ); if( paddingOption && paddingOption->GetNumberOfFunctions() ) { if( paddingOption->GetFunction( 0 )->GetNumberOfParameters() == 3 ) { std::vector lowerBoundVector = parser->ConvertVector( paddingOption->GetFunction( 0 )->GetParameter( 0 ) ); std::vector upperBoundVector = parser->ConvertVector( paddingOption->GetFunction( 0 )->GetParameter( 1 ) ); if( lowerBoundVector.size() != 2 || upperBoundVector.size() != 2 ) { std::cerr << "Incorrect padding specification." << std::endl; return EXIT_FAILURE; } int lowerBoundProduct = lowerBoundVector[0] * lowerBoundVector[1]; int upperBoundProduct = upperBoundVector[0] * upperBoundVector[1]; if( lowerBoundProduct < 0 || upperBoundProduct < 0 || upperBoundProduct * lowerBoundProduct < 0 ) { std::cerr << "Current capabilities do not include mixing of cropping and padding," << " i.e. negative and positive pad values, respectively" << std::endl; return EXIT_FAILURE; } if( lowerBoundVector[0] < 0 ) { paddingType = -1; unsigned int count = 0; for( unsigned int d = 0; d < ImageDimension; d++ ) { if( d != direction ) { croppedSliceSize[count] = size[d] - ( vnl_math_abs( lowerBoundVector[count] ) + vnl_math_abs( upperBoundVector[count] ) ); croppedSliceIndex[count] = vnl_math_abs( lowerBoundVector[count] ); count++; } } croppedSliceRegion.SetSize( croppedSliceSize ); croppedSliceRegion.SetIndex( croppedSliceIndex ); } else { paddingType = 1; for( unsigned int d = 0; d < ImageDimension - 1; d++ ) { lowerBound[d] = lowerBoundVector[d]; upperBound[d] = upperBoundVector[d]; } } } else { int padWidth = 0; std::string padWidthString; if( paddingOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { padWidthString = paddingOption->GetFunction( 0 )->GetName(); } else if( paddingOption->GetFunction( 0 )->GetNumberOfParameters() <= 2 ) { padWidthString = paddingOption->GetFunction( 0 )->GetParameter( 0 ); padValue = parser->Convert( paddingOption->GetFunction( 0 )->GetParameter( 1 ) ); } if( padWidthString.find( std::string( "mask" ) ) != std::string::npos ) { if( !maskImage ) { std::cerr << "Mask image is not specified." << std::endl; return EXIT_FAILURE; } int offset = 0; if( padWidthString.find( std::string( "+" ) ) != std::string::npos ) { std::string offsetString = padWidthString.substr( padWidthString.find( std::string( "+" ) ) + 1 ); offset = parser->Convert( offsetString ); } if( padWidthString.find( std::string( "-" ) ) != std::string::npos ) { std::string offsetString = padWidthString.substr( padWidthString.find( std::string( "-" ) ) + 1 ); offset = parser->Convert( offsetString ); offset *= -1; } paddingType = -1; unsigned int count = 0; for( unsigned int d = 0; d < ImageDimension; d++ ) { if( d != direction ) { croppedSliceSize[count] = maskRegion.GetSize()[d] + 2 * offset; croppedSliceIndex[count] = maskRegion.GetIndex()[d] - offset; count++; } } croppedSliceRegion.SetSize( croppedSliceSize ); croppedSliceRegion.SetIndex( croppedSliceIndex ); } else { padWidth = parser->Convert( padWidthString ); if( padWidth < 0 ) { paddingType = -1; unsigned int count = 0; for( unsigned int d = 0; d < ImageDimension; d++ ) { if( d != direction ) { croppedSliceSize[count] = size[d] - 2 * vnl_math_abs( padWidth ); croppedSliceIndex[count] = vnl_math_abs( padWidth ); count++; } } croppedSliceRegion.SetSize( croppedSliceSize ); croppedSliceRegion.SetIndex( croppedSliceIndex ); } else { paddingType = 1; for( unsigned int d = 0; d < ImageDimension - 1; d++ ) { lowerBound[d] = padWidth; upperBound[d] = padWidth; } } } } } // Get the slices std::vector whichSlices; for( unsigned int n = 0; n < size[direction]; n++ ) { whichSlices.push_back( n ); } itk::ants::CommandLineParser::OptionType::Pointer slicesOption = parser->GetOption( "slices" ); if( slicesOption && slicesOption->GetNumberOfFunctions() ) { int numberOfSlicesToIncrement = 1; int startingSlice = 0; int endSlice = size[direction] - 1; bool readSlices = false; bool reverseOrder = false; if( slicesOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { std::vector slicesVector = parser->ConvertVector( slicesOption->GetFunction( 0 )->GetName() ); if( slicesVector.size() == 1 ) { numberOfSlicesToIncrement = slicesVector[0]; if( numberOfSlicesToIncrement < 0 ) { reverseOrder = true; } } else { whichSlices = parser->ConvertVector( slicesOption->GetFunction( 0 )->GetName() ); readSlices = true; } } if( !readSlices ) { if( slicesOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { numberOfSlicesToIncrement = parser->Convert( slicesOption->GetFunction( 0 )->GetParameter( 0 ) ); if( numberOfSlicesToIncrement < 0 ) { reverseOrder = true; } } if( numberOfSlicesToIncrement == 0 ) { std::cerr << "Need greater than 0 slices for incrementing." << std::endl; return EXIT_FAILURE; } std::ostringstream stream; stream << startingSlice; std::string startingSliceString = stream.str(); stream << endSlice; std::string endSliceString = stream.str(); if( slicesOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { startingSliceString = slicesOption->GetFunction( 0 )->GetParameter( 1 ); } if( slicesOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { endSliceString = slicesOption->GetFunction( 0 )->GetParameter( 2 ); } bool isStartingSliceMaskDependent = startingSliceString.find( std::string( "mask" ) ) != std::string::npos; bool isEndSliceMaskDependent = endSliceString.find( std::string( "mask" ) ) != std::string::npos; if( isStartingSliceMaskDependent || isEndSliceMaskDependent ) { if( !maskImage ) { std::cerr << "Mask image is not specified." << std::endl; return EXIT_FAILURE; } if( isStartingSliceMaskDependent ) { startingSlice = maskRegion.GetIndex()[direction]; } else { startingSlice = parser->Convert( startingSliceString ); } if( startingSliceString.find( std::string( "+" ) ) != std::string::npos ) { std::string offsetString = startingSliceString.substr( startingSliceString.find( std::string( "+" ) ) + 1 ); int offset = parser->Convert( offsetString ); startingSlice += offset; } else if( startingSliceString.find( std::string( "-" ) ) != std::string::npos ) { std::string offsetString = startingSliceString.substr( startingSliceString.find( std::string( "-" ) ) + 1 ); int offset = parser->Convert( offsetString ); startingSlice -= offset; } if( isEndSliceMaskDependent ) { endSlice = maskRegion.GetIndex()[direction] + maskRegion.GetSize()[direction]; } else { endSlice = parser->Convert( endSliceString ); } if( endSliceString.find( std::string( "+" ) ) != std::string::npos ) { std::string offsetString = endSliceString.substr( endSliceString.find( std::string( "+" ) ) + 1 ); int offset = parser->Convert( offsetString ); endSlice += offset; } else if( endSliceString.find( std::string( "-" ) ) != std::string::npos ) { std::string offsetString = endSliceString.substr( endSliceString.find( std::string( "-" ) ) + 1 ); int offset = parser->Convert( offsetString ); endSlice -= offset; } } else { startingSlice = parser->Convert( startingSliceString ); endSlice = parser->Convert( endSliceString ); } startingSlice = vnl_math_max( itk::NumericTraits::ZeroValue(), startingSlice ); startingSlice = vnl_math_min( startingSlice, static_cast( size[direction] - 1 ) ); endSlice = vnl_math_max( itk::NumericTraits::ZeroValue(), endSlice ); endSlice = vnl_math_min( endSlice, static_cast( size[direction] - 1 ) ); whichSlices.clear(); if( reverseOrder ) { for( int n = endSlice; n >= startingSlice; n -= vnl_math_abs( numberOfSlicesToIncrement ) ) { whichSlices.push_back( n ); } } else { for( int n = startingSlice; n <= endSlice; n += numberOfSlicesToIncrement ) { whichSlices.push_back( n ); } } } } // Get tile geometry. unsigned long numberOfSlices = whichSlices.size(); int numberOfRows = 0; int numberOfColumns = 0; itk::ants::CommandLineParser::OptionType::Pointer tileGeometryOption = parser->GetOption( "tile-geometry" ); if( tileGeometryOption && tileGeometryOption->GetNumberOfFunctions() ) { std::vector layout = parser->ConvertVector( tileGeometryOption->GetFunction( 0 )->GetName() ); if( layout.size() > 2 ) { std::cerr << "Tile geometry is specified as numberOfRowsxnumberOfColumns" << std::endl; return EXIT_FAILURE; } numberOfRows = vnl_math_min( static_cast( layout[0] ), static_cast( numberOfSlices ) ); numberOfColumns = vnl_math_min( static_cast( layout[1] ), static_cast( numberOfSlices ) ); } if( numberOfRows <= 0 && numberOfColumns > 0 ) { numberOfRows = std::ceil( static_cast( numberOfSlices ) / static_cast( numberOfColumns ) ); } else if( numberOfColumns <= 0 && numberOfRows > 0 ) { numberOfColumns = std::ceil( static_cast( numberOfSlices ) / static_cast( numberOfRows ) ); } else if( numberOfColumns <= 0 && numberOfRows <= 0 ) { numberOfRows = static_cast( std::sqrt( static_cast( numberOfSlices ) ) ); numberOfColumns = std::ceil( static_cast( numberOfSlices ) / static_cast( numberOfRows ) ); } itk::ants::CommandLineParser::OptionType::Pointer flipOption = parser->GetOption( "flip-slice" ); bool doFlipHorizontally = false; bool doFlipVertically = false; if( flipOption && flipOption->GetNumberOfFunctions() ) { std::vector layout = parser->ConvertVector( flipOption->GetFunction( 0 )->GetName() ); if( layout.size() > 2 ) { std::cerr << "Flip layout is specified as doFlipXxdoFlipY" << std::endl; return EXIT_FAILURE; } doFlipHorizontally = layout[0]; doFlipVertically = layout[1]; } itk::ants::CommandLineParser::OptionType::Pointer permuteOption = parser->GetOption( "permute-axes" ); bool doPermute = false; if( permuteOption && permuteOption->GetNumberOfFunctions() ) { doPermute = parser->Convert( permuteOption->GetFunction( 0 )->GetName() ); } // Now do the tiling std::cout << "Slices[" << direction << "]: " << whichSlices.size() << std::endl; std::cout << "Rows: " << numberOfRows << std::endl; std::cout << "Columns: " << numberOfColumns << std::endl; typedef itk::TileImageFilter TileFilterType; TileFilterType::LayoutArrayType array; array[0] = numberOfColumns; array[1] = numberOfRows; ImageType::RegionType region; size[direction] = 0; TileFilterType::Pointer tileFilter = TileFilterType::New(); tileFilter->SetLayout( array ); typedef itk::TileImageFilter RgbTileFilterType; RgbTileFilterType::Pointer rgbTileFilter = RgbTileFilterType::New(); rgbTileFilter->SetLayout( array ); for( unsigned int i = 0; i < whichSlices.size(); i++ ) { unsigned int whichSlice = whichSlices[i]; std::cout << "Processing slice " << whichSlice << std::endl; ImageType::IndexType index; index.Fill( 0 ); index[direction] = static_cast( whichSlice ); region.SetIndex( index ); region.SetSize( size ); typedef itk::ExtractImageFilter ExtracterType; ExtracterType::Pointer extracter = ExtracterType::New(); extracter->SetInput( inputImage ); extracter->SetExtractionRegion( region ); extracter->SetDirectionCollapseToIdentity(); SliceType::Pointer outputSlice = ITK_NULLPTR; SliceType::Pointer outputSlice2 = ITK_NULLPTR; if( paddingType == -1 ) { typedef itk::ExtractImageFilter ExtracterType2; ExtracterType2::Pointer extracter2 = ExtracterType2::New(); extracter2->SetInput( extracter->GetOutput() ); extracter2->SetExtractionRegion( croppedSliceRegion ); extracter2->SetDirectionCollapseToIdentity(); outputSlice = extracter2->GetOutput(); outputSlice->Update(); outputSlice->DisconnectPipeline(); } else if( paddingType == 1 ) { typedef itk::ConstantPadImageFilter PadderType; PadderType::Pointer padder = PadderType::New(); padder->SetInput( extracter->GetOutput() ); padder->SetPadLowerBound( lowerBound ); padder->SetPadUpperBound( upperBound ); padder->SetConstant( static_cast( padValue ) ); outputSlice = padder->GetOutput(); outputSlice->Update(); outputSlice->DisconnectPipeline(); } else // paddingType == 0 { outputSlice = extracter->GetOutput(); outputSlice->Update(); outputSlice->DisconnectPipeline(); } typedef itk::FlipImageFilter FlipFilterType; FlipFilterType::Pointer flipper = FlipFilterType::New(); FlipFilterType::FlipAxesArrayType flipArray; flipArray[0] = doFlipHorizontally; flipArray[1] = doFlipVertically; flipper->SetInput( outputSlice ); flipper->SetFlipAxes( flipArray ); typedef itk::PermuteAxesImageFilter PermuteAxesImageFilterType; itk::FixedArray order; order[0] = 0; order[1] = 1; if( doPermute ) { order[0] = 1; order[1] = 0; } PermuteAxesImageFilterType::Pointer permuteAxesFilter = PermuteAxesImageFilterType::New(); permuteAxesFilter->SetInput( flipper->GetOutput() ); permuteAxesFilter->SetOrder( order ); outputSlice2 = permuteAxesFilter->GetOutput(); outputSlice2->Update(); outputSlice2->DisconnectPipeline(); if( functionalRgbImages.size() > 0 ) { RgbSliceType::Pointer compositeRgbSlice = ITK_NULLPTR; RealType compositeAlpha = 1.0; for( unsigned int n = 0; n < functionalRgbImages.size(); n++ ) { SliceType::Pointer outputMaskSlice = ITK_NULLPTR; SliceType::Pointer outputMaskSlice2 = ITK_NULLPTR; ExtracterType::Pointer maskExtracter = ExtracterType::New(); maskExtracter->SetInput( functionalMaskImages[n] ); maskExtracter->SetExtractionRegion( region ); maskExtracter->SetDirectionCollapseToIdentity(); if( paddingType == -1 ) { typedef itk::ExtractImageFilter ExtracterType2; ExtracterType2::Pointer maskExtracter2 = ExtracterType2::New(); maskExtracter2->SetInput( maskExtracter->GetOutput() ); maskExtracter2->SetExtractionRegion( croppedSliceRegion ); maskExtracter2->SetDirectionCollapseToIdentity(); outputMaskSlice = maskExtracter2->GetOutput(); outputMaskSlice->Update(); outputMaskSlice->DisconnectPipeline(); } else if( paddingType == 1 ) { typedef itk::ConstantPadImageFilter PadderType; PadderType::Pointer maskPadder = PadderType::New(); maskPadder->SetInput( maskExtracter->GetOutput() ); maskPadder->SetPadLowerBound( lowerBound ); maskPadder->SetPadUpperBound( upperBound ); maskPadder->SetConstant( 0 ); outputMaskSlice = maskPadder->GetOutput(); outputMaskSlice->Update(); outputMaskSlice->DisconnectPipeline(); } else // paddingType == 0 { outputMaskSlice = maskExtracter->GetOutput(); outputMaskSlice->Update(); outputMaskSlice->DisconnectPipeline(); } FlipFilterType::Pointer maskFlipper = FlipFilterType::New(); maskFlipper->SetInput( outputMaskSlice ); maskFlipper->SetFlipAxes( flipArray ); PermuteAxesImageFilterType::Pointer maskPermuteAxesFilter = PermuteAxesImageFilterType::New(); maskPermuteAxesFilter->SetInput( maskFlipper->GetOutput() ); maskPermuteAxesFilter->SetOrder( order ); outputMaskSlice2 = maskPermuteAxesFilter->GetOutput(); outputMaskSlice2->Update(); outputMaskSlice2->DisconnectPipeline(); typedef itk::ExtractImageFilter RgbExtracterType; RgbExtracterType::Pointer rgbExtracter = RgbExtracterType::New(); rgbExtracter->SetInput( functionalRgbImages[n] ); rgbExtracter->SetExtractionRegion( region ); rgbExtracter->SetDirectionCollapseToIdentity(); RgbSliceType::Pointer outputRgbSlice = ITK_NULLPTR; RgbSliceType::Pointer outputRgbSlice2 = ITK_NULLPTR; if( paddingType == -1 ) { typedef itk::ExtractImageFilter RgbExtracterType2; RgbExtracterType2::Pointer rgbExtracter2 = RgbExtracterType2::New(); rgbExtracter2->SetInput( rgbExtracter->GetOutput() ); rgbExtracter2->SetExtractionRegion( croppedSliceRegion ); rgbExtracter2->SetDirectionCollapseToIdentity(); outputRgbSlice = rgbExtracter2->GetOutput(); outputRgbSlice->Update(); outputRgbSlice->DisconnectPipeline(); } else if( paddingType == 1 ) { typedef itk::ConstantPadImageFilter RgbPadderType; RgbPadderType::Pointer rgbPadder = RgbPadderType::New(); rgbPadder->SetInput( rgbExtracter->GetOutput() ); rgbPadder->SetPadLowerBound( lowerBound ); rgbPadder->SetPadUpperBound( upperBound ); rgbPadder->SetConstant( static_cast( padValue ) ); outputRgbSlice = rgbPadder->GetOutput(); outputRgbSlice->Update(); outputRgbSlice->DisconnectPipeline(); } else // paddingType == 0 { outputRgbSlice = rgbExtracter->GetOutput(); outputRgbSlice->Update(); outputRgbSlice->DisconnectPipeline(); } typedef itk::FlipImageFilter RgbFlipFilterType; RgbFlipFilterType::Pointer rgbFlipper = RgbFlipFilterType::New(); RgbFlipFilterType::FlipAxesArrayType rgbFlipArray; rgbFlipArray[0] = doFlipHorizontally; rgbFlipArray[1] = doFlipVertically; rgbFlipper->SetInput( outputRgbSlice ); rgbFlipper->SetFlipAxes( rgbFlipArray ); typedef itk::PermuteAxesImageFilter RgbPermuteAxesImageFilterType; itk::FixedArray rgbOrder; rgbOrder[0] = 0; rgbOrder[1] = 1; if( doPermute ) { rgbOrder[0] = 1; rgbOrder[1] = 0; } RgbPermuteAxesImageFilterType::Pointer rgbPermuteAxesFilter = RgbPermuteAxesImageFilterType::New(); rgbPermuteAxesFilter->SetInput( rgbFlipper->GetOutput() ); rgbPermuteAxesFilter->SetOrder( rgbOrder ); outputRgbSlice2 = rgbPermuteAxesFilter->GetOutput(); outputRgbSlice2->Update(); outputRgbSlice2->DisconnectPipeline(); RealType functionalAlpha = functionalAlphaValues[n]; RealType backgroundAlpha = compositeAlpha; RealType currentAlpha = 1.0 - ( 1.0 - functionalAlpha ) * ( 1.0 - backgroundAlpha ); // combine grayscale slice and rgb slice itk::ImageRegionConstIteratorWithIndex It( outputSlice2, outputSlice2->GetRequestedRegion() ); itk::ImageRegionIterator ItRgb( outputRgbSlice2, outputRgbSlice2->GetRequestedRegion() ); for( It.GoToBegin(), ItRgb.GoToBegin(); !It.IsAtEnd(); ++It, ++ItRgb ) { RgbPixelType rgbPixel = ItRgb.Get(); if( n == 0 ) { PixelType pixel = 255 * ( It.Get() - minIntensityValue ) / ( maxIntensityValue - minIntensityValue ); if( outputMaskSlice2 && outputMaskSlice2->GetPixel( It.GetIndex() ) != 0 ) { rgbPixel.SetRed( static_cast( ( 1.0 - functionalAlpha ) * pixel + functionalAlpha * rgbPixel.GetRed() ) ); rgbPixel.SetGreen( static_cast( ( 1.0 - functionalAlpha ) * pixel + functionalAlpha * rgbPixel.GetGreen() ) ); rgbPixel.SetBlue( static_cast( ( 1.0 - functionalAlpha ) * pixel + functionalAlpha * rgbPixel.GetBlue() ) ); } else { rgbPixel.SetRed( pixel ); rgbPixel.SetGreen( pixel ); rgbPixel.SetBlue( pixel ); } ItRgb.Set( rgbPixel ); } else { // http://stackoverflow.com/questions/726549/algorithm-for-additive-color-mixing-for-rgb-values // or // http://en.wikipedia.org/wiki/Alpha_compositing if( outputMaskSlice2 && outputMaskSlice2->GetPixel( It.GetIndex() ) != 0 ) { RealType functionalRed = rgbPixel.GetRed() / 255.0; RealType functionalGreen = rgbPixel.GetGreen() / 255.0; RealType functionalBlue = rgbPixel.GetBlue() / 255.0; RgbPixelType backgroundRgbPixel = compositeRgbSlice->GetPixel( It.GetIndex() ); RealType backgroundRed = backgroundRgbPixel.GetRed() / 255.0; RealType backgroundGreen = backgroundRgbPixel.GetGreen() / 255.0; RealType backgroundBlue = backgroundRgbPixel.GetBlue() / 255.0; RealType currentRed = functionalRed * functionalAlpha / currentAlpha + backgroundRed * backgroundAlpha * ( 1.0 - functionalAlpha ) / currentAlpha; RealType currentGreen = functionalGreen * functionalAlpha / currentAlpha + backgroundGreen * backgroundAlpha * ( 1.0 - functionalAlpha ) / currentAlpha; RealType currentBlue = functionalBlue * functionalAlpha / currentAlpha + backgroundBlue * backgroundAlpha * ( 1.0 - functionalAlpha ) / currentAlpha; rgbPixel.SetRed( currentRed * 255.0 ); rgbPixel.SetGreen( currentGreen * 255.0 ); rgbPixel.SetBlue( currentBlue * 255.0 ); compositeRgbSlice->SetPixel( It.GetIndex(), rgbPixel ); } } } if( n == 0 ) { typedef itk::ImageDuplicator DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( outputRgbSlice2 ); duplicator->Update(); compositeRgbSlice = duplicator->GetOutput(); } compositeAlpha = currentAlpha; } rgbTileFilter->SetInput( i, compositeRgbSlice ); } else { tileFilter->SetInput( i, outputSlice2 ); } } itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { std::string outputFile = outputOption->GetFunction( 0 )->GetName(); if( functionalRgbImages.size() > 0 ) { rgbTileFilter->Update(); WriteImage( rgbTileFilter->GetOutput(), outputFile.c_str() ); } else { tileFilter->Update(); WriteImage( tileFilter->GetOutput(), outputFile.c_str() ); } } else { std::cerr << "No output filename specified." << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "Main input is a 3-D grayscale image. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "input-image" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "inputImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "An optional Rgb image can be added as an overlay. ") + std::string( "It must have the same image geometry as the input " ) + std::string( "grayscale image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "rgb-image" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "rgbImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specifies the ROI of the RGB voxels used. "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask-image" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "maskImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "If an Rgb image is provided, render the overlay using the specified " ) + std::string( "alpha parameter." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "alpha" ); option->SetShortName( 'a' ); option->SetUsageOption( 0, "value" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "A functional overlay can be specified using both " ) + std::string( "and rgb image and a mask specifying where that " ) + std::string( "rgb image should be applied. Both images must " ) + std::string( "have the same image geometry as the input image. " ) + std::string( "Optionally, an alpha parameter can be specified." ) + std::string( "Note that more than one functional overlays can " ) + std::string( "be rendered, the order in which they are specified " ) + std::string( "on the command line matters, and rgb images are " ) + std::string( "assumed to be unsigned char [0,255]." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "functional-overlay" ); option->SetShortName( 'e' ); option->SetUsageOption( 0, "[rgbImageFileName,maskImageFileName,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The output is the tiled mosaic image. Without an RGB overlay" ) + std::string( "we do not alter the intensity values of the input image. " ) + std::string( "Therefore, the output suffix needs to be associated with a " ) + std::string( "format that supports float images (not .jpg or .png). If " ) + std::string( "one or more RGB overlays are supported then we rescale " ) + std::string( "the input image intensities to also be in the range of [0,255] " ) + std::string( "which permits a .png or .jpg output." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "tiledMosaicImage" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The tile geometry specifies the number of rows and columns " ) + std::string( "in the output image. For example, if the user specifies " ) + std::string( "\'5x10\', then 5 rows by 10 columns of slices are rendered. " ) + std::string( "If R < 0 and C > 0 (or vice versa), the negative value is " ) + std::string( "selected based on direction." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "tile-geometry" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "RxC" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specifies the direction of the slices. This can be based on the how " ) + std::string( "the image is stored in memory or can be based on how the image is aligned " ) + std::string( "in physical space. If no direction is specified, " ) + std::string( "the z-direction (axial?) is chosen." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "direction" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "0/1/2/x/y/(z)" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The user can specify whether to pad or crop a specified voxel-width " ) + std::string( "boundary of each individual slice. For this program, cropping is " ) + std::string( "simply padding with negative voxel-widths. If one pads (+), the " ) + std::string( "user can also specify a constant pad value (default = 0). If a mask is " ) + std::string( "specified, the user can use the mask to define the region, by using " ) + std::string( "the keyword \"mask\" plus an offset, e.g. \"-p mask+3\"." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "pad-or-crop" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "padVoxelWidth" ); option->SetUsageOption( 1, "[padVoxelWidth,]" ); option->SetUsageOption( 2, "[lowerPadding[0]xlowerPadding[1],upperPadding[0]xupperPadding[1],constantValue]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "This option gives the user more control over what slices " ) + std::string( "to use for rendering. The user can specify specific slices " ) + std::string( "for a particular order. Alternatively the user can specify " ) + std::string( "the number slices to increment with the optional specification of " ) + std::string( "which slices to start and end the sequence. A negative value " ) + std::string( "for the numberOfSlicesToIncrement causes rendering in the reverse " ) + std::string( "order. For the third option, minSlice < maxSlice. If a mask is " ) + std::string( "specified, the user can use the mask to define the region, by using " ) + std::string( "the keyword \"mask\" plus an offset, e.g. \"-s [1,mask-3,200]\"." ) + std::string( "For the third option, minSlice < maxSlice." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "slices" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "Slice1xSlice2xSlice3..." ); option->SetUsageOption( 1, "numberOfSlicesToIncrement" ); option->SetUsageOption( 2, "[numberOfSlicesToIncrement,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Flip individual slice images horizontally and/or vertically, specified " ) + std::string( "e.g. as \'0x1\' or \'1x1\'."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "flip-slice" ); option->SetShortName( 'f' ); option->SetUsageOption( 0, "flipXxflipY" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Permute (or swap) the axes of the individual slice images."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "permute-axes" ); option->SetShortName( 'g' ); option->SetUsageOption( 0, "doPermute" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int CreateTiledMosaic( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "CreateTiledMosaic" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "Render a 3-D image volume with optional Rgb overlay." ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc == 1 ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Get dimensionality std::string filename; itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "input-image" ); if( imageOption && imageOption->GetNumberOfFunctions() > 0 ) { filename = imageOption->GetFunction( 0 )->GetName(); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); unsigned int dimension = imageIO->GetNumberOfDimensions(); if( dimension == 3 ) { return CreateMosaic( parser ); } else { std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else { std::cout << "Input image not specified." << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/CreateWarpedGridImage.cxx000066400000000000000000000145141311104306400210770ustar00rootroot00000000000000#include "antsUtilities.h" #include #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMatrixOffsetTransformBase.h" #include "itkWarpImageMultiTransformFilter.h" #include "itkGridImageSource.h" namespace ants { template int CreateWarpedGridImage( int argc, char *argv[] ) { typedef float RealType; typedef itk::Image RealImageType; typedef itk::Vector VectorType; typedef itk::Image VectorImageType; /** * Read in vector field */ typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[2] ); reader->Update(); typedef itk::GridImageSource GridSourceType; typename GridSourceType::Pointer gridder = GridSourceType::New(); gridder->SetSpacing( reader->GetOutput()->GetSpacing() ); gridder->SetOrigin( reader->GetOutput()->GetOrigin() ); gridder->SetSize( reader->GetOutput()->GetLargestPossibleRegion().GetSize() ); typename GridSourceType::ArrayType gridSpacing; typename GridSourceType::ArrayType gridSigma; typename GridSourceType::BoolArrayType which; which.Fill( false ); for( unsigned int i = 0; i < 2; i++ ) { which[i] = true; } if( argc > 4 ) { std::vector directions = ConvertVector( std::string( argv[4] ) ); if( directions.size() != ImageDimension ) { std::cout << "Incorrect direction size." << std::endl; return EXIT_FAILURE; } else { for( unsigned int i = 0; i < ImageDimension; i++ ) { which[i] = static_cast( directions[i] ); } } } for( unsigned int i = 0; i < ImageDimension; i++ ) { gridSpacing[i] = reader->GetOutput()->GetLargestPossibleRegion().GetSize()[i] * reader->GetOutput()->GetSpacing()[i] / 25.0; gridSigma[i] = gridSpacing[i] / 10.0; } if( argc > 5 ) { std::vector spacing = ConvertVector( std::string( argv[5] ) ); if( spacing.size() != ImageDimension ) { std::cout << "Incorrect spacing size." << std::endl; return EXIT_FAILURE; } else { for( unsigned int i = 0; i < ImageDimension; i++ ) { gridSpacing[i] = spacing[i]; gridSigma[i] = gridSpacing[i] / 10.0; } } } if( argc > 6 ) { std::vector sigma = ConvertVector( std::string( argv[6] ) ); if( sigma.size() != ImageDimension ) { std::cout << "Incorrect sigma size." << std::endl; return EXIT_FAILURE; } else { for( unsigned int i = 0; i < ImageDimension; i++ ) { gridSigma[i] = sigma[i] / 10.0; } } } gridder->SetGridSpacing( gridSpacing ); gridder->SetSigma( gridSigma ); gridder->SetWhichDimensions( which ); gridder->Update(); typename RealImageType::Pointer grid = gridder->GetOutput(); grid->SetDirection(reader->GetOutput()->GetDirection() ); grid->SetOrigin(reader->GetOutput()->GetOrigin() ); grid->SetSpacing(reader->GetOutput()->GetSpacing() ); typedef itk::MatrixOffsetTransformBase TransformType; typedef itk::WarpImageMultiTransformFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); warper->SetInput(grid); warper->SetEdgePaddingValue( 0); warper->SetSmoothScale(1); warper->PushBackDisplacementFieldTransform(reader->GetOutput() ); warper->SetOutputParametersFromImage( reader->GetOutput() ); warper->Update(); std::string file = std::string( argv[3] ); typedef itk::ImageFileWriter ImageWriterType; typename ImageWriterType::Pointer gridWriter = ImageWriterType::New(); gridWriter->SetFileName( file.c_str() ); gridWriter->SetInput( warper->GetOutput() ); gridWriter->Update(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int CreateWarpedGridImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "CreateWarpedGridImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Usage: " << argv[0] << " ImageDimension deformationField " << "outputImage [directions, e.g. 1x0x0] [gridSpacing, e.g. 10x10x10] [gridSigma, e.g. 1x1x1]" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { return CreateWarpedGridImage<2>( argc, argv ); } break; case 3: { return CreateWarpedGridImage<3>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/CustomColormaps/000077500000000000000000000000001311104306400173615ustar00rootroot00000000000000ants-2.2.0/Examples/CustomColormaps/bone.txt000066400000000000000000000033001311104306400210410ustar00rootroot000000000000000.000000 0.013889 0.027778 0.041667 0.055556 0.069444 0.083333 0.097222 0.111111 0.125000 0.138889 0.152778 0.166667 0.180556 0.194444 0.208333 0.222222 0.236111 0.250000 0.263889 0.277778 0.291667 0.305556 0.319444 0.333333 0.347222 0.361111 0.375000 0.388889 0.402778 0.416667 0.430556 0.444444 0.458333 0.472222 0.486111 0.500000 0.513889 0.527778 0.541667 0.555556 0.569444 0.583333 0.597222 0.611111 0.625000 0.638889 0.652778 0.674479 0.696181 0.717882 0.739583 0.761285 0.782986 0.804688 0.826389 0.848090 0.869792 0.891493 0.913194 0.934896 0.956597 0.978299 1.000000 0.000000 0.013889 0.027778 0.041667 0.055556 0.069444 0.083333 0.097222 0.111111 0.125000 0.138889 0.152778 0.166667 0.180556 0.194444 0.208333 0.222222 0.236111 0.250000 0.263889 0.277778 0.291667 0.305556 0.319444 0.338542 0.357639 0.376736 0.395833 0.414931 0.434028 0.453125 0.472222 0.491319 0.510417 0.529514 0.548611 0.567708 0.586806 0.605903 0.625000 0.644097 0.663194 0.682292 0.701389 0.720486 0.739583 0.758681 0.777778 0.791667 0.805556 0.819444 0.833333 0.847222 0.861111 0.875000 0.888889 0.902778 0.916667 0.930556 0.944444 0.958333 0.972222 0.986111 1.000000 0.005208 0.024306 0.043403 0.062500 0.081597 0.100694 0.119792 0.138889 0.157986 0.177083 0.196181 0.215278 0.234375 0.253472 0.272569 0.291667 0.310764 0.329861 0.348958 0.368056 0.387153 0.406250 0.425347 0.444444 0.458333 0.472222 0.486111 0.500000 0.513889 0.527778 0.541667 0.555556 0.569444 0.583333 0.597222 0.611111 0.625000 0.638889 0.652778 0.666667 0.680556 0.694444 0.708333 0.722222 0.736111 0.750000 0.763889 0.777778 0.791667 0.805556 0.819444 0.833333 0.847222 0.861111 0.875000 0.888889 0.902778 0.916667 0.930556 0.944444 0.958333 0.972222 0.986111 1.000000 ants-2.2.0/Examples/CustomColormaps/christmas.txt000066400000000000000000000030001311104306400221100ustar00rootroot000000000000000.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.03333333 0.06666667 0.10000000 0.13333333 0.16666667 0.20000000 0.23333333 0.26666667 0.30000000 0.33333333 0.36666667 0.40000000 0.43333333 0.46666667 0.50000000 0.53333333 0.56666667 0.60000000 0.63333333 0.66666667 0.70000000 0.73333333 0.76666667 0.80000000 0.83333333 0.86666667 0.90000000 0.93333333 0.96666667 1.00000000 1.00000000 0.96666667 0.93333333 0.90000000 0.86666667 0.83333333 0.80000000 0.76666667 0.73333333 0.70000000 0.66666667 0.63333333 0.60000000 0.56666667 0.53333333 0.50000000 0.46666667 0.43333333 0.40000000 0.36666667 0.33333333 0.30000000 0.26666667 0.23333333 0.20000000 0.16666667 0.13333333 0.10000000 0.06666667 0.03333333 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ants-2.2.0/Examples/CustomColormaps/christmas2.txt000066400000000000000000000030001311104306400221720ustar00rootroot000000000000001.00000000 0.96666667 0.93333333 0.90000000 0.86666667 0.83333333 0.80000000 0.76666667 0.73333333 0.70000000 0.66666667 0.63333333 0.60000000 0.56666667 0.53333333 0.50000000 0.46666667 0.43333333 0.40000000 0.36666667 0.33333333 0.30000000 0.26666667 0.23333333 0.20000000 0.16666667 0.13333333 0.10000000 0.06666667 0.03333333 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.03333333 0.06666667 0.10000000 0.13333333 0.16666667 0.20000000 0.23333333 0.26666667 0.30000000 0.33333333 0.36666667 0.40000000 0.43333333 0.46666667 0.50000000 0.53333333 0.56666667 0.60000000 0.63333333 0.66666667 0.70000000 0.73333333 0.76666667 0.80000000 0.83333333 0.86666667 0.90000000 0.93333333 0.96666667 1.00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ants-2.2.0/Examples/CustomColormaps/colorcube.txt000066400000000000000000000033001311104306400220730ustar00rootroot000000000000000.333333 0.333333 0.333333 0.666667 0.666667 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.333333 0.333333 0.333333 0.333333 0.666667 0.666667 0.666667 0.666667 1.000000 1.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.333333 0.333333 0.333333 0.333333 0.666667 0.666667 0.666667 0.666667 1.000000 1.000000 1.000000 0.166667 0.333333 0.500000 0.666667 0.833333 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.142857 0.285714 0.428571 0.571429 0.714286 0.857143 1.000000 0.333333 0.666667 1.000000 0.333333 0.666667 1.000000 0.333333 0.666667 1.000000 0.333333 0.666667 1.000000 0.000000 0.333333 0.666667 1.000000 0.000000 0.333333 0.666667 1.000000 0.000000 0.333333 0.666667 1.000000 0.333333 0.666667 1.000000 0.000000 0.333333 0.666667 1.000000 0.000000 0.333333 0.666667 1.000000 0.000000 0.333333 0.666667 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.166667 0.333333 0.500000 0.666667 0.833333 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.142857 0.285714 0.428571 0.571429 0.714286 0.857143 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.166667 0.333333 0.500000 0.666667 0.833333 1.000000 0.000000 0.142857 0.285714 0.428571 0.571429 0.714286 0.857143 1.000000 ants-2.2.0/Examples/CustomColormaps/cool.txt000066400000000000000000000000111311104306400210460ustar00rootroot000000000000000 1 1 0 1ants-2.2.0/Examples/CustomColormaps/flag.txt000066400000000000000000000006001311104306400210270ustar00rootroot000000000000001 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 ants-2.2.0/Examples/CustomColormaps/itkSnap.txt000066400000000000000000000003041311104306400215300ustar00rootroot000000000000000 1 0 0 1 0 1 1 0 0.8039216 0.8235294 0.4 0 0 0 0 1 0 1 1 0 0.9372549 0 0.5215686 0.7058824 0.8039216 0 0.545098 0 0 0 1 0 1 1 0.8352941 0.2470588 0.5490196 0.6666667 0.5019608 0.545098 0.3411765 ants-2.2.0/Examples/CustomColormaps/itkSnap255.txt000066400000000000000000000245261311104306400220000ustar00rootroot000000000000000 1 0 0 1 0 1 1 0 0.803921568627451 0.823529411764706 0.4 0 0 0.180392156862745 1 0.415686274509804 0.866666666666667 0.913725490196078 0.647058823529412 1 0.576470588235294 0.854901960784314 0.294117647058824 1 0.235294117647059 1 1 0.854901960784314 0 0.737254901960784 1 1 0.870588235294118 0.498039215686275 0.545098039215686 0.486274509803922 1 0.274509803921569 0 0.933333333333333 0.933333333333333 0.941176470588235 0.96078431372549 0.72156862745098 0.125490196078431 1 0.0980392156862745 0.43921568627451 0.133333333333333 0.972549019607843 0.96078431372549 1 0.564705882352941 0.67843137254902 0.254901960784314 1 0.980392156862745 0.501960784313725 0.196078431372549 0.956862745098039 1 0.482352941176471 1 0.67843137254902 1 0.498039215686275 1 0.56078431372549 0.862745098039216 0.992156862745098 1 0 0 0.501960784313725 1 0.980392156862745 0.580392156862745 0.698039215686274 1 0.529411764705882 0.392156862745098 0.941176470588235 0.980392156862745 1 0.419607843137255 0.529411764705882 0 0.545098039215686 0.96078431372549 0.729411764705882 1 1 0 0.823529411764706 1 0.184313725490196 0.282352941176471 0.686274509803922 0.501960784313725 0.690196078431373 1 0.545098039215686 0.941176470588235 1 0.847058823529412 0.466666666666667 0.858823529411765 0.282352941176471 1 0.780392156862745 0.603921568627451 0.741176470588235 0.941176470588235 0.901960784313726 0 0.333333333333333 0.250980392156863 0.6 0.803921568627451 0.980392156862745 0.372549019607843 0 1 0.87843137254902 0.690196078431373 0.541176470588235 0.117647058823529 0.941176470588235 0.596078431372549 0.627450980392157 1 0 0 1 0 1 1 0 0.803921568627451 0.823529411764706 0.4 0 0 0.180392156862745 1 0.415686274509804 0.866666666666667 0.913725490196078 0.647058823529412 1 0.576470588235294 0.854901960784314 0.294117647058824 1 0.235294117647059 1 1 0.854901960784314 0 0.737254901960784 1 1 0.870588235294118 0.498039215686275 0.545098039215686 0.486274509803922 1 0.274509803921569 0 0.933333333333333 0.933333333333333 0.941176470588235 0.96078431372549 0.72156862745098 0.125490196078431 1 0.0980392156862745 0.43921568627451 0.133333333333333 0.972549019607843 0.96078431372549 1 0.564705882352941 0.67843137254902 0.254901960784314 1 0.980392156862745 0.501960784313725 0.196078431372549 0.956862745098039 1 0.482352941176471 1 0.67843137254902 1 0.498039215686275 1 0.56078431372549 0.862745098039216 0.992156862745098 1 0 0 0.501960784313725 1 0.980392156862745 0.580392156862745 0.698039215686274 1 0.529411764705882 0.392156862745098 0.941176470588235 0.980392156862745 1 0.419607843137255 0.529411764705882 0 0.545098039215686 0.96078431372549 0.729411764705882 1 1 0 0.823529411764706 1 0.184313725490196 0.282352941176471 0.686274509803922 0.501960784313725 0.690196078431373 1 0.545098039215686 0.941176470588235 1 0.847058823529412 0.466666666666667 0.858823529411765 0.282352941176471 1 0.780392156862745 0.603921568627451 0.741176470588235 0.941176470588235 0.901960784313726 0 0.333333333333333 0.250980392156863 0.6 0.803921568627451 0.980392156862745 0.372549019607843 0 1 0.87843137254902 0.690196078431373 0 0 1 0 1 1 0 0.937254901960784 0 0.52156862745098 0.705882352941177 0.803921568627451 0 0.545098039215686 0.545098039215686 0.894117647058824 0.352941176470588 0.627450980392157 0.588235294117647 0.164705882352941 0.980392156862745 0.43921568627451 0.43921568627451 0 0.713725490196078 0.701960784313725 0.92156862745098 0.894117647058824 0.647058823529412 0.501960784313725 0.56078431372549 0.411764705882353 0.854901960784314 0.72156862745098 1 0.270588235294118 0.988235294117647 1 0.509803921568627 0.392156862745098 0.509803921568627 0.909803921568627 1 0.870588235294118 0.525490196078431 0.698039215686274 0.0784313725490196 0.0980392156862745 0.501960784313725 0.545098039215686 0.972549019607843 1 0.627450980392157 0.933333333333333 1 0.411764705882353 0.388235294117647 0.941176470588235 0 0.803921568627451 0.643137254901961 1 0.407843137254902 0.647058823529412 0.847058823529412 0.752941176470588 1 0.549019607843137 0.737254901960784 0.0784313725490196 0.96078431372549 0.980392156862745 0.807843137254902 1 0 0.980392156862745 0.501960784313725 0 0.133333333333333 0.498039215686275 0.807843137254902 0.584313725490196 0.901960784313726 0.92156862745098 0.96078431372549 0.556862745098039 0.807843137254902 0 0 0.96078431372549 0.333333333333333 0.894117647058824 0.870588235294118 0.749019607843137 0.411764705882353 0.972549019607843 0.309803921568627 0.23921568627451 0.933333333333333 0.501960784313725 0.87843137254902 0.941176470588235 0 1 0.843137254901961 0.749019607843137 0.533333333333333 0.43921568627451 0.819607843137255 0 0.0823529411764706 0.803921568627451 0.717647058823529 0.972549019607843 0.901960784313726 0.980392156862745 0.419607843137255 0.87843137254902 0.196078431372549 0.36078431372549 0.980392156862745 0.619607843137255 0.501960784313725 0.270588235294118 1 0.768627450980392 0.168627450980392 0.564705882352941 0.501960784313725 0.984313725490196 0.32156862745098 0 1 0 1 1 0 0.937254901960784 0 0.52156862745098 0.705882352941177 0.803921568627451 0 0.545098039215686 0.545098039215686 0.894117647058824 0.352941176470588 0.627450980392157 0.588235294117647 0.164705882352941 0.980392156862745 0.43921568627451 0.43921568627451 0 0.713725490196078 0.701960784313725 0.92156862745098 0.894117647058824 0.647058823529412 0.501960784313725 0.56078431372549 0.411764705882353 0.854901960784314 0.72156862745098 1 0.270588235294118 0.988235294117647 1 0.509803921568627 0.392156862745098 0.509803921568627 0.909803921568627 1 0.870588235294118 0.525490196078431 0.698039215686274 0.0784313725490196 0.0980392156862745 0.501960784313725 0.545098039215686 0.972549019607843 1 0.627450980392157 0.933333333333333 1 0.411764705882353 0.388235294117647 0.941176470588235 0 0.803921568627451 0.643137254901961 1 0.407843137254902 0.647058823529412 0.847058823529412 0.752941176470588 1 0.549019607843137 0.737254901960784 0.0784313725490196 0.96078431372549 0.980392156862745 0.807843137254902 1 0 0.980392156862745 0.501960784313725 0 0.133333333333333 0.498039215686275 0.807843137254902 0.584313725490196 0.901960784313726 0.92156862745098 0.96078431372549 0.556862745098039 0.807843137254902 0 0 0.96078431372549 0.333333333333333 0.894117647058824 0.870588235294118 0.749019607843137 0.411764705882353 0.972549019607843 0.309803921568627 0.23921568627451 0.933333333333333 0.501960784313725 0.87843137254902 0.941176470588235 0 1 0.843137254901961 0.749019607843137 0.533333333333333 0.43921568627451 0.819607843137255 0 0.0823529411764706 0.803921568627451 0.717647058823529 0.972549019607843 0.901960784313726 0.980392156862745 0.419607843137255 0.87843137254902 0.196078431372549 0.36078431372549 0.980392156862745 0.619607843137255 0.501960784313725 0.270588235294118 1 0.768627450980392 0 0 0 1 0 1 1 0.835294117647059 0.803921568627451 0.247058823529412 0.549019607843137 0.666666666666667 0.501960784313725 0.545098039215686 0.341176470588235 0.882352941176471 0.803921568627451 0.866666666666667 0.47843137254902 0.164705882352941 0.980392156862745 0.858823529411765 0.83921568627451 0.509803921568627 0.756862745098039 0.443137254901961 0.803921568627451 0.768627450980392 0.125490196078431 0.501960784313725 0.56078431372549 0.705882352941177 0.725490196078431 0.529411764705882 0 0.0745098039215686 0 0.87843137254902 0.705882352941177 0 0.933333333333333 0.666666666666667 0.941176470588235 0.701960784313725 0.0431372549019608 0.666666666666667 0.576470588235294 0.43921568627451 0.564705882352941 0.133333333333333 1 0.980392156862745 0.47843137254902 0.564705882352941 0.184313725490196 0.882352941176471 0.27843137254902 0.901960784313726 0 0.196078431372549 0.376470588235294 0.941176470588235 0.933333333333333 0 0.901960784313726 0.796078431372549 0.831372549019608 0 0.56078431372549 0.235294117647059 0.901960784313726 0.941176470588235 0.819607843137255 0.498039215686275 0.501960784313725 0.803921568627451 0.447058823529412 0.827450980392157 0.133333333333333 0.313725490196078 0.92156862745098 0.929411764705882 0.549019607843137 0.843137254901961 0.933333333333333 0.137254901960784 0.980392156862745 0.545098039215686 0.545098039215686 0.862745098039216 0.827450980392157 0.709803921568627 0.67843137254902 1 0.117647058823529 0.862745098039216 0.309803921568627 0.545098039215686 0.933333333333333 0 0.901960784313726 0.96078431372549 0 1 0 0.847058823529412 0.6 0.576470588235294 0.8 1 0.52156862745098 0.196078431372549 0.419607843137255 1 0.980392156862745 0.603921568627451 0.184313725490196 0.815686274509804 0.8 0.36078431372549 0.823529411764706 0.627450980392157 0 0 1 0.870588235294118 0.886274509803922 1 0.501960784313725 0.596078431372549 0.176470588235294 0 0 1 0 1 1 0.835294117647059 0.803921568627451 0.247058823529412 0.549019607843137 0.666666666666667 0.501960784313725 0.545098039215686 0.341176470588235 0.882352941176471 0.803921568627451 0.866666666666667 0.47843137254902 0.164705882352941 0.980392156862745 0.858823529411765 0.83921568627451 0.509803921568627 0.756862745098039 0.443137254901961 0.803921568627451 0.768627450980392 0.125490196078431 0.501960784313725 0.56078431372549 0.705882352941177 0.725490196078431 0.529411764705882 0 0.0745098039215686 0 0.87843137254902 0.705882352941177 0 0.933333333333333 0.666666666666667 0.941176470588235 0.701960784313725 0.0431372549019608 0.666666666666667 0.576470588235294 0.43921568627451 0.564705882352941 0.133333333333333 1 0.980392156862745 0.47843137254902 0.564705882352941 0.184313725490196 0.882352941176471 0.27843137254902 0.901960784313726 0 0.196078431372549 0.376470588235294 0.941176470588235 0.933333333333333 0 0.901960784313726 0.796078431372549 0.831372549019608 0 0.56078431372549 0.235294117647059 0.901960784313726 0.941176470588235 0.819607843137255 0.498039215686275 0.501960784313725 0.803921568627451 0.447058823529412 0.827450980392157 0.133333333333333 0.313725490196078 0.92156862745098 0.929411764705882 0.549019607843137 0.843137254901961 0.933333333333333 0.137254901960784 0.980392156862745 0.545098039215686 0.545098039215686 0.862745098039216 0.827450980392157 0.709803921568627 0.67843137254902 1 0.117647058823529 0.862745098039216 0.309803921568627 0.545098039215686 0.933333333333333 0 0.901960784313726 0.96078431372549 0 1 0 0.847058823529412 0.6 0.576470588235294 0.8 1 0.52156862745098 0.196078431372549 0.419607843137255 1 0.980392156862745 0.603921568627451 0.184313725490196 0.815686274509804 0.8 0.36078431372549 0.823529411764706 0.627450980392157 0 0 1 0.870588235294118ants-2.2.0/Examples/CustomColormaps/itkSnap_BRATS.txt000066400000000000000000000000351311104306400224640ustar00rootroot000000000000000 1 0 0 1 0 0 1 0 1 0 0 0 1 0ants-2.2.0/Examples/CustomColormaps/lines.txt000066400000000000000000000033001311104306400212300ustar00rootroot000000000000000.000000 0.000000 1.000000 0.000000 0.750000 0.750000 0.250000 0.000000 0.000000 1.000000 0.000000 0.750000 0.750000 0.250000 0.000000 0.000000 1.000000 0.000000 0.750000 0.750000 0.250000 0.000000 0.000000 1.000000 0.000000 0.750000 0.750000 0.250000 0.000000 0.000000 1.000000 0.000000 0.750000 0.750000 0.250000 0.000000 0.000000 1.000000 0.000000 0.750000 0.750000 0.250000 0.000000 0.000000 1.000000 0.000000 0.750000 0.750000 0.250000 0.000000 0.000000 1.000000 0.000000 0.750000 0.750000 0.250000 0.000000 0.000000 1.000000 0.000000 0.750000 0.750000 0.250000 0.000000 0.000000 0.500000 0.000000 0.750000 0.000000 0.750000 0.250000 0.000000 0.500000 0.000000 0.750000 0.000000 0.750000 0.250000 0.000000 0.500000 0.000000 0.750000 0.000000 0.750000 0.250000 0.000000 0.500000 0.000000 0.750000 0.000000 0.750000 0.250000 0.000000 0.500000 0.000000 0.750000 0.000000 0.750000 0.250000 0.000000 0.500000 0.000000 0.750000 0.000000 0.750000 0.250000 0.000000 0.500000 0.000000 0.750000 0.000000 0.750000 0.250000 0.000000 0.500000 0.000000 0.750000 0.000000 0.750000 0.250000 0.000000 0.500000 0.000000 0.750000 0.000000 0.750000 0.250000 0.000000 1.000000 0.000000 0.000000 0.750000 0.750000 0.000000 0.250000 1.000000 0.000000 0.000000 0.750000 0.750000 0.000000 0.250000 1.000000 0.000000 0.000000 0.750000 0.750000 0.000000 0.250000 1.000000 0.000000 0.000000 0.750000 0.750000 0.000000 0.250000 1.000000 0.000000 0.000000 0.750000 0.750000 0.000000 0.250000 1.000000 0.000000 0.000000 0.750000 0.750000 0.000000 0.250000 1.000000 0.000000 0.000000 0.750000 0.750000 0.000000 0.250000 1.000000 0.000000 0.000000 0.750000 0.750000 0.000000 0.250000 1.000000 0.000000 0.000000 0.750000 0.750000 0.000000 0.250000 1.000000 ants-2.2.0/Examples/CustomColormaps/pink.txt000066400000000000000000000033001311104306400210570ustar00rootroot000000000000000.117851 0.195857 0.250661 0.295468 0.334324 0.369112 0.400892 0.430331 0.457882 0.483867 0.508525 0.532042 0.554563 0.576204 0.597061 0.617213 0.636729 0.655663 0.674066 0.691980 0.709441 0.726483 0.743134 0.759421 0.766356 0.773229 0.780042 0.786796 0.793492 0.800132 0.806718 0.813250 0.819730 0.826160 0.832539 0.838870 0.845154 0.851392 0.857584 0.863731 0.869835 0.875897 0.881917 0.887896 0.893835 0.899735 0.905597 0.911421 0.917208 0.922958 0.928673 0.934353 0.939999 0.945611 0.951190 0.956736 0.962250 0.967733 0.973185 0.978607 0.983999 0.989361 0.994695 1.000000 0.000000 0.102869 0.145479 0.178174 0.205738 0.230022 0.251976 0.272166 0.290957 0.308607 0.325300 0.341178 0.356348 0.370899 0.384900 0.398410 0.411476 0.424139 0.436436 0.448395 0.460044 0.471405 0.482498 0.493342 0.517549 0.540674 0.562849 0.584183 0.604765 0.624669 0.643958 0.662687 0.680900 0.698638 0.715937 0.732828 0.749338 0.765493 0.781313 0.796819 0.812029 0.826960 0.841625 0.856040 0.870216 0.884164 0.897896 0.911421 0.917208 0.922958 0.928673 0.934353 0.939999 0.945611 0.951190 0.956736 0.962250 0.967733 0.973185 0.978607 0.983999 0.989361 0.994695 1.000000 0.000000 0.102869 0.145479 0.178174 0.205738 0.230022 0.251976 0.272166 0.290957 0.308607 0.325300 0.341178 0.356348 0.370899 0.384900 0.398410 0.411476 0.424139 0.436436 0.448395 0.460044 0.471405 0.482498 0.493342 0.503953 0.514344 0.524531 0.534522 0.544331 0.553966 0.563436 0.572750 0.581914 0.590937 0.599824 0.608581 0.617213 0.625727 0.634126 0.642416 0.650600 0.658682 0.666667 0.674556 0.682355 0.690066 0.697691 0.705234 0.727166 0.748455 0.769156 0.789314 0.808969 0.828159 0.846913 0.865261 0.883229 0.900837 0.918109 0.935061 0.951711 0.968075 0.984167 1.000000 ants-2.2.0/Examples/CustomColormaps/prism.txt000066400000000000000000000033001311104306400212500ustar00rootroot000000000000001.000000 1.000000 1.000000 0.000000 0.000000 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.666667 1.000000 1.000000 1.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.500000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 ants-2.2.0/Examples/CustomColormaps/vga.txt000066400000000000000000000002011311104306400206700ustar00rootroot000000000000001 0.75 1 1 0 0 0 1 0 0.5 0.5 0.5 0 0 0 0.5 1 0.75 0 1 1 1 0 0 0 0.5 0 0.5 0.5 0.5 0 0 1 0.75 0 0 0 1 1 1 0 0.5 0 0 0 0.5 0.5 0.5 ants-2.2.0/Examples/DeNrrd.cxx000066400000000000000000000162771311104306400161460ustar00rootroot00000000000000/*=========================================================================1 Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ants { // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int DeNrrd( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "DeNrrd" ); const int argc = args.size(); char * * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cerr << "Usage: " << argv[0] << " inImage.nrrd outImage.nii gradients.txt" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } const char * const input_image_filename = argv[1]; const char * const output_image_filename = argv[2]; const char * const output_gradients_filename = argv[3]; typedef float PixelType; typedef itk::VectorImage DiffusionImageType; typedef itk::ImageFileReader > FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(input_image_filename); reader->Update(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); //io->SetNrrdVectorType( nrrdKindList ); io->SetFileType( itk::ImageIOBase::ASCII ); //std::cout << "Number of dwi values " << reader->GetOutput()->GetNumberOfComponentsPerPixel() << std::endl; std::string v_string; std::string b_string; itk::MetaDataDictionary & mdd = reader->GetOutput()->GetMetaDataDictionary(); if ( mdd.HasKey("modality") ) { std::string modality; itk::ExposeMetaData(mdd, "modality", modality); if ( modality.compare("DWMRI") != 0 ) { std::cerr << "Data in not DWMRI" << std::endl; return EXIT_FAILURE; } } else { std::cerr << "Error: No b-value found" << std::endl; return EXIT_FAILURE; } if ( mdd.HasKey( "DWMRI_b-value" ) ) { itk::ExposeMetaData(mdd, "DWMRI_b-value", b_string); //std::cout << "BValue = " << b_string << std::endl; } else { std::cerr << "Error: No b-value found" << std::endl; return EXIT_FAILURE; } std::ofstream gradientfile; gradientfile.open(output_gradients_filename); gradientfile << "VERSION: 2" << std::endl; for ( unsigned int i=0; iGetOutput()->GetNumberOfComponentsPerPixel(); i++ ) { char gradKey[20]; sprintf( gradKey, "DWMRI_gradient_%04d", i ); itk::ExposeMetaData(mdd, gradKey, v_string); //std::cout << "Gradient = " << v_string << std::endl; std::istringstream iss(v_string); double x, y, z; iss >> x >> y >> z; if ( (x*x + y*y + z*z ) == 0 ) { gradientfile << v_string << " 0" << std::endl; } else { gradientfile << v_string << " " << b_string << std::endl; } } gradientfile.close(); /* OutputImageType::Pointer outImage = OutputImageType::New(); OutputImageType::SizeType outSize; outSize[0] = reader->GetOutput()->GetLargestPossibleRegion().GetSize()[0]; outSize[1] = reader->GetOutput()->GetLargestPossibleRegion().GetSize()[1]; outSize[2] = reader->GetOutput()->GetLargestPossibleRegion().GetSize()[2]; outSize[3] = reader->GetOutput()->GetNumberOfComponentsPerPixel(); OutputImageType::RegionType outRegion; outRegion.SetSize( outSize ); outImage->SetRegions( outRegion ); outImage->Allocate(); OutputImageType::SpacingType spacing; spacing[0] = reader->GetOutput()->GetSpacing()[0]; spacing[1] = reader->GetOutput()->GetSpacing()[1]; spacing[2] = reader->GetOutput()->GetSpacing()[2]; spacing[3] = 1.0; outImage->SetSpacing(spacing); OutputImageType::PointType origin; origin[0] = reader->GetOutput()->GetOrigin()[0]; origin[1] = reader->GetOutput()->GetOrigin()[1]; origin[2] = reader->GetOutput()->GetOrigin()[2]; outImage->SetOrigin(origin); OutputImageType::DirectionType dirMat; for ( unsigned int i=0; i<3; i++ ) for ( unsigned int j=0; j<3; j++) dirMat(i,j) = reader->GetOutput()->GetDirection()(i,j); dirMat(3,3) = 1.0; outImage->SetDirection(dirMat); for ( unsigned int x=0; xSetPixel(oIdx,reader->GetOutput()->GetPixel(dIdx)[t]); } */ typedef itk::ImageFileWriter FileWriterType; FileWriterType::Pointer writer = FileWriterType::New(); writer->SetFileName(output_image_filename); writer->SetInput( reader->GetOutput() ); writer->Update(); return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/DenoiseImage.cxx000066400000000000000000000517011311104306400173100ustar00rootroot00000000000000#include "antsAllocImage.h" #include "antsCommandLineParser.h" #include "antsUtilities.h" #include "ReadWriteData.h" #include "itkAddImageFilter.h" #include "itkAdaptiveNonLocalMeansDenoisingImageFilter.h" #include "itkIdentityTransform.h" #include "itkLinearInterpolateImageFunction.h" #include "itkResampleImageFilter.h" #include "itkShrinkImageFilter.h" #include "itkSubtractImageFilter.h" #include "itkTimeProbe.h" #include "ANTsVersion.h" namespace ants { template class CommandProgressUpdate : public itk::Command { public: typedef CommandProgressUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( CommandProgressUpdate ); protected: CommandProgressUpdate() : m_CurrentProgress( 0 ) {}; typedef TFilter FilterType; unsigned int m_CurrentProgress; public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { itk::ProcessObject *po = dynamic_cast( caller ); if (! po) return; // std::cout << po->GetProgress() << std::endl; if( typeid( event ) == typeid ( itk::ProgressEvent ) ) { if( this->m_CurrentProgress < 99 ) { this->m_CurrentProgress++; if( this->m_CurrentProgress % 10 == 0 ) { std::cout << this->m_CurrentProgress << std::flush; } else { std::cout << "*" << std::flush; } } } } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { itk::ProcessObject *po = dynamic_cast( const_cast( object ) ); if (! po) return; if( typeid( event ) == typeid ( itk::ProgressEvent ) ) { if( this->m_CurrentProgress < 99 ) { this->m_CurrentProgress++; if( this->m_CurrentProgress % 10 == 0 ) { std::cout << this->m_CurrentProgress << std::flush; } else { std::cout << "*" << std::flush; } } } } }; template int Denoise( itk::ants::CommandLineParser *parser ) { typedef float RealType; typedef typename itk::ants::CommandLineParser::OptionType OptionType; bool verbose = false; typename itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } if( verbose ) { std::cout << std::endl << "Running for " << ImageDimension << "-dimensional images." << std::endl << std::endl; } typedef itk::Image ImageType; typename ImageType::Pointer inputImage = ITK_NULLPTR; //typedef itk::Image MaskImageType; //typename MaskImageType::Pointer maskImage = ITK_NULLPTR; typename OptionType::Pointer inputImageOption = parser->GetOption( "input-image" ); if( inputImageOption && inputImageOption->GetNumberOfFunctions() ) { std::string inputFile = inputImageOption->GetFunction( 0 )->GetName(); ReadImage( inputImage, inputFile.c_str() ); inputImage->Update(); inputImage->DisconnectPipeline(); } else { if( verbose ) { std::cerr << "Input image not specified." << std::endl; } return EXIT_FAILURE; } typedef itk::AdaptiveNonLocalMeansDenoisingImageFilter DenoiserType; typename DenoiserType::Pointer denoiser = DenoiserType::New(); typedef itk::ShrinkImageFilter ShrinkerType; typename ShrinkerType::Pointer shrinker = ShrinkerType::New(); shrinker->SetInput( inputImage ); shrinker->SetShrinkFactors( 1 ); typename OptionType::Pointer shrinkFactorOption = parser->GetOption( "shrink-factor" ); int shrinkFactor = 1; if( shrinkFactorOption && shrinkFactorOption->GetNumberOfFunctions() ) { shrinkFactor = parser->Convert( shrinkFactorOption->GetFunction( 0 )->GetName() ); } // if( shrinkFactor != 1 && verbose ) // { // std::cout << "A shrink factor of > 1 doesn't seem to be working. I'm turning off this option for now." << std::endl; // } shrinker->SetShrinkFactors( shrinkFactor ); shrinker->Update(); denoiser->SetInput( shrinker->GetOutput() ); typename OptionType::Pointer noiseModelOption = parser->GetOption( "noise-model" ); std::string noiseModel( "gaussian" ); if( noiseModelOption && noiseModelOption->GetNumberOfFunctions() ) { noiseModel = noiseModelOption->GetFunction( 0 )->GetName(); } ConvertToLowerCase( noiseModel ); if( std::strcmp( noiseModel.c_str(), "rician" ) == 0 ) { denoiser->SetUseRicianNoiseModel( true ); } else if( std::strcmp( noiseModel.c_str(), "gaussian" ) == 0 ) { denoiser->SetUseRicianNoiseModel( false ); } else { if( verbose ) { std::cerr << "Unrecognized noise model: " << noiseModel << ". See help menu." << std::endl; } return EXIT_FAILURE; } /** * handle the mask image */ typedef typename DenoiserType::MaskImageType MaskImageType; typename MaskImageType::Pointer maskImage = ITK_NULLPTR; typename OptionType::Pointer maskImageOption = parser->GetOption( "mask-image" ); if( maskImageOption && maskImageOption->GetNumberOfFunctions() ) { std::string inputFile = maskImageOption->GetFunction( 0 )->GetName(); ReadImage( maskImage, inputFile.c_str() ); } denoiser->SetMaskImage( maskImage ); typename DenoiserType::NeighborhoodRadiusType neighborhoodPatchRadius; typename DenoiserType::NeighborhoodRadiusType neighborhoodSearchRadius; neighborhoodPatchRadius.Fill( 1 ); neighborhoodSearchRadius.Fill( 3 ); // Get the search and patch radii typename OptionType::Pointer searchRadiusOption = parser->GetOption( "search-radius" ); if( searchRadiusOption && searchRadiusOption->GetNumberOfFunctions() ) { std::string searchRadiusString = searchRadiusOption->GetFunction( 0 )->GetName(); std::vector searchRadius; searchRadius.push_back( 3 ); if( searchRadiusOption && searchRadiusOption->GetNumberOfFunctions() ) { searchRadius = parser->ConvertVector( searchRadiusString ); } if( searchRadius.size() == 1 ) { for( unsigned int d = 1; d < ImageDimension; d++ ) { searchRadius.push_back( searchRadius[0] ); } } if( searchRadius.size() != ImageDimension ) { if( verbose ) { std::cerr << "Search radius specified incorrectly. Please see usage options." << std::endl; } return EXIT_FAILURE; } for( unsigned int d = 0; d < ImageDimension; d++ ) { neighborhoodSearchRadius[d] = searchRadius[d]; } } denoiser->SetNeighborhoodSearchRadius( neighborhoodSearchRadius ); typename OptionType::Pointer patchRadiusOption = parser->GetOption( "patch-radius" ); if( patchRadiusOption && patchRadiusOption->GetNumberOfFunctions() ) { std::vector patchRadius; patchRadius.push_back( 1 ); patchRadius = parser->ConvertVector( patchRadiusOption->GetFunction( 0 )->GetName() ); if( patchRadius.size() == 1 ) { for( unsigned int d = 1; d < ImageDimension; d++ ) { patchRadius.push_back( patchRadius[0] ); } } if( patchRadius.size() != ImageDimension ) { if( verbose ) { std::cerr << "Patch radius specified incorrectly. Please see usage options." << std::endl; } return EXIT_FAILURE; } for( unsigned int d = 0; d < ImageDimension; d++ ) { neighborhoodPatchRadius[d] = patchRadius[d]; } } denoiser->SetNeighborhoodPatchRadius( neighborhoodPatchRadius ); /** * The parameters below are the default parameters taken from Jose's original * code. I don't have a good handle on them so I'm hiding them from the * user for now. */ typename DenoiserType::NeighborhoodRadiusType neighborhoodRadiusForLocalMeanAndVariance; neighborhoodRadiusForLocalMeanAndVariance.Fill( 1 ); denoiser->SetNeighborhoodRadiusForLocalMeanAndVariance( neighborhoodRadiusForLocalMeanAndVariance ); denoiser->SetEpsilon( 0.00001 ); denoiser->SetMeanThreshold( 0.95 ); denoiser->SetVarianceThreshold( 0.5 ); denoiser->SetSmoothingFactor( 1.0 ); denoiser->SetSmoothingVariance( 2.0 ); itk::TimeProbe timer; timer.Start(); if( verbose ) { typedef CommandProgressUpdate CommandType; typename CommandType::Pointer observer = CommandType::New(); denoiser->AddObserver( itk::ProgressEvent(), observer ); } try { // denoiser->DebugOn(); denoiser->Update(); } catch( itk::ExceptionObject & e ) { if( verbose ) { std::cerr << "Exception caught: " << e << std::endl; } return EXIT_FAILURE; } if( verbose ) { std::cout << std::endl << std::endl; denoiser->Print( std::cout, 3 ); } timer.Stop(); if( verbose ) { std::cout << "Elapsed time: " << timer.GetMean() << std::endl; } /** * output */ typename itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { /** * Get the noise image and resample to full resolution */ typedef itk::SubtractImageFilter SubtracterType; typename SubtracterType::Pointer subtracter = SubtracterType::New(); subtracter->SetInput1( denoiser->GetInput() ); subtracter->SetInput2( denoiser->GetOutput() ); typedef itk::ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); { typedef itk::IdentityTransform TransformType; typename TransformType::Pointer transform = TransformType::New(); transform->SetIdentity(); resampler->SetTransform( transform ); } { typedef itk::LinearInterpolateImageFunction LinearInterpolatorType; typename LinearInterpolatorType::Pointer interpolator = LinearInterpolatorType::New(); interpolator->SetInputImage( subtracter->GetOutput() ); resampler->SetInterpolator( interpolator ); } resampler->SetOutputParametersFromImage( inputImage ); resampler->UseReferenceImageOn(); resampler->SetInput( subtracter->GetOutput() ); typename ImageType::Pointer noiseImage = resampler->GetOutput(); noiseImage->Update(); noiseImage->DisconnectPipeline(); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { WriteImage( noiseImage, ( outputOption->GetFunction( 0 )->GetParameter( 1 ) ).c_str() ); } typename SubtracterType::Pointer subtracter2 = SubtracterType::New(); subtracter2->SetInput1( inputImage ); subtracter2->SetInput2( noiseImage ); subtracter2->Update(); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { WriteImage( subtracter2->GetOutput(), ( outputOption->GetFunction( 0 )->GetName() ).c_str() ); } else if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { WriteImage( subtracter2->GetOutput(), ( outputOption->GetFunction( 0 )->GetParameter( 0 ) ).c_str() ); } } return EXIT_SUCCESS; } void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, the program tries to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "image-dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3/4" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "A scalar image is expected as input for noise correction. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "input-image" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "inputImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Employ a Rician or Gaussian noise model. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "noise-model" ); option->SetShortName( 'n' ); option->SetUsageOption( 0, "Rician/(Gaussian)" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "If a mask image is specified, denoising is " ) + std::string( "only performed in the mask region. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask-image" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "maskImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Running noise correction on large images can be time consuming. " ) + std::string( "To lessen computation time, the input image can be resampled. " ) + std::string( "The shrink factor, specified as a single integer, describes " ) + std::string( "this resampling. Shrink factor = 1 is the default." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "shrink-factor" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "(1)/2/3/..." ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Patch radius. Default = 1x1x1" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "patch-radius" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "1" ); option->SetUsageOption( 1, "1x1x1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Search radius. Default = 3x3x3." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "search-radius" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "3" ); option->SetUsageOption( 1, "3x3x3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The output consists of the noise corrected version of the " ) + std::string( "input image. Optionally, one can also output the estimated " ) + std::string( "noise image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "correctedImage" ); option->SetUsageOption( 1, "[correctedImage,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Get Version Information." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "version" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int DenoiseImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "DenoiseImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "Denoise an image using a spatially adaptive filter originally described in " ) + std::string( "J. V. Manjon, P. Coupe, Luis Marti-Bonmati, D. L. Collins, " ) + std::string( "and M. Robles. Adaptive Non-Local Means Denoising of MR Images With " ) + std::string( "Spatially Varying Noise Levels, Journal of Magnetic Resonance Imaging, " ) + std::string( "31:192-203, June 2010." ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc == 1 ) { parser->PrintMenu( std::cerr, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Show automatic version itk::ants::CommandLineParser::OptionType::Pointer versionOption = parser->GetOption( "version" ); if( versionOption && versionOption->GetNumberOfFunctions() ) { std::string versionFunction = versionOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( versionFunction ); if( versionFunction.compare( "1" ) == 0 || versionFunction.compare( "true" ) == 0 ) { //Print Version Information std::cout << ANTs::Version::ExtendedVersionString() << std::endl; return EXIT_SUCCESS; } } // Get dimensionality unsigned int dimension = 3; itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "image-dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { // Read in the first intensity image to get the image dimension. std::string filename; itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "input-image" ); if( imageOption && imageOption->GetNumberOfFunctions() > 0 ) { if( imageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { filename = imageOption->GetFunction( 0 )->GetParameter( 0 ); } else { filename = imageOption->GetFunction( 0 )->GetName(); } } else { std::cerr << "No input images were specified. Specify an input image" << " with the -i option" << std::endl; return EXIT_FAILURE; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); dimension = imageIO->GetNumberOfDimensions(); } switch( dimension ) { case 2: { return Denoise<2>( parser ); } break; case 3: { return Denoise<3>( parser ); } break; case 4: { return Denoise<4>( parser ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ExtractRegionFromImage.cxx000066400000000000000000000161241311104306400213240ustar00rootroot00000000000000 #include "antsUtilities.h" #include "ReadWriteData.h" #include #include #include "itkCastImageFilter.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkExtractImageFilter.h" #include "itkLabelStatisticsImageFilter.h" #include #include namespace ants { template int ExtractRegionFromImage( int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; typename ImageType::Pointer inputImage = ImageType::New(); ReadImage( inputImage, argv[2] ); typename ImageType::RegionType region; typename ImageType::RegionType::SizeType regionSize; typename ImageType::RegionType::IndexType regionIndex; if( argc == 6 ) { std::vector minIndex; std::vector maxIndex; minIndex = ConvertVector( std::string( argv[4] ) ); maxIndex = ConvertVector( std::string( argv[5] ) ); for( unsigned int i = 0; i < ImageDimension; i++ ) { regionIndex[i] = minIndex[i]; regionSize[i] = maxIndex[i] - minIndex[i] + 1; } region.SetSize( regionSize ); region.SetIndex( regionIndex ); } else if ( argc == 7 ) { typename ImageType::Pointer labimg; ReadImage( labimg, argv[5] ); typedef itk::Image ShortImageType; typedef itk::CastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput( labimg ); caster->Update(); typedef itk::LabelStatisticsImageFilter StatsFilterType; typename StatsFilterType::Pointer stats = StatsFilterType::New(); stats->SetLabelInput( caster->GetOutput() ); stats->SetInput( caster->GetOutput() ); stats->Update(); region = stats->GetRegion( atoi( argv[4] ) ); } else { typename ImageType::Pointer domainImage = ITK_NULLPTR; ReadImage( domainImage, argv[4] ); if( domainImage.IsNotNull() ) { typename ImageType::IndexType maxIndex; typename ImageType::IndexType minIndex; minIndex.Fill( itk::NumericTraits::max() ); maxIndex.Fill( itk::NumericTraits::NonpositiveMin() ); itk::ImageRegionConstIteratorWithIndex It( inputImage, inputImage->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { typename ImageType::IndexType index = It.GetIndex(); typename ImageType::PointType point; inputImage->TransformIndexToPhysicalPoint( index, point ); typename ImageType::IndexType trashIndex; bool isInside = domainImage->TransformPhysicalPointToIndex( point, trashIndex ); if( isInside ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { if( index[d] < minIndex[d] ) { minIndex[d] = index[d]; } if( index[d] > maxIndex[d] ) { maxIndex[d] = index[d]; } } } } for( unsigned int i = 0; i < ImageDimension; i++ ) { regionIndex[i] = minIndex[i]; regionSize[i] = maxIndex[i] - regionIndex[i] + 1; } region.SetSize( regionSize ); region.SetIndex( regionIndex ); } else { typedef itk::Image ShortImageType; typedef itk::CastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput( inputImage ); caster->Update(); typedef itk::LabelStatisticsImageFilter StatsFilterType; typename StatsFilterType::Pointer stats = StatsFilterType::New(); stats->SetLabelInput( caster->GetOutput() ); stats->SetInput( caster->GetOutput() ); stats->Update(); region = stats->GetRegion( atoi( argv[4] ) ); } } typedef itk::ExtractImageFilter CropperType; typename CropperType::Pointer cropper = CropperType::New(); cropper->SetInput( inputImage ); cropper->SetExtractionRegion( region ); cropper->SetDirectionCollapseToSubmatrix(); cropper->Update(); WriteImage( cropper->GetOutput(), argv[3] ); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ExtractRegionFromImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ExtractRegionFromImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 5 || argc > 7 ) { std::cerr << "Usage 1: " << argv[0] << " ImageDimension " << "inputImage outputImage minIndex maxIndex " << std::endl; std::cerr << "Usage 2: " << argv[0] << " ImageDimension " << "inputImage outputImage label " << std::endl; std::cerr << "Usage 3: " << argv[0] << " ImageDimension " << "inputImage outputImage domainImage " << std::endl; std::cerr << "Usage 4: " << argv[0] << " ImageDimension " << "inputImage outputImage label labelImage 1 " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { return ExtractRegionFromImage<2>( argc, argv ); } break; case 3: { return ExtractRegionFromImage<3>( argc, argv ); } break; case 4: { return ExtractRegionFromImage<4>( argc, argv ); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ExtractRegionFromImageByMask.cxx000066400000000000000000000133501311104306400224310ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include #include "itkCastImageFilter.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkExtractImageFilter.h" #include "itkLabelStatisticsImageFilter.h" #include #include namespace ants { template int ExtractRegionFromImageByMask(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName(argv[2]); reader->Update(); typename ImageType::RegionType region; typename ImageType::RegionType::SizeType size; typename ImageType::RegionType::IndexType index; if( 0 ) { std::vector minIndex; std::vector maxIndex; minIndex = ConvertVector(std::string(argv[4]) ); maxIndex = ConvertVector(std::string(argv[5]) ); for( unsigned int i = 0; i < ImageDimension; i++ ) { index[i] = minIndex[i]; size[i] = maxIndex[i] - minIndex[i] + 1; } region.SetSize(size); region.SetIndex(index); } else { typedef itk::Image ShortImageType; // typedef itk::CastImageFilter CasterType; // typename CasterType::Pointer caster = CasterType::New(); // caster->SetInput(reader->GetOutput()); // caster->Update(); typedef itk::ImageFileReader ShortImageReaderType; typename ShortImageReaderType::Pointer shortReader = ShortImageReaderType::New(); shortReader->SetFileName(argv[4]); shortReader->Update(); // typedef itk::LabelStatisticsImageFilter typedef itk::LabelStatisticsImageFilter StatsFilterType; typename StatsFilterType::Pointer stats = StatsFilterType::New(); // stats->SetLabelInput(caster->GetOutput()); stats->SetLabelInput(shortReader->GetOutput() ); // stats->SetInput(caster->GetOutput()); stats->SetInput(reader->GetOutput() ); stats->Update(); const unsigned int label = (argc >= 6) ? atoi(argv[5]) : 1; region = stats->GetRegion(label); std::cout << "bounding box of label=" << label << " : " << region << std::endl; const unsigned int padWidth = (argc >= 7) ? atoi(argv[6]) : 0; region.PadByRadius(padWidth); std::cout << "padding radius = " << padWidth << " : " << region << std::endl; region.Crop(reader->GetOutput()->GetBufferedRegion() ); std::cout << "crop with original image region " << reader->GetOutput()->GetBufferedRegion() << " : " << region << std::endl; } std::cout << "final cropped region: " << region << std::endl; typedef itk::ExtractImageFilter CropperType; typename CropperType::Pointer cropper = CropperType::New(); cropper->SetInput(reader->GetOutput() ); cropper->SetExtractionRegion(region); cropper->SetDirectionCollapseToSubmatrix(); cropper->Update(); typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput(cropper->GetOutput() ); writer->SetFileName(argv[3]); writer->Update(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ExtractRegionFromImageByMask( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ExtractRegionFromImageByMask" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 6 || argc > 7 ) { std::cout << "Extract a sub-region from image using the bounding" " box from a label image, with optional padding radius." << std::endl << "Usage : " << argv[0] << " ImageDimension " << "inputImage outputImage labelMaskImage [label=1] [padRadius=0]" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi(argv[1]) ) { case 2: { return ExtractRegionFromImageByMask<2>(argc, argv); } break; case 3: { return ExtractRegionFromImageByMask<3>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ExtractSliceFromImage.cxx000066400000000000000000000070251311104306400211400ustar00rootroot00000000000000#include "antsUtilities.h" #include "ReadWriteData.h" #include "itkExtractImageFilter.h" namespace ants { template int ExtractSliceFromImage( int itkNotUsed( argc ), char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image SliceType; typename ImageType::Pointer inputImage; ReadImage( inputImage, argv[2] ); typename ImageType::RegionType region; typename ImageType::RegionType::SizeType size = inputImage->GetLargestPossibleRegion().GetSize(); size[atoi( argv[4] )] = 0; typename ImageType::IndexType index; index.Fill( 0 ); index[atoi( argv[4] )] = atoi( argv[5] ); region.SetIndex( index ); region.SetSize( size ); typedef itk::ExtractImageFilter ExtracterType; typename ExtracterType::Pointer extracter = ExtracterType::New(); extracter->SetInput( inputImage ); extracter->SetExtractionRegion( region ); if (ImageDimension < 4) { extracter->SetDirectionCollapseToIdentity(); } else { extracter->SetDirectionCollapseToSubmatrix(); } extracter->Update(); WriteImage( extracter->GetOutput(), argv[3] ); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ExtractSliceFromImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ExtractSliceFromImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc != 6 ) { std::cout << "Usage: " << argv[0] << " imageDimension inputImage outputSlice direction(e.g. 0, 1, 2) slice_number" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { return ExtractSliceFromImage<2>( argc, argv ); } break; case 3: { return ExtractSliceFromImage<3>( argc, argv ); } break; case 4: { return ExtractSliceFromImage<4>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/FitBSplineToPoints.cxx000066400000000000000000000306531311104306400204610ustar00rootroot00000000000000#include "antsUtilities.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkBSplineControlPointImageFilter.h" #include "itkDisplacementFieldToBSplineImageFilter.h" #include "itkImage.h" #include "itkImageRegionIterator.h" #include "itkPointSet.h" #include "ReadWriteData.h" #include #include #include #include template std::vector ConvertDelimitedArray( std::string optionString ) { std::vector values; std::string::size_type crosspos = optionString.find( ',', 0 ); if( crosspos == std::string::npos ) { values.push_back( ants::Convert( optionString ) ); } else { std::string element = optionString.substr( 0, crosspos ) ; TValue value; std::istringstream iss( element ); iss >> value; values.push_back( value ); while ( crosspos != std::string::npos ) { std::string::size_type crossposfrom = crosspos; crosspos = optionString.find( ',', crossposfrom + 1 ); if( crosspos == std::string::npos ) { element = optionString.substr( crossposfrom + 1, optionString.length() ); } else { element = optionString.substr( crossposfrom + 1, crosspos - ( crossposfrom + 1 ) ); } std::istringstream iss2( element ); iss2 >> value; values.push_back( value ); } } return values; } namespace ants { template int FitBSplineWarpFieldToPoints( unsigned int argc, char *argv[] ) { typedef float RealType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::DisplacementFieldToBSplineImageFilter BSplineFilterType; typedef typename BSplineFilterType::InputPointSetType BSplinePointSetType; typename BSplineFilterType::WeightsContainerType::Pointer weights = BSplineFilterType::WeightsContainerType::New(); typename BSplinePointSetType::Pointer pointSet = BSplinePointSetType::New(); pointSet->Initialize(); // Read in points std::ifstream inputPointFile( argv[2] ); std::string line; unsigned int count = 0; if( inputPointFile.is_open() ) { while( std::getline( inputPointFile, line ) ) { typename BSplinePointSetType::PointType point; float weight = 1.0; std::vector pointAndWeight = ConvertDelimitedArray( line ); for( unsigned int d = 0; d < PointDimension; d++ ) { point[d] = pointAndWeight[d]; } weight = pointAndWeight[pointAndWeight.size()-1]; pointSet->SetPoint( count, point ); weights->InsertElement( count, weight ); count++; } } unsigned int numberOfPoints = count; // Read in displacements std::ifstream inputDisplacementsFile( argv[3] ); count = 0; if( inputDisplacementsFile.is_open() ) { while( std::getline( inputDisplacementsFile, line ) ) { VectorType vector; std::vector displacement = ConvertDelimitedArray( line ); for( unsigned int d = 0; d < PointDimension; d++ ) { vector[d] = displacement[d]; } pointSet->SetPointData( count, vector ); count++; } } unsigned int numberOfPixels = count; if( numberOfPixels != numberOfPoints ) { std::cerr << "The number of data does not equal the number of points." << std::endl; return EXIT_FAILURE; } typedef itk::Image ImageType; typename ImageType::Pointer domainImage = ITK_NULLPTR; ReadImage( domainImage, argv[4] ); if( ! domainImage ) { std::cerr << "Cannot read image file." << std::endl; return EXIT_FAILURE; } std::vector meshSize = ConvertVector( std::string( argv[6] ) ); const unsigned int splineOrder = 3; typename BSplineFilterType::ArrayType ncps; if ( meshSize.size() == 1 ) { ncps.Fill( meshSize[0] + splineOrder ); } else if ( meshSize.size() == PointDimension ) { for ( unsigned int d = 0; d < PointDimension; d++ ) { ncps[d] = meshSize[d] + splineOrder; } } else { std::cerr << "Invalid ncps format." << std::endl; return EXIT_FAILURE; } unsigned int numberOfLevels = 1; if( argc > 7 ) { numberOfLevels = Convert( std::string( argv[7] ) ); } typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); bspliner->SetPointSet( pointSet ); bspliner->SetBSplineDomain( domainImage->GetOrigin(), domainImage->GetSpacing(), domainImage->GetLargestPossibleRegion().GetSize(), domainImage->GetDirection() ); bspliner->SetNumberOfControlPoints( ncps ); bspliner->SetSplineOrder( splineOrder ); bspliner->SetNumberOfFittingLevels( numberOfLevels ); bspliner->SetEnforceStationaryBoundary( true ); bspliner->SetEstimateInverse( false ); bspliner->Update(); WriteImage( bspliner->GetOutput(), argv[5] ); return EXIT_SUCCESS; } template int FitBSplineCurveToPoints( unsigned int argc, char *argv[] ) { typedef float RealType; typedef itk::Vector VectorType; typedef itk::Image CurveImageType; typedef itk::PointSet PointSetType; typename PointSetType::Pointer pointSet = PointSetType::New(); pointSet->Initialize(); typedef itk::BSplineScatteredDataPointSetToImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); typename FilterType::WeightsContainerType::Pointer weights = FilterType::WeightsContainerType::New(); RealType totalDistance = 0.0; std::ifstream file( argv[2] ); std::string line; unsigned int count = 0; if( file.is_open() ) { while( std::getline( file, line ) ) { VectorType vector( 0.0 ); float weight = 1.0; std::vector vectorAndWeight = ConvertDelimitedArray( line ); for( unsigned int d = 0; d < PointDimension; d++ ) { vector[d] = vectorAndWeight[d]; } weight = vectorAndWeight[vectorAndWeight.size()-1]; pointSet->SetPointData( count, vector ); if ( count > 0 ) { VectorType previous( 0.0 ); pointSet->GetPointData( count-1, &previous ); totalDistance += ( previous - vector ).GetNorm(); } typename PointSetType::PointType point; point[0] = 0.0; pointSet->SetPoint( count, point ); weights->InsertElement( count, weight ); count++; } } RealType cumSum = 0.0; for ( unsigned int i = 1; i < pointSet->GetNumberOfPoints(); i++ ) { VectorType vector( 0.0 ), previous( 0.0 ); pointSet->GetPointData( i, &vector ); pointSet->GetPointData( i-1, &previous ); cumSum += ( vector - previous ).GetNorm(); typename PointSetType::PointType point; point[0] = cumSum / totalDistance; pointSet->SetPoint( i, point ); } filter->SetInput( pointSet ); filter->SetGenerateOutputImage( true ); typename CurveImageType::PointType origin; origin.Fill( 0.0 ); filter->SetOrigin( origin ); typename CurveImageType::SpacingType spacing; spacing[0] = 0.001; if ( argc > 6 ) { spacing[0] = atof( argv[6] ); } filter->SetSpacing( spacing ); typename CurveImageType::SizeType size; size[0] = static_cast( 1.0 / spacing[0] + 1 ); filter->SetSize( size ); typename FilterType::ArrayType order; order[0] = 3; if ( argc > 3 ) { order[0] = atoi( argv[3] ); } filter->SetSplineOrder( order ); typename FilterType::ArrayType ncps; ncps[0] = order[0] + 1; if ( argc > 5 ) { ncps[0] = atoi( argv[5] ); } filter->SetNumberOfControlPoints( ncps ); typename FilterType::ArrayType nlevels; nlevels[0] = 5; if ( argc > 4 ) { nlevels[0] = atoi( argv[4] ); } filter->SetNumberOfLevels( nlevels ); typename FilterType::ArrayType close; close[0] = false; if ( argc > 7 ) { close[0] = atoi( argv[7] ); } filter->SetCloseDimension( close ); filter->Update(); itk::ImageRegionIterator It( filter->GetOutput(), filter->GetOutput()->GetLargestPossibleRegion() ); for ( It.GoToBegin(); !It.IsAtEnd(); ++It ) { VectorType vector = It.Get(); for( unsigned int d = 0; d < PointDimension-1; d++ ) { std::cout << vector[d] << ","; } std::cout << vector[PointDimension-1] << std::endl; } // { // std::string filename = std::string( argv[2] ) + std::string( "_cps.txt" ); // std::ofstream ostr( filename.c_str() ); // ostr << "0 0 0 0" << std::endl; // // itk::ImageRegionIterator It( // filter->GetPhiLattice(), filter->GetPhiLattice()->GetLargestPossibleRegion() ); // for ( It.GoToBegin(); !It.IsAtEnd(); ++It ) // { // ostr << It.Get()[0] << " " << It.Get()[1] << " " << It.Get()[2] << " 1" << std::endl; // } // ostr << "0 0 0 0" << std::endl; // ostr.close(); // } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int FitBSplineToPoints( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "FitBSplineToPoints" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if ( argc < 4 ) { std::cout << "Usage: " << argv[0] << " pointDimension inputLandmarksFile " << " [order=3] [nlevels=10] " << " [numberOfControlPoints=4] [sampleSpacing=0.001] [closed?=0]" << std::endl; std::cout << "Usage 2: " << argv[0] << " pointDimension inputPointFile inputDisplacementFile " << " domainImage outputDisplacementField controlPointMeshSize [nlevels=1]" << std::endl; std::cout << " Note: 1. Points are assumed to be parametrically ordered for fitting to a curve. " << std::endl << " 2. The last column (pointDimension+1) is used for weights." << std::endl << " 3. To specify a warp field, add a 'w' to the dimension argument, e.g. 2w." << std::endl; return EXIT_FAILURE; } std::string imageDimensionString( argv[1] ); if( imageDimensionString.length() == 2 ) { if( imageDimensionString[1] == 'w' ) { switch( atoi( &imageDimensionString[0] ) ) { case 2: return FitBSplineWarpFieldToPoints<2>( argc, argv ); break; case 3: return FitBSplineWarpFieldToPoints<3>( argc, argv ); break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else { std::cerr << "Incorrect dimension specification. See help." << std::endl; return EXIT_FAILURE; } } else { switch( atoi( argv[1] ) ) { case 1: return FitBSplineCurveToPoints<2>( argc, argv ); break; case 2: return FitBSplineCurveToPoints<2>( argc, argv ); break; case 3: return FitBSplineCurveToPoints<3>( argc, argv ); break; case 4: return FitBSplineCurveToPoints<4>( argc, argv ); break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/GetConnectedComponentsFeatureImages.cxx000066400000000000000000000157341311104306400240370ustar00rootroot00000000000000#include "antsUtilities.h" #include "ReadWriteData.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkBinaryThresholdImageFilter.h" #include "itkConnectedComponentImageFilter.h" #include "itkRelabelComponentImageFilter.h" #include "itkLabelGeometryImageFilter.h" #include "itkLabelPerimeterEstimationCalculator.h" #include "itkLabelStatisticsImageFilter.h" #include "itkMultiplyImageFilter.h" #include "itkSignedMaurerDistanceMapImageFilter.h" #include #include namespace ants { template int GetConnectedComponentsFeatureImages(int itkNotUsed( argc ), char* argv[] ) { typedef int PixelType; typedef itk::Image ImageType; typedef itk::Image RealImageType; typename ImageType::Pointer inputImage = ITK_NULLPTR; ReadImage( inputImage, argv[2] ); // Output images: // [0] = volume (in physical coordinates) // [1] = volume / surface area // [2] = eccentricity // [3] = elongation std::vector outputImages; for( unsigned int n = 0; n < 4; n++ ) { typename RealImageType::Pointer output = RealImageType::New(); output->CopyInformation( inputImage ); output->SetRegions( inputImage->GetRequestedRegion() ); output->Allocate(); output->FillBuffer( 0.0 ); outputImages.push_back( output ); } typename ImageType::SpacingType spacing = inputImage->GetSpacing(); float prefactor = 1.0; for( unsigned int d = 0; d < ImageDimension; d++ ) { prefactor *= spacing[d]; } typedef itk::RelabelComponentImageFilter RelabelerType; typename RelabelerType::Pointer relabeler = RelabelerType::New(); relabeler->SetInput( inputImage ); relabeler->Update(); for( unsigned int i = 1; i <= relabeler->GetNumberOfObjects(); i++ ) { typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( relabeler->GetOutput() ); thresholder->SetLowerThreshold( i ); thresholder->SetUpperThreshold( i ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); thresholder->Update(); typedef itk::ConnectedComponentImageFilter ConnectedComponentType; typename ConnectedComponentType::Pointer filter = ConnectedComponentType::New(); filter->SetInput( thresholder->GetOutput() ); filter->Update(); typename RelabelerType::Pointer relabeler2 = RelabelerType::New(); relabeler2->SetInput( filter->GetOutput() ); relabeler2->Update(); typedef itk::LabelGeometryImageFilter GeometryFilterType; typename GeometryFilterType::Pointer geometry = GeometryFilterType::New(); geometry->SetInput( relabeler2->GetOutput() ); geometry->CalculatePixelIndicesOff(); geometry->CalculateOrientedBoundingBoxOff(); geometry->CalculateOrientedLabelRegionsOff(); geometry->Update(); typedef itk::LabelPerimeterEstimationCalculator AreaFilterType; typename AreaFilterType::Pointer area = AreaFilterType::New(); area->SetImage( relabeler2->GetOutput() ); area->Compute(); itk::ImageRegionIteratorWithIndex It( relabeler->GetOutput(), relabeler->GetOutput()->GetRequestedRegion() ); itk::ImageRegionIterator It2( relabeler2->GetOutput(), relabeler2->GetOutput()->GetRequestedRegion() ); for( It.GoToBegin(), It2.GoToBegin(); !It.IsAtEnd(); ++It, ++It2 ) { int label = It2.Get(); if( label != 0 ) { typename ImageType::IndexType index = It.GetIndex(); // Output images: // [0] = volume (in physical coordinates) // [1] = volume / surface area // [2] = eccentricity // [3] = elongation float volume = prefactor * static_cast( geometry->GetVolume( label ) ); outputImages[0]->SetPixel( index, volume ); outputImages[1]->SetPixel( index, area->GetPerimeter( label ) / volume ); outputImages[2]->SetPixel( index, geometry->GetEccentricity( label ) ); outputImages[3]->SetPixel( index, geometry->GetElongation( label ) ); } } } { std::string filename = std::string( argv[3] ) + std::string( "PHYSICAL_VOLUME.nii.gz" ); WriteImage( outputImages[0], filename.c_str() ); } { std::string filename = std::string( argv[3] ) + std::string( "VOLUME_TO_SURFACE_AREA_RATIO.nii.gz" ); WriteImage( outputImages[1], filename.c_str() ); } { std::string filename = std::string( argv[3] ) + std::string( "ECCENTRICITY.nii.gz" ); WriteImage( outputImages[2], filename.c_str() ); } { std::string filename = std::string( argv[3] ) + std::string( "ELONGATION.nii.gz" ); WriteImage( outputImages[3], filename.c_str() ); } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int GetConnectedComponentsFeatureImages( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "GetConnectedComponentsFeatureImages" ); const int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if ( argc < 4 ) { std::cout << "Usage: " << argv[0] << " imageDimension " << "inputSegmentationImage outputImagePrefix" << std::endl; return EXIT_FAILURE; } int returnStatus=EXIT_FAILURE; switch( atoi( argv[1] ) ) { case 2: returnStatus = GetConnectedComponentsFeatureImages<2>( argc, argv ); break; case 3: returnStatus = GetConnectedComponentsFeatureImages<3>( argc, argv ); break; default: std::cout << "Unsupported dimension" << std::endl; } return returnStatus; } } // namespace ants ants-2.2.0/Examples/GetMeshAndTopology.cxx000066400000000000000000000437641311104306400205050ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include #include #include #include "itkVTKPolyDataWriter.h" #include "vtkSTLWriter.h" #include "vtkXMLPolyDataWriter.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIterator.h" #include "itkMesh.h" #include "itkSphereMeshSource.h" #include "itkBinaryMask3DMeshSource.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "vtkPolyData.h" #include "vtkCellData.h" #include "vtkPolyDataConnectivityFilter.h" #include "vtkExtractEdges.h" #include "vtkPolyDataReader.h" #include "BinaryImageToMeshFilter.h" #include "vtkUnstructuredGrid.h" #include "vtkCallbackCommand.h" #include "vtkPointPicker.h" #include "vtkCellPicker.h" #include "vtkPolyDataWriter.h" #include "vtkPolyDataReader.h" #include "vtkRenderWindow.h" #include "vtkRenderer.h" #include "vtkRenderWindowInteractor.h" #include "vtkDataSetMapper.h" #include #include #include #include #include #include #include #include #include #include #include "ReadWriteData.h" #include "itkRescaleIntensityImageFilter.h" #include "vtkDelaunay2D.h" #include "vtkFloatArray.h" #include #include #include "vtkVolume16Reader.h" #include "vtkImageReader2.h" #include "vtkPolyDataMapper.h" #include "vtkActor.h" #include "vtkOutlineFilter.h" #include "vtkCamera.h" #include "vtkProperty.h" #include "vtkPolyData.h" #include "vtkPolyVertex.h" #include "vtkPointData.h" #include "vtkExtractEdges.h" #include "vtkPolyDataNormals.h" #include "vtkMarchingCubes.h" #include "vtkImageGaussianSmooth.h" #include "vtkDecimatePro.h" #include "vtkContourFilter.h" #include "vtkPolyDataConnectivityFilter.h" #include "vtkSmoothPolyDataFilter.h" #include "vtkSTLWriter.h" #include "itkSurfaceImageCurvature.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkImageRegionIterator.h" #include "itkPointSet.h" #include #include namespace ants { void Display(vtkUnstructuredGrid* vtkgrid, std::string offscreen, bool secondwin = false, bool delinter = true ) { vtkSmartPointer normalGenerator = vtkSmartPointer::New(); normalGenerator->SetInputData(vtkgrid); normalGenerator->ComputePointNormalsOn(); normalGenerator->ComputeCellNormalsOff(); normalGenerator->Update(); vtkSmartPointer graphics_factory = vtkSmartPointer::New(); if ( offscreen.length() > 4 ) graphics_factory->SetOffScreenOnlyMode( 1); graphics_factory->SetUseMesaClasses( 1 ); vtkRenderer* ren1 = vtkRenderer::New(); vtkRenderer* ren2 = vtkRenderer::New(); vtkRenderWindow* renWin = vtkRenderWindow::New(); renWin->AddRenderer(ren1); if( secondwin ) { renWin->AddRenderer(ren2); } vtkRenderWindowInteractor* inter = vtkRenderWindowInteractor::New(); inter->SetRenderWindow(renWin); vtkCallbackCommand *cbc = vtkCallbackCommand::New(); ren1->AddObserver(vtkCommand::KeyPressEvent, cbc); vtkDataSetMapper* mapper = vtkDataSetMapper::New(); mapper->SetInputData( normalGenerator->GetOutput() ); // Create a lookup table to map cell data to colors vtkSmartPointer lut = vtkSmartPointer::New(); int tableSize = std::max( 10, 10); lut->SetNumberOfTableValues(tableSize); lut->Build(); lut->SetTableRange( 0 , 1 ); lut->SetTableValue(0 , 1 , 1 , 1, 1); //Black lut->SetTableValue(1, 0.8900, 0.8100, 0.3400, 1); // Banana lut->SetTableValue(2, 1.0000, 0.3882, 0.2784, 1); // Tomato lut->SetTableValue(3, 0.9608, 0.8706, 0.7020, 1); // Wheat lut->SetTableValue(4, 0.9020, 0.9020, 0.9804, 1); // Lavender lut->SetTableValue(5, 1.0000, 0.4900, 0.2500, 1); // Flesh lut->SetTableValue(6, 0.5300, 0.1500, 0.3400, 1); // Raspberry lut->SetTableValue(7, 0.9804, 0.5020, 0.4471, 1); // Salmon lut->SetTableValue(8, 0.7400, 0.9900, 0.7900, 1); // Mint lut->SetTableValue(9, 0.2000, 0.6300, 0.7900, 1); // Peacock mapper->SetLookupTable(lut); mapper->ScalarVisibilityOn(); mapper->SetScalarRange(0, 1); // mapper->SetScalarModeToUsePointData(); // mapper->SetColorModeToMapScalars(); vtkActor* actor = vtkActor::New(); actor->SetMapper(mapper); vtkSmartPointer scalarBar = vtkSmartPointer::New(); scalarBar->SetLookupTable(mapper->GetLookupTable()); scalarBar->SetTitle("F(x)"); scalarBar->SetNumberOfLabels(4); // Create a lookup table to share between the mapper and the scalarbar vtkSmartPointer hueLut = vtkSmartPointer::New(); hueLut->SetTableRange (0, 1); hueLut->SetHueRange (0, 1); hueLut->SetSaturationRange (1, 1); hueLut->SetValueRange (1, 1); hueLut->Build(); // mapper->SetLookupTable( hueLut ); scalarBar->SetLookupTable( lut ); vtkDataSetMapper* mapper2 = vtkDataSetMapper::New(); if( secondwin ) { mapper2->SetInputData(vtkgrid); } if( secondwin ) { mapper2->SetScalarRange(0, 255); } vtkActor* actor2 = vtkActor::New(); if( secondwin ) { actor2->SetMapper(mapper2); } if( secondwin ) { ren1->SetViewport(0.0, 0.0, 0.5, 1.0); ren2->SetViewport(0.5, 0.0, 1.0, 1.0); } else { ren1->SetViewport(0.0, 0.0, 1.0, 1.0); } if( secondwin ) { ren2->AddActor(actor2); } // add the actor and start the render loop // see http://www.vtk.org/doc/nightly/html/classvtkProperty.html actor->GetProperty()->SetInterpolationToFlat(); actor->GetProperty()->SetInterpolationToGouraud(); actor->GetProperty()->ShadingOff(); ren1->AddActor(actor); ren1->SetBackground(1,1,1); // Background color ren1->AddActor2D(scalarBar); renWin->Render(); if ( offscreen.length() > 4 ) { vtkSmartPointer windowToImageFilter = vtkSmartPointer::New(); windowToImageFilter->SetInput(renWin); windowToImageFilter->SetMagnification( 4 ); windowToImageFilter->Update(); vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetFileName( offscreen.c_str() ); writer->SetInputConnection(windowToImageFilter->GetOutputPort()); writer->Write(); } if ( offscreen.length() < 5 ) inter->Start(); mapper->Delete(); actor->Delete(); ren1->Delete(); mapper2->Delete(); actor2->Delete(); ren2->Delete(); renWin->Delete(); if( delinter ) { inter->Delete(); } } float ComputeGenus(vtkPolyData* pd1) { vtkExtractEdges* edgeex = vtkExtractEdges::New(); edgeex->SetInputData(pd1); edgeex->Update(); vtkPolyData* edg1 = edgeex->GetOutput(); vtkIdType nedg = edg1->GetNumberOfCells(); vtkIdType vers = pd1->GetNumberOfPoints(); int nfac = pd1->GetNumberOfPolys(); float g = 0.5 * (2.0 - vers + nedg - nfac); std::cout << " Genus " << g << std::endl; std::cout << " face " << nfac << " edg " << nedg << " vert " << vers << std::endl; return g; } float vtkComputeTopology(vtkPolyData* pd) { // Marching cubes // std::cout << " Marching Cubes "; // vtkMarchingCubes *marchingCubes = vtkMarchingCubes::New(); // vtkContourFilter *marchingCubes = vtkContourFilter::New(); // vtkKitwareContourFilter *marchingCubes = vtkKitwareContourFilter::New(); // marchingCubes->SetInput((vtkDataSet*) vds); // marchingCubes->SetValue(0, hithresh); // int nc; // std::cout << " Input #conts "; std::cin >> nc; // marchingCubes->SetNumberOfContours(2); // marchingCubes->SetComputeScalars(false); // marchingCubes->SetComputeGradients(false); // marchingCubes->SetComputeNormals(false); vtkPolyDataConnectivityFilter* con = vtkPolyDataConnectivityFilter::New(); con->SetExtractionModeToLargestRegion(); // con->SetInput(marchingCubes->GetOutput()); con->SetInputData(pd); con->Update(); float g = ComputeGenus(con->GetOutput() ); return g; #if 0 int inputNumberOfPoints = con->GetOutput()->GetNumberOfPoints(); int inputNumberOfPolys = con->GetOutput()->GetNumberOfPolys(); vtkPolyDataConnectivityFilter *polyDataConnectivityFilter = vtkPolyDataConnectivityFilter::New(); polyDataConnectivityFilter->SetInputData( con->GetOutput() ); polyDataConnectivityFilter->SetExtractionModeToAllRegions(); polyDataConnectivityFilter->SetExtractionModeToLargestRegion(); polyDataConnectivityFilter->Update(); int connectivityNumberOfExtractedRegions = polyDataConnectivityFilter-> GetNumberOfExtractedRegions(); polyDataConnectivityFilter->Delete(); vtkExtractEdges *extractEdges = vtkExtractEdges::New(); extractEdges->SetInputData( con->GetOutput() ); extractEdges->Update(); int extractNumberOfLines = extractEdges->GetOutput()->GetNumberOfLines(); extractEdges->Delete(); int EulerCharacteristic = inputNumberOfPoints - extractNumberOfLines + inputNumberOfPolys; double genus = 0.5 * ( 2 * connectivityNumberOfExtractedRegions - EulerCharacteristic ); std::cout << "EulerCharacteristic " << EulerCharacteristic << std::endl; std::cout << "genus " << genus << std::endl; return genus; #endif } template void GetValueMesh(typename TImage::Pointer image, typename TImage::Pointer image2, std::string outfn, const char* paramname, float scaledata, float aaParm, std::string offscreen , unsigned int inflate ) { // std::cout << " parname " << std::string(paramname) << std::endl; typedef TImage ImageType; typedef BinaryImageToMeshFilter FilterType; typename FilterType::Pointer fltMesh = FilterType::New(); fltMesh->SetInput( image ); fltMesh->SetAntiAliasMaxRMSError( aaParm ); // to do nothing, set negative fltMesh->SetSmoothingIterations( 0 ); fltMesh->SetDecimateFactor( 0.0 ); fltMesh->Update(); vtkPolyData* vtkmesh = fltMesh->GetMesh(); // assign scalars to the original surface mesh // std::string offsc=std::string(""); // Display((vtkUnstructuredGrid*)vtkmesh, offsc ); vtkSmartPointer smoother = vtkSmartPointer::New(); smoother->SetInputData(vtkmesh); smoother->SetNumberOfIterations(25); smoother->BoundarySmoothingOff(); smoother->FeatureEdgeSmoothingOff(); smoother->SetFeatureAngle(120.0); smoother->SetPassBand(.01); smoother->NonManifoldSmoothingOn(); smoother->NormalizeCoordinatesOn(); smoother->Update(); vtkmesh = smoother->GetOutput(); std::cout << " Genus " << vtkComputeTopology(vtkmesh) << std::endl; vtkPoints* vtkpoints = vtkmesh->GetPoints(); int numPoints = vtkpoints->GetNumberOfPoints(); float mx = 0, mn = 9.e9, meank = 0; for( int i = 0; i < numPoints; i++ ) { typename ImageType::IndexType index; typename ImageType::PointType point; for( int j = 0; j < 3; j++ ) { point[j] = (vtkpoints->GetPoint(i)[j]); } image2->TransformPhysicalPointToIndex(point, index); float temp = image2->GetPixel(index); if( fabs(temp) > mx ) { mx = fabs(temp); } if( fabs(temp) < mn && mn > 0 ) { mn = fabs(temp); } meank += fabs(temp); } std::cout << " max kap " << mx << " mn k " << mn << std::endl; meank /= numPoints; // mx=1.3; // mx=2.0; float localscaledata = scaledata; localscaledata = 1; // while (!done) { vtkFloatArray* param = vtkFloatArray::New(); param->SetName(paramname); float dif = (mx - mn) * localscaledata; const float mx2 = meank + dif; const float mn2 = meank - dif; dif = mx2 - mn2; for( int i = 0; i < (numPoints); i++ ) { typename ImageType::IndexType index; typename ImageType::PointType point; for( int j = 0; j < 3; j++ ) { point[j] = (vtkpoints->GetPoint(i)[j]); } image2->TransformPhysicalPointToIndex(point, index); float temp = image2->GetPixel(index); // param->InsertNextValue(temp); // float temp=surfk->CurvatureAtIndex(index); if( i % 1000 == 0 ) { std::cout << " kappa " << temp << std::endl; } // =fabs(manifoldIntegrator->GetGraphNode(i)->GetTotalCost()); temp = fabs(temp); float vvv = (temp - mn2) * 255. / dif; vvv = (temp - mn) / dif; /* if (vvv > 128) { float dif=255-vvv; vvv = 128 - dif; } else { float dif=128-vvv; vvv = 128 + dif; }*/ param->InsertNextValue(vvv); } vtkmesh->GetPointData()->SetScalars(param); } std::cout <<" Now display to " << offscreen << std::endl; vtkSmartPointer inflater = vtkSmartPointer::New(); inflater->SetInputData(vtkmesh); inflater->SetNumberOfIterations( inflate ); inflater->BoundarySmoothingOn(); inflater->FeatureEdgeSmoothingOff(); inflater->SetFeatureAngle(180.0); inflater->SetEdgeAngle(180.0); inflater->SetPassBand( 0.001 ); // smaller values increase smoothing inflater->NonManifoldSmoothingOn(); inflater->NormalizeCoordinatesOff(); if ( inflate > 0 ) { inflater->Update(); vtkmesh = inflater->GetOutput(); } if ( offscreen.length() > 2 ) Display((vtkUnstructuredGrid*)vtkmesh, offscreen ); std::cout << " done with mesh map "; /* typedef itk::VTKPolyDataWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetInputData( vtkmesh ); writer->SetFileName( outfn.c_str() ); writer->Update(); */ vtkSTLWriter *writer = vtkSTLWriter::New(); writer->SetInputData( vtkmesh ); std::cout << " writing " << outfn << std::endl; writer->SetFileName( outfn.c_str() ); writer->Write(); std::cout << " done writing "; inflater->Delete(); smoother->Delete(); std::cout << " done writing2 "; return; } template float GetImageTopology(typename TImage::Pointer image) { typedef TImage ImageType; double aaParm = 0.024; typedef BinaryImageToMeshFilter FilterType; typename FilterType::Pointer fltMesh = FilterType::New(); fltMesh->SetInput(image); fltMesh->SetAntiAliasMaxRMSError(aaParm); fltMesh->SetAntiAliasMaxRMSError( -1000.0 ); // to do nothing fltMesh->Update(); vtkPolyData* vtkmesh = fltMesh->GetMesh(); // assign scalars to the original surface mesh // Display((vtkUnstructuredGrid*)vtkmesh); float genus = vtkComputeTopology(vtkmesh); std::cout << " Genus " << genus << std::endl; return genus; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int GetMeshAndTopology( std::vector args, std::ostream* ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "GetMeshAndTopology" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 2 ) { std::cout << argv[0] << " binaryimage valueimage out paramname ValueScale AntiaAliasParm=0.001 offscreen.png inflation-interations " << std::endl; std::cout << " outputs vtk version of input image -- assumes object is defined by non-zero values " << std::endl; std::cout << " mesh is colored by the value of the image voxel " << std::endl; std::cout << " the AntiaAliasParm could cause topo problems but makes nicer meshes " << std::endl; std::cout << " the offscreen param will render to screen if set to win, 0 means no rendering " << std::endl; std::cout << " ValueScale controls contrast in image appearance - lower increaseses contrast -- should be <= 1 " << std::endl; return EXIT_FAILURE; } // Define the dimension of the images const unsigned int Dimension = 3; typedef float PixelType; // Declare the types of the output images typedef itk::Image ImageType; // Declare the type of the Mesh std::string outfn = std::string(argv[3]); ImageType::Pointer image; ReadImage(image, argv[1]); ImageType::Pointer image2; ReadImage(image2, argv[2]); ImageType::DirectionType fmat = image->GetDirection(); fmat.SetIdentity(); image->SetDirection(fmat); image2->SetDirection(fmat); // Save the mesh float aaParm = 0.03; const char* paramname = std::string("f(x)").c_str(); if( argc > 4 ) { paramname = (argv[4]); } float scaledata = 0.5; if( argc > 5 ) { scaledata = atof(argv[5]); } if( argc > 6 ) { aaParm = atof(argv[6]); } std::cout << "aaParm " << aaParm << std::endl; std::string offscreen="win"; if( argc > 7 ) { offscreen = std::string( argv[7] ); } unsigned int inflate = 0; if( argc > 8 ) { inflate = atoi(argv[8]); } GetValueMesh(image, image2, outfn, paramname, scaledata, aaParm, offscreen, inflate ); // GetImageTopology(image); return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ImageCompare.cxx000066400000000000000000000234331311104306400173110ustar00rootroot00000000000000#include "antsUtilities.h" #include #include "itkWin32Header.h" #include #include #include "itkNumericTraits.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkExtractImageFilter.h" #include "itkTestingComparisonImageFilter.h" namespace ants { using namespace std; #define ITK_TEST_DIMENSION_MAX 6 int RegressionTestImage(const char *, const char *, int, bool); // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ImageCompare( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ImageCompare" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { cerr << "Usage:" << endl; cerr << "testImage, baselineImage1, [baselineImage2, baselineImage3, ...]" << endl; cerr << "Note that if you supply more than one baselineImage, this test will pass if any" << endl; cerr << "of them match the testImage" << endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } int bestBaselineStatus = 2001; int bestBaseline = 2; try { if( argc == 3 ) { bestBaselineStatus = RegressionTestImage(argv[1], argv[2], 0, false); } else { int currentStatus = 2001; for( int i = 2; i < argc; i++ ) { currentStatus = RegressionTestImage(argv[1], argv[i], 0, false); if( currentStatus < bestBaselineStatus ) { bestBaselineStatus = currentStatus; bestBaseline = i; } if( bestBaselineStatus == 0 ) { break; } } } // generate images of our closest match if( bestBaselineStatus == 0 ) { RegressionTestImage(argv[1], argv[bestBaseline], 1, false); } else { RegressionTestImage(argv[1], argv[bestBaseline], 1, true); } } catch( const itk::ExceptionObject& e ) { std::cout << "ITK test driver caught an ITK exception:\n"; std::cout << e.GetFile() << ":" << e.GetLine() << ":\n" << e.GetDescription() << "\n"; bestBaselineStatus = -1; } catch( const std::exception& e ) { std::cout << "ITK test driver caught an exception:\n"; std::cout << e.what() << "\n"; bestBaselineStatus = -1; } catch( ... ) { std::cout << "ITK test driver caught an unknown exception!!!\n"; bestBaselineStatus = -1; } cout << bestBaselineStatus << endl; return bestBaselineStatus; } // Regression Testing Code int RegressionTestImage(const char *testImageFilename, const char *baselineImageFilename, int reportErrors, bool differences) { // Use the factory mechanism to read the test and baseline files and convert them to double typedef itk::Image ImageType; typedef itk::Image OutputType; typedef itk::Image DiffOutputType; typedef itk::ImageFileReader ReaderType; // Read the baseline file ReaderType::Pointer baselineReader = ReaderType::New(); baselineReader->SetFileName(baselineImageFilename); try { baselineReader->UpdateLargestPossibleRegion(); } catch( itk::ExceptionObject& e ) { std::cout << "Exception detected while reading " << baselineImageFilename << " : " << e.GetDescription(); return 1000; } // Read the file generated by the test ReaderType::Pointer testReader = ReaderType::New(); testReader->SetFileName(testImageFilename); try { testReader->UpdateLargestPossibleRegion(); } catch( itk::ExceptionObject& e ) { std::cout << "Exception detected while reading " << testImageFilename << " : " << e.GetDescription() << std::endl; return 1000; } // The sizes of the baseline and test image must match ImageType::SizeType baselineSize; baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize(); ImageType::SizeType testSize; testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize(); if( baselineSize != testSize ) { std::cout << "The size of the Baseline image and Test image do not match!" << std::endl; std::cout << "Baseline image: " << baselineImageFilename << " has size " << baselineSize << std::endl; std::cout << "Test image: " << testImageFilename << " has size " << testSize << std::endl; return EXIT_FAILURE; } // Now compare the two images typedef itk::Testing::ComparisonImageFilter DiffType; DiffType::Pointer diff = DiffType::New(); diff->SetValidInput(baselineReader->GetOutput() ); diff->SetTestInput(testReader->GetOutput() ); diff->SetDifferenceThreshold(2.0); diff->UpdateLargestPossibleRegion(); double status = diff->GetTotalDifference(); if( reportErrors ) { typedef itk::RescaleIntensityImageFilter RescaleType; typedef itk::ExtractImageFilter ExtractType; typedef itk::ImageFileWriter WriterType; typedef itk::ImageRegion RegionType; OutputType::IndexType index; index.Fill(0); OutputType::SizeType size; size.Fill(0); RescaleType::Pointer rescale = RescaleType::New(); rescale->SetOutputMinimum(itk::NumericTraits::NonpositiveMin() ); rescale->SetOutputMaximum(itk::NumericTraits::max() ); rescale->SetInput(diff->GetOutput() ); rescale->UpdateLargestPossibleRegion(); RegionType region; region.SetIndex(index); size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize(); for( unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; i++ ) { size[i] = 0; } region.SetSize(size); ExtractType::Pointer extract = ExtractType::New(); extract->SetInput(rescale->GetOutput() ); extract->SetExtractionRegion(region); WriterType::Pointer writer = WriterType::New(); writer->SetInput(extract->GetOutput() ); if( differences ) { // if there are discrepencies, create an diff image std::cout << ""; std::cout << status; std::cout << "" << std::endl; std::ostringstream diffName; diffName << testImageFilename << ".diff.png"; try { rescale->SetInput(diff->GetOutput() ); rescale->Update(); } catch( ... ) { std::cout << "Error during rescale of " << diffName.str() << std::endl; } writer->SetFileName(diffName.str().c_str() ); try { writer->Update(); } catch( ... ) { std::cout << "Error during write of " << diffName.str() << std::endl; } std::cout << ""; std::cout << diffName.str(); std::cout << "" << std::endl; } std::ostringstream baseName; baseName << testImageFilename << ".base.png"; try { rescale->SetInput(baselineReader->GetOutput() ); rescale->Update(); } catch( ... ) { std::cout << "Error during rescale of " << baseName.str() << std::endl; } try { writer->SetFileName(baseName.str().c_str() ); writer->Update(); } catch( ... ) { std::cout << "Error during write of " << baseName.str() << std::endl; } std::cout << ""; std::cout << baseName.str(); std::cout << "" << std::endl; std::ostringstream testName; testName << testImageFilename << ".test.png"; try { rescale->SetInput(testReader->GetOutput() ); rescale->Update(); } catch( ... ) { std::cout << "Error during rescale of " << testName.str() << std::endl; } try { writer->SetFileName(testName.str().c_str() ); writer->Update(); } catch( ... ) { std::cout << "Error during write of " << testName.str() << std::endl; } std::cout << ""; std::cout << testName.str(); std::cout << "" << std::endl; } return (status != EXIT_SUCCESS) ? EXIT_FAILURE : EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ImageIntensityStatistics.cxx000066400000000000000000000174721311104306400217720ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include #include "ReadWriteData.h" #include "itkImageRegionIterator.h" #include "itkLabelStatisticsImageFilter.h" #include #include #include namespace ants { template int ImageIntensityStatistics( int argc, char *argv[] ) { typedef int LabelType; typedef float RealType; typedef itk::Image LabelImageType; typedef itk::Image RealImageType; typename RealImageType::Pointer intensityImage = RealImageType::New(); ReadImage( intensityImage, argv[2] ); typename LabelImageType::Pointer labelImage = LabelImageType::New(); if( argc > 3 ) { ReadImage( labelImage, argv[3] ); } else { labelImage->CopyInformation( intensityImage ); labelImage->SetRegions( intensityImage->GetLargestPossibleRegion() ); labelImage->Allocate(); labelImage->FillBuffer( 1 ); } std::vector labels; itk::ImageRegionIterator It( labelImage, labelImage->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { if( It.Get() != 0 && std::find( labels.begin(), labels.end(), It.Get() ) == labels.end() ) { labels.push_back( It.Get() ); } } std::sort( labels.begin(), labels.end() ); RealType maxValue = itk::NumericTraits::NonpositiveMin(); RealType minValue = itk::NumericTraits::max(); itk::ImageRegionConstIterator ItI( intensityImage, intensityImage->GetLargestPossibleRegion() ); for( ItI.GoToBegin(), It.GoToBegin(); !ItI.IsAtEnd(); ++ItI, ++It ) { if( It.Get() == 0 ) { continue; } RealType value = ItI.Get(); if( value < minValue ) { minValue = value; } else if( value > maxValue ) { maxValue = value; } } typedef itk::LabelStatisticsImageFilter HistogramGeneratorType; typename HistogramGeneratorType::Pointer stats = HistogramGeneratorType::New(); stats->SetInput( intensityImage ); stats->SetLabelInput( labelImage ); stats->UseHistogramsOn(); stats->SetHistogramParameters( 200, minValue, maxValue ); stats->Update(); // Calculate moments for skewness and kurtosis calculations std::vector m3( labels.size(), 0.0 ); std::vector m4( labels.size(), 0.0 ); std::vector N( labels.size(), 0.0 ); for( ItI.GoToBegin(), It.GoToBegin(); !ItI.IsAtEnd(); ++ItI, ++It ) { LabelType label = It.Get(); if( label == 0 ) { continue; } typename std::vector::iterator it = std::find( labels.begin(), labels.end(), label ); if( it == labels.end() ) { std::cerr << "Label not found. Shouldn't get here." << std::endl; return EXIT_FAILURE; } RealType difference = ItI.Get() - stats->GetMean( label ); unsigned long index = it - labels.begin(); m3[index] += ( difference * difference * difference ); m4[index] += ( m3[index] * difference ); N[index] += 1.0; } for( unsigned int n = 0; n < N.size(); n++ ) { m3[n] /= N[n]; m4[n] /= N[n]; } // std::cout << " " // << "************ Individual Labels *************" << std::endl; std::cout << std::setw( 8 ) << "Label" << std::setw( 14 ) << "Mean" << std::setw( 14 ) << "Sigma" << std::setw( 14 ) << "Skewness" << std::setw( 14 ) << "Kurtosis" << std::setw( 14 ) << "Entropy" << std::setw( 14 ) << "Sum" << std::setw( 14 ) << "5th%" << std::setw( 14 ) << "95th%" << std::setw( 14 ) << "Min" << std::setw( 14 ) << "Max" << std::endl; std::vector::iterator it; for( it = labels.begin(); it != labels.end(); ++it ) { typedef typename HistogramGeneratorType::HistogramType HistogramType; const HistogramType *histogram = stats->GetHistogram( *it ); RealType fifthPercentileValue = histogram->Quantile( 0, 0.05 ); RealType ninetyFifthPercentileValue = histogram->Quantile( 0, 0.95 ); RealType entropy = 0.0; for( unsigned int i = 0; i < histogram->Size(); i++ ) { RealType p = static_cast( histogram->GetFrequency( i, 0 ) ) / static_cast( histogram->GetTotalFrequency() ); if ( p > 0 ) { entropy += ( -p * std::log( p ) / std::log( 2.0 ) ); } } typename std::vector::iterator it2 = std::find( labels.begin(), labels.end(), *it ); unsigned long index = it2 - labels.begin(); RealType m2 = vnl_math_sqr( stats->GetSigma( *it ) ); RealType k2 = ( N[index] ) / ( N[index] - 1.0 ) * m2; RealType prefactor3 = vnl_math_sqr( N[index] ) / ( ( N[index] - 1.0 ) * ( N[index] - 2.0 ) ); RealType k3 = prefactor3 * m3[index]; RealType prefactor4 = vnl_math_sqr( N[index] ) / ( ( N[index] - 1.0 ) * ( N[index] - 2.0 ) * ( N[index] - 3.0 ) ); RealType k4 = prefactor4 * ( ( N[index] + 1 ) * m4[index] - 3 * ( N[index] - 1 ) * vnl_math_sqr( m2 ) ); RealType skewness = k3 / std::sqrt( k2 * k2 * k2 ); RealType kurtosis = k4 / vnl_math_sqr( k2 ); std::cout << std::setw( 8 ) << *it; std::cout << std::setw( 14 ) << stats->GetMean( *it ); std::cout << std::setw( 14 ) << stats->GetSigma( *it ); std::cout << std::setw( 14 ) << skewness; std::cout << std::setw( 14 ) << kurtosis; std::cout << std::setw( 14 ) << entropy; std::cout << std::setw( 14 ) << stats->GetSum( *it ); std::cout << std::setw( 14 ) << fifthPercentileValue; std::cout << std::setw( 14 ) << ninetyFifthPercentileValue; std::cout << std::setw( 14 ) << stats->GetMinimum( *it ); std::cout << std::setw( 14 ) << stats->GetMaximum( *it ); std::cout << std::endl; } return EXIT_SUCCESS; } int ImageIntensityStatistics( std::vector args, std::ostream* itkNotUsed( out_stream ) ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ImageIntensityStatistics" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if ( argc < 3 ) { std::cerr << "Usage: " << argv[0] << " imageDimension inputImage " << std::endl; exit( 1 ); } switch( atoi( argv[1] ) ) { case 2: return ImageIntensityStatistics<2>( argc, argv ); break; case 3: return ImageIntensityStatistics<3>( argc, argv ); break; case 4: return ImageIntensityStatistics<4>( argc, argv ); break; default: std::cerr << "Unsupported dimension" << std::endl; exit( EXIT_FAILURE ); } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ImageMath.cxx000066400000000000000000001125531311104306400166160ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "antsAllocImage.h" #include "antsSCCANObject.h" #include "itkAlternatingValueDifferenceImageFilter.h" #include "itkAlternatingValueSimpleSubtractionImageFilter.h" #include "itkANTSNeighborhoodCorrelationImageToImageMetricv4.h" #include "itkArray.h" #include "itkAverageOverDimensionImageFilter.h" #include "itkGradientImageFilter.h" #include "itkBlackTopHatImageFilter.h" #include "itkBSplineControlPointImageFilter.h" #include "itkBilateralImageFilter.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkCSVNumericObjectFileWriter.h" #include "itkCannyEdgeDetectionImageFilter.h" #include "itkCastImageFilter.h" #include "itkCompositeValleyFunction.h" #include "itkConnectedComponentImageFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkConvolutionImageFilter.h" #include "itkCorrelationImageToImageMetricv4.h" #include "itkCyclicShiftImageFilter.h" #include "itkDiffusionTensor3D.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkDistanceToCentroidMembershipFunction.h" #include "itkDanielssonDistanceMapImageFilter.h" #include "itkSignedMaurerDistanceMapImageFilter.h" #include "itkDemonsImageToImageMetricv4.h" #include "itkExpImageFilter.h" #include "itkExtractImageFilter.h" #include "itkFastMarchingExtensionImageFilterBase.h" #include "itkFastMarchingExtensionImageFilter.h" #include "itkGaussianImageSource.h" #include "itkGradientAnisotropicDiffusionImageFilter.h" #include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" #include "itkHessianRecursiveGaussianImageFilter.h" #include "itkHistogram.h" #include "itkHistogramMatchingImageFilter.h" #include "itkImage.h" #include "itkImageDuplicator.h" #include "itkImageFileWriter.h" #include "itkImageGaussianModelEstimator.h" #include "itkImageKmeansModelEstimator.h" #include "itkImageMaskSpatialObject.h" #include "itkImageMomentsCalculator.h" #include "itkImageRandomConstIteratorWithIndex.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkLabelGeometryImageFilter.h" #include "itkLabelOverlapMeasuresImageFilter.h" #include "itkLabelPerimeterEstimationCalculator.h" #include "itkKdTree.h" #include "itkKdTreeBasedKmeansEstimator.h" #include "itkLabelContourImageFilter.h" #include "itkLabelStatisticsImageFilter.h" #include "itkLabeledPointSetFileReader.h" #include "itkLabeledPointSetFileWriter.h" #include "itkLaplacianRecursiveGaussianImageFilter.h" #include "itkLaplacianSharpeningImageFilter.h" #include "itkListSample.h" #include "itkMattesMutualInformationImageToImageMetricv4.h" #include "itkMaximumProjectionImageFilter.h" #include "itkMinimumProjectionImageFilter.h" #include "itkMRFImageFilter.h" #include "itkMRIBiasFieldCorrectionFilter.h" #include "itkMaskImageFilter.h" #include "itkMaximumImageFilter.h" #include "itkMedianImageFilter.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkMultiplyImageFilter.h" #include "itkMultivariateLegendrePolynomial.h" #include "itkNeighborhood.h" #include "itkNeighborhoodAlgorithm.h" #include "itkNeighborhoodIterator.h" #include "itkNeighborhoodFirstOrderStatisticsImageFilter.h" #include "itkNormalVariateGenerator.h" #include "itkOtsuThresholdImageFilter.h" #include "itkPseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter.h" #include "itkPulsedArterialSpinLabeledCerebralBloodFlowImageFilter.h" #include "itkRGBPixel.h" #include "itkRelabelComponentImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include "itkSampleToHistogramFilter.h" #include "itkScalarImageKmeansImageFilter.h" #include "itkShrinkImageFilter.h" #include "itkSigmoidImageFilter.h" #include "itkSize.h" #include "itkSliceTimingCorrectionImageFilter.h" #include "itkSphereSpatialFunction.h" #include "itkSplitAlternatingTimeSeriesImageFilter.h" #include "itkSTAPLEImageFilter.h" #include "itkSubtractImageFilter.h" #include "itkSumProjectionImageFilter.h" #include "itkTDistribution.h" #include "itkTileImageFilter.h" #include "itkTimeProbe.h" #include "itkTranslationTransform.h" #include "itkVariableSizeMatrix.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkWeightedCentroidKdTreeGenerator.h" #include "itkWhiteTopHatImageFilter.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "vnl/vnl_matrix_fixed.h" #include "itkTransformFactory.h" #include "itkSurfaceImageCurvature.h" #include "itkMultiScaleLaplacianBlobDetectorImageFilter.h" #include #include #include // Here I'm using a map but you could choose even other containers #include #include #include "ReadWriteData.h" #include "TensorFunctions.h" #include "antsMatrixUtilities.h" #include "antsFastMarchingImageFilter.h" #include "itkFastMarchingImageFilterBase.h" #include "itkFastMarchingThresholdStoppingCriterion.h" namespace ants { //External functions in separate files for more module compilation //These functions are defined in independant compilation units in //ImageMathHelper2D.cpp, ImageMathHelper3D.cpp, and ImageMathHelper4D.cpp extern int ImageMathHelper2D(int argc, char **argv); extern int ImageMathHelper3D(int argc, char **argv); extern int ImageMathHelper4D(int argc, char **argv); // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ImageMath( std::vector args, std::ostream * itkNotUsed( out_stream ) ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ImageMath" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 5 ) { std::cout << "\nUsage: " << argv[0] << " ImageDimension [operations and inputs] " << std::endl; std::cout << "\nUsage Information " << std::endl; std::cout << " ImageDimension: 2 or 3 (for 2 or 3 dimensional operations)." << std::endl; std::cout << " ImageDimension: 4 (for operations on 4D file, e.g. time-series data)." << std::endl; std::cout << " Operator: See list of valid operators below." << std::endl; std::cout << " The last two arguments can be an image or float value " << std::endl; std::cout << " NB: Some options output text files" << std::endl; std::cout << "\nMathematical Operations:" << std::endl; std::cout << " m : Multiply --- use vm for vector multiply " << std::endl; std::cout << " + : Add --- use v+ for vector add " << std::endl; std::cout << " - : Subtract --- use v- for vector subtract " << std::endl; std::cout << " / : Divide" << std::endl; std::cout << " ^ : Power" << std::endl; std::cout << " max : voxelwise max" << std::endl; std::cout << " exp : Take exponent exp(imagevalue*value)" << std::endl; std::cout << " addtozero : add image-b to image-a only over points where image-a has zero values" << std::endl; std::cout << " overadd : replace image-a pixel with image-b pixel if image-b pixel is non-zero" << std::endl; std::cout << " abs : absolute value " << std::endl; std::cout << " total : Sums up values in an image or in image1*image2 (img2 is the probability mask)" << std::endl; std::cout << " mean : Average of values in an image or in image1*image2 (img2 is the probability mask)" << std::endl; std::cout << " vtotal : Sums up volumetrically weighted values in an image or in image1*image2 (img2 is the probability mask)" << std::endl; std::cout << " Decision : Computes result=1./(1.+exp(-1.0*( pix1-0.25)/pix2))" << std::endl; std::cout << " Neg : Produce image negative" << std::endl; std::cout << "\nSpatial Filtering:" << std::endl; std::cout << " Project Image1.ext axis-a which-projection : Project an image along axis a, which-projection=0(sum, 1=max, 2=min)" << std::endl; std::cout << " G Image1.ext s : Smooth with Gaussian of sigma = s" << std::endl; std::cout << " MD Image1.ext s : Morphological Dilation with radius s" << std::endl; std::cout << " ME Image1.ext s : Morphological Erosion with radius s" << std::endl; std::cout << " MO Image1.ext s : Morphological Opening with radius s" << std::endl; std::cout << " MC Image1.ext s : Morphological Closing with radius s" << std::endl; std::cout << " GD Image1.ext s : Grayscale Dilation with radius s" << std::endl; std::cout << " GE Image1.ext s : Grayscale Erosion with radius s" << std::endl; std::cout << " GO Image1.ext s : Grayscale Opening with radius s" << std::endl; std::cout << " GC Image1.ext s : Grayscale Closing with radius s" << std::endl; std::cout << " BlobDetector Image1.ext NumberOfBlobsToExtract Optional-Input-Image2 Blob-2-out.nii.gz N-Blobs-To-Match : blob detection by searching for local extrema of the Laplacian of the Gassian (LoG) " << std::endl; std::cout << " Example matching 6 best blobs from 2 images: " << std::endl; std::cout << " ImageMath 2 blob.nii.gz BlobDetector image1.nii.gz 1000 image2.nii.gz blob2.nii.gz 6 " << std::endl; std::cout << " MatchBlobs Image1.ext Image1LM.ext Image2.ext" << std::endl; std::cout << std::endl; std::cout << "\nTransform Image: " << std::endl; std::cout << "Translate InImage.ext x [ y z ] " << std::endl; std::cout << "\nTime Series Operations:" << std::endl; std::cout << " CompCorrAuto : Outputs a csv file containing global signal vector and N comp-corr eigenvectors determined from PCA of the high-variance voxels. Also outputs a comp-corr + global signal corrected 4D image as well as a 3D image measuring the time series variance. Requires a label image with label 1 identifying voxels in the brain." << std::endl; std::cout << " ImageMath 4 ${out}compcorr.nii.gz ThreeTissueConfounds ${out}.nii.gz ${out}seg.nii.gz 1 3 " << " : Outputs average global, CSF and WM signals. Requires a label image with 3 labels , csf, gm , wm ." << std::endl; std::cout << " Usage : ThreeTissueConfounds 4D_TimeSeries.nii.gz LabeLimage.nii.gz csf-label wm-label " << std::endl; std::cout << " TimeSeriesSubset : Outputs n 3D image sub-volumes extracted uniformly from the input time-series 4D image." << std::endl; std::cout << " Usage : TimeSeriesSubset 4D_TimeSeries.nii.gz n " << std::endl; std::cout << " TimeSeriesDisassemble : Outputs n 3D image volumes for each time-point in time-series 4D image." << std::endl; std::cout << " Usage : TimeSeriesDisassemble 4D_TimeSeries.nii.gz " << std::endl << std::endl; std::cout << " TimeSeriesAssemble : Outputs a 4D time-series image from a list of 3D volumes." << std::endl; std::cout << " Usage : TimeSeriesAssemble time_spacing time_origin *images.nii.gz " << std::endl; std::cout << " TimeSeriesToMatrix : Converts a 4D image + mask to matrix (stored as csv file) where rows are time and columns are space ." << std::endl; std::cout << " Usage : TimeSeriesToMatrix 4D_TimeSeries.nii.gz mask " << std::endl; std::cout << " TimeSeriesSimpleSubtraction : Outputs a 3D mean pair-wise difference list of 3D volumes." << std::endl; std::cout << " Usage : TimeSeriesSimpleSubtraction image.nii.gz " << std::endl; std::cout << " TimeSeriesSurroundSubtraction : Outputs a 3D mean pair-wise difference list of 3D volumes." << std::endl; std::cout << " Usage : TimeSeriesSurroundSubtraction image.nii.gz " << std::endl; std::cout << " TimeSeriesSincSubtraction : Outputs a 3D mean pair-wise difference list of 3D volumes." << std::endl; std::cout << " Usage : TimeSeriesSincSubtraction image.nii.gz " << std::endl; std::cout << " SplitAlternatingTimeSeries : Outputs 2 3D time series" << std::endl; std::cout << " Usage : SplitAlternatingTimeSeries image.nii.gz " << std::endl; std::cout << " ComputeTimeSeriesLeverage : Outputs a csv file that identifies the raw leverage and normalized leverage for each time point in the 4D image. leverage, here, is the difference of the time-point image from the average of the n images. the normalized leverage is = average( sum_k abs(Leverage(t)-Leverage(k)) )/Leverage(t). " << std::endl; std::cout << " Usage : ComputeTimeSeriesLeverage 4D_TimeSeries.nii.gz k_neighbors " << std::endl; std::cout << " SliceTimingCorrection : Outputs a slice-timing corrected 4D time series" << std::endl; std::cout << " Usage : SliceTimingCorrection image.nii.gz sliceTiming [sinc / bspline] [sincRadius=4 / bsplineOrder=3]" << std::endl; std::cout << " PASL : computes the PASL model of CBF " << std::endl << "f = \frac{ lambda DeltaM } " << std::endl << " { 2 alpha M_0 TI_1 exp( - TI_2 / T_{1a} ) } " << std::endl; std::cout << " Usage : PASL 3D/4D_TimeSeries.nii.gz BoolFirstImageIsControl M0Image parameter_list.txt " << std::endl; std::cout << " pCASL : computes the pCASL model of CBF " << std::endl << " f = \frac{ lambda DeltaM R_{1a} } " << std::endl << " { 2 alpha M_0 [ exp( - w R_{1a} ) - exp( -w ( \tau + w ) R_{1a}) ] } " << std::endl; std::cout << " Usage : pCASL 3D/4D_TimeSeries.nii.gz parameter_list.txt " << std::endl; std::cout << " PASLQuantifyCBF : Outputs a 3D CBF image in ml/100g/min from a magnetization ratio image" << std::endl; std::cout << " Usage : PASLQuantifyCBF mag_ratio.nii.gz [TI1=700] [TI2=1900] [T1blood=1664] [Lambda=0.9] [Alpha=0.95] [SliceDelay-45] " << std::endl; std::cout << "\nTensor Operations:" << std::endl; std::cout << " 4DTensorTo3DTensor : Outputs a 3D_DT_Image with the same information. " << std::endl; std::cout << " Usage : 4DTensorTo3DTensor 4D_DTImage.ext" << std::endl; std::cout << " ComponentTo3DTensor : Outputs a 3D_DT_Image with the same information as component images. " << std::endl; std::cout << " Usage : ComponentTo3DTensor component_image_prefix[xx,xy,xz,yy,yz,zz] extension" << std::endl; std::cout << " ExtractComponentFrom3DTensor : Outputs a component images. " << std::endl; std::cout << " Usage : ExtractComponentFrom3DTensor dtImage.ext which={xx,xy,xz,yy,yz,zz}" << std::endl; std::cout << " ExtractVectorComponent: Produces the WhichVec component of the vector " << std::endl; std::cout << " Usage : ExtractVectorComponent VecImage WhichVec" << std::endl; std::cout << " TensorColor : Produces RGB values identifying principal directions " << std::endl; std::cout << " Usage : TensorColor DTImage.ext" << std::endl; std::cout << " TensorFA : " << std::endl; std::cout << " Usage : TensorFA DTImage.ext" << std::endl; std::cout << " TensorFADenominator : " << std::endl; std::cout << " Usage : TensorFADenominator DTImage.ext" << std::endl; std::cout << " TensorFANumerator : " << std::endl; std::cout << " Usage : TensorFANumerator DTImage.ext" << std::endl; std::cout << " TensorIOTest : Will write the DT image back out ... tests I/O processes for consistency. " << std::endl; std::cout << " Usage : TensorIOTest DTImage.ext" << std::endl; std::cout << " TensorMeanDiffusion : Mean of the eigenvalues" << std::endl; std::cout << " Usage : TensorMeanDiffusion DTImage.ext" << std::endl; std::cout << " TensorRadialDiffusion : Mean of the two smallest eigenvalues" << std::endl; std::cout << " Usage : TensorRadialDiffusion DTImage.ext" << std::endl; std::cout << " TensorAxialDiffusion : Largest eigenvalue, equivalent to TensorEigenvalue DTImage.ext 2" << std::endl; std::cout << " Usage : TensorAxialDiffusion DTImage.ext" << std::endl; std::cout << " TensorEigenvalue : Gets a single eigenvalue 0-2, where 0 = smallest, 2 = largest" << std::endl; std::cout << " Usage : TensorEigenvalue DTImage.ext WhichInd" << std::endl; std::cout << " TensorToVector : Produces vector field identifying one of the principal directions, 2 = largest eigenvalue" << std::endl; std::cout << " Usage : TensorToVector DTImage.ext WhichVec" << std::endl; std::cout << " TensorToVectorComponent: 0 => 2 produces component of the principal vector field (largest eigenvalue). 3 = 8 => gets values from the tensor " << std::endl; std::cout << " Usage : TensorToVectorComponent DTImage.ext WhichVec" << std::endl; std::cout << " TensorMask : Mask a tensor image, sets background tensors to zero or to isotropic tensors with specified mean diffusivity " << std::endl; std::cout << " Usage : TensorMask DTImage.ext mask.ext [ backgroundMD = 0 ] " << std::endl; std::cout << " FuseNImagesIntoNDVectorField : Create ND field from N input scalar images" << std::endl; std::cout << " Usage : FuseNImagesIntoNDVectorField imagex imagey imagez" << std::endl; std::cout << "\nLabel Fusion:" << std::endl; std::cout << " MajorityVoting : Select label with most votes from candidates" << std::endl; std::cout << " Usage: MajorityVoting LabelImage1.nii.gz .. LabelImageN.nii.gz" << std::endl; std::cout << " CorrelationVoting : Select label with local correlation weights" << std::endl; std::cout << " Usage: CorrelationVoting Template.ext IntenistyImages* LabelImages* {Optional-Radius=5}" << std::endl; std::cout << " STAPLE : Select label using STAPLE method" << std::endl; std::cout << " Usage: STAPLE confidence-weighting LabelImages*" << std::endl; std::cout << " Note: Gives probabilistic output (float)" << std::endl; std::cout << " MostLikely : Select label from from maximum probabilistic segmentations" << std::endl; std::cout << " Usage: MostLikely probabilityThreshold ProbabilityImages*" << std::endl; std::cout << " AverageLabels : Select label using STAPLE method" << std::endl; std::cout << " Usage: STAPLE LabelImages*" << std::endl; std::cout << " Note: Gives probabilistic output (float)" << std::endl; std::cout << "\nImage Metrics & Info:" << std::endl; std::cout << " PearsonCorrelation: r-value from intesities of two images" << std::endl; std::cout << " Usage: PearsonCorrelation image1.ext image2.ext {Optional-mask.ext}" << std::endl; std::cout << " NeighborhoodCorrelation: local correlations" << std::endl; std::cout << " Usage: NeighborhoodCorrelation image1.ext image2.ext {Optional-radius=5} {Optional-image-mask}" << std::endl; std::cout << " NormalizedCorrelation: r-value from intesities of two images" << std::endl; std::cout << " Usage: NormalizedCorrelation image1.ext image2.ext {Optional-image-mask}" << std::endl; std::cout << " Demons: " << std::endl; std::cout << " Usage: Demons image1.ext image2.ext" << std::endl; std::cout << " Mattes: mutual information" << std::endl; std::cout << " Usage: Mattes image1.ext image2.ext {Optional-number-bins=32} {Optional-image-mask}" << std::endl; std::cout << "\nUnclassified Operators:" << std::endl; std::cout << " ReflectionMatrix : Create a reflection matrix about an axis" << std::endl; std::cout << " out.mat ReflectionMatrix image_in axis " << std::endl << std::endl; std::cout << " MakeAffineTransform : Create an itk affine transform matrix " << std::endl; std::cout << " ClosestSimplifiedHeaderMatrix : does what it says ... image-in, image-out" << std::endl; std::cout << " Byte : Convert to Byte image in [0,255]" << std::endl; std::cout << "\n CompareHeadersAndImages: Tries to find and fix header errors. Outputs a repaired image with new header. " << std::endl; std::cout << " Never use this if you trust your header information. " << std::endl; std::cout << " Usage : CompareHeadersAndImages Image1 Image2" << std::endl; std::cout << "\n ConvertImageSetToMatrix: Each row/column contains image content extracted from mask applied to images in *img.nii " << std::endl; std::cout << " Usage : ConvertImageSetToMatrix rowcoloption Mask.nii *images.nii" << std::endl; std::cout << " ConvertImageSetToMatrix output can be an image type or csv file type." << std::endl; std::cout << "\n RandomlySampleImageSetToCSV: N random samples are selected from each image in a list " << std::endl; std::cout << " Usage : RandomlySampleImageSetToCSV N_samples *images.nii" << std::endl; std::cout << " RandomlySampleImageSetToCSV outputs a csv file type." << std::endl; std::cout << "\n FrobeniusNormOfMatrixDifference: take the difference between two itk-transform matrices and then compute the frobenius norm" << std::endl; std::cout << " Usage : FrobeniusNormOfMatrixDifference mat1 mat2 " << std::endl; std::cout << "\n ConvertImageSetToEigenvectors: Each row/column contains image content extracted from mask applied to images in *img.nii " << std::endl; std::cout << " Usage : ConvertImageSetToEigenvectors N_Evecs Mask.nii *images.nii" << std::endl; std::cout << " ConvertImageSetToEigenvectors output will be a csv file for each label value > 0 in the mask." << std::endl; std::cout << "\n ConvertImageToFile : Writes voxel values to a file " << std::endl; std::cout << " Usage : ConvertImageToFile imagevalues.nii {Optional-ImageMask.nii}" << std::endl; std::cout << "\n ConvertLandmarkFile : Converts landmark file between formats. See ANTS.pdf for description of formats." << std::endl; std::cout << " Usage : ConvertLandmarkFile InFile.txt" << std::endl; std::cout << " Example 1 : ImageMath 3 outfile.vtk ConvertLandmarkFile infile.txt" << std::endl; std::cout << "\n ConvertToGaussian : " << std::endl; std::cout << " Usage : ConvertToGaussian TValueImage sigma-float" << std::endl; std::cout << "\n ConvertVectorToImage : The vector contains image content extracted from a mask. Here the vector is returned to its spatial origins as image content " << std::endl; std::cout << " Usage : ConvertVectorToImage Mask.nii vector.nii" << std::endl; std::cout << "\n CorrelationUpdate : In voxels, compute update that makes Image2 more like Image1." << std::endl; std::cout << " Usage : CorrelationUpdate Image1.ext Image2.ext RegionRadius" << std::endl; std::cout << "\n CountVoxelDifference : The where function from IDL " << std::endl; std::cout << " Usage : CountVoxelDifference Image1 Image2 Mask" << std::endl; std::cout << "\n CorruptImage : " << std::endl; std::cout << " Usage : CorruptImage Image NoiseLevel Smoothing" << std::endl; std::cout << "\n D : Danielson Distance Transform" << std::endl; std::cout << "\n MaurerDistance : Maurer distance transform (much faster than Danielson)" << std::endl; std::cout << " Usage : MaurerDistance inputImage {foreground=1}" << std::endl; std::cout << "\n DiceAndMinDistSum : Outputs DiceAndMinDistSum and Dice Overlap to text log file + optional distance image" << std::endl; std::cout << " Usage : DiceAndMinDistSum LabelImage1.ext LabelImage2.ext OptionalDistImage" << std::endl; std::cout << "\n EnumerateLabelInterfaces: " << std::endl; std::cout << " Usage : EnumerateLabelInterfaces ImageIn ColoredImageOutname NeighborFractionToIgnore" << std::endl; std::cout << "\n ClusterThresholdVariate : for sparse estimation " << std::endl; std::cout << " Usage : ClusterThresholdVariate image mask MinClusterSize" << std::endl; std::cout << "\n ExtractSlice : Extracts slice number from last dimension of volume (2,3,4) dimensions " << std::endl; std::cout << " Usage : ExtractSlice volume.nii.gz slicetoextract" << std::endl; std::cout << "\n FastMarchingSegmentation: final output is the propagated label image. Optional stopping value: higher values allow more distant propagation " << std::endl; std::cout << " Usage : FastMarchingSegmentation speed/binaryimagemask.ext initiallabelimage.ext Optional-Stopping-Value" << std::endl; std::cout << "\n FillHoles : Parameter = ratio of edge at object to edge at background; -- " << std::endl; std::cout << " Parameter = 1 is a definite hole bounded by object only, 0.99 is close" << std::endl; std::cout << " Default of parameter > 1 will fill all holes" << std::endl; std::cout << " Usage : FillHoles Image.ext parameter" << std::endl; std::cout << "\n InPaint : very simple inpainting --- assumes zero values should be inpainted " << std::endl; std::cout << " Usage : InPaint #iterations" << std::endl; std::cout << "\n PeronaMalik : anisotropic diffusion w/varying conductance param (0.25 in example below)" << std::endl; std::cout << " Usage : PeronaMalik image #iterations conductance " << std::endl; std::cout << "\n Convolve : convolve input image with kernel image" << std::endl; std::cout << " Usage : Convolve inputImage kernelImage {normalize=1} " << std::endl; std::cout << " Finite : replace non-finite values with finite-value (default = 0)" << std::endl; std::cout << " Usage : Finite Image.exdt {replace-value=0}" << std::endl; std::cout << "\n LabelSurfaceArea : " << std::endl; std::cout << " Usage : LabelSurfaceArea ImageIn {MaxRad-Default=1}" << std::endl; std::cout << "\n FlattenImage : Replaces values greater than %ofMax*Max to the value %ofMax*Max " << std::endl; std::cout << " Usage : FlattenImage Image %ofMax" << std::endl; std::cout << "\n GetLargestComponent : Get the largest object in an image" << std::endl; std::cout << " Usage : GetLargestComponent InputImage {MinObjectSize}" << std::endl; std::cout << "\n Grad : Gradient magnitude with sigma s (if normalize, then output in range [0, 1])" << std::endl; std::cout << " Usage : Grad Image.ext s normalize?" << std::endl; std::cout << "\n HistogramMatch : " << std::endl; std::cout << " Usage : HistogramMatch SourceImage ReferenceImage {NumberBins-Default=255} {NumberPoints-Default=64} {useThresholdAtMeanIntensity=false}" << std::endl; std::cout << "\n RescaleImage : " << std::endl; std::cout << " Usage : RescaleImage InputImage min max" << std::endl; std::cout << "\n WindowImage : " << std::endl; std::cout << " Usage : WindowImage InputImage windowMinimum windowMaximum outputMinimum outputMaximum" << std::endl; std::cout << "\n NeighborhoodStats : " << std::endl; std::cout << " Usage : NeighborhoodStats inputImage whichStat radius" " whichStat: 1 = min, 2 = max, 3 = variance, 4 = sigma, 5 = skewness, 6 = kurtosis, 7 = entropy" << std::endl; std::cout << "\n InvId : computes the inverse-consistency of two deformations and write the inverse consistency error image " << std::endl; std::cout << " Usage : InvId VectorFieldName VectorFieldName" << std::endl; std::cout << "\n ReplicateDisplacement : replicate a ND displacement to a ND+1 image" << std::endl; std::cout << " Usage : ReplicateDisplacement VectorFieldName TimeDims TimeSpacing TimeOrigin" << std::endl; std::cout << "\n ReplicateImage : replicate a ND image to a ND+1 image" << std::endl; std::cout << " Usage : ReplicateImage ImageName TimeDims TimeSpacing TimeOrigin" << std::endl; std::cout << "\n ShiftImageSlicesInTime : shift image slices by one " << std::endl; std::cout << " Usage : ShiftImageSlicesInTime ImageName shift-amount-default-1 shift-dim-default-last-dim" << std::endl; std::cout << "\n LabelStats : Compute volumes / masses of objects in a label image. Writes to text file" << std::endl; std::cout << " Usage : LabelStats labelimage.ext valueimage.nii" << std::endl; std::cout << "\n Laplacian : Laplacian computed with sigma s (if normalize, then output in range [0, 1])" << std::endl; std::cout << " Usage : Laplacian Image.ext s normalize?" << std::endl; std::cout << "\n Canny : Canny edge detector" << std::endl; std::cout << " Usage : Canny Image.ext sigma lowerThresh upperThresh" << std::endl; std::cout << "\n Lipschitz : Computes the Lipschitz norm of a vector field " << std::endl; std::cout << " Usage : Lipschitz VectorFieldName" << std::endl; std::cout << "\n MakeImage : " << std::endl; std::cout << " Usage : MakeImage SizeX SizeY {SizeZ};" << std::endl; std::cout << "\n MTR : Computes the magnetization transfer ratio ( (M0-M1)/M0 ) and truncates values to [0,1]" << std::endl; std::cout << " Usage : MTR M0Image M1Image [MaskImage];" << std::endl; std::cout << "\n Normalize : Normalize to [0,1]. Option instead divides by average value. If opt is a mask image, then we normalize by mean intensity in the mask ROI." << std::endl; std::cout << " Usage : Normalize Image.ext opt" << std::endl; std::cout << "\n PadImage : If Pad-Number is negative, de-Padding occurs" << std::endl; std::cout << " Usage : PadImage ImageIn Pad-Number" << std::endl; std::cout << "\n SigmoidImage : " << std::endl; std::cout << " Usage : SigmoidImage ImageIn [alpha=1.0] [beta=0.0]" << std::endl; std::cout << "\n Sharpen : " << std::endl; std::cout << " Usage : Sharpen ImageIn" << std::endl; std::cout << "\n CenterImage2inImage1 : " << std::endl; std::cout << " Usage : ReferenceImageSpace ImageToCenter " << std::endl; std::cout << "\n PH : Print Header" << std::endl; std::cout << "\n PoissonDiffusion : Solves Poisson's equation in a designated region using non-zero sources" << std::endl; std::cout << " Usage : PoissonDiffusion inputImage labelImage [sigma=1.0] [regionLabel=1] [numberOfIterations=500] [convergenceThreshold=1e-10]" << std::endl; std::cout << "\n PropagateLabelsThroughMask: Final output is the propagated label image. Optional stopping value: higher values allow more distant propagation" << std::endl; std::cout << " Usage : PropagateLabelsThroughMask speed/binaryimagemask.nii.gz initiallabelimage.nii.gz Optional-Stopping-Value 0/1/2" << std::endl; std::cout << " 0/1/2 => 0, no topology constraint, 1 - strict topology constraint, 2 - no handles " << std::endl; std::cout << "\n PValueImage : " << std::endl; std::cout << " Usage : PValueImage TValueImage dof" << std::endl; std::cout << "\n RemoveLabelInterfaces: " << std::endl; std::cout << " Usage : RemoveLabelInterfaces ImageIn" << std::endl; std::cout << "\n ReplaceVoxelValue: replace voxels in the range [a,b] in the input image with c" << std::endl; std::cout << " Usage : ReplaceVoxelValue inputImage a b c" << std::endl; std::cout << "\n ROIStatistics : computes anatomical locations, cluster size and mass of a stat image which should be in the same physical space (but not nec same resolution) as the label image." << std::endl; std::cout << " Usage : ROIStatistics LabelNames.txt labelimage.ext valueimage.nii" << std::endl; std::cout << "\n SetOrGetPixel : " << std::endl; std::cout << " Usage : SetOrGetPixel ImageIn Get/Set-Value IndexX IndexY {IndexZ}" << std::endl; std::cout << " Example 1 : ImageMath 2 outimage.nii SetOrGetPixel Image Get 24 34; Gets the value at 24, 34" << std::endl; std::cout << " Example 2 : ImageMath 2 outimage.nii SetOrGetPixel Image 1.e9 24 34; This sets 1.e9 as the value at 23 34" << std::endl; std::cout << " You can also pass a boolean at the end to force the physical space to be used" << std::endl; std::cout << "\n SetTimeSpacing : sets spacing for last dimension" << std::endl; std::cout << " Usage : SetTimeSpacing Image.ext tspacing" << std::endl; std::cout << "\n SetTimeSpacingWarp : sets spacing for last dimension" << std::endl; std::cout << " Usage : SetTimeSpacingWarp Warp.ext tspacing" << std::endl; std::cout << "\n stack : Will put 2 images in the same volume" << std::endl; std::cout << " Usage : Stack Image1.ext Image2.ext" << std::endl; std::cout << "\n ThresholdAtMean : See the code" << std::endl; std::cout << " Usage : ThresholdAtMean Image %ofMean" << std::endl; std::cout << "\n TileImages : " << std::endl; std::cout << " Usage : TileImages NumColumns ImageList*" << std::endl; std::cout << "\n TriPlanarView : " << std::endl; std::cout << " Usage : TriPlanarView ImageIn.nii.gz PercentageToClampLowIntensity PercentageToClampHiIntensity x-slice y-slice z-slice" << std::endl; std::cout << "\n TruncateImageIntensity: " << std::endl; std::cout << " Usage : TruncateImageIntensity InputImage.ext {lowerQuantile=0.05} {upperQuantile=0.95} {numberOfBins=65} {binary-maskImage}" << std::endl; std::cout << "\n Where : The where function from IDL" << std::endl; std::cout << " Usage : Where Image ValueToLookFor maskImage-option tolerance" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } int returnvalue = EXIT_SUCCESS; std::string operation = std::string(argv[3]); unsigned int imageDimension = atoi(argv[1]); switch( imageDimension ) { case 2: returnvalue = ImageMathHelper2D(argc,argv); break; case 3: returnvalue = ImageMathHelper3D(argc,argv); break; case 4: returnvalue = ImageMathHelper4D(argc,argv); break; default: std::cout << " Dimension " << imageDimension << " is not supported " << std::endl; return EXIT_FAILURE; } if ( returnvalue == EXIT_FAILURE ) { std::cout << " Operation " << operation << " not found or not supported for dimension " << imageDimension << std::endl; } return returnvalue; } } // namespace ants ants-2.2.0/Examples/ImageMathHelper.cxx000066400000000000000000000033171311104306400177530ustar00rootroot00000000000000// Non-templated functions used by ImageMath templates. #include #include #include #include namespace ants { std::string ANTSGetFilePrefix(const char *str) { const std::string filename = str; const std::string::size_type pos = filename.rfind( "." ); const std::string filepre = std::string( filename, 0, pos ); #if 0 // HACK: This does nothing useful if( pos != std::string::npos ) { std::string extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); // extension = std::string( filepre, pos, filepre.length() - 1 ); } } #endif return filepre; } std::string ANTSOptionName(const char *str) { std::string filename = str; std::string::size_type pos = filename.rfind( "=" ); std::string name = std::string( filename, 0, pos ); return name; } std::string ANTSOptionValue(const char *str) { std::string filename = str; std::string::size_type pos = filename.rfind( "=" ); std::string value = std::string( filename, pos + 1, filename.length() ); return value; } // int is the key, string the return value std::map RoiList(std::string file) { unsigned int wordindex = 0; std::string tempstring = ""; std::map RoiList; // RoiList[0]=std::string("Background"); char str[2000]; std::fstream file_op(file.c_str(), std::ios::in); while( file_op >> str ) { tempstring = std::string(str); RoiList[wordindex] = tempstring; wordindex++; } return RoiList; // returns the maximum index } } // namespace ants ants-2.2.0/Examples/ImageMathHelper2D.cxx000066400000000000000000000006061311104306400201370ustar00rootroot00000000000000#include "ImageMath_Templates.hxx" namespace ants { int ImageMathHelper2D(int argc, char **argv) { int returnval = ImageMathHelperAll<2>(argc,argv); if(returnval == EXIT_FAILURE) { returnval = ImageMathHelper2DOnly<2>(argc,argv); } if(returnval == EXIT_FAILURE) { returnval = ImageMathHelper2DOr3D<2>(argc,argv); } return returnval; } } // namespace ants ants-2.2.0/Examples/ImageMathHelper3D.cxx000066400000000000000000000007471311104306400201460ustar00rootroot00000000000000#include "ImageMath_Templates.hxx" namespace ants { int ImageMathHelper3D(int argc, char **argv) { int returnval = ImageMathHelperAll<3>(argc,argv); if(returnval == EXIT_FAILURE) { returnval = ImageMathHelper3DOnly<3>(argc,argv); } if(returnval == EXIT_FAILURE) { returnval = ImageMathHelper2DOr3D<3>(argc,argv); } if(returnval == EXIT_FAILURE) { returnval = ImageMathHelper3DOr4D<3>(argc,argv); } return returnval; } } // namespace ants ants-2.2.0/Examples/ImageMathHelper4D.cxx000066400000000000000000000006071311104306400201420ustar00rootroot00000000000000#include "ImageMath_Templates.hxx" namespace ants { int ImageMathHelper4D(int argc, char **argv) { int returnval = ImageMathHelperAll<4>(argc,argv); if(returnval == EXIT_FAILURE) { returnval = ImageMathHelper4DOnly<4>(argc,argv); } if(returnval == EXIT_FAILURE) { returnval = ImageMathHelper3DOr4D<4>(argc,argv); } return returnval; } } // namespace ants ants-2.2.0/Examples/ImageMath_Templates.hxx000066400000000000000000016411131311104306400206410ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "antsAllocImage.h" #include "antsSCCANObject.h" #include "itkAlternatingValueDifferenceImageFilter.h" #include "itkAlternatingValueSimpleSubtractionImageFilter.h" #include "itkANTSNeighborhoodCorrelationImageToImageMetricv4.h" #include "itkArray.h" #include "itkAverageOverDimensionImageFilter.h" #include "itkGradientImageFilter.h" #include "itkBlackTopHatImageFilter.h" #include "itkBSplineControlPointImageFilter.h" #include "itkBilateralImageFilter.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkCSVNumericObjectFileWriter.h" #include "itkCannyEdgeDetectionImageFilter.h" #include "itkCastImageFilter.h" #include "itkCompositeValleyFunction.h" #include "itkConnectedComponentImageFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkConvolutionImageFilter.h" #include "itkCorrelationImageToImageMetricv4.h" #include "itkCyclicShiftImageFilter.h" #include "itkDiffusionTensor3D.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkDistanceToCentroidMembershipFunction.h" #include "itkDanielssonDistanceMapImageFilter.h" #include "itkSignedMaurerDistanceMapImageFilter.h" #include "itkDemonsImageToImageMetricv4.h" #include "itkExpImageFilter.h" #include "itkExtractImageFilter.h" #include "itkFastMarchingExtensionImageFilterBase.h" #include "itkFastMarchingExtensionImageFilter.h" #include "itkGaussianImageSource.h" #include "itkGradientAnisotropicDiffusionImageFilter.h" #include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" #include "itkHessianRecursiveGaussianImageFilter.h" #include "itkHistogram.h" #include "itkHistogramMatchingImageFilter.h" #include "itkImage.h" #include "itkImageDuplicator.h" #include "itkImageFileWriter.h" #include "itkImageGaussianModelEstimator.h" #include "itkImageKmeansModelEstimator.h" #include "itkImageMaskSpatialObject.h" #include "itkImageMomentsCalculator.h" #include "itkImageRandomConstIteratorWithIndex.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkLabelGeometryImageFilter.h" #include "itkLabelOverlapMeasuresImageFilter.h" #include "itkLabelPerimeterEstimationCalculator.h" #include "itkKdTree.h" #include "itkKdTreeBasedKmeansEstimator.h" #include "itkLabelContourImageFilter.h" #include "itkLabelStatisticsImageFilter.h" #include "itkLabeledPointSetFileReader.h" #include "itkLabeledPointSetFileWriter.h" #include "itkLaplacianRecursiveGaussianImageFilter.h" #include "itkLaplacianSharpeningImageFilter.h" #include "itkListSample.h" #include "itkMattesMutualInformationImageToImageMetricv4.h" #include "itkMaximumProjectionImageFilter.h" #include "itkMinimumProjectionImageFilter.h" #include "itkMRFImageFilter.h" #include "itkMRIBiasFieldCorrectionFilter.h" #include "itkMaskImageFilter.h" #include "itkMaximumImageFilter.h" #include "itkMedianImageFilter.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkMultiplyImageFilter.h" #include "itkMultivariateLegendrePolynomial.h" #include "itkNeighborhood.h" #include "itkNeighborhoodAlgorithm.h" #include "itkNeighborhoodIterator.h" #include "itkNeighborhoodFirstOrderStatisticsImageFilter.h" #include "itkNormalVariateGenerator.h" #include "itkOtsuThresholdImageFilter.h" #include "itkPseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter.h" #include "itkPulsedArterialSpinLabeledCerebralBloodFlowImageFilter.h" #include "itkRGBPixel.h" #include "itkRelabelComponentImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include "itkSampleToHistogramFilter.h" #include "itkScalarImageKmeansImageFilter.h" #include "itkShrinkImageFilter.h" #include "itkSigmoidImageFilter.h" #include "itkSize.h" #include "itkSliceTimingCorrectionImageFilter.h" #include "itkSphereSpatialFunction.h" #include "itkSplitAlternatingTimeSeriesImageFilter.h" #include "itkSTAPLEImageFilter.h" #include "itkSubtractImageFilter.h" #include "itkSumProjectionImageFilter.h" #include "itkTDistribution.h" #include "itkTileImageFilter.h" #include "itkTimeProbe.h" #include "itkTranslationTransform.h" #include "itkVariableSizeMatrix.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkWeightedCentroidKdTreeGenerator.h" #include "itkWhiteTopHatImageFilter.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "vnl/vnl_matrix_fixed.h" #include "itkTransformFactory.h" #include "itkSurfaceImageCurvature.h" #include "itkMultiScaleLaplacianBlobDetectorImageFilter.h" #include #include #include // Here I'm using a map but you could choose even other containers #include #include #include "ReadWriteData.h" #include "TensorFunctions.h" #include "antsMatrixUtilities.h" #include "antsFastMarchingImageFilter.h" #include "itkFastMarchingImageFilterBase.h" #include "itkFastMarchingThresholdStoppingCriterion.h" namespace ants { extern std::string ANTSGetFilePrefix(const char *str); extern std::map RoiList(std::string file); extern std::string ANTSOptionValue(const char *str); extern std::string ANTSOptionName(const char *str); template bool from_string(T& t, const std::string& s, std::ios_base & (*f)(std::ios_base &) ) { std::istringstream iss(s); iss >> f >> t; // Check to see that there is nothing left over if( !iss.eof() ) { return false; } return true; } template std::string ants_to_string(T t) { std::stringstream istream; istream << t; return istream.str(); } template int FrobeniusNormOfMatrixDifference(int argc, char *argv[]) { if( argc < 6 ) { std::cout << " FrobeniusNormOfMatrixDifference: too few options " << std::endl; std::cout << " ImageMath 3 out FrobeniusNormOfMatrixDifference aff1.mat aff2.mat " << std::endl; return 1; } int argct = 4; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = std::string(argv[argct]); argct++; typedef itk::MatrixOffsetTransformBase AffineTransformType; itk::TransformFactory::RegisterTransform(); typedef itk::TransformFileReader TransformReaderType; typename TransformReaderType::Pointer transformReader1 = TransformReaderType::New(); transformReader1->SetFileName( fn1.c_str() ); try { transformReader1->Update(); } catch( itk::ExceptionObject & /* excp */ ) { std::cout << "no transformation1 that can be read" << fn1 << std::endl; return 0; } typename TransformReaderType::Pointer transformReader2 = TransformReaderType::New(); transformReader2->SetFileName( fn2.c_str() ); try { transformReader2->Update(); } catch( itk::ExceptionObject & /* excp */ ) { std::cout << "no transformation2 that can be read" << fn2 << std::endl; return 0; } typename AffineTransformType::Pointer aff1 = dynamic_cast( (transformReader1->GetTransformList() )->front().GetPointer() ); typename AffineTransformType::Pointer aff2 = dynamic_cast( (transformReader2->GetTransformList() )->front().GetPointer() ); typename AffineTransformType::MatrixType::InternalMatrixType diffmat = aff2->GetMatrix().GetVnlMatrix() - aff1->GetMatrix().GetVnlMatrix(); std::cout << diffmat.frobenius_norm() << std::endl; return 0; } template void ClosestSimplifiedHeaderMatrix(int argc, char *argv[]) { if( argc < 4 ) { std::cout << " need more args -- see usage " << std::endl; } typedef float PixelType; typedef itk::Image ImageType; typedef double RealType; typedef itk::Matrix MatrixType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage( image1, fn1.c_str() ); MatrixType A_solution = image1->GetDirection(); // vnl_svd nearestorth( image1->GetDirection().GetVnlMatrix() ); // vnl_matrix A_solution = nearestorth.V() * nearestorth.U().transpose(); MatrixType mydir; for ( unsigned int d = 0; d < ImageDimension; d++ ) for ( unsigned int dd = 0; dd < ImageDimension; dd++ ) { RealType v = A_solution(d,dd); long vlong = static_cast( vnl_math_abs( v ) + 0.5 ); if ( v < 0 ) vlong = vlong * (-1); mydir(d,dd) = static_cast( vlong ); } image1->SetDirection( mydir ); // also do the origin typename ImageType::PointType origin = image1->GetOrigin(); for ( unsigned int d = 0; d < ImageDimension; d++ ) { RealType rndflt = origin[d] * 100.0; int rndflti = static_cast( rndflt + 0.5 ); rndflt = static_cast( rndflti ) / 100.0; origin[d] = rndflt; } image1->SetOrigin( origin ); WriteImage( image1, outname.c_str() ); return; } template void ReflectionMatrix(int argc, char *argv[]) { if( argc < 4 ) { std::cout << " need more args -- see usage " << std::endl; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::AffineTransform AffineTransformType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; unsigned int axis = atoi(argv[argct]); argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage( image1, fn1.c_str() ); // compute center of mass typedef typename itk::ImageMomentsCalculator ImageCalculatorType; typename ImageCalculatorType::Pointer calculator1 = ImageCalculatorType::New(); calculator1->SetImage( image1 ); typename ImageCalculatorType::VectorType fixed_center; fixed_center.Fill(0); try { calculator1->Compute(); fixed_center = calculator1->GetCenterOfGravity(); } catch( ... ) { // std::cout << " zero image2 error "; fixed_center.Fill(0); } // std::cout << " CenterOfMass " << fixed_center << std::endl; typename AffineTransformType::Pointer aff = AffineTransformType::New(); aff->SetIdentity(); typename AffineTransformType::ParametersType myoff = aff->GetFixedParameters(); for( unsigned int i = 0; i < ImageDimension; i++ ) { myoff[i] = fixed_center[i]; } typename AffineTransformType::MatrixType mymat = aff->GetMatrix(); if( axis < ImageDimension ) { mymat[axis][axis] = ( -1.0 ); } aff->SetFixedParameters( myoff ); aff->SetMatrix( mymat ); typedef itk::TransformFileWriter TransformWriterType; typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetInput( aff ); transformWriter->SetFileName( outname.c_str() ); transformWriter->Update(); return; } template void MakeAffineTransform(int argc, char *argv[]) { if( argc < 3 ) { std::cout << " need more args -- see usage " << std::endl; } typedef itk::AffineTransform AffineTransformType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; typename AffineTransformType::Pointer aff = AffineTransformType::New(); aff->SetIdentity(); typedef itk::TransformFileWriter TransformWriterType; typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetInput( aff ); transformWriter->SetFileName( outname.c_str() ); transformWriter->Update(); return; } template int GetLargestComponent(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; unsigned long smallest = 50; if( argc > argct ) { smallest = atoi(argv[argct]); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); // compute the voxel volume typename ImageType::SpacingType spacing = image1->GetSpacing(); float volumeelement = 1.0; for( unsigned int i = 0; i < spacing.Size(); i++ ) { volumeelement *= spacing[i]; } typedef itk::Image labelimagetype; typedef ImageType InternalImageType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::ConnectedComponentImageFilter FilterType; typedef itk::RelabelComponentImageFilter RelabelType; typename ThresholdFilterType::Pointer threshold = ThresholdFilterType::New(); typename FilterType::Pointer filter = FilterType::New(); typename RelabelType::Pointer relabel = RelabelType::New(); // InternalPixelType threshold_low, threshold_hi; threshold->SetInput(image1); threshold->SetInsideValue(1); threshold->SetOutsideValue(0); threshold->SetLowerThreshold(0.25); threshold->SetUpperThreshold(1.e9); threshold->Update(); filter->SetInput(threshold->GetOutput() ); filter->SetFullyConnected( 0 ); filter->Update(); relabel->SetInput( filter->GetOutput() ); relabel->SetMinimumObjectSize( smallest ); // relabel->SetUseHistograms(true); try { relabel->Update(); } catch( itk::ExceptionObject & /* excep */ ) { // std::cout << "Relabel: exception caught !" << std::endl; // std::cout << excep << std::endl; } // WriteImage(relabel->GetOutput(),outname.c_str()); // return 0; typename ImageType::Pointer Clusters = MakeNewImage(relabel->GetOutput(), 0); // typename ImageType::Pointer Clusters=relabel->GetOutput(); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); float maximum = relabel->GetNumberOfObjects(); float maxtstat = 0; std::vector histogram( (int)maximum + 1); std::vector clustersum( (int)maximum + 1); for( int i = 0; i <= maximum; i++ ) { histogram[i] = 0; clustersum[i] = 0; } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 0 ) { float vox = image1->GetPixel(vfIter.GetIndex() ); histogram[(unsigned int)vfIter.Get()] = histogram[(unsigned int)vfIter.Get()] + 1; clustersum[(unsigned int)vfIter.Get()] += vox; if( vox > maxtstat ) { maxtstat = vox; } } } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 0 ) { Clusters->SetPixel( vfIter.GetIndex(), histogram[(unsigned int)vfIter.Get()] ); // if ( Clusters->GetPixel( vfIter.GetIndex() ) > maximgval ) // maximgval=Clusters->GetPixel( vfIter.GetIndex()); } else { Clusters->SetPixel(vfIter.GetIndex(), 0); } } float maximgval = 0; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( Clusters->GetPixel( vfIter.GetIndex() ) > maximgval ) { maximgval = Clusters->GetPixel( vfIter.GetIndex() ); } } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( Clusters->GetPixel( vfIter.GetIndex() ) >= maximgval ) { image1->SetPixel( vfIter.GetIndex(), 1); } else { image1->SetPixel( vfIter.GetIndex(), 0); } } if( outname.length() > 3 ) { WriteImage(image1, outname.c_str() ); } return 0; } template int ClusterThresholdVariate(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef unsigned long ULPixelType; typedef itk::Image labelimagetype; typedef itk::ImageRegionIteratorWithIndex fIterator; typedef itk::ImageRegionIteratorWithIndex labIterator; typedef itk::ConnectedComponentImageFilter FilterType; typedef itk::RelabelComponentImageFilter RelabelType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string maskfn = std::string(argv[argct]); argct++; unsigned int minclustersize = 50; if( argc > argct ) { minclustersize = atoi( argv[argct] ); } typename ImageType::Pointer image = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); typename ImageType::Pointer mask = ITK_NULLPTR; ReadImage(mask, maskfn.c_str() ); typename FilterType::Pointer filter = FilterType::New(); typename RelabelType::Pointer relabel = RelabelType::New(); filter->SetInput( image ); filter->SetFullyConnected( 0 ); relabel->SetInput( filter->GetOutput() ); relabel->SetMinimumObjectSize( 1 ); try { relabel->Update(); } catch( itk::ExceptionObject & /* excep */ ) { // std::cout << "Relabel: exception caught !" << std::endl; // std::cout << excep << std::endl; } labIterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); float maximum = relabel->GetNumberOfObjects(); std::vector histogram( (int)maximum + 1); for( int i = 0; i <= maximum; i++ ) { histogram[i] = 0; } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { float vox = vfIter.Get(); if( vox > 0 ) { if( vox > 0 ) { histogram[(unsigned long)vox] = histogram[(unsigned long)vox] + 1; } } } // get the largest component's size unsigned long largest_component_size = 0; for( int i = 0; i <= maximum; i++ ) { if( largest_component_size < histogram[i] ) { largest_component_size = histogram[i]; } } if( largest_component_size < minclustersize ) { minclustersize = largest_component_size - 1; } // now create the output vector // iterate through the image and set the voxels where countinlabel[(unsigned // long)(labelimage->GetPixel(vfIter.GetIndex()) - min)] // is < MinClusterSize unsigned long vecind = 0; fIterator mIter( mask, mask->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() > 0 ) { float vox = mask->GetPixel(vfIter.GetIndex() ); if( vox >= 0 ) { const unsigned long clustersize = histogram[(unsigned long)(relabel->GetOutput()->GetPixel(mIter.GetIndex() ) )]; if( clustersize <= minclustersize ) { image->SetPixel( mIter.GetIndex(), 0 ); } vecind++; } } } if( outname.length() > 3 ) { WriteImage( image, outname.c_str() ); } return EXIT_SUCCESS; } template int ExtractSlice(int argc, char *argv[]) { if( argc <= 2 ) { std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; unsigned int slice = atoi(argv[argct]); argct++; // std::cout << " Extract slice " << slice << " from dimension" << ImageDimension << std::endl; typename ImageType::Pointer image1 = ITK_NULLPTR; typename OutImageType::Pointer outimage = ITK_NULLPTR; typedef itk::ExtractImageFilter ExtractFilterType; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } unsigned int timedims = image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; if( slice >= timedims ) { // std::cout << " max slice number is " << timedims << std::endl; return 1; } typename ImageType::RegionType extractRegion = image1->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); extractRegion.SetIndex(ImageDimension - 1, slice ); typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( image1 ); // extractFilter->SetDirectionCollapseToIdentity(); extractFilter->SetDirectionCollapseToSubmatrix(); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); outimage = extractFilter->GetOutput(); /* typename ImageType::SpacingType qspc=warpthisimage->GetSpacing(); typename ImageType::PointType qorg=warpthisimage->GetOrigin(); typename ImageType::DirectionType qdir=warpthisimage->GetDirection(); qdir.Fill(0); for (unsigned int qq=0; qqGetDirection()[qq][pp]; } qspc[qq]=img_mov->GetSpacing()[qq]; qorg[qq]=img_mov->GetOrigin()[qq]; } warpthisimage->SetSpacing(qspc); warpthisimage->SetOrigin(qorg); warpthisimage->SetDirection(qdir); */ if( outname.length() > 3 ) { WriteImage(outimage, outname.c_str() ); } else { return 1; } return 0; } template int Finite(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float replaceValue = 0.0; if( argc > 4 ) { replaceValue = atof( argv[4] ); } typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter2( image1, image1->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { float val = vfIter2.Get(); if( val != val ) { vfIter2.Set( replaceValue ); } } WriteImage(image1, outname.c_str() ); return 0; } template int ThresholdAtMean(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float percentofmean = 1.0; if( argc > argct ) { percentofmean = atof(argv[argct]); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typedef itk::ImageRegionIteratorWithIndex Iterator; double mean = 0, max = -1.e9, min = 1.e9; unsigned long ct = 0; Iterator vfIter2( image1, image1->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { double val = vfIter2.Get(); mean += val; if( val > max ) { max = val; } else if( val < min ) { min = val; } ct++; } if( ct > 0 ) { mean /= (float)ct; } typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer threshold = ThresholdFilterType::New(); threshold->SetInput(image1); threshold->SetInsideValue(1); threshold->SetOutsideValue(0); threshold->SetLowerThreshold(mean * percentofmean); threshold->SetUpperThreshold(max); threshold->Update(); WriteImage(threshold->GetOutput(), outname.c_str() ); return 0; } template int SetTimeSpacing(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float timespacing = 1.0; if( argc > argct ) { timespacing = atof(argv[argct]); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typename ImageType::SpacingType spacing = image1->GetSpacing(); spacing[ ImageDimension - 1 ] = timespacing; image1->SetSpacing( spacing ); WriteImage( image1 , outname.c_str() ); return 0; } template int SetTimeSpacingWarp(int argc, char *argv[]) { typedef float RealType; typedef itk::Vector VectorType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float timespacing = 1.0; if( argc > argct ) { timespacing = atof(argv[argct]); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typename ImageType::SpacingType spacing = image1->GetSpacing(); spacing[ ImageDimension - 1 ] = timespacing; image1->SetSpacing( spacing ); WriteImage( image1 , outname.c_str() ); return 0; } template int FlattenImage(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float percentofmax = 1.0; if( argc > argct ) { percentofmax = atof(argv[argct]); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typedef itk::ImageRegionIteratorWithIndex Iterator; double max = -1.e9, min = 1.e9; Iterator vfIter2( image1, image1->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { double val = vfIter2.Get(); if( val > max ) { max = val; } else if( val < min ) { min = val; } } typename ImageType::Pointer out = MakeNewImage(image1, 0); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { double val = vfIter2.Get(); if( val > max * percentofmax ) { val = (max * percentofmax); } out->SetPixel(vfIter2.GetIndex(), val); } // std::cout << " Flattening to : " << percentofmax << std::endl; WriteImage(out, outname.c_str() ); return 0; } template int TruncateImageIntensity( unsigned int argc, char *argv[] ) { typedef int PixelType; typedef float RealType; // usage ImageMath 3 out.nii.gz TrunateImageIntensity InImage.nii.gz FractionLo(e.g.0.025) FractionHi(e.g.0.975) // Bins Mask if( argc < 4 ) { std::cout << " need more args -- see usage " << std::endl << " ImageMath 3 outimage.nii.gz TruncateImageIntensity inputImage {lowerQuantile=0.025} {upperQuantile=0.975} {numberOfBins=65} {binary-maskImage} {copy-image-space-from-input-to-mask}" << std::endl; throw std::exception(); } unsigned int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float lo = 0.025; if( argc > argct ) { lo = atof(argv[argct]); } argct++; float hi = 0.975; if( argc > argct ) { hi = atof(argv[argct]); } else { hi = 1.0 - lo; } argct++; unsigned int numberOfBins = 64; if( argc > argct ) { numberOfBins = atoi(argv[argct]); } argct++; // std::cout << " bin " << numberOfBins << " lo " << lo << " Hi " << hi << std::endl; typedef itk::Image ImageType; typedef itk::Image RealImageType; typename RealImageType::Pointer image; ReadImage( image, fn1.c_str() ); typename ImageType::Pointer mask; if( argc > argct ) { try { ReadImage(mask, argv[argct] ); } catch( ... ) { // std::cout << " can't read mask " << std::endl; mask = ITK_NULLPTR; } } argct++; bool copyInputSpaceToMask = false; if( argc > argct ) copyInputSpaceToMask = true; if( mask.IsNull() ) { mask = AllocImage( image, itk::NumericTraits::OneValue()); } if ( copyInputSpaceToMask ) { mask->CopyInformation( image ); mask->SetOrigin( image->GetOrigin() ); mask->SetSpacing( image->GetSpacing() ); mask->SetDirection( image->GetDirection() ); } // std::cout << " iterate " << std::endl; itk::ImageRegionIterator ItI( image, image->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItM( mask, mask->GetLargestPossibleRegion() ); RealType maxValue = itk::NumericTraits::NonpositiveMin(); RealType minValue = itk::NumericTraits::max(); ItM.GoToBegin(); for( ItI.GoToBegin(); !ItI.IsAtEnd(); ++ItI ) { // std::cout << " ind " << ItI.GetIndex() << std::endl; if( ItI.Get() > 0 && ItM.Get() >= 0.5 ) { if( ItI.Get() < minValue ) { minValue = ItI.Get(); } else if( ItI.Get() > maxValue ) { maxValue = ItI.Get(); } ItM.Set( itk::NumericTraits::OneValue() ); } else { ItM.Set( 0 ); } if( vnl_math_isnan( ItI.Get() ) || vnl_math_isinf( ItI.Get() ) ) { ItM.Set( 0 ); } ++ItM; } // std::cout << " label " << std::endl; typedef itk::LabelStatisticsImageFilter HistogramGeneratorType; typename HistogramGeneratorType::Pointer stats = HistogramGeneratorType::New(); stats->SetInput( image ); stats->SetLabelInput( mask ); stats->SetUseHistograms( true ); stats->SetHistogramParameters( numberOfBins, minValue, maxValue ); stats->Update(); // std::cout << " labeld " << std::endl; typedef typename HistogramGeneratorType::HistogramType HistogramType; const HistogramType *histogram = stats->GetHistogram( 1 ); double lowerQuantile = histogram->Quantile( 0, lo ); double upperQuantile = histogram->Quantile( 0, hi ); // std::cout << "Lower quantile: " << lowerQuantile << std::endl; // std::cout << "Upper quantile: " << upperQuantile << std::endl; for( ItI.GoToBegin(); !ItI.IsAtEnd(); ++ItI ) { if( ItI.Get() < lowerQuantile ) { ItI.Set( lowerQuantile ); } if( ItI.Get() > upperQuantile ) { ItI.Set( upperQuantile ); } } if( outname.length() > 3 ) { WriteImage( image, argv[2] ); } return EXIT_SUCCESS; } template int TileImages(unsigned int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; unsigned int nx = atoi(argv[argct]); argct++; unsigned int numberofimages = 0; typename ImageType::Pointer averageimage = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::SizeType size; size.Fill( 0 ); typename ImageType::SizeType maxSize; maxSize.Fill( 0 ); double meanval = 1; unsigned int bigimage = 0; for( unsigned int j = argct; j < argc; j++ ) { numberofimages++; // Get the image dimension std::string fn = std::string(argv[j]); typename itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); for( unsigned int i = 0; i < ImageType::ImageDimension; i++ ) { itk::SizeValueType currentDimensionSize = imageIO->GetDimensions( i ); size[i] = currentDimensionSize; if( currentDimensionSize > maxSize[i] ) { maxSize[i] = currentDimensionSize; bigimage = j; } } } std::cout << " bigimage " << bigimage << " size " << maxSize << std::endl; ReadImage(image2, argv[bigimage]); // std::cout << " largest image " << size << std::endl; /** declare the tiled image */ unsigned int xsize = maxSize[0]; unsigned int ysize = maxSize[1]; typename ImageType::SizeType tilesize; unsigned int ny = (unsigned int)( (float)numberofimages / (float)nx + 0.5); if( nx * ny < numberofimages ) { ny++; } // std::cout << " nx " << nx << " ny " << ny << std::endl; tilesize[0] = xsize * nx; tilesize[1] = ysize * ny; typename ImageType::RegionType region; region.SetSize( tilesize ); bool normalizei = false; typename ImageType::Pointer tiledimage = AllocImage(region, image2->GetSpacing(), image2->GetOrigin(), image2->GetDirection() ); unsigned int imagecount = 0, imagexct = 0, imageyct = 0; for( unsigned int j = argct; j < argc; j++ ) { // Get the image dimension std::string fn = std::string(argv[j]); ReadImage(image2, fn.c_str() ); unsigned long ct = 0; if( normalizei ) { meanval = 0.0; Iterator vfIter2( image2, image2->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { meanval += vfIter2.Get(); ct++; } if( ct > 0 ) { meanval /= (float)ct; } if( meanval <= 0 ) { meanval = 1.0; } } imagexct = imagecount % nx; imageyct = imagecount / nx; // std::cout << "doing " << fn << " " << imagecount << " x " << imagexct << " y " << imageyct << std::endl; imagecount++; Iterator vfIter( image2, image2->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { typename ImageType::IndexType locind = vfIter.GetIndex(); typename ImageType::IndexType globind; globind[0] = size[0] * imagexct + locind[0]; globind[1] = size[1] * imageyct + locind[1]; double val = vfIter.Get() / meanval; tiledimage->SetPixel(globind, val ); } } WriteImage(tiledimage, outname.c_str() ); return 0; typedef itk::Image ByteImageType; typedef itk::RescaleIntensityImageFilter RescaleFilterType; typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( 0 ); rescaler->SetOutputMaximum( 255 ); rescaler->SetInput( tiledimage ); // std::cout << " writing output "; WriteImage( rescaler->GetOutput(), outname.c_str() ); return 0; } template int ConvertLandmarkFile(unsigned int argc, char *argv[]) { unsigned int argct = 2; if( argc < 5 ) { // std::cout << " need more args -- see usage " << std::endl; throw std::exception(); } const std::string outname = std::string(argv[argct]); argct += 2; std::string infn = std::string(argv[argct]); argct++; float pointp = 1; typedef itk::PointSet PointSetType; typedef itk::LabeledPointSetFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( infn.c_str() ); reader->SetRandomPercentage( 1 ); if( pointp > 0 && pointp < 1 ) { reader->SetRandomPercentage( pointp ); } reader->Update(); // std::cout << "Number of labels: " << reader->GetNumberOfLabels() << std::endl; // std::cout << "Labels: "; for( unsigned int i = 0; i < reader->GetNumberOfLabels(); i++ ) { // std::cout << reader->GetLabelSet()->operator[](i) << " "; } // std::cout << std::endl; typedef itk::LabeledPointSetFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( outname.c_str() ); writer->SetInput( reader->GetOutput() ); writer->Update(); return 0; } template int TriPlanarView(unsigned int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image MatrixImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; unsigned int argct = 2; if( argc < 5 ) { // std::cout << " need more args -- see usage " << std::endl; throw std::exception(); } const std::string outname = std::string(argv[argct]); argct += 2; std::string maskfn = std::string(argv[argct]); argct++; // std::cout << " file name " << maskfn << std::endl; typename ImageType::Pointer mask = ITK_NULLPTR; ReadImage(mask, maskfn.c_str() ); // WriteImage(mask,"temp.nii"); float clamppercent1 = 0.1; if( argc > argct ) { clamppercent1 = atof(argv[argct]); } argct++; if( clamppercent1 > 1 ) { clamppercent1 = 1; } float clamppercent2 = 0.1; if( argc > argct ) { clamppercent2 = atof(argv[argct]); } argct++; if( clamppercent2 > 1 ) { clamppercent2 = 1; } typename ImageType::SizeType size = mask->GetLargestPossibleRegion().GetSize(); unsigned int xslice = size[0] / 2; if( argc > argct ) { xslice = atoi(argv[argct]); } argct++; unsigned int yslice = size[1] / 2; if( argc > argct ) { yslice = atoi(argv[argct]); } argct++; unsigned int zslice = size[2] / 2; if( argc > argct ) { zslice = atoi(argv[argct]); } argct++; typedef itk::RescaleIntensityImageFilter RescaleFilterType; typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( 0 ); rescaler->SetOutputMaximum( 255 ); rescaler->SetInput( mask ); rescaler->Update(); mask = rescaler->GetOutput(); // typedef itk::IntensityWindowingImageFilter wFilterType; // typename wFilterType::Pointer wer = wFilterType::New(); // wer->SetInput(mask); /** declare the tiled image */ unsigned long xsize = size[0]; unsigned long ysize = size[1]; unsigned long zsize = size[2]; typename MatrixImageType::SizeType ztilesize; ztilesize[0] = xsize; ztilesize[1] = ysize; typename MatrixImageType::SizeType ytilesize; ytilesize[0] = xsize; ytilesize[1] = zsize; typename MatrixImageType::SizeType xtilesize; xtilesize[0] = ysize; xtilesize[1] = zsize; typename MatrixImageType::SizeType tilesize; tilesize[0] = xtilesize[0] + ytilesize[0] + ztilesize[0]; tilesize[1] = xtilesize[1]; if( ytilesize[1] > tilesize[1] ) { tilesize[1] = ytilesize[1]; } if( ztilesize[1] > tilesize[1] ) { tilesize[1] = ztilesize[1]; } // std::cout << " allocate matrix " << tilesize << std::endl; typename MatrixImageType::RegionType region; region.SetSize( tilesize ); typename MatrixImageType::Pointer matimage = AllocImage(region); unsigned int lowgetridof = (unsigned int) (clamppercent1 * 256); unsigned int higetridof = (unsigned int) (256 - clamppercent2 * 256); // std::cout << " get rid of " << getridof << std::endl; matimage->FillBuffer(lowgetridof); // now loop over each slice and put the pixels in the right place in matimage typename MatrixImageType::IndexType index2d; typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter2( mask, mask->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { double val1 = vfIter2.Get(); if( val1 > higetridof ) { vfIter2.Set(higetridof ); } if( val1 < lowgetridof ) { vfIter2.Set(lowgetridof ); } // first do z-slice if( vfIter2.GetIndex()[2] == (long) zslice ) { double val = vfIter2.Get(); typename ImageType::IndexType index = vfIter2.GetIndex(); index2d[0] = index[0] + xtilesize[0] + ytilesize[0]; index2d[1] = index[1]; index2d[1] = tilesize[1] - index2d[1] - 1; matimage->SetPixel(index2d, val); } if( vfIter2.GetIndex()[1] == (long)yslice ) { double val = vfIter2.Get(); typename ImageType::IndexType index = vfIter2.GetIndex(); index2d[0] = index[0] + xtilesize[0]; index2d[1] = index[2]; index2d[1] = tilesize[1] - index2d[1] - 1; matimage->SetPixel(index2d, val); } if( vfIter2.GetIndex()[0] == (long)xslice ) { double val = vfIter2.Get(); typename ImageType::IndexType index = vfIter2.GetIndex(); index2d[0] = index[1]; index2d[1] = index[2]; index2d[1] = tilesize[1] - index2d[1] - 1; matimage->SetPixel(index2d, val); } } typedef itk::Image ByteImageType; typedef itk::RescaleIntensityImageFilter RescaleFilterType2; typename RescaleFilterType2::Pointer rescaler2 = RescaleFilterType2::New(); rescaler2->SetOutputMinimum( 0 ); rescaler2->SetOutputMaximum( 255 ); rescaler2->SetInput( matimage ); rescaler2->Update(); // std::cout << " writing output "; WriteImage( rescaler2->GetOutput(), outname.c_str() ); return 0; } template int ConvertVectorToImage(unsigned int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image MatrixImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::ImageRegionIteratorWithIndex vIterator; int argct = 2; if( argc < 5 ) { // std::cout << " need more args -- see usage " << std::endl; throw std::exception(); } const std::string outname = std::string(argv[argct]); argct += 2; std::string maskfn = std::string(argv[argct]); argct++; std::string vecfn = std::string(argv[argct]); argct++; typename ImageType::Pointer mask = ITK_NULLPTR; ReadImage(mask, maskfn.c_str() ); typename MatrixImageType::Pointer vecimg = ITK_NULLPTR; ReadImage(vecimg, vecfn.c_str() ); unsigned long voxct = 0, mct = 0; Iterator mIter( mask, mask->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { mct++; } } vIterator vIter(vecimg, vecimg->GetLargestPossibleRegion() ); for( vIter.GoToBegin(); !vIter.IsAtEnd(); ++vIter ) { voxct++; } // std::cout << " vct " << voxct << " mct " << mct << std::endl; typename ImageType::Pointer outimage = ITK_NULLPTR; ReadImage(outimage, maskfn.c_str() ); outimage->FillBuffer(0); vIter.GoToBegin(); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { outimage->SetPixel(mIter.GetIndex(), vIter.Get() ); ++vIter; } } WriteImage(outimage, outname.c_str() ); return 0; } template int CorruptImage(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float noiselevel = 1.0; if( argc > argct ) { noiselevel = atof(argv[argct]); argct++; } float smoothlevel = 1.0; if( argc > argct ) { smoothlevel = atof(argv[argct]); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator iter( image1, image1->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { double r = ( (double)rand() / ( (double)(RAND_MAX) + (double)(1) ) ) - 0.5; iter.Set(iter.Get() + r * noiselevel); } if( smoothlevel > 0 ) { { typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(smoothlevel); filter->SetUseImageSpacingOff(); filter->SetMaximumError(.01f); filter->SetInput(image1); filter->Update(); image1 = filter->GetOutput(); } } WriteImage(image1, outname.c_str() ); return 0; } template int Where(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 4; std::string fn1 = std::string(argv[argct]); argct++; float value = atof(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); argct++; } float tol = 0.0; if( argc > argct ) { tol = atof(argv[argct]); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } if( fn2.length() > 3 ) { ReadImage(image2, fn2.c_str() ); } unsigned long ct = 0; typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator iter( image1, image1->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { if( !image2 ) { if( fabs(iter.Get() - value) < tol ) { // std::cout << iter.GetIndex() << std::endl; ct++; } } else if( image2->GetPixel(iter.GetIndex() ) > 0 && fabs(iter.Get() - value) < tol ) { // std::cout << iter.GetIndex() << std::endl; ct++; } } // std::cout << ct << " voxels have the value " << value << std::endl; return 0; } template int SetOrGetPixel(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float value = atof(argv[argct]); std::string Get = std::string(argv[argct]); argct++; float indx = atof(argv[argct]); argct++; float indy = 0; if( ImageDimension >= 2 ) { indy = atof(argv[argct]); argct++; } float indz = 0; if( ImageDimension >= 3 ) { indz = atof(argv[argct]); argct++; } bool usephyspace = false; if( argc > argct ) { usephyspace = atoi(argv[argct]); argct++; } bool get = false; if( strcmp(Get.c_str(), "Get") == 0 ) { get = true; } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); ReadImage(image2, fn1.c_str() ); } if( !image1 ) { // std::cout << " no image ! " << std::endl; throw std::exception(); } typename ImageType::IndexType index; index.Fill(0); if( usephyspace == false ) { index[0] = (long int)indx; index[1] = (long int)indy; if( ImageDimension == 3 ) { index[2] = (long int)indz; } } else { typename ImageType::PointType porig; porig[0] = indx; porig[1] = indy; if( ImageDimension == 3 ) { porig[2] = indz; } image1->TransformPhysicalPointToIndex(porig, index); } // std::cout << " use phy " << usephyspace << " " << indx << " " << indy << " " << indz << std::endl; // std::cout << " Ind " << index << std::endl; bool isinside = true; for( unsigned int i = 0; i < ImageDimension; i++ ) { float shifted = index[i]; if( shifted < 0 || shifted > image1->GetLargestPossibleRegion().GetSize()[i] - 1 ) { isinside = false; } } if( isinside == true ) { if( get ) { // std::cout << " GetValue at " << index << " is " << image1->GetPixel(index) << std::endl; // std::cout << image1->GetPixel(index) << std::endl; } else { // std::cout << " SetValue at " << index << " value " << value << " replaces " << image1->GetPixel(index) // << std::endl; image2->SetPixel(index, value); WriteImage(image2, outname.c_str() ); } } else { // std::cout << "NA" << std::endl; } return 0; } template int HistogramMatching(int argc, char * argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::HistogramMatchingImageFilter MatchingFilterType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = std::string(argv[argct]); argct++; long bins = 255; if( argc > argct ) { bins = atoi( argv[argct] ); } argct++; long points = 64; if( argc > argct ) { points = atoi( argv[argct] ); } argct++; bool useThresholdAtMeanIntensity = false; if( argc > argct ) { useThresholdAtMeanIntensity = static_cast( atoi( argv[argct] ) ); } argct++; typename ImageType::Pointer source; ReadImage( source, fn1.c_str() ); typename ImageType::Pointer reference; ReadImage( reference, fn2.c_str() ); typename MatchingFilterType::Pointer match = MatchingFilterType::New(); match->SetSourceImage( source ); match->SetReferenceImage( reference ); match->SetNumberOfHistogramLevels( bins ); match->SetThresholdAtMeanIntensity( useThresholdAtMeanIntensity ); match->SetNumberOfMatchPoints( points ); match->Update(); WriteImage( match->GetOutput(), outname.c_str() ); return 0; } template int RescaleImage( int argc, char * argv[] ) { if( argc < 7 ) { // std::cout << " need more args -- see usage " << std::endl; throw std::exception(); } typedef float PixelType; typedef itk::Image ImageType; // Usage: ImageMath 3 output.nii.gz RescaleImage input.nii.gz min max const std::string outputName = std::string( argv[2] ); const std::string inputName = std::string( argv[4] ); const PixelType min = static_cast( atof( argv[5] ) ); const PixelType max = static_cast( atof( argv[6] ) ); typename ImageType::Pointer input; ReadImage( input, inputName.c_str() ); typedef itk::RescaleIntensityImageFilter RescaleFilterType; typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( min ); rescaler->SetOutputMaximum( max ); rescaler->SetInput( input ); rescaler->Update(); WriteImage( rescaler->GetOutput(), outputName.c_str() ); return 0; } template int WindowImage( int argc, char * argv[] ) { if( argc < 9 ) { // std::cout << " need more args -- see usage " << std::endl; throw std::exception(); } typedef float PixelType; typedef itk::Image ImageType; // Usage: ImageMath 3 output.nii.gz RescaleImage input.nii.gz min max const std::string outputName = std::string( argv[2] ); const std::string inputName = std::string( argv[4] ); const PixelType inputMin = static_cast( atof( argv[5] ) ); const PixelType inputMax = static_cast( atof( argv[6] ) ); const PixelType outputMin = static_cast( atof( argv[7] ) ); const PixelType outputMax = static_cast( atof( argv[8] ) ); typename ImageType::Pointer input; ReadImage( input, inputName.c_str() ); typedef itk::IntensityWindowingImageFilter IntensityWindowingFilterType; typename IntensityWindowingFilterType::Pointer rescaler = IntensityWindowingFilterType::New(); rescaler->SetWindowMinimum( inputMin ); rescaler->SetWindowMaximum( inputMax ); rescaler->SetOutputMinimum( outputMin ); rescaler->SetOutputMaximum( outputMax ); rescaler->SetInput( input ); rescaler->Update(); WriteImage( rescaler->GetOutput(), outputName.c_str() ); return 0; } template int NeighborhoodStats( int itkNotUsed( argc ), char * argv[] ) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::VectorImage VectorImageType; typedef itk::FlatStructuringElement KernelType; typedef itk::NeighborhoodFirstOrderStatisticsImageFilter TextureFilterType; const std::string outputName = std::string( argv[2] ); const std::string inputName = std::string( argv[4] ); const unsigned int whichStat = static_cast( atoi( argv[5] ) ); const unsigned int rad = static_cast( atoi( argv[6] ) ); typename ImageType::Pointer input; ReadImage( input, inputName.c_str() ); typename KernelType::SizeType radius; radius.Fill( rad ); KernelType kernel = KernelType::Box( radius ); typename TextureFilterType::Pointer filter = TextureFilterType::New(); filter->SetKernel( kernel ); filter->SetInput( input ); filter->Update(); typedef itk::VectorIndexSelectionCastImageFilter IndexSelectionType; typename IndexSelectionType::Pointer indexSelectionFilter = IndexSelectionType::New(); indexSelectionFilter->SetInput( filter->GetOutput() ); switch( whichStat ) { case 0: { indexSelectionFilter->SetIndex( 0 ); break; } case 1: { indexSelectionFilter->SetIndex( 1 ); break; } case 2: { indexSelectionFilter->SetIndex( 2 ); break; } case 3: { indexSelectionFilter->SetIndex( 3 ); break; } case 4: { indexSelectionFilter->SetIndex( 4 ); break; } case 5: { indexSelectionFilter->SetIndex( 5 ); break; } case 6: { indexSelectionFilter->SetIndex( 6 ); break; } case 7: { indexSelectionFilter->SetIndex( 7 ); break; } default: { std::cerr << "Unrecognized option: " << whichStat << std::endl; return EXIT_FAILURE; } } indexSelectionFilter->Update(); WriteImage( indexSelectionFilter->GetOutput(), outputName.c_str() ); return 0; } template int PadImage(int /*argc */, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; const float padvalue = atof(argv[argct]); argct += 2; typename ImageType::Pointer image1 = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } typename ImageType::PointType origin2 = image1->GetOrigin(); typename ImageType::SizeType size = image1->GetLargestPossibleRegion().GetSize(); typename ImageType::SizeType newsize = image1->GetLargestPossibleRegion().GetSize(); typename ImageType::RegionType newregion; // determine new image size for( unsigned int i = 0; i < ImageDimension; i++ ) { float dimsz = (float)size[i]; newsize[i] = (unsigned int)(dimsz + padvalue * 2); } newregion.SetSize(newsize); newregion.SetIndex(image1->GetLargestPossibleRegion().GetIndex() ); typename ImageType::Pointer padimage = AllocImage(newregion, image1->GetSpacing(), origin2, image1->GetDirection(), 0); typename ImageType::IndexType index; typename ImageType::IndexType index2; if( padvalue > 0 ) { index.Fill(0); index2.Fill( (unsigned int)fabs(padvalue) ); } else { index2.Fill(0); index.Fill( (unsigned int)fabs(padvalue) ); } typename ImageType::PointType point1, pointpad; image1->TransformIndexToPhysicalPoint(index, point1); padimage->TransformIndexToPhysicalPoint(index2, pointpad); for( unsigned int i = 0; i < ImageDimension; i++ ) { origin2[i] += (point1[i] - pointpad[i]); } padimage->SetOrigin(origin2); Iterator iter( image1, image1->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { typename ImageType::IndexType oindex = iter.GetIndex(); typename ImageType::IndexType padindex = iter.GetIndex(); bool isinside = true; for( unsigned int i = 0; i < ImageDimension; i++ ) { float shifted = ( (float)oindex[i] + padvalue); if( shifted < 0 || shifted > newsize[i] - 1 ) { isinside = false; } // if (shifted < 0) shifted=0; // padindex[i]= } if( isinside ) { for( unsigned int i = 0; i < ImageDimension; i++ ) { float shifted = ( (float)oindex[i] + padvalue); padindex[i] = (unsigned int)shifted; } padimage->SetPixel(padindex, iter.Get() ); } } WriteImage(padimage, outname.c_str() ); return 0; } template int SigmoidImage(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string( argv[argct++] ); argct++; std::string inputFilename = std::string( argv[argct++] ); double alpha = 1.0; if( argc > argct ) { alpha = atof( argv[argct++] ); } double beta = 0.0; if( argc > argct ) { beta = atof( argv[argct++] ); } typename ImageType::Pointer inputImage = ITK_NULLPTR; if( inputFilename.length() > 3 ) { ReadImage( inputImage, inputFilename.c_str() ); } typedef itk::SigmoidImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( inputImage ); filter->SetAlpha( alpha ); filter->SetBeta( beta ); filter->SetOutputMinimum( 0.0 ); filter->SetOutputMaximum( 1.0 ); filter->Update(); WriteImage( filter->GetOutput(), outname.c_str() ); return 0; } template int SharpenImage(int argc, char *argv[]) { if( argc < 5 ) { // std::cout << "Error. Not enough arguments. See help menu." << std::endl; return EXIT_FAILURE; } typedef float PixelType; typedef itk::Image ImageType; const std::string outputFilename = std::string( argv[2] ); const std::string inputFilename = std::string( argv[4] ); typename ImageType::Pointer inputImage = ITK_NULLPTR; ReadImage( inputImage, inputFilename.c_str() ); typedef itk::LaplacianSharpeningImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( inputImage ); filter->Update(); WriteImage( filter->GetOutput(), outputFilename.c_str() ); return 0; } template int CenterImage2inImage1(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); } argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } image1->FillBuffer(0); typename ImageType::Pointer image2 = ITK_NULLPTR; if( fn2.length() > 3 ) { ReadImage(image2, fn2.c_str() ); } // compute center of mass in image2 in point space double iweight = 0; typename ImageType::PointType cm_point; cm_point.Fill(0); Iterator iter( image2, image2->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { typename ImageType::PointType point; image2->TransformIndexToPhysicalPoint(iter.GetIndex(), point); for( unsigned int d = 0; d < ImageDimension; d++ ) { cm_point[d] += point[d] * iter.Get(); } iweight += iter.Get(); } // center of image1 typename ImageType::IndexType image_1_center_index; image_1_center_index.Fill(0); for( unsigned int d = 0; d < ImageDimension; d++ ) { cm_point[d] /= iweight; image_1_center_index[d] = image1->GetLargestPossibleRegion().GetSize()[d] / 2; } typename ImageType::PointType image_1_center_point; image1->TransformIndexToPhysicalPoint(image_1_center_index, image_1_center_point); // now we translate the cm_point to the center of image1 typename ImageType::PointType trans; for( unsigned int d = 0; d < ImageDimension; d++ ) { trans[d] = image_1_center_point[d] - cm_point[d]; } for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { typename ImageType::PointType point; image2->TransformIndexToPhysicalPoint(iter.GetIndex(), point); for( unsigned int d = 0; d < ImageDimension; d++ ) { point[d] = point[d] + trans[d]; } typename ImageType::IndexType newindex; newindex.Fill(0); bool isinside = image1->TransformPhysicalPointToIndex(point, newindex); if( isinside ) { image1->SetPixel(newindex, iter.Get() ); } } WriteImage(image1, outname.c_str() ); return 0; } template int TimeSeriesMask( int argc, char *argv[] ) { if ( argc <= 5 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image MaskImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = std::string(argv[argct]); argct++; typename ImageType::Pointer timeseries = ImageType::New(); ReadImage( timeseries, fn1.c_str() ); typename MaskImageType::Pointer mask = MaskImageType::New(); ReadImage( mask, fn2.c_str() ); Iterator it( mask, mask->GetLargestPossibleRegion() ); while ( ! it.IsAtEnd() ) { if ( it.Value() == 0 ) { typename MaskImageType::IndexType maskIdx = it.GetIndex(); typename ImageType::IndexType timeIdx; for (unsigned int i=0; i<(ImageDimension-1); i++) { timeIdx[i] = maskIdx[i]; } for (unsigned int t=0; tGetLargestPossibleRegion().GetSize()[ImageDimension-1]; t++) { timeIdx[ImageDimension-1] = t; timeseries->SetPixel(timeIdx, 0); } } ++it; } WriteImage(timeseries, outname.c_str()); return 0; } template int TimeSeriesDisassemble(int argc, char *argv[]) { if( argc <= 4 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; typename OutImageType::Pointer outimage = ITK_NULLPTR; typedef itk::ExtractImageFilter ExtractFilterType; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } typename OutImageType::PointType outOrigin; for( unsigned int i = 0; i < (ImageDimension - 1); i++ ) { outOrigin[i] = image1->GetOrigin()[i]; } unsigned int n_sub_vols = image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; // Extract filename while allowing directory names with '.' in them // (cluster temp dirs) std::string::size_type idx = outname.find_last_of('/'); std::string dirname = outname.substr(0, idx + 1); std::string filename = outname.substr(idx + 1); std::string::size_type idx2 = filename.find_first_of('.'); std::string tempname = filename.substr(0, idx2); std::string extension = filename.substr(idx2); for( unsigned int i = 0; i < n_sub_vols; i++ ) { std::string s; std::stringstream out; out << (1000 + i); s = out.str(); std::string kname = dirname + tempname + s + extension; typename ImageType::RegionType extractRegion = image1->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); extractRegion.SetIndex(ImageDimension - 1, i ); typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( image1 ); extractFilter->SetDirectionCollapseToSubmatrix(); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); outimage = extractFilter->GetOutput(); outimage->SetOrigin( outOrigin ); WriteImage(outimage, kname.c_str() ); } return 0; } template int TimeSeriesAssemble(int argc, char *argv[]) { if( argc <= 6 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; float time = atof( argv[argct] ); argct++; float origin = atof( argv[argct] ); argct++; typename OutImageType::Pointer outimage = OutImageType::New(); typedef itk::ImageRegionIteratorWithIndex ImageIt; for( int i = 6; i < argc; i++ ) { typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, argv[i] ); if( i == 6 ) { typename OutImageType::SizeType outSize; typename OutImageType::SpacingType outSpacing; typename OutImageType::PointType outOrigin; typename OutImageType::DirectionType outDirection; for( unsigned int d = 0; d < (ImageDimension - 1); d++ ) { outSize[d] = image1->GetLargestPossibleRegion().GetSize()[d]; outSpacing[d] = image1->GetSpacing()[d]; outOrigin[d] = image1->GetOrigin()[d]; for( unsigned int e = 0; e < (ImageDimension - 1); e++ ) { outDirection(e, d) = image1->GetDirection() (e, d); } } for( unsigned int d = 0; d < (ImageDimension - 1); d++ ) { outDirection(d, ImageDimension - 1) = 0; outDirection(ImageDimension - 1, d) = 0; } outDirection(ImageDimension - 1, ImageDimension - 1) = 1.0; outSize[ImageDimension - 1] = argc - 6; outSpacing[ImageDimension - 1] = time; outOrigin[ImageDimension - 1] = origin; typename OutImageType::RegionType outRegion; outRegion.SetSize( outSize ); outimage->SetRegions( outRegion ); outimage->SetSpacing( outSpacing ); outimage->SetOrigin( outOrigin ); outimage->SetDirection( outDirection ); outimage->Allocate(); } ImageIt it( image1, image1->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { typename OutImageType::IndexType index; for( unsigned int d = 0; d < (ImageDimension - 1); d++ ) { index[d] = it.GetIndex()[d]; } index[ImageDimension - 1] = i - 6; outimage->SetPixel(index, it.Value() ); ++it; } } WriteImage( outimage, outname.c_str() ); return 0; } template int TimeSeriesSubset(int argc, char *argv[]) { if( argc <= 2 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; unsigned int n_sub_vols = atoi(argv[argct]); argct++; std::string::size_type idx; idx = outname.find_first_of('.'); std::string tempname = outname.substr(0, idx); std::string extension = outname.substr(idx, outname.length() ); typename ImageType::Pointer image1 = ITK_NULLPTR; typename OutImageType::Pointer outimage = ITK_NULLPTR; typedef itk::ExtractImageFilter ExtractFilterType; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { // std::cout << "Failed to read input image" << std::endl; return 1; } unsigned int timedims = image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; float step = (float)timedims / (float)n_sub_vols; if( n_sub_vols >= timedims ) { n_sub_vols = timedims; step = 1; } for( unsigned int i = 0; i < n_sub_vols; i++ ) { std::string s; std::stringstream out; out << (100 + i); s = out.str(); std::string kname = tempname + s + extension; typename ImageType::RegionType extractRegion = image1->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); unsigned int sub_vol = (unsigned int)( (float)i * step); if( sub_vol >= timedims ) { sub_vol = timedims - 1; } extractRegion.SetIndex(ImageDimension - 1, sub_vol ); typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( image1 ); // extractFilter->SetDirectionCollapseToIdentity(); extractFilter->SetDirectionCollapseToSubmatrix(); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); outimage = extractFilter->GetOutput(); WriteImage(outimage, kname.c_str() ); } return 0; } template int TimeSeriesSimpleSubtraction(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::AlternatingValueSimpleSubtractionImageFilter ImageFilterType; typedef itk::AverageOverDimensionImageFilter MeanFilterType; int argct = 2; const std::string outname = std::string(argv[argct++]); argct++; std::string fn1 = std::string(argv[argct++]); bool mean = false; typename ImageFilterType::Pointer filter = ImageFilterType::New(); if( argc >= 6 ) { if( atoi(argv[argct++]) > 0 ) { mean = true; } } typename InputImageType::Pointer image1 = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } filter->SetInput( image1 ); filter->Update(); if( mean ) { typename MeanFilterType::Pointer meanFilter = MeanFilterType::New(); meanFilter->SetInput( filter->GetOutput() ); meanFilter->SetAveragingDimension( ImageDimension - 1 ); meanFilter->SetDirectionCollapseToSubmatrix(); meanFilter->Update(); WriteImage(meanFilter->GetOutput(), outname.c_str() ); } else { WriteImage(filter->GetOutput(), outname.c_str() ); } return 0; } template int TimeSeriesInterpolationSubtraction(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::AlternatingValueDifferenceImageFilter ImageFilterType; typedef itk::AverageOverDimensionImageFilter MeanFilterType; typedef itk::BSplineInterpolateImageFunction BSplineInterpolatorType; typedef typename BSplineInterpolatorType::Pointer BSplineInterpolatorPointerType; typedef itk::WindowedSincInterpolateImageFunction SincInterpolatorType; typedef typename SincInterpolatorType::Pointer SincInterpolatorPointerType; int argct = 2; const std::string outname = std::string(argv[argct++]); argct++; std::string fn1 = std::string(argv[argct++]); typename ImageFilterType::Pointer filter = ImageFilterType::New(); filter->SetSubtractionDimension( ImageDimension - 1 ); if( argc >= 6 ) { std::string interp = argv[argct++]; if( strcmp( "sinc", interp.c_str() ) == 0 ) { const unsigned int SincRadius = 4; // std::cout << "Using sinc interpolation" << std::endl; SincInterpolatorPointerType labelInterp = SincInterpolatorType::New(); SincInterpolatorPointerType controlInterp = SincInterpolatorType::New(); filter->SetControlInterpolator( controlInterp ); filter->SetLabelInterpolator( labelInterp ); filter->SetIndexPadding( SincRadius ); } else if( strcmp( "bspline", interp.c_str() ) == 0 ) { // std::cout << "Using bspline interpolation of order 3" << std::endl; BSplineInterpolatorPointerType labelInterpB = BSplineInterpolatorType::New(); labelInterpB->SetSplineOrder( 3 ); BSplineInterpolatorPointerType controlInterpB = BSplineInterpolatorType::New(); controlInterpB->SetSplineOrder( 3 ); filter->SetControlInterpolator( controlInterpB ); filter->SetLabelInterpolator( labelInterpB ); filter->SetIndexPadding( 1 ); } else { // std::cout << "Using linear interpolation" << std::endl; } } bool mean = false; if( argc >= 7 ) { if( atoi(argv[argct++]) > 0 ) { mean = true; } } typename InputImageType::Pointer image1 = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } filter->SetInput( image1 ); filter->Update(); if( mean ) { typename MeanFilterType::Pointer meanFilter = MeanFilterType::New(); meanFilter->SetInput( filter->GetOutput() ); meanFilter->SetAveragingDimension( ImageDimension - 1 ); meanFilter->SetDirectionCollapseToSubmatrix(); meanFilter->Update(); WriteImage(meanFilter->GetOutput(), outname.c_str() ); } else { WriteImage(filter->GetOutput(), outname.c_str() ); } if( argc >= 8 ) { std::string control_out = argv[argct++]; WriteImage(filter->GetModifiableControlOutputImage(), control_out.c_str() ); } if( argc >= 8 ) { std::string label_out = argv[argct++]; WriteImage(filter->GetModifiableLabelOutputImage(), label_out.c_str() ); } return 0; } template int SplitAlternatingTimeSeries(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::SplitAlternatingTimeSeriesImageFilter ImageFilterType; if( argc < 5 ) { // std::cout << "Usage: ImageMath 4 split.nii.gz AlternatingTimeSeriesExtraction time.nii.gz" << std::endl; return 1; } int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string::size_type idx; idx = outname.find_first_of('.'); std::string basename = outname.substr(0, idx); std::string extension = outname.substr(idx, outname.length() ); std::string zero( "0" ); std::string one( "1" ); std::string outname0 = basename + zero + extension; std::string outname1 = basename + one + extension; typename ImageFilterType::Pointer filter = ImageFilterType::New(); typename ImageType::Pointer image1 = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } filter->SetInput( image1 ); filter->Update(); WriteImage(filter->GetOutput(0), outname0.c_str() ); WriteImage(filter->GetOutput(1), outname1.c_str() ); return 0; } template int SliceTimingCorrection(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::SliceTimingCorrectionImageFilter ImageFilterType; typedef itk::BSplineInterpolateImageFunction BSplineInterpolatorType; typedef typename BSplineInterpolatorType::Pointer BSplineInterpolatorPointerType; typedef itk::WindowedSincInterpolateImageFunction SincInterpolatorType; typedef typename SincInterpolatorType::Pointer SincInterpolatorPointerType; if( argc < 5 ) { std::cout << "Usage: ImageMath 4 out.nii.gz SliceTimingCorrection input.nii.gz [sliceTiming] [sinc/bspline] [sincRadius=4/bsplineOrder=3]" << std::endl; return 1; } int argct = 2; const std::string outname = std::string(argv[argct++]); argct++; std::string fn1 = std::string(argv[argct++]); float sliceTiming = 0; if( argc > 5 ) { sliceTiming = atof( argv[argct++] ); } typename ImageFilterType::Pointer filter = ImageFilterType::New(); filter->SetTimeDimension( ImageDimension - 1 ); if( argc >= 7 ) { std::string interp = argv[argct++]; if( strcmp( "sinc", interp.c_str() ) == 0 ) { unsigned int sincRadius = 4; if( argc >= 8 ) { sincRadius = atoi( argv[argct++] ); } // std::cout << "Using sinc interpolation of radius " << sincRadius << std::endl; SincInterpolatorPointerType sInterp = SincInterpolatorType::New(); filter->SetInterpolator( sInterp ); filter->SetIndexPadding( sincRadius ); } else if( strcmp( "bspline", interp.c_str() ) == 0 ) { unsigned int order = 3; if( argc >= 8 ) { order = atoi( argv[argct++] ); } // std::cout << "Using bspline interpolation of order " << order << std::endl; BSplineInterpolatorPointerType interpB = BSplineInterpolatorType::New(); interpB->SetSplineOrder( order ); filter->SetInterpolator( interpB ); filter->SetIndexPadding( 1 ); } else { // std::cout << "Using linear interpolation" << std::endl; } } typename InputImageType::Pointer image1 = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } if( sliceTiming == 0 ) { sliceTiming = image1->GetSpacing()[ImageDimension - 1] / image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 2]; // std::cout << "Using default slice timing = " << sliceTiming << std::endl; } // FIXME - rounding error hack if ( sliceTiming * image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 2] > image1->GetSpacing()[ImageDimension - 1] ) { sliceTiming = sliceTiming - 1e-8; // std::cout << "Corrected timing = " << sliceTiming << std::endl; } else { // std::cout << "Slice timing is valid" << std::endl; } filter->SetSliceTiming( sliceTiming ); filter->SetInput( image1 ); filter->DebugOn(); try { filter->Update(); } catch( itk::ExceptionObject& /* exp */) { // std::cout << "Exception caught!" << std::endl; // std::cout << exp << std::endl; return EXIT_FAILURE; } //filter->Update(); WriteImage(filter->GetOutput(), outname.c_str() ); return 0; } template int AverageOverDimension(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image AverageImageType; typedef itk::AverageOverDimensionImageFilter ImageFilterType; if( argc < 6 ) { // std::cout << "Usage: ImageMath 4 average.nii.gz AverageOverDimension time.nii.gz dimension" << std::endl; return EXIT_FAILURE; } int argct = 2; const std::string outname = std::string(argv[argct++]); argct++; std::string fn1 = std::string(argv[argct++]); unsigned int dim = atoi( argv[argct++] ); typename ImageType::Pointer image1 = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } typename ImageFilterType::Pointer filter = ImageFilterType::New(); filter->SetInput( image1 ); filter->SetAveragingDimension( dim ); filter->SetDirectionCollapseToSubmatrix(); filter->Update(); WriteImage( filter->GetOutput(), outname.c_str() ); return 0; } template int TimeSeriesRegionSCCA(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image InputImageType; typedef itk::Image LabelImageType; typedef itk::MinimumMaximumImageCalculator LabelCalculatorType; typedef itk::ants::antsSCCANObject SCCANType; typedef typename SCCANType::MatrixType MatrixType; typedef typename SCCANType::VectorType VectorType; if( argc < 6 ) { return EXIT_FAILURE; } int argct = 2; const std::string outname = std::string(argv[argct++]); argct++; std::string labelName = std::string(argv[argct++]); std::string timeName = std::string(argv[argct++]); // FIXME - add option for multi input for combined CCA typename LabelImageType::Pointer labels = ITK_NULLPTR; ReadImage( labels, labelName.c_str() ); typename InputImageType::Pointer time = ITK_NULLPTR; ReadImage( time, timeName.c_str() ); typename LabelCalculatorType::Pointer calc = LabelCalculatorType::New(); calc->SetImage( labels ); calc->ComputeMaximum(); unsigned int nLabels = calc->GetMaximum(); unsigned int nVoxels = labels->GetLargestPossibleRegion().GetSize()[0]; unsigned int nTimes = time->GetLargestPossibleRegion().GetSize()[0]; // unsigned int labelCounts[nLabels]; unsigned int *labelCounts = new unsigned int[nLabels]; for( unsigned int i = 0; i < nLabels; i++ ) { typename LabelImageType::IndexType idx; idx[1] = 0; labelCounts[i] = 0; for( unsigned int v = 0; v < nVoxels; v++ ) { idx[0] = v; if( labels->GetPixel(idx) == (i + 1) ) { ++labelCounts[i]; } } } typename InputImageType::Pointer connmat = InputImageType::New(); typename InputImageType::RegionType region; region.SetSize(0, nLabels); region.SetSize(1, nLabels); connmat->SetRegions( region ); connmat->Allocate(); connmat->FillBuffer(0); // Coorelation parameters bool robust = false; unsigned int iterct = 20; bool useL1 = false; float gradstep = vnl_math_abs( useL1 ); bool keepPositive = false; float sparsity = 1.0; unsigned int minClusterSize = 1; unsigned int minRegionSize = 1; // used to rankify matrices if using robust typename SCCANType::Pointer cca_rankify = SCCANType::New(); for( unsigned int i = 0; i < nLabels; i++ ) { typename LabelImageType::IndexType idx; idx[1] = 0; MatrixType P(nTimes, labelCounts[i], 0.0); unsigned int iCount = 0; for( unsigned int v = 0; v < nVoxels; v++ ) { idx[0] = v; typename InputImageType::IndexType timeIdx; timeIdx[1] = v; if( labels->GetPixel(idx) == (i + 1) ) { for( unsigned int t = 0; t < nTimes; t++ ) { timeIdx[0] = t; P(t, iCount) = time->GetPixel(timeIdx); } ++iCount; } } if( robust && ( labelCounts[i] >= minRegionSize) ) { P = cca_rankify->RankifyMatrixColumns(P); } if( labelCounts[i] >= minRegionSize ) { for( unsigned int j = i + 1; j < nLabels; j++ ) { MatrixType Q(nTimes, labelCounts[j], 0.0); typename LabelImageType::IndexType idx2; idx2[1] = 0; unsigned int jCount = 0; for( unsigned int v2 = 0; v2 < nVoxels; v2++ ) { idx2[0] = v2; typename InputImageType::IndexType timeIdx2; timeIdx2[1] = v2; if( labels->GetPixel(idx2) == (j + 1) ) { for( unsigned int t2 = 0; t2 < nTimes; t2++ ) { timeIdx2[0] = t2; Q(t2, jCount) = time->GetPixel(timeIdx2); } ++jCount; } } if( robust ) { Q = cca_rankify->RankifyMatrixColumns(Q); } if( labelCounts[j] >= minRegionSize ) { // Correlation magic goes here typename SCCANType::Pointer cca = SCCANType::New(); cca->SetSilent( true ); cca->SetMaximumNumberOfIterations(iterct); cca->SetUseL1( useL1 ); cca->SetGradStep( gradstep ); cca->SetKeepPositiveP( keepPositive ); cca->SetKeepPositiveQ( keepPositive ); cca->SetFractionNonZeroP( sparsity ); cca->SetFractionNonZeroQ( sparsity ); cca->SetMinClusterSizeP( minClusterSize ); cca->SetMinClusterSizeQ( minClusterSize ); cca->SetMatrixP( P ); cca->SetMatrixQ( Q ); VectorType sccancorrs = cca->GetCanonicalCorrelations(); typename InputImageType::IndexType connIdx; connIdx[0] = i; connIdx[1] = j; connmat->SetPixel( connIdx, sccancorrs[0] ); connIdx[0] = j; connIdx[1] = i; connmat->SetPixel( connIdx, sccancorrs[0] ); } } } } WriteImage(connmat, outname.c_str() ); delete []labelCounts; return 0; } template int TimeSeriesRegionCorr(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image InputImageType; typedef itk::Image LabelImageType; typedef itk::MinimumMaximumImageCalculator LabelCalculatorType; typedef itk::ants::antsSCCANObject SCCANType; typedef typename SCCANType::MatrixType MatrixType; typedef typename SCCANType::VectorType VectorType; if( argc < 6 ) { return EXIT_FAILURE; } int argct = 2; std::string outname = std::string(argv[argct++]); std::string operation = std::string(argv[argct++]); std::string labelName = std::string(argv[argct++]); std::string timeName = std::string(argv[argct++]); unsigned int minRegionSize = 3; if( argc > 6 ) { minRegionSize = atoi( argv[argct++] ); } // FIXME - add option for multi input for combined CCA typename LabelImageType::Pointer labels = ITK_NULLPTR; ReadImage( labels, labelName.c_str() ); typename InputImageType::Pointer time = ITK_NULLPTR; ReadImage( time, timeName.c_str() ); typename LabelCalculatorType::Pointer calc = LabelCalculatorType::New(); calc->SetImage( labels ); calc->ComputeMaximum(); unsigned int nLabels = calc->GetMaximum(); unsigned int nVoxels = labels->GetLargestPossibleRegion().GetSize()[0]; unsigned int nTimes = time->GetLargestPossibleRegion().GetSize()[0]; VectorType labelCounts( nLabels, 0 ); typename InputImageType::Pointer connmat = InputImageType::New(); typename InputImageType::RegionType region; region.SetSize(0, nLabels); region.SetSize(1, nLabels); connmat->SetRegions( region ); connmat->Allocate(); connmat->FillBuffer(-1); MatrixType timeSig( nLabels, nTimes, 0.0 ); for( unsigned int i = 0; i < nLabels; i++ ) { typename LabelImageType::IndexType idx; idx[1] = 0; for( unsigned int v = 0; v < nVoxels; v++ ) { idx[0] = v; if( labels->GetPixel(idx) == (i + 1) ) { labelCounts[i]++; typename InputImageType::IndexType timeIdx; timeIdx[1] = v; for( unsigned int t = 0; t < nTimes; t++ ) { timeIdx[0] = t; timeSig(i, t) += time->GetPixel(timeIdx); } } } } for( unsigned int i = 0; i < nLabels; i++ ) { for( unsigned int j = 0; j < nTimes; j++ ) { timeSig(i, j) /= labelCounts[i]; } } for( unsigned int i = 0; i < nLabels; i++ ) { for( unsigned int j = (i + 1); j < nLabels; j++ ) { if( (labelCounts[i] > minRegionSize) && (labelCounts[j] > minRegionSize ) ) { VectorType p = timeSig.get_row(i); VectorType q = timeSig.get_row(j); double corr = 0.0; double xysum = 0; for( unsigned int z = 0; z < p.size(); z++ ) { xysum += (p[z] * q[z]); } double frac = 1.0 / (double)p.size(); double xsum = p.sum(); double ysum = q.sum(); double xsqr = p.squared_magnitude(); double ysqr = q.squared_magnitude(); double numer = xysum - frac * xsum * ysum; double denom = sqrt( ( xsqr - frac * xsum * xsum) * ( ysqr - frac * ysum * ysum) ); if( denom > 0 ) { corr = numer / denom; } if( !vnl_math_isfinite( corr ) ) { corr = 0.0; } typename InputImageType::IndexType connIdx; connIdx[0] = i; connIdx[1] = j; connmat->SetPixel( connIdx, corr ); connIdx[0] = j; connIdx[1] = i; connmat->SetPixel( connIdx, corr ); } } } WriteImage(connmat, outname.c_str() ); return 0; } template int PASLQuantifyCBF(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image TimeImageType; typedef itk::Image ImageType; typedef itk::PulsedArterialSpinLabeledCerebralBloodFlowImageFilter FilterType; int argct = 2; const std::string outname = std::string(argv[argct++]); argct++; std::string fn1 = std::string(argv[argct++]); std::string m0name = std::string(argv[argct++]); typename FilterType::Pointer getCBF = FilterType::New(); // scan for optional parameters while( argct < argc ) { if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "TI1") == 0 ) { getCBF->SetTI1( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } else if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "TI2") == 0 ) { getCBF->SetTI2( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } else if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "T1blood") == 0 ) { getCBF->SetT1blood( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } else if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "Lambda") == 0 ) { getCBF->SetLambda( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } else if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "Alpha") == 0 ) { getCBF->SetAlpha( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } else if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "SliceDelay") == 0 ) { getCBF->SetSliceDelay( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } argct++; } // read in optional parameters here // float m_TI1; // float m_TI2; // float m_T1blood; // float m_lmabda; // float m_alpha; // float m_sliceDelay; typename TimeImageType::Pointer diff = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(diff, fn1.c_str() ); } else { return 1; } typename ImageType::Pointer m0 = ITK_NULLPTR; if( m0name.length() > 3 ) { ReadImage(m0, m0name.c_str() ); } else { return 1; } getCBF->SetDifferenceImage( diff ); getCBF->SetReferenceImage( m0 ); getCBF->Update(); WriteImage( getCBF->GetOutput(), outname.c_str() ); return 0; } template int PCASLQuantifyCBF(int argc, char * /*NOT USED argv*/[]) { if( argc < 6 ) { return EXIT_FAILURE; } /* typedef float PixelType; typedef itk::Image TimeImageType; typedef itk::Image ImageType; typedef itk::PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter FilterType; int argct = 2; std::string outname = std::string(argv[argct++]); std::string operation = std::string(argv[argct++]); std::string fn1 = std::string(argv[argct++]); std::string m0name = std::string(argv[argct++]); typename FilterType::Pointer getCBF = FilterType::New(); // scan for optional parameters while( argct < argc ) { if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "TI1") == 0 ) { getCBF->SetTI1( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } else if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "TI2") == 0 ) { getCBF->SetTI2( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } else if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "T1blood") == 0 ) { getCBF->SetT1blood( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } else if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "Lambda") == 0 ) { getCBF->SetLambda( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } else if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "Alpha") == 0 ) { getCBF->SetAlpha( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } else if( strcmp(ANTSOptionName( argv[argct] ).c_str(), "SliceDelay") == 0 ) { getCBF->SetSliceDelay( atof( ANTSOptionValue( argv[argct] ).c_str() ) ); } argct++; } typename TimeImageType::Pointer diff = NULL; if( fn1.length() > 3 ) { ReadImage(diff, fn1.c_str() ); } else { return 1; } typename ImageType::Pointer m0 = NULL; if( m0name.length() > 3 ) { ReadImage(m0, m0name.c_str() ); } else { return 1; } getCBF->SetDifferenceImage( diff ); getCBF->SetReferenceImage( m0 ); getCBF->Update(); WriteImage( getCBF->GetOutput(), outname.c_str() ); */ return 0; } template int ComputeTimeSeriesLeverage(int argc, char *argv[]) { if( argc <= 2 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; typedef typename OutImageType::IndexType OutIndexType; typedef typename ImageType::IndexType IndexType; typedef double Scalar; typedef itk::ants::antsMatrixUtilities matrixOpType; typename matrixOpType::Pointer matrixOps = matrixOpType::New(); int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; unsigned int k_neighbors = atoi(argv[argct]); argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } unsigned int timedims = image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; typedef itk::ExtractImageFilter ExtractFilterType; typedef itk::ImageRegionIteratorWithIndex SliceIt; typename ImageType::RegionType extractRegion = image1->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); unsigned int sub_vol = 0; extractRegion.SetIndex(ImageDimension - 1, sub_vol ); typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( image1 ); // extractFilter->SetDirectionCollapseToIdentity(); extractFilter->SetDirectionCollapseToSubmatrix(); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); typename OutImageType::Pointer outimage = extractFilter->GetOutput(); outimage->FillBuffer(0); typedef itk::ImageRegionIteratorWithIndex SliceIt; // step 1. compute , for each image in the time series, the effect on the average. // step 2. the effect is defined as the influence of that point on the average or, more simply, the distance of that // image from the average .... typedef vnl_vector timeVectorType; timeVectorType mSample(timedims, 0); timeVectorType mLeverage(timedims, 0); timeVectorType kDistance(timedims, 0); SliceIt vfIter2( outimage, outimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); IndexType tind; // first collect all samples for that location for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } double all_mean = 0; for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); mSample(t) = pix; all_mean += pix; } // compute mean time series value at this voxel all_mean /= (double)timedims; // second compute the leverage for each time point and add that to the total leverage // // this is a simple approach --- just the difference from the mean. // for( unsigned int t = 0; t < timedims; t++ ) { mLeverage(t) += fabs(all_mean - mSample(t) ) / (Scalar)timedims; } } // now use k neighbors to get a distance kDistance.fill(0); for( unsigned int t = 0; t < timedims; t++ ) { int lo = (int)t - k_neighbors / 2; int hi = (int)t + k_neighbors / 2; if( lo < (int) 0 ) { lo = 0; } if( hi > (int)(timedims - 1) ) { hi = timedims - 1; } unsigned int ct = 0; for( int k = lo; k < hi; k++ ) { if( k != (int)t ) { kDistance(t) += fabs(mLeverage(t) - mLeverage(k) ); ct++; } } kDistance(t) /= (double)ct; kDistance(t) = kDistance(t) / mLeverage(t); } // now write the mLeverage value for each time point ... std::ofstream logfile; logfile.open(outname.c_str() ); if( logfile.good() ) { // std::cout << "Raw_Leverage,K_Neighbors_Distance" << std::endl; logfile << "Raw_Leverage,K_Neighbors_Distance" << std::endl; for( unsigned int t = 0; t < timedims; t++ ) { // std::cout << mLeverage(t) << "," << kDistance(t) << std::endl; logfile << mLeverage(t) << "," << kDistance(t) << std::endl; } } logfile.close(); return 0; } template int TimeSeriesToMatrix(int argc, char *argv[]) { if( argc <= 2 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image MatrixImageType; typedef itk::Image OutImageType; typedef typename OutImageType::IndexType OutIndexType; typedef typename ImageType::IndexType IndexType; typedef double Scalar; typedef itk::ants::antsMatrixUtilities matrixOpType; typename matrixOpType::Pointer matrixOps = matrixOpType::New(); bool tomha = true; int argct = 2; const std::string outname = std::string(argv[argct]); argct++; std::string ext = itksys::SystemTools::GetFilenameExtension( outname ); if( ( strcmp(ext.c_str(), ".csv") != 0 ) && ( strcmp(ext.c_str(), ".mha") != 0 ) ) { // std::cout << " must use .csv or .mha as output file extension " << std::endl; return EXIT_FAILURE; } if( ( strcmp(ext.c_str(), ".csv") == 0 ) ) tomha = false; argct++; std::string fn1 = std::string(argv[argct]); argct++; std::string maskfn = std::string(argv[argct]); argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; typename OutImageType::Pointer mask = ITK_NULLPTR; typename MatrixImageType::Pointer matriximage = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } if( maskfn.length() > 3 ) { ReadImage(mask, maskfn.c_str() ); } else { return 1; } unsigned int timedims = image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; unsigned long voxct = 0; typedef itk::ExtractImageFilter ExtractFilterType; typedef itk::ImageRegionIteratorWithIndex SliceIt; SliceIt mIter( mask, mask->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { voxct++; } } // allocate the matrix image typename MatrixImageType::SizeType size; size[0] = timedims; size[1] = voxct; typename MatrixImageType::RegionType newregion; newregion.SetSize(size); if ( tomha ) matriximage = AllocImage(newregion, 0); typename ImageType::RegionType extractRegion = image1->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); unsigned int sub_vol = 0; extractRegion.SetIndex(ImageDimension - 1, sub_vol ); typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( image1 ); // extractFilter->SetDirectionCollapseToIdentity(); extractFilter->SetDirectionCollapseToSubmatrix(); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); typename OutImageType::Pointer outimage = extractFilter->GetOutput(); outimage->FillBuffer(0); typedef itk::ImageRegionIteratorWithIndex SliceIt; typedef vnl_vector timeVectorType; timeVectorType mSample(timedims, 0); typedef itk::Array2D MatrixType; std::vector ColumnHeaders; MatrixType matrix; if ( ! tomha ) { matrix.set_size(timedims, voxct); matrix.Fill(0); } SliceIt vfIter2( outimage, outimage->GetLargestPossibleRegion() ); voxct = 0; PixelType meanval = 0; unsigned long fullct = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); if( mask->GetPixel(ind) >= 0.5 ) { IndexType tind; // first collect all samples for that location for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } for( unsigned int t = 0; t < timedims; t++ ) { typename MatrixImageType::IndexType matind; matind.Fill(0); matind[1] = t; matind[2] = voxct; tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); mSample(t) = pix; if ( ! tomha ) matrix[t][voxct] = pix; if ( tomha ) { matriximage->SetPixel( matind, pix ); } meanval += pix; fullct++; } std::string colname = std::string("V") + ants_to_string(voxct); ColumnHeaders.push_back( colname ); voxct++; } // check mask } // std::cout << " Mean " << meanval / fullct << std::endl; if ( ! tomha ) { // write out the array2D object typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( outname ); writer->SetInput( &matrix ); writer->SetColumnHeaders( ColumnHeaders ); try { writer->Write(); } catch( itk::ExceptionObject& /* exp */ ) { // std::cout << "Exception caught!" << std::endl; // std::cout << exp << std::endl; return EXIT_FAILURE; } } if ( tomha ) { WriteImage( matriximage , outname.c_str() ); } return 0; } template int PASL(int argc, char *argv[]) { if( argc <= 3 ) { // std::cout << " too few options " << argv[0] << std::endl; // std::cout << argv[0] << " NDImage Bool_FirstImageIsControl optional-M0mask.nii.gz " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; typedef typename ImageType::IndexType IndexType; typedef typename OutImageType::IndexType OutIndexType; typedef double RealType; typedef vnl_vector timeVectorType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; bool firstiscontrol = atoi(argv[argct]); argct++; std::string m0fn = ""; if( argc > argct ) { m0fn = std::string(argv[argct]); } argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; typename OutImageType::Pointer outimage = ITK_NULLPTR; typename OutImageType::Pointer M0image = ITK_NULLPTR; typedef itk::ExtractImageFilter ExtractFilterType; // RealType M0W = 1300; // FIXME // RealType TE = 4000; // RealType calculatedM0 = 1.06 * M0W * exp( 1 / 40.0 - 1 / 80.0) * TE; RealType calculatedM0 = 2800; // from "Impact of equilibrium magnetization of blood on ASL quantification" by YChen et // al if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } unsigned int timedims = image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; typename ImageType::RegionType extractRegion = image1->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); if( firstiscontrol ) { extractRegion.SetIndex(ImageDimension - 1, 0 ); } else { extractRegion.SetIndex(ImageDimension - 1, 1 ); } typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( image1 ); // extractFilter->SetDirectionCollapseToIdentity(); extractFilter->SetDirectionCollapseToSubmatrix(); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); M0image = extractFilter->GetOutput(); outimage = AllocImage(M0image, 0); bool haveM0 = true; if( m0fn.length() > 3 ) { ReadImage( M0image, m0fn.c_str() ); } else { haveM0 = false; std::cout << "Warning: using calculatedM0 as reference M0 value --- see see 'Impact of equilibrium magnetization of blood on ASL quantification' " << std::endl; M0image->FillBuffer( calculatedM0 ); } typedef itk::ImageRegionIteratorWithIndex labIterator; labIterator vfIter2( outimage, outimage->GetLargestPossibleRegion() ); timeVectorType sample( timedims, 0 ); timeVectorType cbf( timedims, 0 ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } RealType total = 0; unsigned long cbfct = 0; RealType M_0 = M0image->GetPixel( ind ); // FIXME can be taken from an input reference image or defined for // each tissue bool getCBF = true; if( haveM0 && M_0 == 0 ) { getCBF = false; } else if( haveM0 ) { M_0 = calculatedM0; } if( getCBF ) { for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; RealType pix = image1->GetPixel(tind); sample( t ) = pix; if( ( t % 2 ) == 1 ) { /** the best resource i've found so far equation 1 http://cfn.upenn.edu/perfusion/articles/perfmri_9.pdf "Pediatric Perfusion Imaging Using Pulsed Arterial Spin Labeling" CBF images calculated as: f = \frac{ \lambda DeltaM } { 2 alpha M_0 TI_1 exp( - TI_2 / T_{1a} ) } TI_2 = TI_1 + t * slice_delay where t is the image index , DeltaM is the difference signal between tag and control acquisitions, lambda = 0.9 ml/g is the blood/tissue water partition, T_{1a} = 1200 ms is the longitudinal relaxation time of blood, alpha = 0.95 is the inversion (or labeling or tagging?) efciency, TI_1 = 800 millisec is the duration between the inversion and saturation pulses, TI_2 = TI_1 + w is the image acquisition time. M_0 is the acquired image. These parameters were primarily based on experience in healthy adults; potential effects of ignoring the difference between adults and children on CBF quantication will be discussed below. ... also see https://gate.nmr.mgh.harvard.edu/wiki/whynhow/images/e/e2/ASL_whyNhow.pdf */ RealType lambda = 0.9; // grams / mL RealType alpha = 0.95; // labeling efficiency RealType deltaM = sample( t - 1 ) - sample( t ); // if 1st image is control if( !firstiscontrol ) { deltaM = sample( t ) - sample( t - 1 ); // 2nd image is control } bool is1pt5T = false; RealType T_1a = 1650; // 3T if( is1pt5T ) { T_1a = 1390; // 1.5T } // see "Impact of equilibrium magnetization of blood on ASL quantification" RealType TI_1 = 600; // FIXME milliseconds RealType slice_delay = 42.0; // FIXME milliseconds // TI2(slice) = TI2 + slice_number * slice_delay (slice_delay = the time taken to acquire each slice) RealType TI_2 = TI_1 + t * slice_delay; RealType scaling = 2 * alpha * M_0 * TI_1 * exp( -TI_2 / T_1a ); cbf( t ) = lambda * deltaM / scaling; total += cbf( t ); cbfct++; } } } RealType mean = total / (RealType) cbfct; vfIter2.Set( mean ); } /** From Quantitative Imaging of Perhsion Using a Single Subtraction (QUIPSS and QUIPSS 11) In a proton density weighted, high-resolution, gradient-echo conventional image (TE = 5 ms , TR = 1000 ms , a = l o o ) , the measured ratio R of proton density of blood in the saggital sinus to that of white matter was 1.06. In a single-shot EPI image (TR = \infty ), the signal M_{0WM} from white matter was measured. The fully T_1 relaxed signal from blood was then taken to be M_OB = R M_{0WM} exp [ ( 1 / T_{2WM} - 1 / T_{2B} ) TE ] , where T_{2WM} = 80ms T_{2B} = 200 ms and TE = 5ms. */ // RealType M_0B = 1.06 * M0wm * exp( 5 / 80 - 5 / 200 ); /* RealType M0W = 1; // FIXME M_0 = 1.06 * M0W * exp( 1 / 40.0 - 1 / 80.0) * TE; The term, M_{0B} is calculated as follows (Wong1998), M0b = A * M_{0WM} * exp(1/T2_{WM}^* - 1/T2_B^*) * TE where: A is the proton density ratio between blood and white matter (assumed to be 1.06) T2^* (GRE echo-planar imaging) T2_{WM} is 55 msec (1.5T), 40 (3.0T), and 30 (4.0T) T2_B is 100 msec (1.5T), 80 (3.0T), and 60 (4.0T) M_{0WM} is the mean value in an homogenous white matter region from a image acquired with short TE long TR. */ WriteImage(outimage, outname.c_str() ); return 0; } template int pCASL(int argc, char *argv[]) { if( argc <= 3 ) { // std::cout << " too few options " << argv[0] << std::endl; // std::cout << argv[0] << " NDImage Bool_FirstImageIsControl optional-M0mask.nii.gz " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; typedef typename ImageType::IndexType IndexType; typedef typename OutImageType::IndexType OutIndexType; typedef double RealType; typedef vnl_vector timeVectorType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; bool firstiscontrol = atoi(argv[argct]); argct++; std::string m0fn = ""; if( argc > argct ) { m0fn = std::string(argv[argct]); } argct++; typename ImageType::Pointer image1; typename OutImageType::Pointer outimage; typename OutImageType::Pointer M0image; typedef itk::ExtractImageFilter ExtractFilterType; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } unsigned int timedims = image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; typename ImageType::RegionType extractRegion = image1->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); if( firstiscontrol ) { extractRegion.SetIndex(ImageDimension - 1, 0 ); } else { extractRegion.SetIndex(ImageDimension - 1, 1 ); } typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( image1 ); // extractFilter->SetDirectionCollapseToIdentity(); extractFilter->SetDirectionCollapseToSubmatrix(); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); M0image = extractFilter->GetOutput(); outimage = AllocImage(M0image, 0); // RealType M0W = 1300; // FIXME // RealType TE = 4000; // RealType calculatedM0 = 1.06 * M0W * exp( 1 / 40.0 - 1 / 80.0) * TE; RealType calculatedM0 = 2800; // from "Impact of equilibrium magnetization of blood on ASL quantification" by YChen et // al bool haveM0 = true; if( m0fn.length() > 3 ) { ReadImage( M0image, m0fn.c_str() ); } else { haveM0 = false; std::cout << "Warning: using calculated value as reference M0 value --- see see 'Impact of equilibrium magnetization of blood on ASL quantification' " << std::endl; M0image->FillBuffer( calculatedM0 ); } typedef itk::ImageRegionIteratorWithIndex labIterator; labIterator vfIter2( outimage, outimage->GetLargestPossibleRegion() ); timeVectorType sample( timedims, 0 ); timeVectorType cbf( timedims, 0 ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } RealType total = 0; unsigned long cbfct = 0; RealType M_0 = M0image->GetPixel( ind ); // FIXME can be taken from an input reference image or defined for // each tissue bool getCBF = true; if( haveM0 && M_0 == 0 ) { getCBF = false; } else if( haveM0 ) { M_0 = calculatedM0; } if( getCBF ) { for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; RealType pix = image1->GetPixel(tind); sample( t ) = pix; if( ( t % 2 ) == 1 ) { // see http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3049525/?tool=pubmed Quantitative CBF section /** Quantitative CBF Cerebral blood flow in mL per 100g per minute was calculated pixel-by-pixel using equation (1), where the PLD was taken to be longer than ATT, reducing to (Wang et al, 2002), CBF = \frac{ \lambda DeltaM } { 2 alpha M_0 T_1a [ exp( - w / T_1a ) - exp( - ( tau + w ) / T_1a ) ] } w of 0.7seconds was determined based on the results of experiment (1) for optimal contrast of the GM. Cerebral blood flow was calculated for a single PLD. Quantitative CBF for the whole brain, GM, and WM were tabulated. The bottom slice was excluded from this analysis because it covered only a small part of the cerebrum. */ // f = \frac{ \lambda DeltaM } { 2 alpha M_0 T_1a [ exp( - w / T_1a ) - exp( - ( tau + w ) / // T_1a ) ] } RealType lambda = 0.9; // grams / mL RealType deltaM = sample( t ) - sample( t - 1 ); // control - tagged if control is odd RealType alpha = 0.85; // labeling efficiency // or Tagging efficiency: Magic parameter. Reference values: pCASL 0.85, CASL at 3T 0.68, PASL 0.95. bool is1pt5T = false; RealType T_1a = 1650; // 3T from ASL_whyNhow.pdf if( is1pt5T ) { T_1a = 1390; // 1.5T } RealType T_1t = 1300; // 3T if( is1pt5T ) { T_1t = 900; // 1.5T } // from "Impact of equilibrium magnetization of blood on ASL quantification" RealType tau = 2100; // FIXME milliseconds from PMC3049525 RealType w = 700; // FIXME milliseconds from PMC3049525 // Label width: Not in dicom, but sequence-specific -- magic parameter. Reference values: pCASL 1.5, CASL 1.6, // PASL 0.7. // from PMC3049525 const RealType scaling = 4 * alpha * M_0 * T_1t * ( exp( -1.0 * ( tau + w ) / T_1a ) - exp( -1.0 * w / T_1t ) ); cbf( t ) = lambda * deltaM * ( -1.0 ) / scaling; total += cbf( t ); cbfct++; } } } RealType mean = total / (RealType) cbfct; vfIter2.Set( mean ); } // see "Impact of equilibrium magnetization of blood on ASL quantification" /* RealType M0W = 1300; // FIXME RealType TE = 4000; M_0 = 1.06 * M0W * exp( 1 / 40.0 - 1 / 80.0) * TE; The term, M_{0B} is calculated as follows (Wong1998), M0b = A * M_{0WM} * exp(1/T2_{WM}^* - 1/T2_B^*) * TE where: A is the proton density ratio between blood and white matter (assumed to be 1.06) T2^* (GRE echo-planar imaging) T2_{WM} is 55 msec (1.5T), 40 (3.0T), and 30 (4.0T) T2_B is 100 msec (1.5T), 80 (3.0T), and 60 (4.0T) M_{0WM} is the mean value in an homogenous white matter region from a image acquired with short TE long TR. */ WriteImage(outimage, outname.c_str() ); return 0; } /* Calculate the Magnetization Transfer Ratio from a reference image * and an MT image. Truncate values to be in range [0,1] */ template int MTR(int argc, char *argv[]) { if( argc <= 5 ) { // std::cout << " too few options " << argv[0] << std::endl; // std::cout << argv[0] << " M0Image.nii.gz M1Image.nii.gz [OptionalMask.nii.gz] " << std::endl; return 1; } typedef itk::Image ImageType; typename ImageType::Pointer M0; typename ImageType::Pointer M1; ReadImage(M0, argv[4] ); ReadImage(M1, argv[5] ); typename ImageType::Pointer MTR = AllocImage(M0, 0); typename ImageType::Pointer mask; if( argc > 6 ) { ReadImage(mask, argv[6]); } else { mask = AllocImage(M0, 1); } typedef itk::ImageRegionIteratorWithIndex ImageIt; ImageIt it( mask, mask->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { if( it.Value() > 0 ) { float m0 = M0->GetPixel( it.GetIndex() ); float m1 = M1->GetPixel( it.GetIndex() ); float mtr = ( m0 - m1 ) / m0; if( mtr < 0 ) { mtr = 0; } else if( mtr > 1 ) { mtr = 1; } MTR->SetPixel( it.GetIndex(), mtr ); } ++it; } WriteImage( MTR, argv[2] ); return 0; } template int CompCorrAuto(int argc, char *argv[]) { if( argc <= 2 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; typedef typename OutImageType::IndexType OutIndexType; typedef typename ImageType::IndexType IndexType; typedef double Scalar; typedef itk::ants::antsMatrixUtilities matrixOpType; typename matrixOpType::Pointer matrixOps = matrixOpType::New(); int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn_label = std::string(argv[argct]); argct++; unsigned int n_comp_corr_vecs = 4; // number of eigenvectors to get from high variance voxels if( argc > argct ) { n_comp_corr_vecs = atoi(argv[argct]); } argct++; std::string::size_type idx; idx = outname.find_first_of('.'); std::string tempname = outname.substr(0, idx); std::string extension = outname.substr(idx, outname.length() ); typename ImageType::Pointer image1 = ITK_NULLPTR; typename OutImageType::Pointer outimage = ITK_NULLPTR; typename OutImageType::Pointer outimage2 = ITK_NULLPTR; typename OutImageType::Pointer label_image = ITK_NULLPTR; typename OutImageType::Pointer var_image = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } if( fn_label.length() > 3 ) { ReadImage(label_image, fn_label.c_str() ); } else { return 1; } if( fn_label.length() > 3 ) { ReadImage(outimage, fn_label.c_str() ); } if( fn_label.length() > 3 ) { ReadImage(outimage2, fn_label.c_str() ); } if( fn_label.length() > 3 ) { ReadImage(var_image, fn_label.c_str() ); } var_image->FillBuffer(0); outimage->FillBuffer(0); outimage2->FillBuffer(0); // std::cout << " read images " << std::endl; unsigned int timedims = image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; // std::cout << "timedims " << timedims << " size " << image1->GetLargestPossibleRegion().GetSize() << std::endl; // first, count the label numbers typedef itk::ImageRegionIteratorWithIndex labIterator; labIterator vfIter2( label_image, label_image->GetLargestPossibleRegion() ); unsigned long ct_nuis = 0; unsigned long ct_vox = 0; // std::cout << " verify input " << std::endl; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { if( vfIter2.Get() == 1 ) // in brain { ct_vox++; } } // std::cout << " counted " << ct_vox << " voxels " << std::endl; if( ct_vox == 0 ) { // std::cout << ct_vox << " not enough voxels labeled as gm (or brain) " << std::endl; return 1; } // step 1. compute , in label 3 ( the nuisance region ), the representative value of the time series over the region. // at the same time, compute the average value in label 2 ( the reference region ). // step 2. factor out the nuisance region from the activation at each voxel in the ROI (nonzero labels). // step 3. compute the correlation of the reference region with every voxel in the roi. typedef vnl_matrix timeMatrixType; typedef vnl_vector timeVectorType; timeMatrixType mSample(timedims, ct_vox, 0); unsigned long nuis_vox = 0; timeVectorType sample(timedims, 0); // FIRST -- get high variance (in time) voxels float maxvar = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); if( vfIter2.Get() > 0 ) // in-brain { IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } float total = 0; for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); sample(t) = pix; total += pix; } float mean = total / (float)timedims; float var = 0; for( unsigned int t = 0; t < timedims; t++ ) { var += ( (sample(t) - mean) * (sample(t) - mean) ); } var /= (float)(timedims); var = sqrt(var); if( var > maxvar ) { maxvar = var; } var_image->SetPixel(ind, var); } } // std::cout << " got var " << std::endl; // now build the histogram unsigned int histsize = 50; float binsize = maxvar / histsize; timeVectorType varhist(histsize, 0); float varhistsum = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); float var = var_image->GetPixel(ind); if( vfIter2.Get() > 0 && var > 0 ) // in-brain { int bin = (int)( var / binsize ) - 1; if( bin < 0 ) { bin = 0; } varhist[bin] += 1; varhistsum += 1; } } varhist = varhist / varhistsum; // std::cout << " got var hist " << std::endl; float temp = 0; float varval_csf = 0; for( unsigned int j = 0; j < histsize; j++ ) { temp += varhist(j); if( temp >= 0.95 && varval_csf <= 0 ) { varval_csf = (float)j * binsize; } } // std::cout << " maxvar " << maxvar << " varval_csf " << varval_csf << std::endl; ct_nuis = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); if( var_image->GetPixel(ind) > varval_csf ) // nuisance { ct_nuis++; } } timeMatrixType mNuisance(timedims, ct_nuis, 0); nuis_vox = 0; unsigned long brain_vox = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); if( var_image->GetPixel(ind) > varval_csf ) // nuisance { IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); mNuisance(t, nuis_vox) = pix; } nuis_vox++; } if( vfIter2.Get() > 0 ) // in brain { IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); mSample(t, brain_vox) = pix; } brain_vox++; } } // factor out the nuisance variables by OLS timeVectorType vGlobal = matrixOps->AverageColumns(mSample); // typedef itk::Array2D csvMatrixType; timeMatrixType reducedNuisance(timedims, n_comp_corr_vecs + 1); std::vector ColumnHeaders; std::string colname = std::string("GlobalSignal"); ColumnHeaders.push_back( colname ); reducedNuisance.set_column(0, vGlobal); if( ct_nuis == 0 ) { n_comp_corr_vecs = 1; } for( unsigned int i = 0; i < n_comp_corr_vecs; i++ ) { timeVectorType nuisi = matrixOps->GetCovMatEigenvector(mNuisance, i); reducedNuisance.set_column(i + 1, nuisi); colname = std::string("CompCorrVec") + ants_to_string(i + 1); ColumnHeaders.push_back( colname ); } // write out these nuisance variables // write out the array2D object typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); std::string kname = tempname + std::string("_compcorr.csv"); writer->SetFileName( kname ); writer->SetInput( &reducedNuisance ); writer->SetColumnHeaders( ColumnHeaders ); try { writer->Write(); } catch( itk::ExceptionObject& /* exp */ ) { // std::cout << "Exception caught!" << std::endl; // std::cout << exp << std::endl; return EXIT_FAILURE; } timeMatrixType RRt = matrixOps->ProjectionMatrix(reducedNuisance); mSample = matrixOps->NormalizeMatrix(mSample); mSample = mSample - RRt * mSample; brain_vox = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); if( vfIter2.Get() > 0 ) { timeVectorType samp = mSample.get_column(brain_vox); // correct the original image IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; image1->SetPixel(tind, samp[t]); } brain_vox++; } } kname = tempname + std::string("_corrected") + extension; WriteImage(image1, kname.c_str() ); kname = tempname + std::string("_variance") + extension; WriteImage(var_image, kname.c_str() ); return 0; } template int ThreeTissueConfounds(int argc, char *argv[]) { if( argc <= 2 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; typedef typename OutImageType::IndexType OutIndexType; typedef typename ImageType::IndexType IndexType; typedef double Scalar; typedef itk::ants::antsMatrixUtilities matrixOpType; typename matrixOpType::Pointer matrixOps = matrixOpType::New(); int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn_label = std::string(argv[argct]); argct++; unsigned int wmlabel = 1; unsigned int csflabel = 3; if( argc > argct ) { csflabel = atoi(argv[argct]); } argct++; if( argc > argct ) { wmlabel = atoi(argv[argct]); } argct++; std::string::size_type idx; idx = outname.find_first_of('.'); std::string tempname = outname.substr(0, idx); std::string extension = outname.substr(idx, outname.length() ); typename ImageType::Pointer image1 = ITK_NULLPTR; typename OutImageType::Pointer outimage = ITK_NULLPTR; typename OutImageType::Pointer outimage2 = ITK_NULLPTR; typename OutImageType::Pointer label_image = ITK_NULLPTR; typename OutImageType::Pointer var_image = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } else { return 1; } if( fn_label.length() > 3 ) { ReadImage(label_image, fn_label.c_str() ); } else { return 1; } if( fn_label.length() > 3 ) { ReadImage(outimage, fn_label.c_str() ); } if( fn_label.length() > 3 ) { ReadImage(outimage2, fn_label.c_str() ); } if( fn_label.length() > 3 ) { ReadImage(var_image, fn_label.c_str() ); } var_image->FillBuffer(0); outimage->FillBuffer(0); outimage2->FillBuffer(0); // std::cout << " read images " << std::endl; unsigned int timedims = image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; // std::cout << "timedims " << timedims << " size " << image1->GetLargestPossibleRegion().GetSize() << std::endl; // first, count the label numbers typedef itk::ImageRegionIteratorWithIndex labIterator; labIterator vfIter2( label_image, label_image->GetLargestPossibleRegion() ); unsigned long ct_nuis = 0; unsigned long ct_ref = 0; unsigned long ct_gm = 0; // std::cout << " verify input " << std::endl; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { if( vfIter2.Get() == csflabel ) // nuisance { ct_nuis++; } if( vfIter2.Get() == wmlabel ) // reference { ct_ref++; } if( vfIter2.Get() > 0 ) // gm roi { ct_gm++; } } // std::cout << " counted " << ct_gm << " gm voxels " << ct_ref << " reference region voxels " << std::endl; if( ct_gm == 0 ) { // std::cout << ct_gm << " not enough voxels labeled as gm (or brain) " << ct_gm << std::endl; return 1; } if( ct_ref == 0 ) { // std::cout << ct_ref << " not enough voxels labeled as reference region " << std::endl; return 1; } // step 1. compute , in label 3 ( the nuisance region ), the representative value of the time series over the region. // at the same time, compute the average value in label 2 ( the reference region ). // step 2. factor out the nuisance region from the activation at each voxel in the ROI (nonzero labels). // step 3. compute the correlation of the reference region with every voxel in the roi. typedef vnl_matrix timeMatrixType; typedef vnl_vector timeVectorType; timeMatrixType mReference(timedims, ct_ref, 0); timeMatrixType mSample(timedims, ct_gm, 0); unsigned long nuis_vox = 0; unsigned long ref_vox = 0; unsigned long gm_vox = 0; timeVectorType smoother(timedims, 0); timeVectorType smoother_out(timedims, 0); // FIRST -- get high variance (in time) voxels float maxvar = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); if( vfIter2.Get() > 0 ) // in-brain { IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } float total = 0; for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); smoother(t) = pix; total += pix; } float mean = total / (float)timedims; float var = 0; for( unsigned int t = 0; t < timedims; t++ ) { var += ( (smoother(t) - mean) * (smoother(t) - mean) ); } var /= (float)(timedims); var = sqrt(var); if( var > maxvar ) { maxvar = var; } var_image->SetPixel(ind, var); } } // std::cout << " got var " << std::endl; // now build the histogram unsigned int histsize = 50; float binsize = maxvar / histsize; timeVectorType varhist(histsize, 0); float varhistsum = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); float var = var_image->GetPixel(ind); if( vfIter2.Get() > 0 && var > 0 ) // in-brain { int bin = (int)( var / binsize ) - 1; if( bin < 0 ) { bin = 0; } varhist[bin] += 1; varhistsum += 1; } } varhist = varhist / varhistsum; // std::cout << " got var hist " << std::endl; float temp = 0; float varval_csf = 0; for( unsigned int j = 0; j < histsize; j++ ) { temp += varhist(j); if( temp >= 0.95 && varval_csf <= 0 ) { varval_csf = (float)j * binsize; } } // std::cout << " maxvar " << maxvar << " varval_csf " << varval_csf << std::endl; // WriteImage(var_image,"varimage.nii.gz"); // ct_nuis = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { // OutIndexType ind = vfIter2.GetIndex(); if( vfIter2.Get() == csflabel ) // reference // if( var_image->GetPixel(ind) > varval_csf ) // nuisance { ct_nuis++; } } timeMatrixType mNuisance(timedims, ct_nuis, 0); ref_vox = 0; nuis_vox = 0; gm_vox = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); // if ( vfIter2.Get() == 3 ) { // nuisance // if( var_image->GetPixel(ind) > varval_csf ) // nuisance if( vfIter2.Get() == csflabel ) // reference { IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); mNuisance(t, nuis_vox) = pix; } nuis_vox++; } if( vfIter2.Get() == wmlabel ) // reference { IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); mReference(t, ref_vox) = pix; } ref_vox++; } if( vfIter2.Get() > 0 ) // in brain { IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); mSample(t, gm_vox) = pix; } gm_vox++; } } // factor out the nuisance variables by OLS unsigned int nnuis = 3; // global , csf , wm if( ct_nuis == 0 ) { nnuis = 1; } timeMatrixType reducedNuisance(timedims, nnuis); timeVectorType vGlobal = matrixOps->AverageColumns( mSample ); reducedNuisance.set_column( 0, vGlobal); vGlobal = matrixOps->AverageColumns( mNuisance ); // csf reducedNuisance.set_column( 1, vGlobal); vGlobal = matrixOps->AverageColumns( mReference ); // wm reducedNuisance.set_column( 2, vGlobal); std::vector ColumnHeaders; std::string colname = std::string("GlobalSignal"); ColumnHeaders.push_back( colname ); colname = std::string("CSF"); ColumnHeaders.push_back( colname ); colname = std::string("WM"); ColumnHeaders.push_back( colname ); // write out these nuisance variables // write out the array2D object typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); std::string kname = tempname + std::string("_compcorr.csv"); writer->SetFileName( kname ); writer->SetInput( &reducedNuisance ); writer->SetColumnHeaders( ColumnHeaders ); try { writer->Write(); } catch( itk::ExceptionObject& /* exp */ ) { // std::cout << "Exception caught!" << std::endl; // std::cout << exp << std::endl; return EXIT_FAILURE; } return 0; timeMatrixType RRt = matrixOps->ProjectionMatrix(reducedNuisance); mReference = matrixOps->NormalizeMatrix(mReference); mReference = mReference - RRt * mReference; mSample = matrixOps->NormalizeMatrix(mSample); mSample = mSample - RRt * mSample; // reduce your reference region to the first & second eigenvector timeVectorType vReference = matrixOps->GetCovMatEigenvector(mReference, 0); timeVectorType vReference2 = matrixOps->AverageColumns(mReference); Scalar testcorr = matrixOps->PearsonCorr(vReference, vReference2); if( testcorr < 0 ) { vReference = vReference * (-1); } if( vReference.size() != timedims ) { // std::cout << " CompCorr Error exiting " << std::endl; throw std::exception(); } gm_vox = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); if( vfIter2.Get() > 0 ) { timeVectorType samp = mSample.get_column(gm_vox); // correct the original image IndexType tind; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; image1->SetPixel(tind, samp[t]); } // compute the gm-reference correlation Scalar corr = matrixOps->PearsonCorr(samp, vReference); // Scalar corr2=matrixOps->PearsonCorr(samp,vReference2); outimage->SetPixel(ind, corr); // outimage2->SetPixel(ind,corr2); outimage2->SetPixel(ind, samp.two_norm() ); // the power of the time series gm_vox++; } } // std::cout << "write results" << std::endl; kname = tempname + std::string("first_evec") + extension; WriteImage(outimage, kname.c_str() ); // kname=tempname+std::string("second_evec")+extension; kname = tempname + std::string("power") + extension; WriteImage(outimage2, kname.c_str() ); kname = tempname + std::string("_corrected") + extension; WriteImage(image1, kname.c_str() ); kname = tempname + std::string("_variance") + extension; WriteImage(var_image, kname.c_str() ); return 0; } template int StackImage(int argc, char *argv[]) { if( argc <= 2 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); //unsigned int nImages = argc - argct; //// std::cout << "Stacking " << nImages << " images" << std::endl; // Reference image is the first image passed // All other input images must match in all dimensions except the stack dimension typename ImageType::Pointer refImage = ITK_NULLPTR; // Re-used image pointer for all images to be stacked typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(refImage, fn1.c_str()); typename ImageType::SpacingType refSpacing = refImage->GetSpacing(); typename ImageType::DirectionType refDirection = refImage->GetDirection(); typename ImageType::PointType refOrigin = refImage->GetOrigin(); typename ImageType::SizeType refSize = refImage->GetLargestPossibleRegion().GetSize(); unsigned int nDims = refImage->GetImageDimension(); if ( nDims != ImageDimension ) { // std::cout << "Image dimensions not consistent with passed parameters" << std::endl; return EXIT_FAILURE; } unsigned int stackLength = refSize[nDims-1]; // Check headers align to within this tolerance. Set eps relatively large, aims to catch gross errors // without getting hung up on floating point precision issues, which can be sizeable when dealing with // nifti I/O float eps = 1E-4; for ( int i=(argct+1); i(image1, fn2.c_str()); typename ImageType::SpacingType im1Spacing = image1->GetSpacing(); typename ImageType::DirectionType im1Direction = image1->GetDirection(); typename ImageType::PointType im1Origin = image1->GetOrigin(); typename ImageType::SizeType im1Size = image1->GetLargestPossibleRegion().GetSize(); if ( image1->GetImageDimension() != nDims ) { // std::cout << "Inconsistent image dimension in " << argv[i] << std::endl; return EXIT_FAILURE; } // Check input images for ( unsigned int d=0; d eps ) { // std::cout << "Inconsistent image spacing not allowed" << std::endl; return EXIT_FAILURE; } for ( unsigned int d2=0; d2 eps ) { // std::cout << "Inconsistent image direction not allowed" << std::endl; return EXIT_FAILURE; } } // Only check origin and size up to nDims - 1 // Thus we allow stacked images to have different origins and dimension along // the stack dimension. For example, allow stacking of two time series with // 100 volumes and 50 volumes respectively if ( d < (nDims-1) ) { if ( fabs(im1Origin[d] - refOrigin[d]) > eps ) { // std::cout << "Inconsistent image origins not allowed" << std::endl; return EXIT_FAILURE; } if ( im1Size[d] != refSize[d] ) { // std::cout << "Size variation in stacking dimension only" << std::endl; return EXIT_FAILURE; } } } stackLength += im1Size[nDims-1]; } typename ImageType::SizeType stackSize = refSize; stackSize[nDims-1] = stackLength; typename ImageType::RegionType region = refImage->GetLargestPossibleRegion(); region.SetSize( stackSize ); typename ImageType::Pointer stackImage = ImageType::New(); stackImage->SetRegions( region ); stackImage->SetDirection( refDirection ); stackImage->SetOrigin( refOrigin ); stackImage->SetSpacing( refSpacing ); stackImage->Allocate(); unsigned int offset = 0; while( argc > argct ) { std::string fn2 = std::string(argv[argct++]); ReadImage(image1, fn2.c_str()); Iterator iter( image1, image1->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { typename ImageType::IndexType oIndex = iter.GetIndex(); oIndex[nDims-1] = oIndex[nDims-1] + offset; stackImage->SetPixel(oIndex, iter.Value() ); } offset += image1->GetLargestPossibleRegion().GetSize()[nDims-1]; } WriteImage(stackImage, outname.c_str() ); return 0; } template int Stack2Images(int argc, char *argv[]) { if( argc <= 2 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = std::string(argv[argct]); argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::Pointer tiledimage = ITK_NULLPTR; if( fn1.length() > 3 ) { ReadImage(image1, fn1.c_str() ); } if( fn2.length() > 3 ) { ReadImage(image2, fn2.c_str() ); } itk::FixedArray< unsigned int, ImageDimension > layout; for ( unsigned int i = 0; i < (ImageDimension-1); i++ ) { layout[i]=1; } layout[ ImageDimension - 1 ] = 0; typedef itk::TileImageFilter TileImageFilterType; typename TileImageFilterType::Pointer tileFilter = TileImageFilterType::New (); tileFilter->SetLayout( layout ); unsigned int inputImageNumber = 0; tileFilter->SetInput( inputImageNumber++, image1 ); tileFilter->SetInput( inputImageNumber++, image2 ); tileFilter->SetDefaultPixelValue( 0 ); tiledimage = tileFilter->GetOutput(); WriteImage(tiledimage, outname.c_str() ); return 0; } template int MakeImage(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; unsigned int sizevalx = atoi(argv[argct]); argct++; unsigned int sizevaly = 0; if( argc > argct ) { sizevaly = atoi(argv[argct]); argct++; } unsigned int sizevalz = 0; if( argc > argct && ImageDimension > 2 ) { sizevalz = atoi(argv[argct]); argct++; } unsigned int sizevalt = 0; if( argc > argct && ImageDimension > 3 ) { sizevalt = atoi(argv[argct]); argct++; } typename ImageType::SizeType size; size[0] = sizevalx; size[1] = sizevaly; if( ImageDimension > 2 ) { size[2] = sizevalz; } if( ImageDimension > 3 ) { size[3] = sizevalt; } typename ImageType::RegionType newregion; newregion.SetSize(size); typename ImageType::Pointer padimage = AllocImage(newregion, 0); WriteImage(padimage, outname.c_str() ); return 0; } template typename TImage::Pointer LabelSurface(typename TImage::Pointer input, typename TImage::Pointer input2 ) { typedef TImage ImageType; enum { ImageDimension = ImageType::ImageDimension }; typename ImageType::Pointer Image = AllocImage(input); typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for( int j = 0; j < ImageDimension; j++ ) { rad[j] = 1; } iteratorType GHood(rad, input, input->GetLargestPossibleRegion() ); GHood.GoToBegin(); while( !GHood.IsAtEnd() ) { typename TImage::PixelType p = GHood.GetCenterPixel(); typename TImage::IndexType ind = GHood.GetIndex(); if( p >= 0.5 ) { bool atedge = false; for( unsigned int i = 0; i < GHood.Size(); i++ ) { const typename TImage::IndexType & ind2 = GHood.GetIndex(i); float dist = 0.0; for( int j = 0; j < ImageDimension; j++ ) { dist += (float)(ind[j] - ind2[j]) * (float)(ind[j] - ind2[j]); } dist = sqrt(dist); bool secondval = true; if( input2 ) { if( input2->GetPixel(ind2) >= 0.5 ) { secondval = true; } } if( GHood.GetPixel(i) < 0.5 && dist < 2. && secondval ) { atedge = true; } } if( atedge && p >= 0.5 ) { Image->SetPixel(ind, 1); } else { Image->SetPixel(ind, 0); } } ++GHood; } return Image; } template int LabelSurfaceArea(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef double Scalar; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; typename ImageType::Pointer input = ITK_NULLPTR; ReadImage( input, fn1.c_str() ); typename ImageType::Pointer areaImage = AllocImage( input ); typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; rad.Fill( 1 ); if ( argc > argct ) rad.Fill( atoi( argv[argct] ) ); Scalar voxspc = 0; for ( unsigned int i = 0; i < ImageDimension; i++ ) voxspc += input->GetSpacing()[i]; voxspc = voxspc / static_cast( ImageDimension ); Scalar voxspc2 = voxspc * voxspc; Scalar dm1 = static_cast( ImageDimension - 1 ); Scalar refarea = std::pow( static_cast( rad[1] ) , dm1 ); iteratorType GHood(rad, input, input->GetLargestPossibleRegion() ); GHood.GoToBegin(); while( !GHood.IsAtEnd() ) { typename ImageType::IndexType ind = GHood.GetIndex(); Scalar area = 0.0; Scalar locrefarea = refarea; if ( GHood.GetCenterPixel() > 0.1 ) { for( unsigned int i = 0; i < GHood.Size(); i++ ) { const typename ImageType::IndexType & ind2 = GHood.GetIndex(i); float dist = 0.0; for( unsigned int j = 0; j < ImageDimension; j++ ) { dist += (float)(ind[j] - ind2[j]) * (float)(ind[j] - ind2[j]); } dist = sqrt( dist ); if( dist < (2.0*voxspc) ) { area += ( ( GHood.GetPixel( i ) * voxspc2 ) / locrefarea ); } } } areaImage->SetPixel( ind, area ); ++GHood; } WriteImage( areaImage, outname.c_str() ); return 0; } template int FitSphere(int argc, char *argv[]) { if( argc <= 2 ) { std::cout << " too few options " << std::string(argv[1]) << std::endl; return 1; } /* typedef float PixelType; typedef itk::Vector VectorType; typedef itk::Image FieldType; typedef itk::Image ImageType; typedef itk::ImageFileReader readertype; typedef itk::ImageFileWriter writertype; typedef typename ImageType::IndexType IndexType; typedef typename ImageType::SizeType SizeType; typedef typename ImageType::SpacingType SpacingType; typedef itk::AffineTransform AffineTransformType; typedef itk::LinearInterpolateImageFunction InterpolatorType1; typedef itk::NearestNeighborInterpolateImageFunction InterpolatorType2; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct=2; std::string outname=std::string(argv[argct]); argct++; std::string operation = std::string(argv[argct]); argct++; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if (argc > argct) fn2=std::string(argv[argct]); argct++; float MaxRad=5; if (argc > argct) MaxRad = atof(argv[argct]); argct++; typename ImageType::Pointer image1 = NULL; typename ImageType::Pointer radimage = NULL; typename ImageType::Pointer radimage2 = NULL; typename ImageType::Pointer priorimage = NULL; typename ImageType::Pointer wmimage = NULL; if (fn2.length() > 3) ReadImage(wmimage, fn2.c_str()); // std::cout <<" read " << fn1 << " MXR " << MaxRad << std::endl; ReadImage(image1, fn1.c_str()); ReadImage(radimage, fn1.c_str()); ReadImage(radimage2, fn1.c_str()); ReadImage(priorimage, fn1.c_str()); radimage->FillBuffer(0); radimage2->FillBuffer(0); priorimage->FillBuffer(0); typename ImageType::SpacingType spacing=image1->GetSpacing(); typename ImageType::Pointer surf = LabelSurface(image1,wmimage); typedef itk::LinearInterpolateImageFunction ScalarInterpolatorType; typename ScalarInterpolatorType::Pointer ginterp = ScalarInterpolatorType::New(); ginterp->SetInputImage(image1); typename ScalarInterpolatorType::ContinuousIndexType Y1; typename ScalarInterpolatorType::ContinuousIndexType Y2; typename ScalarInterpolatorType::ContinuousIndexType GMx; typename ScalarInterpolatorType::ContinuousIndexType WMx; typename ScalarInterpolatorType::Pointer winterp=NULL; if (wmimage) { winterp=ScalarInterpolatorType::New(); winterp->SetInputImage(wmimage); } // float x=0,y=0,z=0; // float xc=0,yc=0,zc=0; float globalbestrad=0; typename ImageType::IndexType bestind; bestind.Fill(0); typename ImageType::IndexType ind2; ind2.Fill(0); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator iter( image1, image1->GetLargestPossibleRegion() ); // std::cout <<" Begin " << std::endl; unsigned long npx=0; // float pi=3.141; unsigned long numpx=image1->GetBufferedRegion().GetNumberOfPixels(); //unsigned int prog=0; // float gmtotal,wmtotal,gvol,wvol,warea,garea; for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { npx++; typename ImageType::IndexType ind=iter.GetIndex(); // float val=surf->GetPixel(ind);//iter.Get(); // float minrad=1.e9; */ /* if (val > 0.5 && fabs((float)ind[2]-80.) < 6 ) { //float bestrad=0; //parameterize sphere at this index float epspi=pi*0.1; float epsrad=0.2; gvol=0;wvol=0;warea=0;garea=0; float svol=0;//4./3.*pi*MaxRad*MaxRad*MaxRad; // float sarea=0;//4.*pi*MaxRad*MaxRad; //float bestvol=0; // float wdiff=0; // for (float theta=0; theta<=pi; theta+=epspi) float theta=0; gmtotal=0; wmtotal=0; float glength=0,wlength=0; while ( theta < pi ) { garea=0;warea=0; float psi=0; while ( psi < pi ) { glength=0;wlength=0; float rr=0; bool raddone=false; GMx.Fill(0); WMx.Fill(0); while ( rr <= MaxRad && !raddone) { // integrate the wm/gm probability along this radius, at these angles Y1[0]=(float)ind[0]/spacing[0]+rr*cos(psi)*sin(theta); Y1[1]=(float)ind[1]/spacing[1]+rr*sin(psi)*sin(theta); Y1[2]=(float)ind[2]/spacing[2]+rr*cos(theta); Y2[0]=(float)ind[0]/spacing[0]+rr*cos(psi+pi)*sin(theta); Y2[1]=(float)ind[1]/spacing[1]+rr*sin(psi+pi)*sin(theta); Y2[2]=(float)ind[2]/spacing[2]+rr*cos(theta); float gval1 = ginterp->EvaluateAtContinuousIndex( Y1 ); float gval2 = ginterp->EvaluateAtContinuousIndex( Y2 ); float wval1=0,wval2=0; if (wmimage) wval1 = winterp->EvaluateAtContinuousIndex( Y1 ); if (wmimage) wval2 = winterp->EvaluateAtContinuousIndex( Y2 ); glength+=(gval1+gval2)*epsrad*2; wlength+=(wval2+wval2)*epsrad*2; gmtotal+=(gval1+gval2); wmtotal+=(wval1+wval2); for (unsigned int dd=0; dd 0) gwrat=garea/warea; if (wvol > 0) gvrat=gvol/wvol; priorimage->SetPixel(ind,gmtotal/(wmtotal+gmtotal)); radimage->SetPixel(ind,cmdist); } */ /* // radimage2->SetPixel(ind,gvrat); if (image1->GetPixel(ind) >= 0.5) { bool okfit=true; float dorad=1; float bestrad=1; while (okfit && dorad <= MaxRad ) { typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad,rad2; rad2.Fill(0); for (unsigned int j=0; jGetLargestPossibleRegion()); GHood.SetLocation(ind); typename ImageType::PixelType p = GHood.GetCenterPixel(); unsigned int goodct=0; unsigned int possct=0; if ( p > 0 && radimage->GetPixel(ind) <= 0 ) { for (unsigned int i = 0; i < GHood.Size(); i++) { ind2=GHood.GetIndex(i); float dist=sqrt(((float)ind2[0]-(float)ind[0])*((float)ind2[0]-(float)ind[0])+ ((float)ind2[1]-(float)ind[1])*((float)ind2[1]-(float)ind[1])+ ((float)ind2[2]-(float)ind[2])*((float)ind2[2]-(float)ind[2])); if ( GHood.GetPixel(i) == p && dist <= tardist) { goodct++; possct++; } else if ( dist <= tardist ) possct++; // // std::cout << " Ind " << ind << " : " << bestrad << " tardist " << tardist << " gct " << goodct <<" pos " << possct << " dist " << dist << " ind2 " << ind2 << std::endl; } if (goodct==possct) { bestrad=dorad; radimage->SetPixel(ind,bestrad*(-1.0)); } else { okfit=false; radimage->SetPixel(ind,radimage->GetPixel(ind)*(-1.0)); } } dorad=dorad+1; } if (bestrad >= globalbestrad) { globalbestrad=bestrad; bestind=ind; } if (npx % 10000 == 0) { // std::cout <<" prog " << (float)npx/(float)numpx << std::endl; // WriteImage(radimage,outname.c_str()); // WriteImage(radimage2,(std::string("Sphere")+outname).c_str()); //WriteImage(priorimage,(std::string("Prior")+outname).c_str()); } } } for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { float val=iter.Get(); typename ImageType::IndexType ind=iter.GetIndex(); if (val > 0) { unsigned int dorad=(unsigned int)fabs(radimage->GetPixel(ind)); typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for (unsigned int j=0; jGetLargestPossibleRegion()); GHood.SetLocation(ind); typename ImageType::PixelType p = GHood.GetCenterPixel(); for (unsigned int i = 0; i < GHood.Size(); i++) { ind2=GHood.GetIndex(i); float dist=sqrt(((float)ind2[0]-(float)ind[0])*((float)ind2[0]-(float)ind[0])+ ((float)ind2[1]-(float)ind[1])*((float)ind2[1]-(float)ind[1])+ ((float)ind2[2]-(float)ind[2])*((float)ind2[2]-(float)ind[2])); if ( GHood.GetPixel(i) == p && dist <= tardist && diameter > priorimage->GetPixel(ind2)) { priorimage->SetPixel(ind2,diameter); } } } } // now, make rad image // std::cout << " Best " << bestind << " gbr " << globalbestrad << std::endl; typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for (unsigned int j=0; jGetLargestPossibleRegion()); GHood.SetLocation(bestind); typename ImageType::PixelType p = GHood.GetCenterPixel(); for (unsigned int i = 0; i < GHood.Size(); i++) { ind2=GHood.GetIndex(i); float dist=0; dist+=sqrt((float)(ind2[0]-(float)bestind[0])*(float)(ind2[0]-(float)bestind[0])+ (float)(ind2[1]-(float)bestind[1])*(float)(ind2[1]-(float)bestind[1])+ (float)(ind2[2]-(float)bestind[2])*(float)(ind2[2]-(float)bestind[2])); if ( dist <= (globalbestrad*sqrt((double)2))) { radimage2->SetPixel(ind2,p); } } // WriteImage(radimage,outname.c_str()); WriteImage(radimage2,outname.c_str()); //WriteImage(priorimage,(std::string("Prior")+outname).c_str()); */ return 0; } template int ImageMath(int argc, char *argv[]) { typedef float PixelType; // const unsigned int ImageDimension = AvantsImageDimension; typedef itk::Image ImageType; typedef typename ImageType::IndexType IndexType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct++; std::string operation = std::string(argv[argct]); argct++; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::Pointer varimage = ITK_NULLPTR; bool isfloat = false; float floatval = 1.0; if( from_string(floatval, fn2, std::dec) ) { isfloat = true; } else { ReadImage(image2, fn2.c_str() ); } ReadImage(image1, fn1.c_str() ); varimage = AllocImage(image1); if( strcmp(operation.c_str(), "mresample") == 0 && !isfloat ) { typename ImageType::SpacingType spc = image2->GetSpacing(); typedef itk::TranslationTransform TransformType0; typename TransformType0::Pointer m_Transform0 = TransformType0::New(); typename TransformType0::ParametersType trans = m_Transform0->GetParameters(); for( unsigned int i = 0; i < ImageDimension; i++ ) { trans[i] = 0; spc[i] = image1->GetSpacing()[i] * image1->GetLargestPossibleRegion().GetSize()[i] / image2->GetLargestPossibleRegion().GetSize()[i]; } image2->SetSpacing(spc); image2->SetOrigin(image1->GetOrigin() ); image2->SetDirection(image1->GetDirection() ); m_Transform0->SetParameters(trans); // std::cout << " trans " << m_Transform0->GetParameters() << " Nspc " << image2->GetSpacing() << std::endl; typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resample = ResampleFilterType::New(); resample->SetTransform( m_Transform0 ); resample->SetInput( image2 ); resample->SetOutputParametersFromImage( image1 ); typename ImageType::IndexType zeroind; zeroind.Fill(0); resample->SetDefaultPixelValue( image1->GetPixel(zeroind) ); resample->UpdateLargestPossibleRegion(); image2 = resample->GetOutput(); WriteImage(image2, outname.c_str() ); return 0; } float volumeelement = 1.0; for( unsigned int i = 0; i < ImageDimension; i++ ) { volumeelement *= varimage->GetSpacing()[i]; } float result = 0; unsigned long ct = 0; Iterator vfIter2( varimage, varimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { IndexType ind = vfIter2.GetIndex(); float pix2; if( isfloat ) { pix2 = floatval; } else { pix2 = image2->GetPixel(ind); } float pix1 = image1->GetPixel(ind); if( strcmp(operation.c_str(), "m") == 0 ) { result = pix1 * pix2; } else if( strcmp(operation.c_str(), "+") == 0 ) { result = pix1 + pix2; } else if( strcmp(operation.c_str(), "-") == 0 ) { result = pix1 - pix2; } else if( strcmp(operation.c_str(), "/") == 0 ) { if( pix2 > 0 ) { result = pix1 / pix2; } } else if( strcmp(operation.c_str(), "^") == 0 ) { result = std::pow(pix1, pix2); } else if( strcmp(operation.c_str(), "exp") == 0 ) { result = exp(pix1 * pix2); } else if( strcmp(operation.c_str(), "max") == 0 ) { result = vnl_math_max( pix1, pix2 ); } else if( strcmp(operation.c_str(), "abs") == 0 ) { result = fabs(pix1); } else if( strcmp(operation.c_str(), "addtozero") == 0 && pix1 == 0 ) { result = pix1 + pix2; } else if( strcmp(operation.c_str(), "addtozero") == 0 && pix1 != 0 ) { result = pix1; } else if( strcmp(operation.c_str(), "overadd") == 0 && pix2 != 0 ) { result = pix2; } else if( strcmp(operation.c_str(), "overadd") == 0 ) { result = pix1; } else if( strcmp(operation.c_str(), "Decision") == 0 ) { result = 1. / (1. + exp(-1.0 * ( pix1 - 0.25) / pix2) ); } else if( strcmp(operation.c_str(), "total") == 0 ) { result += pix1 * pix2; } else if( strcmp(operation.c_str(), "mean") == 0 ) { result += pix1 * pix2; ct++; } vfIter2.Set(result); } if( strcmp(operation.c_str(), "total") == 0 ) { std::cout << "total: " << result << " total-volume: " << result * volumeelement << std::endl; } else if( strcmp(operation.c_str(), "mean") == 0 ) { std::cout << result / ct << std::endl; } else { // std::cout << "operation " << operation << std::endl; } if( outname.length() > 3 ) { WriteImage(varimage, outname.c_str() ); } return 0; } template int FuseNImagesIntoNDVectorField(int argc, char *argv[]) { typedef itk::Vector VectorType; typedef itk::Image FieldType; typedef itk::Image ImageType; typedef typename ImageType::IndexType IndexType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct++; std::string operation = std::string(argv[argct]); argct++; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; std::string fn3 = ""; if( argc > argct ) { fn2 = std::string(argv[argct++]); } if( argc > argct ) { fn3 = std::string(argv[argct++]); } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::Pointer image3 = ITK_NULLPTR; if ( fn1.length() > 3 ) ReadImage(image1, fn1.c_str() ); if ( fn2.length() > 3 ) ReadImage(image2, fn2.c_str() ); if ( fn3.length() > 3 ) ReadImage(image3, fn3.c_str() ); typename FieldType::Pointer vimage = AllocImage(image1->GetLargestPossibleRegion(), image1->GetSpacing(), image1->GetOrigin(), image1->GetDirection() ); VectorType vec; vec.Fill(0); Iterator vfIter2( vimage, vimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { IndexType ind = vfIter2.GetIndex(); vec[0] = image1->GetPixel(ind); vec[1] = image2->GetPixel(ind); if ( ImageDimension > 2 ) vec[2] = image3->GetPixel(ind); vfIter2.Set(vec); } WriteImage(vimage, outname.c_str() ); return EXIT_SUCCESS; } template int VImageMath(int argc, char *argv[]) { typedef itk::Vector VectorType; typedef itk::Image FieldType; typedef FieldType ImageType; typedef typename ImageType::PixelType PixelType; typedef typename ImageType::IndexType IndexType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct++; std::string operation = std::string(argv[argct]); argct++; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct++]); } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::Pointer varimage = ITK_NULLPTR; bool isfloat = false; float floatval = 1.0; if( from_string(floatval, fn2, std::dec) ) { isfloat = true; } else { ReadImage(image2, fn2.c_str() ); } ReadImage(image1, fn1.c_str() ); varimage = AllocImage(image1); if( strcmp(operation.c_str(), "mresample") == 0 && !isfloat ) { typename ImageType::SpacingType spc = image2->GetSpacing(); typedef itk::TranslationTransform TransformType0; typename TransformType0::Pointer m_Transform0 = TransformType0::New(); typename TransformType0::ParametersType trans = m_Transform0->GetParameters(); for( unsigned int i = 0; i < ImageDimension; i++ ) { trans[i] = 0; spc[i] = image1->GetSpacing()[i] * image1->GetLargestPossibleRegion().GetSize()[i] / image2->GetLargestPossibleRegion().GetSize()[i]; } image2->SetSpacing(spc); image2->SetOrigin(image1->GetOrigin() ); image2->SetDirection(image1->GetDirection() ); m_Transform0->SetParameters(trans); // std::cout << " trans " << m_Transform0->GetParameters() << " Nspc " << image2->GetSpacing() << std::endl; typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resample = ResampleFilterType::New(); resample->SetTransform( m_Transform0 ); resample->SetInput( image2 ); resample->SetOutputParametersFromImage( image1 ); typename ImageType::IndexType zeroind; zeroind.Fill(0); resample->SetDefaultPixelValue( image1->GetPixel(zeroind) ); resample->UpdateLargestPossibleRegion(); image2 = resample->GetOutput(); WriteImage(image2, outname.c_str() ); return 0; } float volumeelement = 1.0; for( unsigned int i = 0; i < ImageDimension; i++ ) { volumeelement *= varimage->GetSpacing()[i]; } PixelType result; result.Fill( 0 ); Iterator vfIter2( varimage, varimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { IndexType ind = vfIter2.GetIndex(); PixelType pix2; if( isfloat ) { pix2 = floatval; } else { pix2 = image2->GetPixel(ind); } PixelType pix1 = image1->GetPixel(ind); if( strcmp(operation.c_str(), "vm") == 0 ) { result = pix1 * pix2; } else if( strcmp(operation.c_str(), "v+") == 0 ) { result = pix1 + pix2; } else if( strcmp(operation.c_str(), "v-") == 0 ) { result = pix1 - pix2; } else if( strcmp(operation.c_str(), "v/") == 0 ) { // std::cout << "Operation v/ not implemented" << std::endl; // result = // pix1 / pix2; } else if( strcmp(operation.c_str(), "vtotal") == 0 ) { result += pix1 * pix2; } vfIter2.Set(result); } if( strcmp(operation.c_str(), "vtotal") == 0 ) { std::cout << "total: " << result << " total-volume: " << result * volumeelement << std::endl; } else { // std::cout << "operation " << operation << std::endl; } if( outname.length() > 3 ) { WriteImage(varimage, outname.c_str() ); } return 0; } template int SmoothTensorImage(int argc, char *argv[]) { typedef float ValueType; typedef itk::DiffusionTensor3D TensorType; typedef itk::Image TensorImageType; typedef itk::RecursiveGaussianImageFilter GaussianFilterType; int argct = 2; const std::string outname = std::string(argv[argct++]); std::string operation = std::string(argv[argct++]); std::string fn1 = std::string(argv[argct++]); float sigma = atof(argv[argct++]); std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct++]); } typename TensorImageType::Pointer inDT = ITK_NULLPTR; ReadTensorImage(inDT, fn1.c_str() ); typename GaussianFilterType::Pointer gFilter = GaussianFilterType::New(); gFilter->SetInput( inDT ); gFilter->SetSigma( sigma ); gFilter->Update(); WriteTensorImage(gFilter->GetOutput(), outname.c_str() ); return 0; } template int TensorFunctions(int argc, char *argv[]) { typedef float PixelType; // Tensor4DType may contain b0 typedef float D4TensorType; typedef itk::SymmetricSecondRankTensor TensorType; typedef typename itk::RGBPixel RGBType; typedef itk::Image TensorImageType; typedef itk::Image D4TensorImageType; typedef typename TensorImageType::IndexType IndexType; typedef itk::Image ImageType; typedef itk::Image ColorImageType; typedef itk::ImageFileWriter ColorWriterType; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::Vector VectorType; typedef itk::Image VectorImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct++; std::string operation = std::string(argv[argct]); argct++; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; // used for whichvec and mask file name below float backgroundMD = 0.0; // mean diffusivity of isotropic background voxels typename TensorImageType::Pointer timage = ITK_NULLPTR; // input tensor image typename ImageType::Pointer vimage = ITK_NULLPTR; // output scalar image typename ColorImageType::Pointer cimage = ITK_NULLPTR; // output color image typename VectorImageType::Pointer vecimage = ITK_NULLPTR; // output vector image typename TensorImageType::Pointer toimage = ITK_NULLPTR; // output tensor image typename ImageType::Pointer mimage = ITK_NULLPTR; // mask image if( strcmp(operation.c_str(), "4DTensorTo3DTensor") == 0 ) { std::cout << " Convert a 4D tensor to a 3D tensor --- if there are 7 components to the tensor, we throw away the first component b/c its probably b0 " << std::endl; itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fn1.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn1.c_str() ); imageIO->ReadImageInformation(); unsigned int dim = imageIO->GetNumberOfDimensions(); if( dim == 4 ) { typename D4TensorImageType::Pointer d4img = ITK_NULLPTR; ReadImage(d4img, fn1.c_str() ); unsigned int d4size = d4img->GetLargestPossibleRegion().GetSize()[3]; if( d4size != 6 && d4size != 7 ) { std::cout << " you should not be using this function if the input data is not a tensor. " << std::endl; std::cout << " there is no way for us to really check if your use of this function is correct right now except checking the size of the 4th dimension which should be 6 or 7 (the latter if you store b0 in the first component) --- you should really store tensors not as 4D images but as 3D images with tensor voxel types. " << std::endl; throw std::exception(); } typename TensorImageType::SizeType size; typename TensorImageType::RegionType tensorregion; typename TensorImageType::SpacingType spacing; typename TensorImageType::PointType origin; typename TensorImageType::DirectionType direction; for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { size[dd] = d4img->GetLargestPossibleRegion().GetSize()[dd]; origin[dd] = d4img->GetOrigin()[dd]; spacing[dd] = d4img->GetSpacing()[dd]; for( unsigned int ee = 0; ee < ImageDimension; ee++ ) { direction[dd][ee] = d4img->GetDirection()[dd][ee]; } } tensorregion.SetSize(size); timage = AllocImage(tensorregion, spacing, origin, direction); // now iterate through & set the values of the tensors. Iterator tIter(timage, timage->GetLargestPossibleRegion() ); for( tIter.GoToBegin(); !tIter.IsAtEnd(); ++tIter ) { typename TensorImageType::IndexType ind = tIter.GetIndex(); typename D4TensorImageType::IndexType ind2; for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { ind2[dd] = ind[dd]; } TensorType pix6 = tIter.Get(); if( d4size == 6 ) { for( unsigned int ee = 0; ee < d4size; ee++ ) { ind2[3] = ee; if( ee == 2 ) { pix6[2] = d4img->GetPixel(ind2); } else if( ee == 3 ) { pix6[3] = d4img->GetPixel(ind2); } else { pix6[ee] = d4img->GetPixel(ind2); } } } // ITK-way // xx, xy, yy, xz , yz, zz // VTK/other-way // xx, xy, xz, yy , yz, zz else if( d4size == 7 ) { for( unsigned int ee = 1; ee < d4size; ee++ ) { ind2[3] = ee; pix6[ee - 1] = d4img->GetPixel(ind2); } } timage->SetPixel(ind, pix6); } WriteTensorImage(timage, outname.c_str(), false); return 0; } // std::cout << " cannot convert --- input image not 4D --- " << fn1 << std::endl; return 0; } if( strcmp(operation.c_str(), "ComponentTo3DTensor") == 0 ) { std::string extension = std::string( ".nii.gz" ); if( argc > argct ) { extension = std::string( argv[argct++] ); } typename ImageType::Pointer xx, xy, xz, yy, yz, zz; std::string fn1xx = fn1 + std::string( "xx" ) + extension; std::string fn1xy = fn1 + std::string( "xy" ) + extension; std::string fn1xz = fn1 + std::string( "xz" ) + extension; std::string fn1yy = fn1 + std::string( "yy" ) + extension; std::string fn1yz = fn1 + std::string( "yz" ) + extension; std::string fn1zz = fn1 + std::string( "zz" ) + extension; ReadImage( xx, fn1xx.c_str() ); ReadImage( xy, fn1xy.c_str() ); ReadImage( xz, fn1xz.c_str() ); ReadImage( yy, fn1yy.c_str() ); ReadImage( yz, fn1yz.c_str() ); ReadImage( zz, fn1zz.c_str() ); timage = AllocImage(xx); Iterator tIter( timage, timage->GetLargestPossibleRegion() ); for( tIter.GoToBegin(); !tIter.IsAtEnd(); ++tIter ) { typename TensorImageType::IndexType ind = tIter.GetIndex(); TensorType pix6 = tIter.Get(); pix6[0] = xx->GetPixel( ind ); pix6[1] = xy->GetPixel( ind ); pix6[2] = xz->GetPixel( ind ); pix6[3] = yy->GetPixel( ind ); pix6[4] = yz->GetPixel( ind ); pix6[5] = zz->GetPixel( ind ); tIter.Set( pix6 ); } WriteTensorImage( timage, outname.c_str(), false ); return 0; } if( strcmp(operation.c_str(), "ExtractComponentFrom3DTensor") == 0 ) { ReadImage( timage, fn1.c_str() ); unsigned int which = 0; if( argc > argct ) { std::string component = std::string( argv[argct++] ); if( component.find( "xx" ) != std::string::npos ) { which = 0; } else if( component.find( "xy" ) != std::string::npos ) { which = 1; } else if( component.find( "xz" ) != std::string::npos ) { which = 2; } else if( component.find( "yy" ) != std::string::npos ) { which = 3; } else if( component.find( "yz" ) != std::string::npos ) { which = 4; } else if( component.find( "zz" ) != std::string::npos ) { which = 5; } else { std::cout << "Unrecognized component. Need to specify " << "xx, xy, xz, yy, yz, or zz"; return EXIT_FAILURE; } } else { // std::cout << "Error: need to specify component (xx, xy, xz, yy, yz, zz)"; return EXIT_FAILURE; } typename ImageType::Pointer componentImage = AllocImage(timage, 0.0); Iterator tIter( timage, timage->GetLargestPossibleRegion() ); for( tIter.GoToBegin(); !tIter.IsAtEnd(); ++tIter ) { typename TensorImageType::IndexType ind = tIter.GetIndex(); TensorType pix6 = tIter.Get(); componentImage->SetPixel( ind, pix6[which] ); } WriteTensorImage( timage, outname.c_str(), false ); return 0; } unsigned int whichvec = ImageDimension - 1; if( argc > argct ) { fn2 = std::string(argv[argct]); whichvec = atoi(fn2.c_str() ); argct++; } if ( argc > argct ) { backgroundMD = atof( std::string(argv[argct]).c_str() ); argct++; } ReadTensorImage(timage, fn1.c_str(), false); if( strcmp(operation.c_str(), "TensorIOTest") == 0 ) { // std::cout << " test function for tensor I/O " << std::endl; WriteTensorImage(timage, outname.c_str(), false); return 0; } // std::cout << " imagedir " << timage->GetDirection() << std::endl; if( strcmp(operation.c_str(), "TensorColor") == 0 ) { cimage = AllocImage(timage); if( argc > 5 ) { // std::cout << "Using mask image: " << fn2 << std::endl; ReadImage(mimage, fn2.c_str() ); } } else if( strcmp(operation.c_str(), "TensorMask") == 0 ) { // std::cout << "Using mask image: " << fn2 << std::endl; ReadImage(mimage, fn2.c_str() ); typename TensorImageType::PixelType zero; zero.Fill(0); toimage = AllocImage(timage, zero); } else if( strcmp(operation.c_str(), "TensorToVector") == 0 ) { VectorType zero; zero.Fill(0); vecimage = AllocImage(timage, zero); } else if( (strcmp(operation.c_str(), "TensorToPhysicalSpace") == 0) || (strcmp(operation.c_str(), "TensorToLocalSpace") == 0) || (strcmp(operation.c_str(), "ValidTensor") == 0) ) { typename TensorImageType::PixelType zero; zero.Fill(0); toimage = AllocImage(timage, zero); } else { vimage = AllocImage(timage); } TensorType backgroundTensor; // for masking background tensors for( unsigned int i = 0; i < 6; i++ ) { backgroundTensor[i] = 0.0; } if ( backgroundMD > 0.0 ) { // std::cout << "Setting background voxels to isotropic tensors with mean diffusivity " << backgroundMD << std::endl; backgroundTensor[0] = backgroundMD; backgroundTensor[3] = backgroundMD; backgroundTensor[5] = backgroundMD; } RGBType rgbZero; rgbZero[0] = 0; rgbZero[1] = 0; rgbZero[2] = 0; Iterator tIter(timage, timage->GetLargestPossibleRegion() ); for( tIter.GoToBegin(); !tIter.IsAtEnd(); ++tIter ) { IndexType ind = tIter.GetIndex(); float result = 0; if( strcmp(operation.c_str(), "TensorFA") == 0 ) { result = 0.0; if( IsRealTensor( tIter.Value() ) ) { result = GetTensorFA(tIter.Value() ); } vimage->SetPixel(ind, result); } else if( strcmp(operation.c_str(), "TensorMeanDiffusion") == 0 ) { result = GetTensorADC(tIter.Value(), 0); if( vnl_math_isnan(result) ) { result = 0; } vimage->SetPixel(ind, result); } else if( strcmp(operation.c_str(), "TensorRadialDiffusion") == 0 ) { result = GetTensorADC(tIter.Value(), 2); if( vnl_math_isnan(result) ) { result = 0; } vimage->SetPixel(ind, result); } else if( strcmp(operation.c_str(), "TensorEigenvalue") == 0 ) { result = GetTensorADC(tIter.Value(), 3 + whichvec); if( vnl_math_isnan(result) ) { result = 0; } vimage->SetPixel(ind, result); } else if( strcmp(operation.c_str(), "TensorAxialDiffusion") == 0 ) { result = GetTensorADC(tIter.Value(), 5); if( vnl_math_isnan(result) ) { result = 0; } vimage->SetPixel(ind, result); } else if( strcmp(operation.c_str(), "TensorFANumerator") == 0 ) { result = GetTensorFANumerator(tIter.Value() ); if( vnl_math_isnan(result) ) { result = 0; } vimage->SetPixel(ind, result); } else if( strcmp(operation.c_str(), "TensorFADenominator") == 0 ) { result = GetTensorFADenominator(tIter.Value() ); if( vnl_math_isnan(result) ) { result = 0; } vimage->SetPixel(ind, result); } else if( strcmp(operation.c_str(), "TensorColor") == 0 ) { if( argc > 5 ) { if( mimage->GetPixel( tIter.GetIndex() ) > 0 ) { cimage->SetPixel(ind, GetTensorRGB(tIter.Value() ) ); } else { cimage->SetPixel(ind, rgbZero); } } else { RGBType rgb = GetTensorRGB(tIter.Value() ); cimage->SetPixel(ind, rgb); } } else if( strcmp(operation.c_str(), "TensorMask") == 0 ) { float maskVal = mimage->GetPixel(ind); if( maskVal > 0.0 ) { toimage->SetPixel( ind, tIter.Value() ); } else { toimage->SetPixel( ind, backgroundTensor ); } } else if( strcmp(operation.c_str(), "TensorToVector") == 0 ) { VectorType vv = GetTensorPrincipalEigenvector(tIter.Value(), whichvec); vecimage->SetPixel(ind, vv); } else if( strcmp(operation.c_str(), "TensorToVectorComponent") == 0 ) { if( whichvec <= 2 ) { VectorType vv = GetTensorPrincipalEigenvector(tIter.Value(), 2); vimage->SetPixel(ind, vv[whichvec]); } else if( whichvec > 2 && whichvec < 9 ) { vimage->SetPixel(ind, tIter.Value()[whichvec]); } } else if( strcmp(operation.c_str(), "TensorToPhysicalSpace") == 0 ) { typename TensorType::EigenValuesArrayType eigenValues; typename TensorType::EigenVectorsMatrixType eigenVectors; typename TensorType::EigenVectorsMatrixType eigenVectorsPhysical; typename TensorType::EigenVectorsMatrixType eigenValuesMatrix; eigenValuesMatrix.Fill( 0.0 ); tIter.Value().ComputeEigenAnalysis( eigenValues, eigenVectors ); for( unsigned int i = 0; i < 3; i++ ) { eigenValuesMatrix(i, i) = fabs( eigenValues[i] ); itk::Vector ev; for( unsigned int j = 0; j < 3; j++ ) { ev[j] = eigenVectors(j, i); } itk::Vector evp; timage->TransformLocalVectorToPhysicalVector( ev, evp ); itk::Vector evl; timage->TransformPhysicalVectorToLocalVector( evp, evl ); for( unsigned int j = 0; j < 3; j++ ) { eigenVectorsPhysical(j, i) = evp[j]; } } typename TensorType::MatrixType::InternalMatrixType phyTensor = eigenVectorsPhysical.GetTranspose() * eigenValuesMatrix.GetVnlMatrix() * eigenVectorsPhysical.GetVnlMatrix(); TensorType oTensor = Matrix2Vector( phyTensor ); toimage->SetPixel( tIter.GetIndex(), oTensor ); } else if( strcmp(operation.c_str(), "TensorToLocalSpace") == 0 ) { typename TensorType::EigenValuesArrayType eigenValues; typename TensorType::EigenVectorsMatrixType eigenVectors; typename TensorType::EigenVectorsMatrixType eigenValuesMatrix; eigenValuesMatrix.Fill( 0.0 ); tIter.Value().ComputeEigenAnalysis( eigenValues, eigenVectors ); for( unsigned int i = 0; i < 3; i++ ) { eigenValuesMatrix(i, i) = fabs( eigenValues[i] ); itk::Vector ev; for( unsigned int j = 0; j < 3; j++ ) { ev[j] = eigenVectors(j, i); } itk::Vector evp; timage->TransformPhysicalVectorToLocalVector( ev, evp ); for( unsigned int j = 0; j < 3; j++ ) { eigenVectors(j, i) = evp[j]; } } typename TensorType::MatrixType::InternalMatrixType lclTensor = eigenVectors.GetTranspose() * eigenValuesMatrix.GetVnlMatrix() * eigenVectors.GetVnlMatrix(); TensorType oTensor = Matrix2Vector( lclTensor ); toimage->SetPixel( tIter.GetIndex(), oTensor ); } else if( strcmp(operation.c_str(), "ValidTensor") == 0 ) { typename TensorType::EigenValuesArrayType eigenValues; typename TensorType::EigenVectorsMatrixType eigenVectors; typename TensorType::EigenVectorsMatrixType eigenValuesMatrix; eigenValuesMatrix.Fill( 0.0 ); tIter.Value().ComputeEigenAnalysis( eigenValues, eigenVectors ); // NOT USED bool hasNeg = false; typename TensorType::MatrixType::InternalMatrixType lclTensor = eigenVectors.GetTranspose() * eigenValuesMatrix.GetVnlMatrix() * eigenVectors.GetVnlMatrix(); TensorType oTensor = Matrix2Vector( lclTensor ); toimage->SetPixel( tIter.GetIndex(), oTensor ); } } if( strcmp(operation.c_str(), "TensorColor") == 0 ) { typename ColorWriterType::Pointer cwrite = ColorWriterType::New(); cwrite->SetInput(cimage); cwrite->SetFileName(outname.c_str() ); cwrite->Update(); } else if( strcmp(operation.c_str(), "TensorToVector") == 0 ) { WriteImage(vecimage, outname.c_str() ); } else if( (strcmp(operation.c_str(), "TensorToPhysicalSpace") == 0) || (strcmp(operation.c_str(), "TensorToLocalSpace") == 0 ) || (strcmp(operation.c_str(), "TensorMask") == 0 ) || (strcmp(operation.c_str(), "ValidTensor") == 0 ) ) { WriteTensorImage(toimage, outname.c_str(), false ); } else { // std::cout << "Writing scalar image" << std::endl; WriteImage(vimage, outname.c_str() ); } return 0; } template int CompareHeadersAndImages(int argc, char *argv[]) { typedef float PixelType; // const unsigned int ImageDimension = AvantsImageDimension; typedef itk::Image ImageType; typedef typename ImageType::IndexType IndexType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; bool isfloat = false; try { ReadImage( image2, fn2.c_str() ); } catch( ... ) { // std::cout << " Error reading " << fn2 << std::endl; isfloat = true; } float floatval = 1.0; if( isfloat ) { floatval = atof(argv[argct]); } else { ReadImage(image2, fn2.c_str() ); } try { ReadImage(image1, fn1.c_str() ); } catch( ... ) { // std::cout << " read 1 error "; } // compute error in spacing, in orientation and in offset unsigned int failure = 0; float sperr = 0, merr = 0, operr = 0, orsignerr = 0; typename ImageType::SpacingType sp1, sp2; sp1 = image1->GetSpacing(); sp2 = image2->GetSpacing(); for( unsigned int i = 0; i < ImageDimension; i++ ) { float temp = sp1[i] - sp2[i]; sperr += temp * temp; } // std::cout << " SpacingError: " << sqrt(sperr) << std::endl; typename ImageType::PointType op1, op2; op1 = image1->GetOrigin(); op2 = image2->GetOrigin(); for( unsigned int i = 0; i < ImageDimension; i++ ) { float temp = op1[i] - op2[i]; operr += temp * temp; if( op1[i] > 0 && op2[i] <= 0 ) { orsignerr += 1; } else if( op1[i] < 0 && op2[i] >= 0 ) { orsignerr += 1; } } // std::cout << " OriginError: " << sqrt(operr) << std::endl; // std::cout << " OriginSignError: " << orsignerr << std::endl; for( unsigned int i = 0; i < ImageDimension; i++ ) { for( unsigned int j = 0; j < ImageDimension; j++ ) { float temp = image1->GetDirection()[i][j] - image2->GetDirection()[i][j]; merr += temp * temp; } } // std::cout << " OrientError: " << sqrt(merr) << std::endl; bool samesize = true; for( unsigned int i = 0; i < ImageDimension; i++ ) { if( image1->GetLargestPossibleRegion().GetSize()[i] != image2->GetLargestPossibleRegion().GetSize()[i] ) { samesize = false; } } if( samesize ) { // run a quick registration if( false ) { typedef typename itk::ImageMomentsCalculator ImageCalculatorType; typename ImageCalculatorType::Pointer calculator = ImageCalculatorType::New(); calculator->SetImage( image1 ); typename ImageCalculatorType::VectorType fixed_center; fixed_center.Fill(0); typename ImageCalculatorType::VectorType moving_center; moving_center.Fill(0); try { calculator->Compute(); fixed_center = calculator->GetCenterOfGravity(); calculator->SetImage( image2 ); try { calculator->Compute(); moving_center = calculator->GetCenterOfGravity(); } catch( ... ) { // std::cout << " zero image2 error "; fixed_center.Fill(0); } } catch( ... ) { // std::cout << " zero image1 error "; } typedef itk::TranslationTransform TransformType0; typename TransformType0::Pointer m_Transform0 = TransformType0::New(); typename TransformType0::ParametersType trans = m_Transform0->GetParameters(); for( unsigned int i = 0; i < ImageDimension; i++ ) { trans[i] = moving_center[i] - fixed_center[i]; } m_Transform0->SetParameters(trans); // std::cout << " trans " << m_Transform0->GetParameters() << std::endl; typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resample = ResampleFilterType::New(); resample->SetTransform( m_Transform0 ); resample->SetInput( image2 ); resample->SetOutputParametersFromImage( image1 ); typename ImageType::IndexType zeroind; zeroind.Fill(0); resample->SetDefaultPixelValue( image1->GetPixel(zeroind) ); resample->UpdateLargestPossibleRegion(); typename ImageType::Pointer varimage = resample->GetOutput(); } float i1norm = 0, i2norm = 0, i1i2norm = 0; // i3norm=0,i1i3norm=0; unsigned long ct1 = 1, ct2 = 1, ct12 = 1; // ct3=1,ct13=1; Iterator vfIter2( image1, image1->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { IndexType ind = vfIter2.GetIndex(); float pix2; if( isfloat ) { pix2 = floatval; } else { pix2 = image2->GetPixel(ind); } if( vnl_math_isnan(pix2) || vnl_math_isinf(pix2) ) { pix2 = 0; image2->SetPixel(ind, 0); } // float pix3=varimage->GetPixel(ind); float pix1 = image1->GetPixel(ind); if( pix1 > 0 ) { i1norm += fabs(pix1); ct1++; } if( pix2 > 0 ) { i2norm += fabs(pix2); ct2++; } // if (pix3 > 0 ) { i3norm+=fabs(pix3); ct3++; } } float mean1 = i1norm / ct1; if( mean1 == 0 ) { mean1 = 1; } float mean2 = i2norm / ct2; if( mean2 == 0 ) { mean2 = 1; } for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { IndexType ind = vfIter2.GetIndex(); float pix2; if( isfloat ) { pix2 = floatval; } else { pix2 = image2->GetPixel(ind) / mean2; } // float pix3=vfIter2.Get()/mean2; float pix1 = image1->GetPixel(ind) / mean1; if( pix1 > 0 || pix2 > 0 ) { i1i2norm += fabs(pix1 - pix2); ct12++; } // if ( pix1 > 0 || pix3 > 0) // { // i1i3norm+=fabs(pix1-pix3); // ct13++; // } } // float idice1 = 1.0 - 2.0*i1i3norm/ct13 / ( i1norm/ct1 + i3norm / ct3 ); // std::cout << " DiceImageDifference: " << idice0 << " IntensityDifference: " << i1i2norm << std::endl; // std::cout << " CenterOfMassTransImageDifference: " << idice1 << " and " << i1i3norm << std::endl; } typename ImageType::PointType fixedorig = image2->GetOrigin(); if( orsignerr > 0 ) { failure = 1; // now fix the error for( unsigned int i = 0; i < ImageDimension; i++ ) { if( (op1[i] > 0 && op2[i] < 0 ) || (op1[i] < 0 && op2[i] > 0) ) { fixedorig[i] *= (-1.0); } else if( (op1[i] > 0 && op2[i] == 0 ) || (op1[i] < 0 && op2[i] == 0) ) { fixedorig[i] = image1->GetOrigin()[i]; } } } image2->SetOrigin(fixedorig); // if ( sqrt(operr) > 100) failure= if( sqrt(merr) >= 0.7 && failure == 0 ) { failure = 2; } if( sqrt(merr) >= 0.7 && failure != 0 ) { failure = 3; } if( failure > 1 ) { image2->SetDirection(image1->GetDirection() ); } // write repaired images WriteImage( image2, outname.c_str() ); // std::cout << " FailureState: " << failure << " for " << fn2 << std::endl; return failure; } // template // typename TImage::Pointer // SegmentKMeans(typename TImage::Pointer image , unsigned int nclasses) // { // // typedef TImage ImageType; // typedef typename TImage::PixelType PixelType; // enum { ImageDimension = ImageType::ImageDimension }; // typedef itk::ImageRegionIteratorWithIndex Iterator; // // typedef itk::Statistics::ScalarImageToListAdaptor< ImageType > AdaptorType; // // typename AdaptorType::Pointer adaptor = AdaptorType::New(); // // adaptor->SetImage( image ); // // // Define the Measurement vector type from the AdaptorType // typedef typename AdaptorType::MeasurementVectorType MeasurementVectorType; // // // Create the K-d tree structure // typedef itk::Statistics::WeightedCentroidKdTreeGenerator< // AdaptorType > // TreeGeneratorType; // // typename TreeGeneratorType::Pointer treeGenerator = TreeGeneratorType::New(); // // treeGenerator->SetSample( adaptor ); // treeGenerator->SetBucketSize( 16 ); // treeGenerator->Update(); // // typedef typename TreeGeneratorType::KdTreeType TreeType; // typedef itk::Statistics::KdTreeBasedKmeansEstimator EstimatorType; // // typename EstimatorType::Pointer estimator = EstimatorType::New(); // // typename EstimatorType::ParametersType initialMeans( nclasses ); // // Iterator vfIter2( image, image->GetLargestPossibleRegion() ); // double mx =-1.e12, mn=1.e12; // for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) // { // double px = vfIter2.Get(); // if (px > mx) mx=px; // else if (px < mn) mn=px; // } // float range=(mx-mn); // // float bins=1.0/((float)nclasses+1.); // for (unsigned int i=0; iSetParameters( initialMeans ); // // estimator->SetKdTree( treeGenerator->GetOutput() ); // estimator->SetMaximumIteration( 200 ); // estimator->SetCentroidPositionChangesThreshold(0.0); // estimator->StartOptimization(); // // typename EstimatorType::ParametersType estimatedMeans = estimator->GetParameters(); // // // typename ImageType::Pointer varimage=ImageType::New(); // varimage->SetLargestPossibleRegion( image->GetLargestPossibleRegion() ); // varimage->SetBufferedRegion( image->GetLargestPossibleRegion() ); // varimage->SetLargestPossibleRegion( image->GetLargestPossibleRegion() ); // varimage->Allocate(); // varimage->SetSpacing(image->GetSpacing()); // varimage->SetOrigin(image->GetOrigin()); // varimage->SetDirection(image->GetDirection()); // // // float var=sqrt(range); // for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) // { // double px = vfIter2.Get(); // unsigned int best=0; // float mindist=1.e9; // for ( unsigned int i = 0 ; i < nclasses ; ++i ) // { // float dist=fabs(px-estimatedMeans[i]); // if (dist < mindist) { mindist=dist; best=i; } // //vec[i]=exp(-1.0*dist*dist/var); // } // varimage->SetPixel(vfIter2.GetIndex(),best+1); // // vecimage->SetPixel(vfIter2.GetIndex(),vec); // } // // return varimage; // // // } // template // typename TImage::Pointer // BayesianSegmentation(typename TImage::Pointer image , unsigned int nclasses, std::string priorfn , unsigned int // nsmooth = 2 ) // { // // typedef TImage ImageType; // typedef typename TImage::PixelType PixelType; // enum { ImageDimension = ImageType::ImageDimension }; // typedef itk::ImageRegionIteratorWithIndex Iterator; // // // const unsigned int ImageDimension = AvantsImageDimension; // typedef itk::Vector VectorType; // typedef itk::Image FieldType; // typedef itk::ImageFileReader readertype; // typedef itk::ImageFileWriter writertype; // typedef typename ImageType::IndexType IndexType; // typedef typename ImageType::SizeType SizeType; // typedef typename ImageType::SpacingType SpacingType; // typedef itk::AffineTransform AffineTransformType; // typedef itk::LinearInterpolateImageFunction InterpolatorType1; // typedef itk::NearestNeighborInterpolateImageFunction InterpolatorType2; // typedef itk::ImageRegionIteratorWithIndex Iterator; // // // // typedef itk::Statistics::ScalarImageToListAdaptor< ImageType > AdaptorType; // typename AdaptorType::Pointer adaptor = AdaptorType::New(); // adaptor->SetImage( image ); // // Define the Measurement vector type from the AdaptorType // typedef typename AdaptorType::MeasurementVectorType MeasurementVectorType; // // Create the K-d tree structure // typedef itk::Statistics::WeightedCentroidKdTreeGenerator< // AdaptorType > // TreeGeneratorType; // typename TreeGeneratorType::Pointer treeGenerator = TreeGeneratorType::New(); // treeGenerator->SetSample( adaptor ); // treeGenerator->SetBucketSize( 16 ); // treeGenerator->Update(); // typedef typename TreeGeneratorType::KdTreeType TreeType; // typedef itk::Statistics::KdTreeBasedKmeansEstimator EstimatorType; // typename EstimatorType::Pointer estimator = EstimatorType::New(); // typename EstimatorType::ParametersType initialMeans( nclasses ); // Iterator vfIter2( image, image->GetLargestPossibleRegion() ); // double mx =-1.e12, mn=1.e12; // for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) // { // double px = vfIter2.Get(); // if (px > mx) mx=px; // else if (px < mn) mn=px; // } // float range=(mx-mn); // float bins=1.0/((float)nclasses+1.); // for (unsigned int i=0; iSetParameters( initialMeans ); // estimator->SetKdTree( treeGenerator->GetOutput() ); // estimator->SetMaximumIteration( 200 ); // estimator->SetCentroidPositionChangesThreshold(0.0); // estimator->StartOptimization(); // typename EstimatorType::ParametersType estimatedMeans = estimator->GetParameters(); // // typename ImageType::Pointer varimage=ImageType::New(); // varimage->SetLargestPossibleRegion( image->GetLargestPossibleRegion() ); // varimage->SetBufferedRegion( image->GetLargestPossibleRegion() ); // varimage->SetLargestPossibleRegion( image->GetLargestPossibleRegion() ); // varimage->SetSpacing(image->GetSpacing()); // varimage->SetOrigin(image->GetOrigin()); // varimage->SetDirection(image->GetDirection()); // varimage->Allocate(); // // std::vector estimatedVar(nclasses,0); // std::vector estimatedCounts(nclasses,0); // // // float var=sqrt(range); // for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) // { // double px = vfIter2.Get(); // unsigned int best=0; // float mindist=1.e9; // for ( unsigned int i = 0 ; i < nclasses ; ++i ) // { // float dist=fabs(px-estimatedMeans[i]); // if (dist < mindist) { mindist=dist; best=i; } // //vec[i]=exp(-1.0*dist*dist/var); // } // varimage->SetPixel(vfIter2.GetIndex(),best); // } // for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) // { // double px = vfIter2.Get(); // unsigned int i = (unsigned int) varimage->GetPixel(vfIter2.GetIndex()); // estimatedCounts[i]=estimatedCounts[i]+1; // float dist=(px -estimatedMeans[i]); // estimatedVar[i]+=dist*dist; // } // // for (unsigned int i=0; i 0) estimatedVar[i]=(estimatedVar[i])/ct; // else estimatedVar[i]=0; // // std::cout << " Sample SD Ests " << sqrt(estimatedVar[i]) << " Mean " << estimatedMeans[i] << std::endl; // } // // typedef float InputPixelType; // typedef itk::VectorImage< InputPixelType, ImageDimension > InputImageType; // typedef float LabelType; // typedef float PriorType; // typedef float PosteriorType; // typedef itk::BayesianClassifierImageFilter< // InputImageType,LabelType, // PosteriorType,PriorType > ClassifierFilterType; // // typename InputImageType::Pointer vecImage = InputImageType::New(); // typedef typename InputImageType::PixelType VecPixelType; // vecImage->SetSpacing(image->GetSpacing()); // vecImage->SetOrigin(image->GetOrigin()); // vecImage->SetRegions( image->GetLargestPossibleRegion() ); // vecImage->SetVectorLength(nclasses); // vecImage->Allocate(); // VecPixelType vvv(nclasses); // vvv.Fill(0); // vecImage->FillBuffer(vvv); // // for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { // double px = vfIter2.Get(); // VecPixelType probs(nclasses); // float total=0; // for (unsigned int i=0; i0) probs[i]/=total; // } // vecImage->SetPixel( vfIter2.GetIndex(), probs); // } // // typename ClassifierFilterType::Pointer filter = ClassifierFilterType::New(); // // // typedef itk::ImageFileReader< InputImageType > ReaderType; // typename ReaderType::Pointer reader = ReaderType::New(); // typename InputImageType::Pointer priors=NULL; // // if (priorfn.length() > 3 ) // { // // std::cout << " Setting Priors " << priorfn << std::endl; // bool geometric=false; // if ( strcmp(priorfn.c_str(),"Geometric") == 0) geometric=true; // if (geometric) // { // // std::cout <<" Using a geometric thickness prior to aid cortical segmentation " << std::endl; // typename ImageType::Pointer outbrainmask = BinaryThreshold(0,nclasses-3,1,varimage); // typename ImageType::Pointer inwmask = BinaryThreshold(nclasses-1,nclasses,1,varimage); // typename ImageType::Pointer outwmask = BinaryThreshold(0,nclasses-2,1,varimage); // typedef itk::DanielssonDistanceMapImageFilter FilterType; // typename FilterType::Pointer distmap = FilterType::New(); // distmap->InputIsBinaryOn(); // distmap->SetUseImageSpacing(true); // distmap->SetInput(outbrainmask); // distmap->Update(); // typename ImageType::Pointer distcortex=distmap->GetOutput(); // // typename FilterType::Pointer distmap2 = FilterType::New(); // distmap2->InputIsBinaryOn(); // distmap2->SetUseImageSpacing(true); // distmap2->SetInput(inwmask); // distmap2->Update(); // typename ImageType::Pointer distwm=distmap2->GetOutput(); // // typedef itk::LaplacianRecursiveGaussianImageFilter dgf; // typename dgf::Pointer lfilter = dgf::New(); // lfilter->SetSigma(1.3); // lfilter->SetInput(distwm); // lfilter->Update(); // typename ImageType::Pointer image2=lfilter->GetOutput(); // typedef itk::RescaleIntensityImageFilter RescaleFilterType; // typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); // rescaler->SetOutputMinimum( 0 ); // rescaler->SetOutputMaximum( 1 ); // rescaler->SetInput( image2 ); // rescaler->Update(); // typename ImageType::Pointer sulci= rescaler->GetOutput(); // // priors= InputImageType::New(); // typedef typename InputImageType::PixelType VecPixelType; // priors->SetSpacing(image->GetSpacing()); // priors->SetOrigin(image->GetOrigin()); // priors->SetDirection(image->GetDirection()); // priors->SetRegions( image->GetLargestPossibleRegion() ); // priors->SetVectorLength(nclasses); // priors->Allocate(); // // std::cout <<" Allocated " << std::endl; // // for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) // { // // // std::cout <<" ind " <GetPixel( vfIter2.GetIndex()); // // float inw = inwmask->GetPixel( vfIter2.GetIndex()); // float distance = distcortex->GetPixel( vfIter2.GetIndex()); // float wdistance = distwm->GetPixel( vfIter2.GetIndex()); // VecPixelType probs(nclasses); // probs.Fill(1.0/(float)nclasses); // VecPixelType posteriors=vecImage->GetPixel(vfIter2.GetIndex()); // if (nclasses > 2) // { // float dvar=2; // float basedist=4; // float distmag=basedist-distance; // // if (distmag > 0) distmag=0; // distmag*=distmag; // float gdistprob=1.0-exp(-1.0*distmag/dvar); // // float wdistprob=1.0/(1.0+exp(-1.0*distance/5)); // // float wdistmag=basedist-wdistance; // if (wdistmag > 0) wdistmag=0; // wdistmag*=wdistmag; // float gdistprob2=exp(-1.0*wdistmag/dvar); // // float sulcprob=sulci->GetPixel( vfIter2.GetIndex()); // float sdiff=(0.25-sulcprob); // if (sdiff > 0) sdiff=0; // sdiff*=sdiff; // sulcprob=exp(-1.0*sdiff/0.5); // // // std::cout << " Sulc " << sulcprob << std::endl; // // bool test = (outbrain < 1 && inw > 1); // if ( true ) // { // for (unsigned int i=0; i 0) probs[i]=sulcprob; // } // } // else // { // for (unsigned int i=0; i0) probs[i]/=prtotal; else probs[i]=1.0/(float)nclasses; // if (pototal>0) posteriors[i]/=pototal; else posteriors[i]=1.0/(float)nclasses; // } // priors->SetPixel( vfIter2.GetIndex(), probs); // vecImage->SetPixel( vfIter2.GetIndex(), posteriors); // } // // std::cout << " ok " << std::endl; // // // } // else // { // reader->SetFileName( priorfn.c_str() ); // reader->Update(); // priors=reader->GetOutput(); // // for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) // { // VecPixelType posteriors=vecImage->GetPixel(vfIter2.GetIndex()); // VecPixelType probs=priors->GetPixel(vfIter2.GetIndex()); // for (unsigned int i=0; i0) probs[i]/=prtotal; else probs[i]=1.0/(float)nclasses; // if (pototal>0) posteriors[i]/=pototal; else posteriors[i]=1.0/(float)nclasses; // } // vecImage->SetPixel( vfIter2.GetIndex(), posteriors); // } // } // // if (priors) filter->SetInput( 1, priors ); // Bug -- // // classification filter does not actually use priors // } else // std::cout << " No Priors " << std::endl; // // filter->SetInput( vecImage ); // // // if( nsmooth >= 1 ) // { // // std::cout << " Smoothing Iterations: " << nsmooth << std::endl; // filter->SetNumberOfSmoothingIterations( nsmooth ); // typedef typename ClassifierFilterType::ExtractedComponentImageType ExtractedComponentImageType; // typedef itk::DiscreteGaussianImageFilter< // ExtractedComponentImageType, ExtractedComponentImageType > SmoothingFilterType; // typedef itk::BilateralImageFilter< // ExtractedComponentImageType, ExtractedComponentImageType > SmoothingFilterType2; // typename SmoothingFilterType::Pointer smoother = SmoothingFilterType::New(); // smoother->SetVariance(1.0); // smoother->SetUseImageSpacingOff(); // //smoother->SetDomainSigma(1); // //smoother->SetRangeSigma(1); // filter->SetSmoothingFilter( smoother ); // } // // // SET FILTER'S PRIOR PARAMETERS // // do nothing here to default to uniform priors // // otherwise set the priors to some user provided values // // // // // Setup writer.. Rescale the label map to the dynamic range of the // // datatype and write it // // // filter->Update(); // // return filter->GetOutput(); // // } template int NegativeImage(int /*argc */, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef typename ImageType::IndexType IndexType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage( image1, fn1.c_str() ); Iterator vfIter2( image1, image1->GetLargestPossibleRegion() ); double mx = -1.e12, mn = 1.e12; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { double px = vfIter2.Get(); if( px > mx ) { mx = px; } else if( px < mn ) { mn = px; } } if( mx == mn ) { mx = 1; mn = 0; } for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { IndexType ind = vfIter2.GetIndex(); double pix = image1->GetPixel(ind); pix = (pix - mn) / (mx - mn); pix = (1.0 - pix) * (mx - mn); vfIter2.Set(pix); } WriteImage( image1, outname.c_str() ); return 0; } // template // typename TImage::Pointer // //void // SegmentMRF(typename TImage::Pointer image , // typename TImage::Pointer labelimage, unsigned int nclasses, float smf, unsigned int maxits) // { // // typedef TImage ImageType; // typedef typename TImage::PixelType PixelType; // enum { ImageDimension = ImageType::ImageDimension }; // enum { Dimension = ImageType::ImageDimension }; // // // // const unsigned int NUMBANDS=1; // typedef itk::Image,ImageDimension> VecImageType; // typedef typename VecImageType::PixelType VecPixelType; // // /** copy the input image into this vector image. stupid. */ // // typename VecImageType::Pointer vecImage = VecImageType::New(); // vecImage->SetSpacing(image->GetSpacing()); // vecImage->SetOrigin(image->GetOrigin()); // vecImage->SetLargestPossibleRegion( image->GetLargestPossibleRegion() ); // vecImage->SetBufferedRegion( image->GetLargestPossibleRegion() ); // vecImage->Allocate(); // VecPixelType vvv; // vvv.Fill(0); // vecImage->FillBuffer(vvv); // // // setup the iterators // // typedef VecImageType::PixelType::VectorType VecPixelType; // // enum { VecImageDimension = VecImageType::ImageDimension }; // typedef itk::ImageRegionIterator< VecImageType > VecIterator; // // VecIterator outIt( vecImage, vecImage->GetBufferedRegion() ); // for (outIt.GoToBegin(); !outIt.IsAtEnd(); ++outIt) // { // vvv[0]=image->GetPixel(outIt.GetIndex()); // outIt.Set(vvv); // } // // // namespace stat = itk::Statistics; // typedef itk::Image ClassImageType; // typedef stat::MahalanobisDistanceMembershipFunction< VecPixelType > // MembershipFunctionType ; // typedef typename MembershipFunctionType::Pointer MembershipFunctionPointer ; // typedef std::vector< MembershipFunctionPointer > MembershipFunctionPointerVector; // // typedef itk::ImageGaussianModelEstimator // ImageGaussianModelEstimatorType; // // typename ImageGaussianModelEstimatorType::Pointer applyEstimateModel = // ImageGaussianModelEstimatorType::New(); // /* // typedef itk::Statistics::WeightedCentroidKdTreeGenerator< // AdaptorType > // TreeGeneratorType; // typedef typename TreeGeneratorType::KdTreeType TreeType; // typedef itk::Statistics::KdTreeBasedKmeansEstimator EstimatorType; // // */ // applyEstimateModel->SetNumberOfModels(nclasses); // applyEstimateModel->SetInputImage(vecImage); // applyEstimateModel->SetTrainingImage(labelimage); // // //Run the gaussian classifier algorithm // applyEstimateModel->Update(); // applyEstimateModel->Print(// std::cout); // // MembershipFunctionPointerVector membershipFunctions = // applyEstimateModel->GetMembershipFunctions(); // // //---------------------------------------------------------------------- // //Set the decision rule // //---------------------------------------------------------------------- // typedef typename itk::DecisionRuleBase::Pointer DecisionRuleBasePointer; // // typedef itk::MinimumDecisionRule DecisionRuleType; // typename DecisionRuleType::Pointer // myDecisionRule = DecisionRuleType::New(); // // //---------------------------------------------------------------------- // // Set the classifier to be used and assigne the parameters for the // // supervised classifier algorithm except the input image which is // // grabbed from the MRF application pipeline. // //---------------------------------------------------------------------- // //--------------------------------------------------------------------- // typedef PixelType MeasurementVectorType; // // typedef itk::ImageClassifierBase< VecImageType, // ClassImageType > ClassifierType; // // typedef typename itk::ClassifierBase::Pointer // ClassifierBasePointer; // // typedef typename ClassifierType::Pointer ClassifierPointer; // ClassifierPointer myClassifier = ClassifierType::New(); // // Set the Classifier parameters // myClassifier->SetNumberOfClasses(nclasses); // // // Set the decison rule // myClassifier-> // SetDecisionRule((DecisionRuleBasePointer) myDecisionRule ); // // //Add the membership functions // double meanDistance=0; // for( unsigned int i=0; iAddMembershipFunction( membershipFunctions[i] ); // meanDistance+=membershipFunctions[i]->GetMean()[0]; // } // meanDistance/=(float)nclasses; // // std::cout << " mean dist " << meanDistance << std::endl; // // // //---------------------------------------------------------------------- // // Set the MRF labeller and populate the parameters // //---------------------------------------------------------------------- // // //Set the MRF labeller // typedef itk::MRFImageFilter MRFImageFilterType; // typename MRFImageFilterType::Pointer applyMRFImageFilter = MRFImageFilterType::New(); // // // Set the MRF labeller parameters // applyMRFImageFilter->SetNumberOfClasses( nclasses ); // applyMRFImageFilter->SetMaximumNumberOfIterations( maxits ); // applyMRFImageFilter->SetErrorTolerance( 1.e-4 ); // applyMRFImageFilter->SetSmoothingFactor( smf ); // applyMRFImageFilter->SetInput(vecImage); // applyMRFImageFilter->SetClassifier( myClassifier ); // // //For setting up a square/cubic or hypercubic neighborhood // applyMRFImageFilter->SetNeighborhoodRadius( 1 ); // std::vector weights = // applyMRFImageFilter->GetMRFNeighborhoodWeight(); // std::vector testNewNeighborhoodWeight( weights.size(), 1); // double totalWeight = 0; // for(std::vector< double >::const_iterator wcIt = weights.begin(); // wcIt != weights.end(); ++wcIt ) // { // totalWeight += *wcIt; // } // unsigned int jj = 0; // for(std::vector< double >::iterator wIt = weights.begin(); // wIt != weights.end(); wIt++ ) // { // testNewNeighborhoodWeight[jj] = static_cast< double > ( (*wIt) * meanDistance / (2 * totalWeight)); // // // std::cout << " ow " << weights[jj] << " nw " << testNewNeighborhoodWeight[jj] << std::endl; // jj++; // } // // // applyMRFImageFilter->SetMRFNeighborhoodWeight( testNewNeighborhoodWeight ); // // applyMRFImageFilter->SetMRFNeighborhoodWeight( weights ); // // //Kick off the MRF labeller function // applyMRFImageFilter->Update(); // // applyMRFImageFilter->Print(// std::cout); // // std::cout << "Number of Iterations : " << applyMRFImageFilter->GetNumberOfIterations() // << std::endl; // // std::cout << "Stop condition: (1) Maximum number of iterations (2) Error tolerance: " // << applyMRFImageFilter->GetStopCondition() << std::endl; // // typename ClassImageType::Pointer outClassImage = applyMRFImageFilter->GetOutput(); // // //Testing of different parameter access functions in the filter // // std::cout << "The number of classes labelled was: " << // applyMRFImageFilter->GetNumberOfClasses() << std::endl; // // std::cout << "The maximum number of iterations were: " << // applyMRFImageFilter->GetMaximumNumberOfIterations() << std::endl; // // std::cout << "The error tolerace threshold was: " << // applyMRFImageFilter->GetErrorTolerance() << std::endl; // // std::cout << "The smoothing MRF parameter used was: " << // applyMRFImageFilter->GetSmoothingFactor() << std::endl; // // std::cout << "The MRF neighborhood weights are: " << std::endl; // // // return outClassImage; // // } template typename TImage::Pointer // void itkMRIBiasFieldCorrectionFilter(typename TImage::Pointer image, typename TImage::Pointer labelimage, unsigned int sd = 2) { // std::cout << "doing Bias corr " << std::endl; typedef TImage ImageType; enum { ImageDimension = ImageType::ImageDimension }; typedef itk::ImageRegionIteratorWithIndex ImageIteratorType; // bool SaveImages = false; // WriteImage(image,"temp1.nii"); // WriteImage(labelimage,"temp2.nii"); // class statistics for two classes: a bright sphere and background // 3 or 1 classes ? SD // unsigned int numclasses=1; unsigned int numclasses = 4; itk::Array classMeans(numclasses); itk::Array classSigmas(numclasses); itk::Array classCounts(numclasses); classMeans.Fill(0.0); classSigmas.Fill(0.0); classCounts.Fill(0); // ******** get output mask from image SD typename ImageType::Pointer outputmask = BinaryThreshold(0.1, 1.e9, 1, image); // ******** get output mask from image SD // WriteImage(mask,"outMK1.nii"); // SD play with mask to see if we can move the artifact line // mask=MorphologicalErosion(1.5, mask); // mask=MorphologicalErosion(3.5, mask); // WriteImage(mask,"outMK2.nii"); ImageIteratorType o_iter( labelimage, labelimage->GetLargestPossibleRegion() ); o_iter.GoToBegin(); while( !o_iter.IsAtEnd() ) { unsigned int label = (unsigned int) o_iter.Get(); float pix = image->GetPixel(o_iter.GetIndex() ); // uncomment for using one tissue class only for input mask SD /* if ( mask->GetPixel(o_iter.GetIndex()) == 1 ) { label=label-1; unsigned int ind=0; classCounts[ind] +=1; float n = classCounts[ind]; classMeans[ind] = (n-1.0)/( n )* classMeans[ind] + 1.0 / n * pix; double sqrdf = ( pix - classMeans[ind])*( pix - classMeans[ind]); if (n > 1)classSigmas[ind]= (n-1)/n * classSigmas[ind] + 1.0 / (n-1) * sqrdf; } */ // uncomment for using 3 tissue classes SD if( outputmask->GetPixel(o_iter.GetIndex() ) == 1 ) { // label=label-1; classCounts[label] += 1; float n = classCounts[label]; classMeans[label] = (n - 1.0) / ( n ) * classMeans[label] + 1.0 / n * pix; double sqrdf = ( pix - classMeans[label]) * ( pix - classMeans[label]); if( n > 1 ) { classSigmas[label] = (n - 1) / n * classSigmas[label] + 1.0 / (n - 1) * sqrdf; } } ++o_iter; } for( unsigned int k = 0; k < numclasses; k++ ) { classSigmas[k] = sqrt(classSigmas[k]); // std::cout << " Initial Means pre-bias " << classMeans[k] << " sig " << classSigmas[k] << std::endl; } // creats a normal random variate generator // itk::Statistics::NormalVariateGenerator::Pointer randomGenerator = // itk::Statistics::NormalVariateGenerator::New() ; // creates a bias correction filter and run it. typedef itk::MRIBiasFieldCorrectionFilter FilterType; // std::cout << "before new filter" << std::endl; typename FilterType::Pointer filter = FilterType::New(); // std::cout << "after new filter" << std::endl; // typename FilterType::BiasFieldType::CoefficientArrayType filter->SetInput( image.GetPointer() ); // filter->SetInput( image ) ; filter->IsBiasFieldMultiplicative( true ); // correct with multiplicative bias unsigned int biasDegree = 4; filter->SetBiasFieldDegree( biasDegree ); // default value = 3 filter->SetTissueClassStatistics( classMeans, classSigmas ); // filter->SetOptimizerGrowthFactor( 1.01 ) ; // default value // filter->SetOptimizerInitialRadius( 0.02 ) ; // default value // SD debug don't do interslice correction // filter->SetUsingInterSliceIntensityCorrection( true ) ; // default value filter->SetUsingInterSliceIntensityCorrection( false ); // default value filter->SetVolumeCorrectionMaximumIteration( 200); // default value = 100 filter->SetInterSliceCorrectionMaximumIteration( 100 ); // default value = 100 filter->SetUsingSlabIdentification( false ); // default value = false // filter->SetSlabBackgroundMinimumThreshold( 0 ) ; // default // value // filter->SetSlabNumberOfSamples( 10 ) ; // default value // filter->SetSlabTolerance(0.0) ; // default value filter->SetSlicingDirection(sd); // default value filter->SetUsingBiasFieldCorrection( true ); // default value filter->SetGeneratingOutput( true ); // default value filter->SetInputMask( outputmask ); // ******** Try different output mask SD // filter->SetOutputMask( labelimage ) ; filter->SetOutputMask( outputmask ); // filter->SetDebug( true ); // WriteImage(mask,"mask.nii"); // WriteImage(outputmask,"outputmask.nii"); // WriteImage(image,"image.nii"); // WriteImage(labelimage,"labelimage.nii"); // ******** Try different output mask SD // default schedule is 2 2 2 - 1 1 1, let's change this bool setnewsched = false; if( setnewsched ) { unsigned int nlev = 1; typename FilterType::ScheduleType schedule( nlev, ImageDimension); schedule.Fill( 4 ); // for (unsigned int jj=0; jjSetNumberOfLevels( nlev ); // Important to set this first, otherwise the filter rejects the new // schedule filter->SetSchedule( schedule ); } // filter->SetInitialBiasFieldCoefficients(initCoefficients); filter->SetVolumeCorrectionMaximumIteration( 200 ); // default value = 100 filter->SetInterSliceCorrectionMaximumIteration( 100 ); // default value = 100 // filter->SetOptimizerInitialRadius( 0.02 ) ; // default // value // timing // long int t1 = time(ITK_NULLPTR); filter->Update(); // long int t2 = time(ITK_NULLPTR); // std::cout << "Run time (in s)" << t2 - t1 << std::endl; return filter->GetOutput(); } // template // typename TImage::Pointer // //void // SegmentMRFKM(typename TImage::Pointer image , // typename TImage::Pointer labelimage, unsigned int nclasses, float smf, unsigned int maxit) // { // // typedef TImage ImageType; // typedef typename TImage::PixelType PixelType; // enum { ImageDimension = ImageType::ImageDimension }; // enum { Dimension = ImageType::ImageDimension }; // // // // const unsigned int NUMBANDS=1; // typedef itk::Image,ImageDimension> VecImageType; // typedef typename VecImageType::PixelType VecPixelType; // // /** copy the input image into this vector image. stupid. */ // // typename VecImageType::Pointer vecImage = VecImageType::New(); // vecImage->SetSpacing(image->GetSpacing()); // vecImage->SetOrigin(image->GetOrigin()); // vecImage->SetLargestPossibleRegion( image->GetLargestPossibleRegion() ); // vecImage->SetBufferedRegion( image->GetLargestPossibleRegion() ); // vecImage->Allocate(); // VecPixelType vvv; // vvv.Fill(0); // vecImage->FillBuffer(vvv); // // // setup the iterators // // typedef VecImageType::PixelType::VectorType VecPixelType; // // enum { VecImageDimension = VecImageType::ImageDimension }; // typedef itk::ImageRegionIterator< VecImageType > VecIterator; // // VecIterator outIt( vecImage, vecImage->GetBufferedRegion() ); // for (outIt.GoToBegin(); !outIt.IsAtEnd(); ++outIt) // { // vvv[0]=image->GetPixel(outIt.GetIndex()); // outIt.Set(vvv); // } // // // namespace stat = itk::Statistics; // // typedef itk::Image ClassImageType; // // // //---------------------------------------------------------------------- // //Set membership function (Using the statistics objects) // //---------------------------------------------------------------------- // // typedef itk::Statistics::DistanceToCentroidMembershipFunction< VecPixelType > // MembershipFunctionType ; // // typedef typename MembershipFunctionType::Pointer MembershipFunctionPointer ; // // typedef std::vector< MembershipFunctionPointer > // MembershipFunctionPointerVector; // // //---------------------------------------------------------------------- // //Set the image model estimator // //---------------------------------------------------------------------- // typedef itk::ImageKmeansModelEstimator< VecImageType, // MembershipFunctionType> ImageKmeansModelEstimatorType; // // typename ImageKmeansModelEstimatorType::Pointer // applyKmeansModelEstimator = ImageKmeansModelEstimatorType::New(); // // //---------------------------------------------------------------------- // //Set the parameters of the clusterer // //---------------------------------------------------------------------- // // // std::cout << "Starting to build the K-means model ....." << std::endl; // // applyKmeansModelEstimator->SetInputImage( vecImage ); // applyKmeansModelEstimator->SetNumberOfModels(nclasses); // applyKmeansModelEstimator->SetThreshold(0.0001); // applyKmeansModelEstimator->Update(); // // MembershipFunctionPointerVector membershipFunctions = // applyKmeansModelEstimator->GetMembershipFunctions(); // // typedef std::vector TempVectorType; // typedef TempVectorType::iterator TempVectorIterator; // TempVectorIterator start, end; // // std::vector kmeansResultForClass(membershipFunctions.size()); // // // // std::cout << "Result of K-Means clustering" << std::endl; // // double meanDistance=0; // for(unsigned int classIndex=0; classIndex < membershipFunctions.size(); // classIndex++ ) // { // kmeansResultForClass[classIndex] = // (double) (membershipFunctions[classIndex]->GetCentroid())[0]; // meanDistance+=kmeansResultForClass[classIndex];//membershipFunctions[i]->GetMean()[0]; // } // meanDistance/=(float)nclasses; // // std::cout << " mean dist " << meanDistance << std::endl; // // // start = kmeansResultForClass.begin(); // end = kmeansResultForClass.end(); // // std::sort( start, end ); // // vnl_vector temp = membershipFunctions[0]->GetCentroid(); // for(unsigned int classIndex=0; classIndex < membershipFunctions.size(); // classIndex++ ) // { // temp[0] = (double) kmeansResultForClass[classIndex]; // membershipFunctions[classIndex]->SetCentroid(temp); // } // // for(unsigned int classIndex=0; classIndex < membershipFunctions.size(); // classIndex++ ) // { // // std::cout << (membershipFunctions[classIndex]->GetCentroid())[0] << std::endl; // } // // //---------------------------------------------------------------------- // //Set the decision rule // //---------------------------------------------------------------------- // typedef itk::DecisionRuleBase::Pointer DecisionRuleBasePointer; // // typedef itk::MinimumDecisionRule DecisionRuleType; // DecisionRuleType::Pointer // classifierDecisionRule = DecisionRuleType::New(); // // //------------------------------------------------------ // //Instantiate the classifier model (as the input image is in right format) // //------------------------------------------------------ // // //Assign a class label image type // // typedef itk::Image ClassImageType; // // typedef itk::ImageClassifierBase< VecImageType,ClassImageType > // SupervisedClassifierType; // // typename SupervisedClassifierType::Pointer // classifierPointer = SupervisedClassifierType::New(); // // // //------------------------------------------------------ // // Set the Classifier parameters // //------------------------------------------------------ // classifierPointer->SetNumberOfClasses( nclasses ); // classifierPointer->SetInputImage( vecImage ); // // // Set the decison rule // classifierPointer-> // SetDecisionRule( (DecisionRuleBasePointer) classifierDecisionRule ); // // MembershipFunctionPointer membershipFunction; // //------------------------------------------------------ // //Set the classifier membership functions // //------------------------------------------------------ // for( unsigned int i=0; iAddMembershipFunction( membershipFunctions[i] ); // } // // //Do the classification // //Run the kmeans classifier algorithm // classifierPointer->Update(); // // //Get the classified image // typedef typename ClassImageType::Pointer ClassifiedImagePointer; // ClassifiedImagePointer outClassImage = // classifierPointer->GetClassifiedImage(); // // //------------------------------------------------------ // //Mask the output of the classifier // //------------------------------------------------------ // // // Declare the type for the MaskInput filter // // typedef itk::MaskImageFilter< ClassImageType, // ClassImageType, // ClassImageType > MaskFilterType; // // typedef typename ClassImageType::Pointer MaskedOutputImagePointer; // typedef typename MaskFilterType::Pointer MaskFilterTypePointer; // // // Create an ADD Filter // MaskFilterTypePointer maskfilter = MaskFilterType::New(); // // // Connect the input images // maskfilter->SetInput1( outClassImage ); // maskfilter->SetInput2( labelimage ); // // // Execute the filter // maskfilter->Update(); // // // Get the Smart Pointer to the Filter Output // MaskedOutputImagePointer maskedOutputImage = maskfilter->GetOutput(); // // // this->SetClassifiedImage( maskedOutputImage ); // // //------------------------------------------------------ // //Set the MRF labeller and populate the parameters // //------------------------------------------------------ // //Set the MRF labeller // typedef itk::MRFImageFilter // MRFFilterType; // // typename MRFFilterType::Pointer applyMRFFilter = MRFFilterType::New(); // // // Set the MRF labeller parameters // applyMRFFilter->SetNumberOfClasses(nclasses); // unsigned int m_MaximumNumberOfIterations=maxit; // applyMRFFilter->SetMaximumNumberOfIterations(m_MaximumNumberOfIterations); // float m_ErrorTolerance=1.e-5; // applyMRFFilter->SetErrorTolerance(m_ErrorTolerance); // float m_SmoothingFactor=smf; // applyMRFFilter->SetSmoothingFactor( m_SmoothingFactor ); // // //For setting up a square/cubic or hypercubic neighborhood // applyMRFFilter->SetNeighborhoodRadius( 1 ); // std::vector weights = // applyMRFFilter->GetMRFNeighborhoodWeight(); // std::vector testNewNeighborhoodWeight( weights.size(), 1); // double totalWeight = 0; // for(std::vector< double >::const_iterator wcIt = weights.begin(); // wcIt != weights.end(); ++wcIt ) // { // totalWeight += *wcIt; // } // unsigned int jj = 0; // for(std::vector< double >::iterator wIt = weights.begin(); // wIt != weights.end(); wIt++ ) // { // testNewNeighborhoodWeight[jj] = static_cast< double > ( (*wIt) * meanDistance / (2 * totalWeight)); // //// std::cout << " ow " << weights[jj] << " nw " << testNewNeighborhoodWeight[jj] << std::endl; // jj++; // } // // applyMRFFilter->SetMRFNeighborhoodWeight( testNewNeighborhoodWeight ); // // applyMRFFilter->SetInput(vecImage); // applyMRFFilter->SetClassifier( classifierPointer ); // // //Kick off the MRF labeller function // applyMRFFilter->Update(); // // applyMRFFilter->Print(// std::cout); // outClassImage = applyMRFFilter->GetOutput(); // // //------------------------------------------------------ // //Mask the output of the classifier // //------------------------------------------------------ // // // Declare the type for the MaskInput filter // // // Create an ADD Filter // MaskFilterTypePointer maskfilter2 = MaskFilterType::New(); // // // Connect the input images // maskfilter2->SetInput1( outClassImage ); // maskfilter2->SetInput2( labelimage ); // // // Execute the filter // maskfilter2->Update(); // // // Get the Smart Pointer to the Filter Output // maskedOutputImage = maskfilter2->GetOutput(); // // // this->SetClassifiedImage( maskedOutputImage ); // // return outClassImage;//maskedOutputImage; // // } template int SmoothImage(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::vector sigmaVector; if( argc > argct ) { sigmaVector = ConvertVector( argv[argct] ); } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer varimage = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); if( sigmaVector.size() == 1 ) { filter->SetVariance( vnl_math_sqr( sigmaVector[0] ) ); } else if( sigmaVector.size() == ImageDimension ) { typename dgf::ArrayType varianceArray; for( unsigned int d = 0; d < ImageDimension; d++ ) { varianceArray[d] = vnl_math_sqr( sigmaVector[d] ); } filter->SetVariance( varianceArray ); } else { std::cerr << "Incorrect sigma vector size. Must either be of size 1 or ImageDimension." << std::endl; } bool usespacing = true; if( !usespacing ) { filter->SetUseImageSpacingOff(); } else { filter->SetUseImageSpacingOn(); } filter->SetMaximumError(.01f); filter->SetInput(image1); filter->Update(); varimage = filter->GetOutput(); WriteImage( varimage, outname.c_str() ); return 0; } template int MorphImage(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct++; std::string operation = std::string(argv[argct]); argct++; std::string fn1 = std::string(argv[argct]); argct++; float sigma = 1.0; if( argc > argct ) { sigma = atof(argv[argct]); } unsigned int morphopt = 1; if( strcmp(operation.c_str(), "ME") == 0 ) { morphopt = 0; } else if( strcmp(operation.c_str(), "MO") == 0 ) { morphopt = 2; } else if( strcmp(operation.c_str(), "MC") == 0 ) { morphopt = 3; } else if( strcmp(operation.c_str(), "GE") == 0 ) { morphopt = 4; } else if( strcmp(operation.c_str(), "GD") == 0 ) { morphopt = 5; } else if( strcmp(operation.c_str(), "GO") == 0 ) { morphopt = 6; } else if( strcmp(operation.c_str(), "GC") == 0 ) { morphopt = 7; } typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); // SRD set dilatevalue float dilateval = 1; if( argc > argct + 1 ) { dilateval = atof(argv[argct + 1]); } image1 = ants::Morphological(image1, sigma, morphopt, dilateval); if( outname.length() > 3 ) { WriteImage(image1, outname.c_str() ); } return 0; } template int FastMarchingSegmentation( unsigned int argc, char *argv[] ) { unsigned int argct = 2; const std::string outname = std::string(argv[argct]); std::cout << outname << argc << " This function has been disabled --- see PropagateLabelsThroughMask " << std::endl; return 0; } template int PropagateLabelsThroughMask(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; float thresh = 0.5; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; float stopval = 100.0; if( argc > argct ) { fn2 = std::string(argv[argct]); argct++; } else { // std::cout << " not enough parameters -- need label image " << std::endl; return 0; } if( argc > argct ) { stopval = atof(argv[argct]); argct++; } unsigned int topocheck = 0; if( argc > argct ) { topocheck = atoi(argv[argct]); argct++; } typename ImageType::Pointer speedimage = ITK_NULLPTR; ReadImage(speedimage, fn1.c_str() ); typename ImageType::Pointer labimage = ITK_NULLPTR; ReadImage(labimage, fn2.c_str() ); typename ImageType::Pointer fastimage = ITK_NULLPTR; ReadImage(fastimage, fn1.c_str() ); typename ImageType::Pointer outlabimage = ITK_NULLPTR; ReadImage(outlabimage, fn2.c_str() ); fastimage->FillBuffer(1.e9); // compute max label double maxlabel = 0; Iterator vfIter2( labimage, labimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { bool isinside = true; double speedval = speedimage->GetPixel(vfIter2.GetIndex() ); double labval = labimage->GetPixel(vfIter2.GetIndex() ); if( speedval < thresh ) { isinside = false; } if( isinside ) { if( labval > maxlabel ) { maxlabel = labval; } } } typedef itk::FMarchingImageFilter FastMarchingFilterType; typedef typename FastMarchingFilterType::LabelImageType LabelImageType; typename FastMarchingFilterType::Pointer fastMarching; for( unsigned int lab = 1; lab <= (unsigned int)maxlabel; lab++ ) { typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( labimage ); thresholder->SetLowerThreshold( lab ); thresholder->SetUpperThreshold( lab ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); typedef itk::LabelContourImageFilter ContourFilterType; typename ContourFilterType::Pointer contour = ContourFilterType::New(); contour->SetInput( thresholder->GetOutput() ); contour->FullyConnectedOff(); contour->SetBackgroundValue( itk::NumericTraits::ZeroValue() ); contour->Update(); typename ImageType::Pointer contourimage = contour->GetOutput(); fastMarching = FastMarchingFilterType::New(); fastMarching->SetInput( speedimage ); fastMarching->SetTopologyCheck( FastMarchingFilterType::None ); if( topocheck == 1 ) // Strict { // std::cout << " strict " << std::endl; fastMarching->SetTopologyCheck( FastMarchingFilterType::Strict ); } if( topocheck == 2 ) // No handles { // std::cout << " no handles " << std::endl; fastMarching->SetTopologyCheck( FastMarchingFilterType::NoHandles ); } typedef typename FastMarchingFilterType::NodeContainer NodeContainer; typedef typename FastMarchingFilterType::NodeType NodeType; typename NodeContainer::Pointer seeds = NodeContainer::New(); seeds->Initialize(); typename NodeContainer::Pointer alivePoints = NodeContainer::New(); alivePoints->Initialize(); unsigned long aliveCount = 0; unsigned long ct = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { double labval = labimage->GetPixel( vfIter2.GetIndex() ); double contourval = contourimage->GetPixel( vfIter2.GetIndex() ); if( ( (unsigned int) contourval == 1 ) && ( (unsigned int) labval == lab ) ) { NodeType node; const double seedValue = 0.0; node.SetValue( seedValue ); node.SetIndex( vfIter2.GetIndex() ); seeds->InsertElement( ct, node ); ct++; } if( ( (unsigned int) contourval == 0 ) && ( (unsigned int) labval == lab ) ) { NodeType node; const double seedValue = 0.0; node.SetValue( seedValue ); node.SetIndex( vfIter2.GetIndex() ); alivePoints->InsertElement( aliveCount, node ); aliveCount++; } } fastMarching->SetTrialPoints( seeds ); fastMarching->SetAlivePoints( alivePoints ); fastMarching->SetStoppingValue( stopval ); fastMarching->Update(); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { bool isinside = true; double speedval = speedimage->GetPixel(vfIter2.GetIndex() ); double labval = labimage->GetPixel(vfIter2.GetIndex() ); if( speedval < thresh ) { isinside = false; } if( isinside && labval == 0 ) { double fmarrivaltime = fastMarching->GetOutput()->GetPixel( vfIter2.GetIndex() ); double mmm = fastimage->GetPixel(vfIter2.GetIndex() ); if( fmarrivaltime < mmm ) { fastimage->SetPixel(vfIter2.GetIndex(), fmarrivaltime ); outlabimage->SetPixel(vfIter2.GetIndex(), lab ); } } else if( !isinside ) { outlabimage->SetPixel(vfIter2.GetIndex(), 0 ); } } } std::string::size_type idx; idx = outname.find_first_of('.'); std::string tempname = outname.substr(0, idx); std::string extension = outname.substr(idx, outname.length() ); std::string kname = tempname + std::string("_speed") + extension; std::string lname = tempname + std::string("_label") + extension; WriteImage(fastimage, kname.c_str() ); WriteImage(outlabimage, outname.c_str() ); WriteImage(fastMarching->GetLabelImage(), lname.c_str() ); return 0; } template int FastMarchingExtension(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; // speed image std::string fn2 = ""; // label image std::string fn3 = ""; // value image if( argc > argct ) { fn2 = std::string(argv[argct]); argct++; } else { // std::cout << " not enough parameters -- need label image " << std::endl; return 0; } if( argc > argct ) { fn3 = std::string(argv[argct]); argct++; } else { // std::cout << " not enough parameters -- need value image " << std::endl; return 0; } typename ImageType::Pointer speedimage = ITK_NULLPTR; ReadImage(speedimage, fn1.c_str() ); typename ImageType::Pointer labimage = ITK_NULLPTR; ReadImage(labimage, fn2.c_str() ); typename ImageType::Pointer valimage = ITK_NULLPTR; ReadImage(valimage, fn3.c_str() ); typedef itk::FastMarchingThresholdStoppingCriterion< ImageType, ImageType > CriterionType; typedef typename CriterionType::Pointer CriterionPointer; CriterionPointer criterion = CriterionType::New(); criterion->SetThreshold( 1.e9 ); // something large typedef itk::FastMarchingExtensionImageFilterBase MarcherBaseType; typedef typename MarcherBaseType::LabelImageType LabelImageType; typename MarcherBaseType::Pointer fastMarching; typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( labimage ); thresholder->SetLowerThreshold( 0.5 ); thresholder->SetUpperThreshold( 1.001 ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); typedef itk::LabelContourImageFilter ContourFilterType; typename ContourFilterType::Pointer contour = ContourFilterType::New(); contour->SetInput( thresholder->GetOutput() ); contour->FullyConnectedOff(); contour->SetBackgroundValue( itk::NumericTraits::ZeroValue() ); contour->Update(); typename ImageType::Pointer contourimage = contour->GetOutput(); // contour defines starting points fastMarching = MarcherBaseType::New(); fastMarching->SetInput( speedimage ); typedef typename MarcherBaseType::NodePairType NodePairType; typedef typename MarcherBaseType::NodePairContainerType NodePairContainerType; typedef typename MarcherBaseType::AuxValueVectorType AuxValueVectorType; typedef typename MarcherBaseType::AuxValueContainerType AuxValueContainerType; typename AuxValueContainerType::Pointer auxAliveValues = AuxValueContainerType::New(); typename AuxValueContainerType::Pointer auxTrialValues = AuxValueContainerType::New(); typename NodePairContainerType::Pointer seeds = NodePairContainerType::New(); seeds->Initialize(); typename NodePairContainerType::Pointer alivePoints = NodePairContainerType::New(); alivePoints->Initialize(); unsigned int seedct = 0, alivect = 0; Iterator vfIter2( labimage, labimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { typename ImageType::IndexType ind = vfIter2.GetIndex(); double labval = labimage->GetPixel( ind ); double contourval = contourimage->GetPixel( ind ); if ( ( (unsigned int) contourval == 1 ) ) { seeds->push_back( NodePairType( ind, 0. ) ); AuxValueVectorType vector; vector[0] = valimage->GetPixel( ind ); auxTrialValues->push_back( vector ); seedct++; } if ( ( labval > 0 ) && ((unsigned int) contourval != 1 ) ) { alivePoints->push_back( NodePairType( ind, 0. ) ); AuxValueVectorType vector; vector[0] = valimage->GetPixel( ind ); auxAliveValues->push_back( vector ); alivect++; } } fastMarching->SetTrialPoints( seeds ); fastMarching->SetAuxiliaryTrialValues( auxTrialValues ); fastMarching->SetAlivePoints( alivePoints ); fastMarching->SetAuxiliaryAliveValues( auxAliveValues ); fastMarching->SetStoppingCriterion( criterion ); fastMarching->Update(); WriteImage( fastMarching->GetAuxiliaryImage(0), outname.c_str() ); return 0; } template int itkPropagateLabelsThroughMask(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; float thresh = 1.e-9; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; float stopval = 100.0; if( argc > argct ) { fn2 = std::string(argv[argct]); argct++; } else { // std::cout << " not enough parameters -- need label image " << std::endl; return 0; } if( argc > argct ) { stopval = atof(argv[argct]); argct++; } unsigned int topocheck = 0; if( argc > argct ) { topocheck = atoi(argv[argct]); argct++; } typename ImageType::Pointer speedimage = ITK_NULLPTR; ReadImage(speedimage, fn1.c_str() ); typename ImageType::Pointer labimage = ITK_NULLPTR; ReadImage(labimage, fn2.c_str() ); typename ImageType::Pointer fastimage = ITK_NULLPTR; ReadImage(fastimage, fn1.c_str() ); typename ImageType::Pointer outlabimage = ITK_NULLPTR; ReadImage(outlabimage, fn2.c_str() ); fastimage->FillBuffer(1.e9); // compute max label double maxlabel = 0; Iterator vfIter2( labimage, labimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { bool isinside = true; double speedval = speedimage->GetPixel(vfIter2.GetIndex() ); double labval = labimage->GetPixel(vfIter2.GetIndex() ); if( speedval < thresh ) { isinside = false; } if( isinside ) { if( labval > maxlabel ) { maxlabel = labval; } } } typedef itk::FastMarchingThresholdStoppingCriterion< ImageType, ImageType > CriterionType; typedef typename CriterionType::Pointer CriterionPointer; CriterionPointer criterion = CriterionType::New(); criterion->SetThreshold( stopval ); typedef itk::FastMarchingImageFilterBase FastMarchingFilterType; typedef typename FastMarchingFilterType::LabelImageType LabelImageType; typename FastMarchingFilterType::Pointer fastMarching; for( unsigned int lab = 1; lab <= (unsigned int)maxlabel; lab++ ) { typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( labimage ); thresholder->SetLowerThreshold( lab ); thresholder->SetUpperThreshold( lab ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); typedef itk::LabelContourImageFilter ContourFilterType; typename ContourFilterType::Pointer contour = ContourFilterType::New(); contour->SetInput( thresholder->GetOutput() ); contour->FullyConnectedOff(); contour->SetBackgroundValue( itk::NumericTraits::ZeroValue() ); contour->Update(); typename ImageType::Pointer contourimage = contour->GetOutput(); fastMarching = FastMarchingFilterType::New(); fastMarching->SetInput( speedimage ); fastMarching->SetStoppingCriterion( criterion ); if( topocheck == 1 ) // Strict { // std::cout << " strict " << std::endl; fastMarching->SetTopologyCheck( FastMarchingFilterType::Strict ); } if( topocheck == 2 ) // No handles { // std::cout << " no handles " << std::endl; fastMarching->SetTopologyCheck( FastMarchingFilterType::NoHandles ); } typedef typename FastMarchingFilterType::NodePairContainerType NodeContainer; typedef typename FastMarchingFilterType::NodePairType NodePairType; typename NodeContainer::Pointer seeds = NodeContainer::New(); seeds->Initialize(); typename NodeContainer::Pointer alivePoints = NodeContainer::New(); alivePoints->Initialize(); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { double labval = labimage->GetPixel( vfIter2.GetIndex() ); double contourval = contourimage->GetPixel( vfIter2.GetIndex() ); if( ( (unsigned int) contourval == 1 ) && ( (unsigned int) labval == lab ) ) { seeds->push_back( NodePairType( vfIter2.GetIndex(), 0. ) ); } if( ( (unsigned int) contourval == 0 ) && ( (unsigned int) labval == lab ) ) { alivePoints->push_back( NodePairType( vfIter2.GetIndex(), 0. ) ); } } fastMarching->SetTrialPoints( seeds ); fastMarching->SetAlivePoints( alivePoints ); fastMarching->Update(); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { bool isinside = true; double speedval = speedimage->GetPixel(vfIter2.GetIndex() ); double labval = labimage->GetPixel(vfIter2.GetIndex() ); if( speedval < thresh ) { isinside = false; } if( isinside && labval == 0 ) { double fmarrivaltime = fastMarching->GetOutput()->GetPixel( vfIter2.GetIndex() ); double mmm = fastimage->GetPixel(vfIter2.GetIndex() ); if( fmarrivaltime < mmm ) { fastimage->SetPixel(vfIter2.GetIndex(), fmarrivaltime ); outlabimage->SetPixel(vfIter2.GetIndex(), lab ); } } else if( !isinside ) { outlabimage->SetPixel(vfIter2.GetIndex(), 0 ); } } } std::string::size_type idx; idx = outname.find_first_of('.'); std::string tempname = outname.substr(0, idx); std::string extension = outname.substr(idx, outname.length() ); std::string kname = tempname + std::string("_speed") + extension; std::string lname = tempname + std::string("_label") + extension; WriteImage(fastimage, kname.c_str() ); WriteImage(outlabimage, outname.c_str() ); WriteImage(fastMarching->GetLabelImage(), lname.c_str() ); return 0; } template int DistanceMap(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; if( argc < 5 ) { // std::cout << "Missing required arguments ( output name, operation & fn1)" << std::endl; throw; } const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typedef itk::DanielssonDistanceMapImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->InputIsBinaryOff(); filter->SetUseImageSpacing(true); filter->SetInput(image1); filter->Update(); if( outname.length() > 3 ) { WriteImage(filter->GetOutput(), outname.c_str() ); } return 0; } template int GenerateMaurerDistanceImage( int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; // Usage: ImageMath 3 output.nii.gz MaurerDistance input.nii.gz {foreground=1} const std::string outputName = std::string( argv[2] ); const std::string inputName = std::string( argv[4] ); typename ImageType::Pointer input = ITK_NULLPTR; ReadImage( input, inputName.c_str() ); PixelType foreground = 1; if( argc > 5 ) { foreground = static_cast( atof( argv[5] ) ); } typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( input ); thresholder->SetLowerThreshold( foreground ); thresholder->SetUpperThreshold( foreground ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); typedef itk::SignedMaurerDistanceMapImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( thresholder->GetOutput() ); filter->SetSquaredDistance( false ); filter->SetUseImageSpacing( true ); filter->SetInsideIsPositive( false ); filter->Update(); WriteImage( filter->GetOutput(), outputName.c_str() ); return 0; } template int FillHoles(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image LabelImageType; typedef itk::CastImageFilter CastFilterType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float holeparam = 2.0; if( argc > argct ) { holeparam = atof(argv[argct]); } typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typename ImageType::Pointer imageout = ITK_NULLPTR; ReadImage(imageout, fn1.c_str() ); image1 = BinaryThreshold(0.5, 1.e9, 1, image1); typedef itk::DanielssonDistanceMapImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->InputIsBinaryOff(); filter->SetUseImageSpacing(false); filter->SetInput(image1); filter->Update(); // algorithm : // 1. get distance map of object // 2. threshold // 3. label connected components // 4. label surface // 5. if everywhere on surface is next to object then it's a hole // 6. make sure it's not the background typedef itk::Image labelimagetype; typename ImageType::Pointer dist = filter->GetOutput(); typename ImageType::Pointer regions = BinaryThreshold(0.001, 1.e9, 1, dist); typedef itk::ConnectedComponentImageFilter ccFilterType; typedef itk::RelabelComponentImageFilter RelabelType; typename RelabelType::Pointer relabel = RelabelType::New(); typename ccFilterType::Pointer ccfilter = ccFilterType::New(); typename CastFilterType::Pointer castRegions = CastFilterType::New(); castRegions->SetInput( regions ); ccfilter->SetInput(castRegions->GetOutput() ); ccfilter->SetFullyConnected( 0 ); ccfilter->Update(); relabel->SetInput( ccfilter->GetOutput() ); relabel->SetMinimumObjectSize( 0 ); // relabel->SetUseHistograms(true); try { relabel->Update(); } catch( itk::ExceptionObject & /* excep */ ) { // std::cout << "Relabel: exception caught !" << std::endl; // std::cout << excep << std::endl; } // WriteImage(relabel->GetOutput(),"test.nii"); if( holeparam == 2 ) { typedef itk::ImageRegionIteratorWithIndex RelabelIterator; RelabelIterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 1 ) { imageout->SetPixel(vfIter.GetIndex(), 1); } } WriteImage(imageout, outname.c_str() ); return 0; } typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for( unsigned int j = 0; j < ImageDimension; j++ ) { rad[j] = 1; } iteratorType GHood(rad, relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); float maximum = relabel->GetNumberOfObjects(); // now we have the exact number of objects labeled independently for( unsigned int lab = 2; lab <= maximum; lab++ ) { float erat = 2; if( holeparam <= 1 ) { GHood.GoToBegin(); unsigned long objectedge = 0; unsigned long backgroundedge = 0; unsigned long totaledge = 0; unsigned long volume = 0; while( !GHood.IsAtEnd() ) { typename ImageType::PixelType p = GHood.GetCenterPixel(); typename ImageType::IndexType ind2; if( p == lab ) { volume++; for( unsigned int i = 0; i < GHood.Size(); i++ ) { ind2 = GHood.GetIndex(i); float val2 = image1->GetPixel(ind2); if( val2 >= 0.5 && GHood.GetPixel(i) != lab ) { objectedge++; totaledge++; } else if( val2 < 0.5 && GHood.GetPixel(i) != lab ) { backgroundedge++; totaledge++; } } } ++GHood; } erat = (float)objectedge / (float)totaledge; // std::cout << " Lab " << lab << " volume " << volume << " v-rat " << vrat << " edge " << erat << std::endl; } if( erat > holeparam ) // fill the hole { // std::cout << " Filling " << lab << " of " << maximum << std::endl; typedef itk::ImageRegionIteratorWithIndex RelabelIterator; RelabelIterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() == lab ) { imageout->SetPixel(vfIter.GetIndex(), 1); } } } } WriteImage(imageout, outname.c_str() ); return 0; } template int NormalizeImage(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float option = 0; bool useMaskImage = false; typename ImageType::Pointer maskImage = ITK_NULLPTR; if( argc > argct ) { std::string maskFileName = std::string( argv[argct] ); ReadImage( maskImage, maskFileName.c_str() ); if( maskImage.IsNotNull() ) { useMaskImage = true; } else { option = atof( argv[argct] ); } } typename ImageType::Pointer image = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); if( useMaskImage ) { itk::ImageRegionIterator It( maskImage, maskImage->GetLargestPossibleRegion() ); itk::ImageRegionIterator It2( image, image->GetLargestPossibleRegion() ); float roiMean = 0.0; float count = 0.0; for( It.GoToBegin(), It2.GoToBegin(); !It.IsAtEnd(); ++It, ++It2 ) { if( It.Get() != 0 ) { roiMean += It2.Get(); count += 1.0; } } roiMean /= count; for( It2.GoToBegin(); !It2.IsAtEnd(); ++It2 ) { It2.Set( It2.Get() / roiMean ); } if( outname.length() > 3 ) { WriteImage( image, outname.c_str() ); } } else { float max = 0; float min = 1.e9; float mean = 0.0; unsigned long ct = 0; typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator iter( image, image->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { float pix = iter.Get(); // if (option == 0) if (pix < 0) pix=0; mean += pix; ct++; if( pix > max ) { max = pix; } if( pix < min ) { min = pix; } } mean /= (float)ct; for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { float pix = iter.Get(); if( option == 0 ) { pix = (pix - min) / (max - min); iter.Set(pix); } else { iter.Set(pix / mean); } } if( outname.length() > 3 ) { WriteImage( image, outname.c_str() ); } } return 0; } template int PrintHeader(int argc, char *argv[]) { typedef float inPixelType; typedef itk::Image ImageType; int argct = 4; std::string fn1 = std::string(argv[argct]); if( argc > 20 ) { // std::cout << " k " << std::endl; } typename ImageType::Pointer image; ReadImage(image, fn1.c_str() ); // std::cout << " Spacing " << image->GetSpacing() << std::endl; // std::cout << " Origin " << image->GetOrigin() << std::endl; // std::cout << " Direction " << std::endl << image->GetDirection() << std::endl; // std::cout << " Size " << std::endl << image->GetLargestPossibleRegion().GetSize() << std::endl; // if (strcmp(operation.c_str(),"n_last_dim") == 0){ // unsigned int lastdim=image->GetLargestPossibleRegion().GetSize()[ImageDimension-1]; // std::ofstream logfile; // logfile.open(outname.c_str() ); // if (logfile.good() ) // { // logfile << lastdim << std::endl; // } // cd // std::cout << lastdim << std::endl; // } return 1; } template int GradientImage( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float sig = 1; if( argc > argct ) { sig = atof(argv[argct]); } argct++; if( sig <= 0 ) { sig = 0.5; } bool normalize = false; if( argc > argct ) { normalize = atoi(argv[argct]); } typename ImageType::Pointer image = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); typedef itk::GradientMagnitudeRecursiveGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetSigma(sig); filter->SetInput(image); filter->Update(); image2 = filter->GetOutput(); if( !normalize ) { WriteImage( image2, outname.c_str() ); return 0; } typedef itk::RescaleIntensityImageFilter RescaleFilterType; typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( 0 ); rescaler->SetOutputMaximum( 1 ); rescaler->SetInput( image2 ); rescaler->Update(); WriteImage( rescaler->GetOutput(), outname.c_str() ); return 0; } template int LaplacianImage( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float sig = 1; if( argc > argct ) { sig = atof(argv[argct]); } if( sig <= 0 ) { sig = 0.5; } bool normalize = false; if( argc > argct ) { normalize = atoi(argv[argct]); } typename ImageType::Pointer image = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); typedef itk::LaplacianRecursiveGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetSigma(sig); filter->SetInput(image); filter->Update(); image2 = filter->GetOutput(); if( !normalize ) { WriteImage( image2, outname.c_str() ); return 0; } typedef itk::RescaleIntensityImageFilter RescaleFilterType; typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( 0 ); rescaler->SetOutputMaximum( 1 ); rescaler->SetInput( image2 ); rescaler->Update(); WriteImage( rescaler->GetOutput(), outname.c_str() ); return 0; } template int CannyImage( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float sig = 1; if( argc > argct ) { sig = atof(argv[argct]); argct++; } if( sig <= 0 ) { sig = 0.5; } PixelType lowerThreshold = -1.0; if( argc > argct ) { lowerThreshold = atof(argv[argct]); argct++; } PixelType upperThreshold = -1.0; if( argc > argct ) { upperThreshold = atof(argv[argct]); argct++; } typename ImageType::Pointer image = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); typedef itk::CannyEdgeDetectionImageFilter< ImageType, ImageType > FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetVariance( sig ); if ( upperThreshold != -1.0) { filter->SetUpperThreshold( (PixelType) (upperThreshold) ); } if (lowerThreshold != -1.0) { filter->SetLowerThreshold( (PixelType) (lowerThreshold) ); } filter->Update(); WriteImage( filter->GetOutput(), outname.c_str() ); return 0; } template int PoissonDiffusion( int argc, char *argv[]) { if( argc < 6 ) { std::cout << "Usage error---not enough arguments. See help menu." << std::endl; throw std::exception(); } typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image LabelImageType; typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[4] ); reader->Update(); typedef itk::ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( reader->GetOutput() ); duplicator->Update(); typename ImageType::Pointer output = duplicator->GetModifiableOutput(); output->DisconnectPipeline(); typedef itk::ImageFileReader LabelReaderType; typename LabelReaderType::Pointer labelReader = LabelReaderType::New(); labelReader->SetFileName( argv[5] ); labelReader->Update(); float label = 1.0; if( argc > 7 ) { label = atof( argv[7] ); } typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( labelReader->GetOutput() ); thresholder->SetOutsideValue( 0 ); thresholder->SetInsideValue( 1 ); thresholder->SetLowerThreshold( label ); thresholder->SetUpperThreshold( label ); thresholder->Update(); typedef itk::BinaryThresholdImageFilter ThresholderType2; typename ThresholderType2::Pointer thresholder2 = ThresholderType2::New(); thresholder2->SetInput( reader->GetOutput() ); thresholder2->SetOutsideValue( 0 ); thresholder2->SetInsideValue( 1 ); thresholder2->SetLowerThreshold( 0.2 ); thresholder2->SetUpperThreshold( 1.e9 ); thresholder2->Update(); float sigma = 1.0; if( argc > 6 ) { sigma = atof( argv[6] ); } float convergence = itk::NumericTraits::max(); float convergenceThreshold = 1e-10; if( argc > 9 ) { convergenceThreshold = atof( argv[9] ); } unsigned int maximumNumberOfIterations = 500; if( argc > 8 ) { maximumNumberOfIterations = atoi( argv[8] ); } typedef itk::ImageRegionIteratorWithIndex Iterator; float lastmean = 0; unsigned int iterations = 0; while( iterations++ < maximumNumberOfIterations && convergence >= convergenceThreshold ) { // std::cout << " Iteration " << iterations << ": " << convergence << std::endl; typedef itk::DiscreteGaussianImageFilter SmootherType; typename SmootherType::Pointer smoother = SmootherType::New(); smoother->SetVariance( vnl_math_sqr( sigma ) ); smoother->SetMaximumError( 0.01f ); smoother->SetInput( output ); /* typedef itk::MaximumImageFilter MaximumFilterType; typename MaximumFilterType::Pointer maximumFilter = MaximumFilterType::New(); maximumFilter->SetInput1( smoother->GetOutput() ); maximumFilter->SetInput2( reader->GetOutput() ); typedef itk::MultiplyImageFilter MultiplierType; typename MultiplierType::Pointer multiplier = MultiplierType::New(); multiplier->SetInput1( maximumFilter->GetOutput() ); multiplier->SetInput2( thresholder->GetOutput() ); typedef itk::AddImageFilter AdderType; typename AdderType::Pointer adder = AdderType::New(); adder->SetInput1( reader->GetOutput() ); adder->SetInput2( multiplier->GetOutput() ); typedef itk::SubtractImageFilter SubtracterType; typename SubtracterType::Pointer subtracter = SubtracterType::New(); subtracter->SetInput1( adder->GetOutput() ); subtracter->SetInput2( output ); */ typedef itk::LabelStatisticsImageFilter StatsFilterType; typename StatsFilterType::Pointer stats = StatsFilterType::New(); stats->SetInput( smoother->GetOutput() ); stats->SetLabelInput( thresholder->GetOutput() ); stats->Update(); convergence = stats->GetMean( 1 ) - lastmean; lastmean = stats->GetMean( 1 ); output = smoother->GetOutput(); output->DisconnectPipeline(); Iterator vfIter( output, output->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( thresholder2->GetOutput()->GetPixel(vfIter.GetIndex() ) == 1 ) { vfIter.Set(reader->GetOutput()->GetPixel(vfIter.GetIndex() ) ); } } } Iterator vfIter( output, output->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( thresholder2->GetOutput()->GetPixel(vfIter.GetIndex() ) == 1 ) { vfIter.Set(reader->GetOutput()->GetPixel(vfIter.GetIndex() ) ); } if( thresholder->GetOutput()->GetPixel(vfIter.GetIndex() ) == 0 ) { vfIter.Set(0); } } WriteImage( output, argv[2] ); return 0; } template void RemoveLabelInterfaces(int argc, char *argv[]) { if( argc <= 2 ) { // std::cout << " too few options " << std::endl; return; } typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; typename ImageType::Pointer input = ITK_NULLPTR; ReadImage(input, fn1.c_str() ); typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for( unsigned int j = 0; j < ImageDimension; j++ ) { rad[j] = 1; } iteratorType GHood(rad, input, input->GetLargestPossibleRegion() ); GHood.GoToBegin(); // std::cout << " foreg " << (int) foreground; while( !GHood.IsAtEnd() ) { typename ImageType::PixelType p = GHood.GetCenterPixel(); typename ImageType::IndexType ind = GHood.GetIndex(); if( p > 0 ) { bool atedge = false; for( unsigned int i = 0; i < GHood.Size(); i++ ) { if( GHood.GetPixel(i) > 0 && GHood.GetPixel(i) != p ) { atedge = true; } } if( atedge ) { input->SetPixel(ind, 0); } } ++GHood; } WriteImage(input, outname.c_str() ); return; } template void ReplaceVoxelValue( int argc, char *argv[] ) { // Usage: ImageMath 3 output.nii.gz ReplaceVoxelValue input.nii.gz a b c if( argc < 8 ) { // std::cout << " too few options " << std::endl; return; } typedef float PixelType; typedef itk::Image ImageType; const std::string outputFile = std::string( argv[2] ); const std::string inputFile = std::string( argv[4] ); const PixelType lowThreshold = atof( argv[5] ); const PixelType highThreshold = atof( argv[6] ); const PixelType replacementValue = atof( argv[7] ); typename ImageType::Pointer inputImage = ITK_NULLPTR; ReadImage( inputImage, inputFile.c_str() ); typename ImageType::Pointer outputImage = ImageType::New(); outputImage->CopyInformation( inputImage ); outputImage->SetRegions( inputImage->GetRequestedRegion() ); outputImage->Allocate(); outputImage->FillBuffer( 0 ); typename itk::ImageRegionIterator ItI( inputImage, inputImage->GetRequestedRegion() ); typename itk::ImageRegionIterator ItO( outputImage, outputImage->GetRequestedRegion() ); for( ItI.GoToBegin(), ItO.GoToBegin(); !ItI.IsAtEnd(); ++ItI, ++ItO ) { PixelType inputVoxel = ItI.Get(); if( inputVoxel >= lowThreshold && inputVoxel <= highThreshold ) { ItO.Set( replacementValue ); } else { ItO.Set( inputVoxel ); } } WriteImage( outputImage, outputFile.c_str() ); } template void EnumerateLabelInterfaces(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string outname2 = ""; if( argc > argct ) { outname2 = std::string(argv[argct]); } argct++; float nfrac = 0.1; if( argc > argct ) { nfrac = atof(argv[argct]); } argct++; typename ImageType::Pointer input = ITK_NULLPTR; ReadImage(input, fn1.c_str() ); typename ImageType::Pointer output = ITK_NULLPTR; ReadImage(output, fn1.c_str() ); output->FillBuffer(0); typename ImageType::Pointer colored = ITK_NULLPTR; ReadImage(colored, fn1.c_str() ); colored->FillBuffer(0); unsigned int max = 0; Iterator o_iter( input, input->GetLargestPossibleRegion() ); o_iter.GoToBegin(); while( !o_iter.IsAtEnd() ) { unsigned int label = (unsigned int) o_iter.Get(); if( label > max ) { max = label; } ++o_iter; } // std::cout << " Max Label " << max << std::endl; typedef itk::Image myInterfaceImageType; typename myInterfaceImageType::SizeType size; size[0] = max + 1; size[1] = max + 1; typename myInterfaceImageType::RegionType region; typename myInterfaceImageType::SpacingType spacing; spacing.Fill(1); typename myInterfaceImageType::PointType origin; origin.Fill(max / 2); region.SetSize(size); typename myInterfaceImageType::DirectionType direction; direction.SetIdentity(); typename myInterfaceImageType::Pointer faceimage = AllocImage(region, spacing, origin, direction, 0); typename myInterfaceImageType::Pointer colorimage = AllocImage(region, spacing, origin, direction, 0); // we can use this to compute a 4-coloring of the brain typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for( unsigned int j = 0; j < ImageDimension; j++ ) { rad[j] = 1; } iteratorType GHood(rad, input, input->GetLargestPossibleRegion() ); GHood.GoToBegin(); // std::cout << " foreg " << (int) foreground; while( !GHood.IsAtEnd() ) { const typename ImageType::PixelType & p = GHood.GetCenterPixel(); if( p > 0 ) { bool atedge = false; unsigned long linearinda = 0, linearindb = 0, linearind = 0; for( unsigned int i = 0; i < GHood.Size(); i++ ) { if( GHood.GetPixel(i) > 0 && GHood.GetPixel(i) != p ) { atedge = true; typename myInterfaceImageType::IndexType inda, indb; inda[0] = (long int)p; inda[1] = (long int)GHood.GetPixel(i); indb[1] = (long int)p; indb[0] = (long int)GHood.GetPixel(i); faceimage->SetPixel(inda, faceimage->GetPixel(inda) + 1); faceimage->SetPixel(indb, faceimage->GetPixel(indb) + 1); linearinda = inda[1] * size[0] + inda[0]; linearindb = indb[1] * size[0] + indb[0]; if( linearinda < linearindb ) { linearind = linearinda; } else { linearind = linearindb; } } } if( atedge ) { const typename ImageType::IndexType & ind = GHood.GetIndex(); output->SetPixel(ind, linearind); } } ++GHood; } // first normalize the interfaces typename myInterfaceImageType::IndexType find; typename myInterfaceImageType::IndexType find2; find2.Fill(0); for( unsigned int j = 0; j <= max; j++ ) { find[0] = j; float total = 0; for( unsigned int i = 0; i <= max; i++ ) { find[1] = i; total += faceimage->GetPixel(find); // if ( faceimage->GetPixel(find) > 50 ) // std::cout << i <<" :: " << faceimage->GetPixel(find) << std::endl; } // std::cout << " total interfaces for label : " << j << " are " << total << std::endl; for( unsigned int i = 0; i <= max; i++ ) { find[1] = i; if( total > 0 ) { faceimage->SetPixel(find, faceimage->GetPixel(find) / total); } if( faceimage->GetPixel(find) > 0.01 ) { // std::cout << i << " :: " << faceimage->GetPixel(find) << std::endl; } } } // colors 1 , 2 , 3 , 4 // set all to color 1. typedef std::vector ColorSetType; colorimage->FillBuffer(0); // for (unsigned int j=1; j<=max; j++) for( unsigned int j = max; j >= 1; j-- ) { ColorSetType myColorSet1; ColorSetType myColorSet2; ColorSetType myColorSet3; ColorSetType myColorSet4; ColorSetType myColorSet5; find[0] = j; // list all indices that have an interface with j // for (unsigned int i=1; i<=max; i++) for( unsigned int i = max; i >= 1; i-- ) { if( i != j ) { find[1] = i; find2[0] = i; unsigned int color = (unsigned int) colorimage->GetPixel(find2); if( faceimage->GetPixel(find) > nfrac ) // then color { if( color == 1 ) { myColorSet1.push_back( (unsigned int) i ); } if( color == 2 ) { myColorSet2.push_back( (unsigned int) i ); } if( color == 3 ) { myColorSet3.push_back( (unsigned int) i ); } if( color == 4 ) { myColorSet4.push_back( (unsigned int) i ); } if( color == 5 ) { myColorSet5.push_back( (unsigned int) i ); } // now you know all of j's neighbors and their colors } } } // we have to recolor j so that his color is different // than any in the label set unsigned int okcolor = 6; find[0] = j; find[1] = 0; if( myColorSet1.empty() ) { okcolor = 1; } else if( myColorSet2.empty() ) { okcolor = 2; } else if( myColorSet3.empty() ) { okcolor = 3; } else if( myColorSet4.empty() ) { okcolor = 4; } else if( myColorSet5.empty() ) { okcolor = 5; } colorimage->SetPixel(find, okcolor); // std::cout << " Label " << j << " color " << okcolor << std::endl; } o_iter.GoToBegin(); colored->FillBuffer(0); while( !o_iter.IsAtEnd() ) { unsigned int label = (unsigned int) o_iter.Get(); if( label > 0 ) { find[0] = label; find[1] = 0; unsigned int color = (unsigned int)colorimage->GetPixel(find); colored->SetPixel(o_iter.GetIndex(), color); } ++o_iter; } WriteImage(output, outname.c_str() ); WriteImage(faceimage, (std::string("face") + outname).c_str() ); if( outname2.length() > 3 ) { WriteImage(colored, outname2.c_str() ); } return; } template int CountVoxelDifference( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); argct++; } std::string maskfn = ""; if( argc > argct ) { maskfn = std::string(argv[argct]); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::Pointer mask = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); ReadImage(image2, fn2.c_str() ); ReadImage(mask, maskfn.c_str() ); unsigned long maskct = 0; float err = 0, negerr = 0; Iterator It( mask, mask->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { if( It.Get() > 0 ) { float p1 = image1->GetPixel(It.GetIndex() ); float p2 = image2->GetPixel(It.GetIndex() ); float locerr = p1 - p2; err += fabs(locerr); if( locerr < 0 ) { negerr += locerr; } maskct++; } } if( maskct == 0 ) { maskct = 1; } std::ofstream logfile; logfile.open(outname.c_str() ); if( logfile.good() ) { logfile << " Err " << " : " << err << " %ER " << " : " << err / (maskct) * 100. << " NER " << " : " << negerr / maskct * 100.0 << std::endl; } return 0; } template int DiceAndMinDistSum( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); argct++; } std::string outdistfn = ""; if( argc > argct ) { outdistfn = std::string(argv[argct]); argct++; } std::string diceimagename = ANTSGetFilePrefix(outname.c_str() ) + std::string("dice.nii.gz"); std::string mdsimagename = ANTSGetFilePrefix(outname.c_str() ) + std::string("mds.nii.gz"); typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::Pointer outdist = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); if( fn2.length() > 3 ) { ReadImage(image2, fn2.c_str() ); } if( outdistfn.length() > 3 ) { ReadImage(outdist, fn1.c_str() ); outdist->FillBuffer(0); } // 1: for each label, and each image, compute the distance map // 2: sum min-dist(i) over every point in each image // 3: take average over all points typedef float PixelType; typedef std::vector LabelSetType; LabelSetType myLabelSet1; { /** count the labels in the image */ Iterator It( image1, image1->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( fabs(label) > 0 ) { if( find( myLabelSet1.begin(), myLabelSet1.end(), label ) == myLabelSet1.end() ) { myLabelSet1.push_back( label ); } } } } LabelSetType myLabelSet2; unsigned int labct = 0; { Iterator It( image2, image2->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( fabs(label) > 0 ) { if( find( myLabelSet2.begin(), myLabelSet2.end(), label ) == myLabelSet2.end() && find( myLabelSet1.begin(), myLabelSet1.end(), label ) != myLabelSet1.end() ) { myLabelSet2.push_back( label ); labct++; } } } } vnl_vector distances(labct, 0.0); vnl_vector dicevals(labct, 0.0); vnl_vector rovals(labct, 0.0); vnl_vector tpvals(labct, 0.0); vnl_vector tpvals2(labct, 0.0); /** now we have the common labels */ std::ofstream logfile; logfile.open(outname.c_str() ); labct = 0; typename LabelSetType::const_iterator it; for( it = myLabelSet2.begin(); it != myLabelSet2.end(); ++it ) { typename ImageType::Pointer mask1 = BinaryThreshold(*it, *it, 1, image1); typename ImageType::Pointer surf = ITK_NULLPTR; typename ImageType::Pointer d1 = ITK_NULLPTR; typename ImageType::Pointer d2 = ITK_NULLPTR; float count1 = 0; // count vox in label 1 float count2 = 0; // count vox in label 2 float counti = 0; // count vox intersection in label 1 and label 2 float countu = 0; // count vox union label 1 and label 2 // float surfdist = 0, surfct = 0; // float truepos=0; if( outdist ) { surf = LabelSurface(mask1, mask1); // WriteImage(surf,outdistfn.c_str()); // throw std::exception(); typedef itk::DanielssonDistanceMapImageFilter FilterType; typename FilterType::Pointer dfilter1 = FilterType::New(); dfilter1->InputIsBinaryOn(); dfilter1->SetUseImageSpacing(true); dfilter1->SetInput(mask1); typename ImageType::Pointer mask2 = BinaryThreshold(*it, *it, 1, image2); typename FilterType::Pointer dfilter2 = FilterType::New(); dfilter2->InputIsBinaryOn(); dfilter2->SetUseImageSpacing(true); dfilter2->SetInput(mask2); dfilter1->Update(); d1 = dfilter1->GetOutput(); dfilter2->Update(); d2 = dfilter2->GetOutput(); } float dist1 = 0; float dist2 = 0; Iterator It2( image2, image2->GetLargestPossibleRegion() ); for( It2.GoToBegin(); !It2.IsAtEnd(); ++It2 ) { if( It2.Get() == *it || image1->GetPixel(It2.GetIndex() ) == *it ) { countu++; } if( It2.Get() == *it && image1->GetPixel(It2.GetIndex() ) == *it ) { counti++; } if( It2.Get() == *it ) { count2++; if( d1 ) { dist2 += d1->GetPixel(It2.GetIndex() ); } } if( image1->GetPixel(It2.GetIndex() ) == *it ) { count1++; if( d2 ) { dist1 += d2->GetPixel(It2.GetIndex() ); } } if( outdist ) { if( surf->GetPixel(It2.GetIndex() ) > 0 ) { float sdist = d2->GetPixel(It2.GetIndex() ); outdist->SetPixel(It2.GetIndex(), sdist); // surfdist += sdist; // surfct += 1; } } } // // std::cout << " sdist " << surfdist << " sct " << surfct << std::endl if( outdist ) { WriteImage(outdist, outdistfn.c_str() ); } if( count2 + count1 > 0 ) { distances[labct] += (dist2 + dist1) / (count2 + count1); dicevals[labct] = 2.0 * counti / (count2 + count1); rovals[labct] = counti / (countu); tpvals[labct] = counti / count1; tpvals2[labct] = counti / count2; } labct++; } labct = 0; float sum = 0, sumdice = 0, sumro = 0; // std::sort(myLabelSet2.begin(),myLabelSet2.end()); unsigned long labelcount = 0; for( it = myLabelSet2.begin(); it != myLabelSet2.end(); ++it ) { labelcount++; } float temp = sqrt( (float)labelcount); unsigned int squareimagesize = (unsigned int)(temp + 1); typedef itk::Image TwoDImageType; typename TwoDImageType::RegionType newregion; typename TwoDImageType::SizeType size; size[0] = size[1] = squareimagesize; newregion.SetSize(size); typename TwoDImageType::Pointer squareimage = AllocImage(newregion, 0.0); typename TwoDImageType::Pointer squareimage2 = AllocImage(newregion, 0.0); std::vector ColumnHeaders; std::vector RowHeaders; unsigned int NumberOfLabels = labelcount; if( outdist ) { vnl_matrix OutputValues(NumberOfLabels, 2); ColumnHeaders.push_back("Label Name"); ColumnHeaders.push_back("Min_Distance"); ColumnHeaders.push_back("Dice"); for( it = myLabelSet2.begin(); it != myLabelSet2.end(); ++it ) { OutputValues(labct, 0) = distances[labct]; OutputValues(labct, 1) = dicevals[labct]; std::string LabelName = "Label_"; int LabelNumber; LabelNumber = *it; char LabelNumberAsString[50]; sprintf(LabelNumberAsString, "%.2d", LabelNumber); LabelName = LabelName + LabelNumberAsString; RowHeaders.push_back(LabelName); labct++; } typedef itk::CSVNumericObjectFileWriter CSVType; CSVType::Pointer OutputCSV = CSVType::New(); OutputCSV->SetInput(&OutputValues); OutputCSV->SetFileName(std::string(outname.c_str() ) + ".csv"); OutputCSV->SetColumnHeaders(ColumnHeaders); OutputCSV->SetRowHeaders(RowHeaders); try { OutputCSV->Write(); } catch( itk::ExceptionObject& /* exp */) { // std::cout << "Exception caught!" << std::endl; // std::cout << exp << std::endl; } } else { vnl_matrix OutputValues(NumberOfLabels, 4); ColumnHeaders.push_back("Label Name"); ColumnHeaders.push_back("Dice"); ColumnHeaders.push_back("RO"); ColumnHeaders.push_back("Percent_of_Region_1_In_Overlap"); ColumnHeaders.push_back("Percent_of_Region_2_In_Overlap"); for( it = myLabelSet2.begin(); it != myLabelSet2.end(); ++it ) { OutputValues(labct, 0) = dicevals[labct]; OutputValues(labct, 1) = rovals[labct]; OutputValues(labct, 2) = tpvals[labct]; OutputValues(labct, 3) = tpvals2[labct]; std::string LabelName = "Label_"; int LabelNumber; LabelNumber = *it; char LabelNumberAsString[50]; sprintf(LabelNumberAsString, "%.2d", LabelNumber); LabelName = LabelName + LabelNumberAsString; RowHeaders.push_back(LabelName); labct++; } typedef itk::CSVNumericObjectFileWriter CSVType; CSVType::Pointer OutputCSV = CSVType::New(); OutputCSV->SetInput(&OutputValues); OutputCSV->SetFileName(std::string(outname.c_str() ) + ".csv"); OutputCSV->SetColumnHeaders(ColumnHeaders); OutputCSV->SetRowHeaders(RowHeaders); try { OutputCSV->Write(); // std::cout << "Output written to " << outname.c_str() << ".csv." << std::endl; } catch( itk::ExceptionObject& /* exp */) { // std::cout << "Exception caught!" << std::endl; // std::cout << exp << std::endl; } } labelcount = 0; labct = 0; for( it = myLabelSet2.begin(); it != myLabelSet2.end(); ++it ) { sum += distances[labct]; sumdice += dicevals[labct]; sumro += rovals[labct]; // square image squareimage->GetBufferPointer()[labct] = distances[labct]; squareimage2->GetBufferPointer()[labct] = dicevals[labct]; labct++; } if( labct == 0 ) { labct = 1; } if( logfile.good() ) { if( outdist ) { logfile << " AvgMinDist " << " : " << sum / (float)labct << " AvgDice " << " : " << sumdice / (float)labct << " AvgRO " << " : " << sumro / (float)labct << std::endl; } else { logfile << " AvgDice " << " : " << sumdice / (float)labct << std::endl; } } WriteImage(squareimage, mdsimagename.c_str() ); WriteImage(squareimage2, diceimagename.c_str() ); return 0; } template int Lipschitz( int argc, char *argv[] ) { if( argc > 20 ) { // std::cout << " k " << std::endl; } // std::cout << " Compute Lipschitz continuity of the mapping " << std::endl; typedef float RealType; typedef itk::Image RealImageType; typedef itk::Vector VectorType; typedef itk::Image VectorImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string vecname = std::string(argv[argct]); argct++; /** * Read in vector field */ typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( vecname.c_str() ); // reader->SetUseAvantsNamingConvention( true ); reader->Update(); typename VectorImageType::Pointer vecimage = reader->GetOutput(); typename RealImageType::Pointer lipcon = AllocImage(vecimage, 0); itk::TimeProbe timer; timer.Start(); /** global Lipschitz points */ typename VectorImageType::PointType gx; gx.Fill(0); typename VectorImageType::PointType gxt; gxt.Fill(0); typename VectorImageType::PointType gy; gy.Fill(0); typename VectorImageType::PointType gyt; gyt.Fill(0); typename VectorImageType::PointType lx; lx.Fill(0); typename VectorImageType::PointType lxt; lxt.Fill(0); typename VectorImageType::PointType ly; ly.Fill(0); typename VectorImageType::PointType lyt; lyt.Fill(0); float globalmaxval = 0; unsigned long ct1 = 0; // unsigned long numpx=vecimage->GetBufferedRegion().GetNumberOfPixels(); Iterator It1( vecimage, vecimage->GetLargestPossibleRegion() ); for( It1.GoToBegin(); !It1.IsAtEnd(); ++It1 ) { ct1++; typename VectorImageType::PointType x; typename VectorImageType::PointType xt; vecimage->TransformIndexToPhysicalPoint( It1.GetIndex(), x); typename VectorImageType::PixelType vecx = It1.Get(); for( unsigned int i = 0; i < ImageDimension; i++ ) { xt[i] = x[i] + vecx[i]; } float localmaxval = 0; unsigned long ct2 = 0; Iterator It2( vecimage, vecimage->GetLargestPossibleRegion() ); for( It2.GoToBegin(); !It2.IsAtEnd(); ++It2 ) { ct2++; if( ct2 != ct1 ) { typename VectorImageType::PointType y; typename VectorImageType::PointType yt; vecimage->TransformIndexToPhysicalPoint( It2.GetIndex(), y); typename VectorImageType::PixelType vecy = It2.Get(); float numer = 0; float denom = 0; for( unsigned int i = 0; i < ImageDimension; i++ ) { yt[i] = y[i] + vecy[i]; numer += (yt[i] - xt[i]) * (yt[i] - xt[i]); denom += (y[i] - x[i]) * (y[i] - x[i]); } numer = sqrt(numer); denom = sqrt(denom); float localval = numer / denom; if( localval > localmaxval ) { lx = x; ly = y; lxt = xt; lyt = yt; localmaxval = localval; } } } if( localmaxval > globalmaxval ) { gx = lx; gy = ly; gxt = lxt; gyt = lyt; globalmaxval = localmaxval; } lipcon->SetPixel(It1.GetIndex(), localmaxval); // if (ct1 % 1000 == 0) // std::cout << " Progress : " << (float ) ct1 / (float) numpx *100.0 << " val " << // localmaxval << std::endl; } // std::cout << " Lipschitz continuity related to: " << globalmaxval << std::endl; // std::cout << " Tx : " << gxt << " Ty: " << gyt << std::endl; // std::cout << " x : " << gx << " y: " << gy << std::endl; timer.Stop(); // // std::cout << "Elapsed time: " << timer.GetMeanTime() << std::endl; if( outname.length() > 3 ) { WriteImage( lipcon, outname.c_str() ); } return 0; } template int ExtractVectorComponent( int argc, char *argv[] ) { if( argc <= 2 ) { // std::cout << " too few options " << std::endl; return 1; } typedef float PixelType; typedef itk::VectorImage ImageType; typedef itk::Image RealImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string inname = std::string(argv[argct]); argct++; unsigned int whichvec = atoi(argv[argct]); argct++; typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader1 = ReaderType::New(); reader1->SetFileName( inname.c_str() ); reader1->Update(); typename ImageType::Pointer vecimage = reader1->GetOutput(); if( whichvec >= vecimage->GetVectorLength() ) { std::cout << " input image " << inname << " only has " << vecimage->GetVectorLength() << " components " << std::endl; return EXIT_FAILURE; } else { typename RealImageType::Pointer component = AllocImage(vecimage, 0); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator It1( vecimage, vecimage->GetLargestPossibleRegion() ); for( It1.GoToBegin(); !It1.IsAtEnd(); ++It1 ) { component->SetPixel(It1.GetIndex(), It1.Get()[whichvec]); } WriteImage( component, outname.c_str() ); } return EXIT_SUCCESS; } template int InvId( int argc, char *argv[] ) { if( argc > 2 ) { // std::cout << " Compute phi( phi^{-1}(x)) " << std::endl; } else { return 1; } typedef float RealType; typedef itk::Image RealImageType; typedef itk::Vector VectorType; typedef itk::Image VectorImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string vecname1 = std::string(argv[argct]); argct++; std::string vecname2 = std::string(argv[argct]); argct++; /** * Read in vector field */ typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader1 = ReaderType::New(); reader1->SetFileName( vecname1.c_str() ); // reader1->SetUseAvantsNamingConvention( true ); reader1->Update(); typename VectorImageType::Pointer vecimage1 = reader1->GetOutput(); typename ReaderType::Pointer reader2 = ReaderType::New(); reader2->SetFileName( vecname2.c_str() ); // reader2->SetUseAvantsNamingConvention( true ); reader2->Update(); typename VectorImageType::Pointer vecimage2 = reader2->GetOutput(); typename RealImageType::Pointer invid = AllocImage(vecimage1, 0); itk::TimeProbe timer; timer.Start(); typedef itk::VectorLinearInterpolateImageFunction DefaultInterpolatorType; typename DefaultInterpolatorType::Pointer vinterp = DefaultInterpolatorType::New(); vinterp->SetInputImage(vecimage2); /** global Lipschitz points */ typename VectorImageType::PointType gx; gx.Fill(0); float globalmaxval = 0; Iterator It1( vecimage1, vecimage1->GetLargestPossibleRegion() ); for( It1.GoToBegin(); !It1.IsAtEnd(); ++It1 ) { typename VectorImageType::PointType x; typename DefaultInterpolatorType::PointType xt, yt; vecimage1->TransformIndexToPhysicalPoint( It1.GetIndex(), x); typename VectorImageType::PixelType vecx = It1.Get(); for( unsigned int i = 0; i < ImageDimension; i++ ) { xt[i] = x[i] + vecx[i]; } // above, we warped the point -- now we index the other field with this // point typename VectorImageType::PixelType disp; if( vinterp->IsInsideBuffer(xt) ) { disp = vinterp->Evaluate(xt); } else { disp.Fill(0); } for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { yt[jj] = xt[jj] + disp[jj]; } float error = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { error += (yt[jj] - x[jj]) * (yt[jj] - x[jj]); } error = sqrt(error); if( error > globalmaxval ) { globalmaxval = error; gx = x; } invid->SetPixel(It1.GetIndex(), error); } // std::cout << " Max error " << globalmaxval << " at " << gx << std::endl; timer.Stop(); // // std::cout << "Elapsed time: " << timer.GetMeanTime() << std::endl; WriteImage( invid, outname.c_str() ); return 0; } template int ReplicateDisplacement( int argc, char *argv[] ) { if( argc > 6 ) { // std::cout << " Replicate a ND displacement to ND+1 dimensions " << std::endl; } else { // std::cout << "ImageMath 3 out4DWarp.nii.gz ReplicateDisplacement in3DWarp.nii.gz nreplications time-spacing time-origin" << std::endl; // std::cout << "ImageMath 3 out4DWarp.nii.gz ReplicateDisplacement in3DWarp.nii.gz 10 2.5 0.0" << std::endl; return 1; } typedef float RealType; typedef itk::Vector VectorType; typedef itk::Image VectorImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::Vector VectorRType; typedef itk::Image VectorRImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string vecname1 = std::string(argv[argct]); argct++; unsigned int timedims = atoi(argv[argct]); argct++; RealType tr = atof(argv[argct]); argct++; RealType torigin = atof(argv[argct]); argct++; /** * Read in vector field */ typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader1 = ReaderType::New(); reader1->SetFileName( vecname1.c_str() ); reader1->Update(); typename VectorImageType::Pointer vecimage1 = reader1->GetOutput(); typename VectorRImageType::Pointer outputImage = VectorRImageType::New(); typename VectorRImageType::RegionType outRegion; typename VectorRImageType::SizeType outSize; typename VectorRImageType::SpacingType outSpacing; typename VectorRImageType::PointType outOrigin; typename VectorRImageType::DirectionType outDirection; for( unsigned int d = 0; d < ImageDimension; d++ ) { outSize[d] = vecimage1->GetLargestPossibleRegion().GetSize()[d]; outSpacing[d] = vecimage1->GetSpacing()[d]; outOrigin[d] = vecimage1->GetOrigin()[d]; for( unsigned int e = 0; e < ImageDimension; e++ ) { outDirection(e, d) = vecimage1->GetDirection() (e, d); } } for( unsigned int d = 0; d < ImageDimension; d++ ) { outDirection(d, ImageDimension) = 0; outDirection(ImageDimension, d) = 0; } outDirection(ImageDimension, ImageDimension) = 1.0; outSize[ImageDimension] = timedims; outSpacing[ImageDimension] = tr; outOrigin[ImageDimension] = torigin; outRegion.SetSize( outSize ); outputImage->SetRegions( outRegion ); outputImage->SetSpacing( outSpacing ); outputImage->SetOrigin( outOrigin ); outputImage->SetDirection( outDirection ); outputImage->Allocate(); VectorRType vec; vec.Fill( 0 ); outputImage->FillBuffer( vec ); // perform the replication typename VectorImageType::IndexType ind; typename VectorRImageType::IndexType indp1; Iterator It1( vecimage1, vecimage1->GetLargestPossibleRegion() ); for( It1.GoToBegin(); !It1.IsAtEnd(); ++It1 ) { ind = It1.GetIndex(); typename VectorImageType::PixelType vecx = It1.Get(); for( unsigned int i = 0; i < ImageDimension; i++ ) { vec[i] = vecx[i]; indp1[i] = ind[i]; } for( unsigned int i = 0; i < timedims; i++ ) { indp1[ImageDimension] = i; outputImage->SetPixel( indp1, vec ); } } WriteImage( outputImage, outname.c_str() ); return 0; } template int ShiftImageSlicesInTime( int argc, char *argv[] ) { if( argc < 4 ) { return 1; } typedef float RealType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; unsigned int shiftamount = 1; if( argc > argct ) { shiftamount = atoi( argv[argct] ); argct++; } unsigned int shiftdim = ImageDimension - 1; if( argc > argct ) { shiftdim = atoi( argv[argct] ); argct++; } typename ImageType::Pointer image = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); typedef itk::CyclicShiftImageFilter FilterType; typename FilterType::Pointer cyfilter = FilterType::New(); cyfilter->SetInput(image); typename FilterType::OffsetType myshift; myshift.Fill( 0 ); myshift[shiftdim]=shiftamount; cyfilter->SetShift( myshift ); WriteImage(cyfilter->GetOutput(), outname.c_str() ); return 0; } template int ReplicateImage( int argc, char *argv[] ) { if( argc > 6 ) { // std::cout << " Replicate a ND image to ND+1 dimensions " << std::endl; } else { // std::cout << "ImageMath 3 out4D.nii.gz ReplicateImage in3D.nii.gz nreplications time-spacing time-origin" << std::endl; // std::cout << "ImageMath 3 out4D.nii.gz ReplicateImage in3D.nii.gz 10 2.5 0.0" << std::endl; return 1; } typedef float RealType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::Image RImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string vecname1 = std::string(argv[argct]); argct++; unsigned int timedims = atoi(argv[argct]); argct++; float tr = atof(argv[argct]); argct++; float torigin = atof(argv[argct]); argct++; typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader1 = ReaderType::New(); reader1->SetFileName( vecname1.c_str() ); reader1->Update(); typename ImageType::Pointer vecimage1 = reader1->GetOutput(); typename RImageType::Pointer outputImage = RImageType::New(); typename RImageType::RegionType outRegion; typename RImageType::SizeType outSize; typename RImageType::SpacingType outSpacing; typename RImageType::PointType outOrigin; typename RImageType::DirectionType outDirection; for( unsigned int d = 0; d < ImageDimension; d++ ) { outSize[d] = vecimage1->GetLargestPossibleRegion().GetSize()[d]; outSpacing[d] = vecimage1->GetSpacing()[d]; outOrigin[d] = vecimage1->GetOrigin()[d]; for( unsigned int e = 0; e < ImageDimension; e++ ) { outDirection(e, d) = vecimage1->GetDirection() (e, d); } } for( unsigned int d = 0; d < ImageDimension; d++ ) { outDirection(d, ImageDimension) = 0; outDirection(ImageDimension, d) = 0; } outDirection(ImageDimension, ImageDimension) = 1.0; outSize[ImageDimension] = timedims; outSpacing[ImageDimension] = tr; outOrigin[ImageDimension] = torigin; outRegion.SetSize( outSize ); outputImage->SetRegions( outRegion ); outputImage->SetSpacing( outSpacing ); outputImage->SetOrigin( outOrigin ); outputImage->SetDirection( outDirection ); outputImage->Allocate(); outputImage->FillBuffer( 0 ); // perform the replication typename ImageType::IndexType ind; typename RImageType::IndexType indp1; ind.Fill(0); indp1.Fill( 0 ); Iterator It1( vecimage1, vecimage1->GetLargestPossibleRegion() ); for( It1.GoToBegin(); !It1.IsAtEnd(); ++It1 ) { ind = It1.GetIndex(); typename ImageType::PixelType vecx = It1.Get(); for( unsigned int i = 0; i < ImageDimension; i++ ) { indp1[i] = ind[i]; } for( unsigned int i = 0; i < timedims; i++ ) { indp1[ImageDimension] = i; outputImage->SetPixel( indp1, vecx ); } } WriteImage( outputImage, outname.c_str() ); return 0; } template int LabelStats( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); std::string imagename = ANTSGetFilePrefix(outname.c_str() ) + std::string("_square.nii.gz"); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); argct++; } typename ImageType::Pointer image = ITK_NULLPTR; typename ImageType::Pointer valimage = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); if( fn2.length() > 3 ) { ReadImage(valimage, fn2.c_str() ); } typedef float PixelType; typedef std::vector LabelSetType; LabelSetType myLabelSet; /** count the labels in the image */ Iterator It( image, image->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( fabs(label) > 0 ) { if( find( myLabelSet.begin(), myLabelSet.end(), label ) == myLabelSet.end() ) { myLabelSet.push_back( label ); } } } // compute the voxel volume typename ImageType::SpacingType spacing = image->GetSpacing(); float volumeelement = 1.0; for( unsigned int i = 0; i < spacing.Size(); i++ ) { volumeelement *= spacing[i]; } std::ofstream logfile; logfile.open(outname.c_str() ); logfile << "x,y,z,t,label,mass,volume,count" << std::endl; std::sort(myLabelSet.begin(), myLabelSet.end() ); typename LabelSetType::const_iterator it; unsigned long labelcount = 0; for( it = myLabelSet.begin(); it != myLabelSet.end(); ++it ) { labelcount++; } float temp = sqrt( (float)labelcount); unsigned int squareimagesize = (unsigned int)(temp + 1); typedef itk::Image TwoDImageType; typename TwoDImageType::RegionType newregion; typename TwoDImageType::SizeType size; size[0] = size[1] = squareimagesize; newregion.SetSize(size); typename TwoDImageType::Pointer squareimage = AllocImage(newregion, 0); labelcount = 0; for( it = myLabelSet.begin(); it != myLabelSet.end(); ++it ) { float currentlabel = *it; float totalvolume = 0; float totalmass = 0; float totalct = 0; typename ImageType::PointType myCenterOfMass; myCenterOfMass.Fill(0); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( label == currentlabel ) { totalct += 1; if( valimage ) { totalmass += valimage->GetPixel( It.GetIndex() ); } // compute center of mass typename ImageType::PointType point; image->TransformIndexToPhysicalPoint( It.GetIndex(), point ); for( unsigned int i = 0; i < spacing.Size(); i++ ) { myCenterOfMass[i] += point[i]; } } } totalvolume = volumeelement * totalct; for( unsigned int i = 0; i < spacing.Size(); i++ ) { myCenterOfMass[i] /= (float)totalct; } if( !valimage ) { // // std::cout << " Volume Of Label " << *it << " is " << totalvolume << " Avg-Location " << myCenterOfMass // << std::endl; } else // if ( totalvolume > 500 && totalmass/totalct > 1/500 ) { { // // std::cout << " Volume Of Label " << *it << " is " << totalvolume << " Avg-Location " << myCenterOfMass // << " mass is " << totalmass << " average-val is " << totalmass / totalct << std::endl; // // // std::cout << *it << " " << totalvolume << " & " << totalmass/totalct << " \ " << std::endl; } // square image squareimage->GetBufferPointer()[labelcount] = totalmass / totalct; if( ImageDimension == 2 ) { logfile << myCenterOfMass[0] << "," << myCenterOfMass[1] << ",0,0," << currentlabel << "," << totalmass << "," << totalvolume << "," << totalct << std::endl; } if( ImageDimension == 3 ) { logfile << myCenterOfMass[0] << "," << myCenterOfMass[1] << "," << myCenterOfMass[2] << ",0," << currentlabel << "," << totalmass << "," << totalvolume << "," << totalct << std::endl; } if( ImageDimension == 4 ) { logfile << myCenterOfMass[0] << "," << myCenterOfMass[1] << "," << myCenterOfMass[2] << "," << myCenterOfMass[3] << "," << currentlabel << "," << totalmass << "," << totalvolume << "," << totalct << std::endl; } labelcount++; } logfile.close(); //WriteImage(squareimage, imagename.c_str() ); return 0; } template int LabelThickness( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; if( argc < 5 ) { // std::cout << " Not enough inputs " << std::endl; return EXIT_FAILURE; } int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; typename ImageType::Pointer image = ITK_NULLPTR; typename ImageType::Pointer eimage = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); ReadImage(eimage, fn1.c_str() ); // typename TImage::Pointer Morphological( typename TImage::Pointer input, float rad, unsigned int option, // float dilateval) eimage = ants::Morphological(image, 1, 4, 1); typedef float PixelType; typedef std::vector LabelSetType; LabelSetType myLabelSet; /** count the labels in the image */ unsigned long maxlab = 0; Iterator iIt( image, image->GetLargestPossibleRegion() ); for( iIt.GoToBegin(); !iIt.IsAtEnd(); ++iIt ) { PixelType label = iIt.Get(); if( fabs(label) > 0 ) { if( find( myLabelSet.begin(), myLabelSet.end(), label ) == myLabelSet.end() ) { myLabelSet.push_back( label ); } if( label > maxlab ) { maxlab = (unsigned long)label; } } } std::sort(myLabelSet.begin(), myLabelSet.end() ); typename LabelSetType::const_iterator it; unsigned long labelcount = 0; for( it = myLabelSet.begin(); it != myLabelSet.end(); ++it ) { labelcount++; } typename ImageType::SpacingType spacing = image->GetSpacing(); float volumeelement = 1.0; for( unsigned int i = 0; i < spacing.Size(); i++ ) { volumeelement *= spacing[i]; } volumeelement = std::pow( static_cast( volumeelement ), static_cast( 0.3333 ) ); vnl_vector surface(maxlab + 1, 0); vnl_vector volume(maxlab + 1, 0); typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; rad.Fill( 1 ); iteratorType GHood(rad, image, image->GetLargestPossibleRegion() ); float Gsz = (float)GHood.Size(); // iterate over the label image and index into the stat image for( iIt.GoToBegin(); !iIt.IsAtEnd(); ++iIt ) { PixelType label = iIt.Get(); if( label > 0 ) { volume[(unsigned long) label] = volume[(unsigned long) label] + 1; } label = label - eimage->GetPixel( iIt.GetIndex() ); if( label > 0 ) { GHood.SetLocation( iIt.GetIndex() ); bool isedge = false; for( unsigned int ii = 0; ii < Gsz; ii++ ) { if( GHood.GetPixel( ii ) == 0 ) { isedge = true; } } if( isedge ) { surface[(unsigned long) label] = surface[(unsigned long) label] + 1; } } } for( unsigned int i = 0; i < surface.size(); i++ ) { if( surface[i] > 0 ) { // std::cout << " S " << surface[i] << " V " << volume[i] << " T " << volume[i] / surface[i] * 2.0 << std::endl; } } for( iIt.GoToBegin(); !iIt.IsAtEnd(); ++iIt ) { PixelType label = iIt.Get(); if( label > 0 ) { PixelType thicknessprior = volumeelement; if( surface[label] > 0 ) { thicknessprior = volume[label] / surface[label] * 2.0 * volumeelement; } eimage->SetPixel( iIt.GetIndex(), thicknessprior ); } } WriteImage( eimage, outname.c_str() ); return EXIT_SUCCESS; } template int LabelThickness2( int argc, char *argv[] ) { typedef unsigned int LabelType; typedef itk::Image LabelImageType; typedef float RealType; typedef itk::Image RealImageType; if( argc < 5 ) { // std::cout << " Not enough inputs " << std::endl; return EXIT_FAILURE; } int argct = 2; const std::string outname = std::string( argv[argct] ); argct += 2; std::string fn = std::string( argv[argct++] ); typename LabelImageType::Pointer labelImage = ITK_NULLPTR; ReadImage( labelImage, fn.c_str() ); typename LabelImageType::SpacingType spacing = labelImage->GetSpacing(); float volumeElement = 1.0; for( unsigned int i = 0; i < spacing.Size(); i++ ) { volumeElement *= spacing[i]; } typedef itk::LabelGeometryImageFilter GeometryFilterType; typename GeometryFilterType::Pointer geometryFilter = GeometryFilterType::New(); geometryFilter->SetInput( labelImage ); geometryFilter->CalculatePixelIndicesOff(); geometryFilter->CalculateOrientedBoundingBoxOff(); geometryFilter->CalculateOrientedLabelRegionsOff(); geometryFilter->Update(); typedef itk::LabelPerimeterEstimationCalculator AreaFilterType; typename AreaFilterType::Pointer areaFilter = AreaFilterType::New(); areaFilter->SetImage( labelImage ); areaFilter->SetFullyConnected( true ); areaFilter->Compute(); typename GeometryFilterType::LabelsType allLabels = geometryFilter->GetLabels(); typename GeometryFilterType::LabelsType::iterator allLabelsIt; std::sort( allLabels.begin(), allLabels.end() ); for( allLabelsIt = allLabels.begin(); allLabelsIt != allLabels.end(); allLabelsIt++ ) { if( *allLabelsIt == 0 ) { continue; } // RealType volume = geometryFilter->GetVolume( *allLabelsIt ) * volumeElement; // RealType perimeter = areaFilter->GetPerimeter( *allLabelsIt ); // RealType thicknessPrior = volume / perimeter; // std::cout << "Label " << *allLabelsIt << ": "; // std::cout << "volume = " << volume << ", "; // std::cout << "area = " << perimeter << ", "; // std::cout << "thickness = " << thicknessPrior << std::endl; } typename RealImageType::Pointer thicknessPriorImage = RealImageType::New(); thicknessPriorImage->CopyInformation( labelImage ); thicknessPriorImage->SetRegions( labelImage->GetLargestPossibleRegion() ); thicknessPriorImage->Allocate(); thicknessPriorImage->FillBuffer( 0 ); itk::ImageRegionIteratorWithIndex It( labelImage, labelImage->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { LabelType label = It.Get(); if( label == 0 ) { continue; } RealType volume = geometryFilter->GetVolume( label ) * volumeElement; RealType perimeter = areaFilter->GetPerimeter( label ); RealType thicknessPrior = volume / perimeter; thicknessPriorImage->SetPixel( It.GetIndex(), thicknessPrior ); } WriteImage( thicknessPriorImage, outname.c_str() ); return EXIT_SUCCESS; } // now words can be accessed like this WordList[n]; where 'n' is the index template int ROIStatistics( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; // if(grade_list.find("Tim") == grade_list.end()) { // std::cout<<"Tim is not in the map!"<second int argct = 2; if( argc < 6 ) { std::cout << " not enough parameters --- usage example 1 :" << "" << std::endl; std::cout << argv[0] << " ImageMath 3 output.csv ROIStatistics roinames.txt LabelImage.nii.gz ValueImage.nii.gz " << std::endl; throw std::exception(); } const std::string outname = std::string(argv[argct]); typedef vnl_matrix MatrixType; argct += 2; std::string fn0 = std::string(argv[argct]); argct++; // std::cout << " fn0 " << fn0 << std::endl; std::map roimap = RoiList(fn0); std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); argct++; } std::string fn3 = ""; if( argc > argct ) { fn3 = std::string(argv[argct]); argct++; } std::string fn4 = ""; if( argc > argct ) { fn4 = std::string(argv[argct]); argct++; } std::string fn5 = ""; if( argc > argct ) { fn5 = std::string(argv[argct]); argct++; } typename ImageType::Pointer image = ITK_NULLPTR; typename ImageType::Pointer valimage = ITK_NULLPTR; typename ImageType::Pointer valimage3 = ITK_NULLPTR; typename ImageType::Pointer valimage4 = ITK_NULLPTR; typename ImageType::Pointer valimage5 = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); if( fn2.length() > 3 ) { ReadImage(valimage, fn2.c_str() ); } if( fn3.length() > 3 ) { ReadImage(valimage3, fn3.c_str() ); } if( fn4.length() > 3 ) { ReadImage(valimage4, fn4.c_str() ); } if( fn5.length() > 3 ) { ReadImage(valimage5, fn5.c_str() ); } typedef float PixelType; typedef std::vector LabelSetType; LabelSetType myLabelSet; /** count the labels in the image */ unsigned long maxlab = 0; Iterator It( image, image->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( fabs(label) > 0 ) { if( find( myLabelSet.begin(), myLabelSet.end(), label ) == myLabelSet.end() ) { myLabelSet.push_back( label ); } if( label > maxlab ) { maxlab = (unsigned long)label; } } } // define the output matrix // for a csv file of n_regions for rows // with columns of Volume,CenterOfMassX,CenterOfMassY,CenterOfMassZ,CenterOfMassTime, MatrixType mCSVMatrix(maxlab, ImageDimension + 1, 0); // maxlab=32; // for cortical analysis // compute the voxel volume typename ImageType::SpacingType spacing = image->GetSpacing(); float volumeelement = 1.0; for( unsigned int i = 0; i < spacing.Size(); i++ ) { volumeelement *= spacing[i]; } vnl_vector pvals(maxlab + 1, 1.0); vnl_vector pvals3(maxlab + 1, 1.0); vnl_vector pvals4(maxlab + 1, 1.0); vnl_vector pvals5(maxlab + 1, 1.0); vnl_vector clusters(maxlab + 1, 0.0); vnl_vector masses(maxlab + 1, 0.0); // typename ImageType::PointType mycomlist[33]; std::ofstream logfile; logfile.open(outname.c_str() ); std::sort(myLabelSet.begin(), myLabelSet.end() ); typename LabelSetType::const_iterator it; unsigned long labelcount = 0; for( it = myLabelSet.begin(); it != myLabelSet.end(); ++it ) { labelcount++; } typedef itk::LinearInterpolateImageFunction ScalarInterpolatorType; typename ScalarInterpolatorType::Pointer interp = ScalarInterpolatorType::New(); if( valimage ) { interp->SetInputImage(valimage); } else { interp->SetInputImage(image); } // iterate over the label image and index into the stat image for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); typename ImageType::PointType point; image->TransformIndexToPhysicalPoint(It.GetIndex(), point); float vv = interp->Evaluate( point ); // float vv = valimage->GetPixel(It.GetIndex()); if( label > 0 && fabs(vv) > 1.e-9 ) { clusters[(unsigned long) label] = clusters[(unsigned long) label] + 1; masses[(unsigned long) label] = masses[(unsigned long) label] + vv; } } logfile << "ROIName,ROINumber,ClusterSize,Mass,Mean,comX,comY,comZ,comT" << std::endl; // for( it = myLabelSet.begin(); it != myLabelSet.end(); ++it ) for( unsigned int mylabel = 1; mylabel < maxlab + 1; mylabel++ ) { unsigned int roi = mylabel - 1; /* first count which roi it is by iterating through the label sets it = myLabelSet.begin(); while ( (*it) != mylabel && it != myLabelSet.end() ) { // std::cout << " it " << *it << " roi " << roi << " mylabel " << mylabel << std::endl; roi++; ++it; }*/ typename ImageType::PointType myCenterOfMass; myCenterOfMass.Fill(0); unsigned long totalct = 0; if( clusters[mylabel] > 0 ) { for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( label == mylabel ) { totalct += 1; // compute center of mass typename ImageType::PointType point; image->TransformIndexToPhysicalPoint(It.GetIndex(), point); for( unsigned int i = 0; i < spacing.Size(); i++ ) { myCenterOfMass[i] += point[i]; } } } } // if clusters big enough if( totalct == 0 ) { totalct = 1; } for( unsigned int i = 0; i < spacing.Size(); i++ ) { myCenterOfMass[i] /= (float)totalct; } double comy = 0, comz = 0, comt = 0; double comx = myCenterOfMass[0]; if( ImageDimension >= 2 ) { comy = myCenterOfMass[1]; } if( ImageDimension >= 3 ) { comz = myCenterOfMass[2]; } if( ImageDimension == 4 ) { comt = myCenterOfMass[3]; } if( roi < roimap.size() ) { logfile << roimap[roi] << "," << roi + 1 << "," << clusters[roi + 1] << "," << masses[roi + 1] << "," << masses[roi + 1] / clusters[roi + 1] << "," << comx << "," << comy << "," << comz << "," << comt << std::endl; } } logfile.close(); return 0; float temp = sqrt( (float)labelcount); unsigned int squareimagesize = (unsigned int)(temp + 1); typedef itk::Image TwoDImageType; typename TwoDImageType::RegionType newregion; typename TwoDImageType::SizeType size; size[0] = size[1] = squareimagesize; newregion.SetSize(size); typename TwoDImageType::Pointer squareimage = AllocImage(newregion, 0); labelcount = 0; typename ImageType::PointType myCenterOfMass; myCenterOfMass.Fill(0); // for (unsigned int i=0; i<=maxlab; i++) mycomlist[i]=myCenterOfMass; for( it = myLabelSet.begin(); it != myLabelSet.end(); ++it ) { float currentlabel = *it; float totalvolume = 0; float totalmass = 0; float totalct = 0; float maxoneminuspval = 0.0; float maxoneminuspval3 = 0.0; float maxoneminuspval4 = 0.0; float maxoneminuspval5 = 0.0; myCenterOfMass.Fill(0); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( label == currentlabel ) { totalvolume += volumeelement; totalct += 1; float vv = valimage->GetPixel(It.GetIndex() ); if( valimage ) { totalmass += vv; } if( vv > maxoneminuspval ) { maxoneminuspval = vv; } if( valimage3 ) { vv = valimage3->GetPixel(It.GetIndex() ); if( fabs(vv) > fabs(maxoneminuspval3) ) { maxoneminuspval3 = vv; } } if( valimage4 ) { vv = valimage4->GetPixel(It.GetIndex() ); if( vv > maxoneminuspval4 ) { maxoneminuspval4 = vv; } } if( valimage5 ) { vv = valimage5->GetPixel(It.GetIndex() ); if( vv > maxoneminuspval5 ) { maxoneminuspval5 = vv; } } // compute center of mass typename ImageType::PointType point; image->TransformIndexToPhysicalPoint(It.GetIndex(), point); for( unsigned int i = 0; i < spacing.Size(); i++ ) { myCenterOfMass[i] += point[i]; } } } if( totalct == 0 ) { totalct = 1; } for( unsigned int i = 0; i < spacing.Size(); i++ ) { myCenterOfMass[i] /= (float)totalct; } clusters[(unsigned long)*it] = totalvolume; masses[(unsigned long)*it] = totalmass / totalct; pvals[(unsigned long)*it] = 1.0 - maxoneminuspval; pvals3[(unsigned long)*it] = maxoneminuspval3; pvals4[(unsigned long)*it] = 1.0 - maxoneminuspval4; pvals5[(unsigned long)*it] = 1.0 - maxoneminuspval5; // mycomlist[(unsigned long)*it]=myCenterOfMass; // square image squareimage->GetBufferPointer()[labelcount] = totalmass / totalct; labelcount++; } for( unsigned int roi = 1; roi <= maxlab; roi++ ) { logfile << roimap.find(roi)->second << ","; } for( unsigned int roi = maxlab + 1; roi <= maxlab * 2; roi++ ) { if( roi < maxlab * 2 ) { logfile << roimap.find(roi - maxlab)->second + std::string("mass") << ","; } else { logfile << roimap.find(roi - maxlab)->second + std::string("mass") << std::endl; } } for( unsigned int roi = 1; roi <= maxlab; roi++ ) { logfile << clusters[roi] << ","; } for( unsigned int roi = maxlab + 1; roi <= maxlab * 2; roi++ ) { if( roi < maxlab * 2 ) { logfile << masses[roi - maxlab] << ","; } else { logfile << masses[roi - maxlab] << std::endl; } } logfile.close(); return 0; } template int ByteImage( int /*argc */, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image ByteImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); typename ImageType::Pointer image = ITK_NULLPTR; typename ByteImageType::Pointer image2 = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); typedef itk::RescaleIntensityImageFilter RescaleFilterType; typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( 0 ); rescaler->SetOutputMaximum( 255 ); rescaler->SetInput( image ); WriteImage( rescaler->GetOutput(), outname.c_str() ); return 0; } template int PValueImage( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; unsigned int dof = 1; if( argc > argct ) { dof = atoi(argv[argct]); } argct++; typename ImageType::Pointer image = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); // std::cout << " read Image" << fn1 << " dof " << dof << std::endl; typedef itk::Statistics::TDistribution DistributionType; typename DistributionType::Pointer distributionFunction = DistributionType::New(); distributionFunction->SetDegreesOfFreedom( dof ); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( image, image->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { float val = image->GetPixel(vfIter.GetIndex() ); if( !vnl_math_isnan(val) && !vnl_math_isinf(val) ) { if( fabs( val ) > 0 ) { float pval = 0; if( val >= 1 ) { pval = 1.0 - distributionFunction->EvaluateCDF( val, dof ); } else if( val < 1 ) { pval = distributionFunction->EvaluateCDF( val, dof ); } image->SetPixel(vfIter.GetIndex(), pval ); } } else { image->SetPixel(vfIter.GetIndex(), 0); } } WriteImage(image, outname.c_str() ); return 0; } template int ConvertImageSetToMatrix(unsigned int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image MatrixImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; if( argc < 5 ) { // std::cout << " need more args -- see usage " << std::endl; throw std::exception(); } const std::string outname = std::string(argv[argct]); std::string ext = itksys::SystemTools::GetFilenameExtension( outname ); argct += 2; unsigned int rowcoloption = atoi(argv[argct]); argct++; std::string maskfn = std::string(argv[argct]); argct++; unsigned int numberofimages = 0; typename ImageType::Pointer mask = ITK_NULLPTR; ReadImage(mask, maskfn.c_str() ); unsigned long voxct = 0; Iterator mIter( mask, mask->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { voxct++; } } typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::SizeType size; size.Fill(0); for( unsigned int j = argct; j < argc; j++ ) { numberofimages++; // Get the image dimension std::string fn = std::string(argv[j]); typename itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); for( unsigned int i = 0; i < imageIO->GetNumberOfDimensions(); i++ ) { if( imageIO->GetDimensions(i) > size[i] ) { size[i] = imageIO->GetDimensions(i); // std::cout << " bigimage " << j << " size " << size << std::endl; } } } // std::cout << " largest image " << size << " num images " << numberofimages << " voxct " << voxct << std::endl; unsigned long xx1 = 0, yy1 = 0; if( rowcoloption == 0 ) { // std::cout << " row option " << std::endl; xx1 = voxct; yy1 = numberofimages; } if( rowcoloption == 1 ) { // std::cout << " col option " << std::endl; yy1 = voxct; xx1 = numberofimages; } unsigned long xsize = xx1; unsigned long ysize = yy1; if( strcmp(ext.c_str(), ".csv") == 0 ) { typedef itk::Array2D MatrixType; std::vector ColumnHeaders; MatrixType matrix(xsize, ysize); matrix.Fill(0); unsigned int imagecount = 0; for( unsigned int j = argct; j < argc; j++ ) { std::string fn = std::string(argv[j]); ReadImage(image2, fn.c_str() ); // std::cout << " image " << j << " is " << fn << std::endl; unsigned long xx = 0, yy = 0, tvoxct = 0; if( rowcoloption == 0 ) { yy = imagecount; } if( rowcoloption == 1 ) { xx = imagecount; } for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { if( rowcoloption == 0 ) { xx = tvoxct; } if( rowcoloption == 1 ) { yy = tvoxct; } matrix[xx][yy] = image2->GetPixel(mIter.GetIndex() ); std::string colname = std::string("V") + ants_to_string(tvoxct); if( imagecount == 0 ) { ColumnHeaders.push_back( colname ); } tvoxct++; } } imagecount++; } // write out the array2D object typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( outname ); writer->SetInput( &matrix ); writer->SetColumnHeaders( ColumnHeaders ); try { writer->Write(); } catch( itk::ExceptionObject& /* exp */) { // std::cout << "Exception caught!" << std::endl; // std::cout << exp << std::endl; return EXIT_FAILURE; } } else { /** declare the tiled image */ typename MatrixImageType::SizeType tilesize; tilesize[0] = xsize; tilesize[1] = ysize; // std::cout << " allocate matrix " << tilesize << std::endl; typename MatrixImageType::RegionType region; region.SetSize( tilesize ); typename MatrixImageType::Pointer matimage = AllocImage(region); unsigned int imagecount = 0; for( unsigned int j = argct; j < argc; j++ ) { std::string fn = std::string(argv[j]); ReadImage(image2, fn.c_str() ); // std::cout << " image " << j << " is " << fn << std::endl; unsigned long xx = 0, yy = 0, tvoxct = 0; if( rowcoloption == 0 ) { yy = imagecount; } if( rowcoloption == 1 ) { xx = imagecount; } typename MatrixImageType::IndexType mind; for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { if( rowcoloption == 0 ) { xx = tvoxct; } if( rowcoloption == 1 ) { yy = tvoxct; } mind[0] = xx; mind[1] = yy; // // std::cout << " Mind " << mind << std::endl; matimage->SetPixel(mind, image2->GetPixel(mIter.GetIndex() ) ); tvoxct++; } } imagecount++; } // std::cout << " mat size " << matimage->GetLargestPossibleRegion().GetSize() << std::endl; WriteImage(matimage, outname.c_str() ); } return 0; } template int RandomlySampleImageSetToCSV(unsigned int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRandomConstIteratorWithIndex Iterator; int argct = 2; if( argc < 5 ) { // std::cout << " need more args -- see usage " << std::endl; throw std::exception(); } const std::string outname = std::string(argv[argct]); std::string ext = itksys::SystemTools::GetFilenameExtension( outname ); argct += 2; unsigned int n_samples = atoi(argv[argct]); argct++; /* std::string maskfn=std::string(argv[argct]); argct++; typename ImageType::Pointer mask = NULL; ReadImage(mask,maskfn.c_str()); Iterator mIter( mask,mask->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) if (mIter.Get() >= 0.5) voxct++; */ unsigned int numberofimages = 0; typename ImageType::Pointer image2 = ITK_NULLPTR; for( unsigned int j = argct; j < argc; j++ ) { numberofimages++; } unsigned long xsize = numberofimages; unsigned long ysize = n_samples; if( strcmp(ext.c_str(), ".csv") == 0 ) { typedef itk::Array2D MatrixType; std::vector ColumnHeaders; MatrixType matrix(xsize, ysize); matrix.Fill(0); unsigned int imagecount = 0; for( unsigned int j = argct; j < argc; j++ ) { std::string fn = std::string(argv[j]); ReadImage(image2, fn.c_str() ); Iterator mIter( image2, image2->GetLargestPossibleRegion() ); mIter.SetNumberOfSamples(n_samples); // std::cout << " image " << j << " is " << fn << std::endl; unsigned long voxct = 0; for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { matrix[imagecount][voxct] = image2->GetPixel(mIter.GetIndex() ); if( imagecount == 0 ) { std::string colname = std::string("V") + ants_to_string(voxct); ColumnHeaders.push_back( colname ); } voxct++; } imagecount++; } // write out the array2D object typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( outname ); writer->SetInput( &matrix ); writer->SetColumnHeaders( ColumnHeaders ); try { writer->Write(); } catch( itk::ExceptionObject& /* exp */) { // std::cout << "Exception caught!" << std::endl; // std::cout << exp << std::endl; return EXIT_FAILURE; } } else { // std::cout << " need a csv file as output type , you tried " << outname << std::endl; } return 0; } template inline std::string to_string(const T& t) { std::stringstream ss; ss << t; return ss.str(); } template int ConvertImageSetToEigenvectors(unsigned int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; if( argc < 5 ) { // std::cout << " need more args -- see usage " << std::endl; throw std::exception(); } const std::string outname = std::string(argv[argct]); std::string ext = std::string(".csv"); // itksys::SystemTools::GetFilenameExtension( outname ); argct += 2; unsigned int n_evecs = atoi(argv[argct]); argct++; unsigned int rowcoloption = 1; std::string maskfn = std::string(argv[argct]); argct++; unsigned int numberofimages = 0; typename ImageType::Pointer mask = ITK_NULLPTR; ReadImage(mask, maskfn.c_str() ); /** 1. compute max value in mask */ unsigned long maxval = 0; for( Iterator mIter( mask, mask->GetLargestPossibleRegion() ); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() > maxval ) { maxval = (unsigned long) mIter.Get(); } } if( maxval == 0 ) { // std::cout << " Max value in mask is <= 0, aborting. " << maxval << std::endl; throw std::exception(); } typedef itk::Array2D MatrixType; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::SizeType size; size.Fill(0); for( unsigned int j = argct; j < argc; j++ ) { numberofimages++; // Get the image dimension std::string fn = std::string(argv[j]); typename itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); for( unsigned int i = 0; i < imageIO->GetNumberOfDimensions(); i++ ) { if( imageIO->GetDimensions(i) > size[i] ) { size[i] = imageIO->GetDimensions(i); // std::cout << " bigimage " << j << " size " << size << std::endl; } } } // std::cout << " largest image " << size << " num images " << numberofimages << std::endl; MatrixType avg_matrix(numberofimages, maxval); avg_matrix.Fill(0); for( unsigned long mv = 1; mv <= maxval; mv++ ) { /** 2. count the voxels in this label */ unsigned long voxct = 0; for( Iterator mIter( mask, mask->GetLargestPossibleRegion() ); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() == mv ) { voxct++; } } unsigned long xx1 = 0, yy1 = 0; if( rowcoloption == 0 ) { // std::cout << " row option " << std::endl; xx1 = voxct; yy1 = numberofimages; } if( rowcoloption == 1 ) { // std::cout << " col option " << std::endl; yy1 = voxct; xx1 = numberofimages; } unsigned long xsize = xx1; unsigned long ysize = yy1; if( strcmp(ext.c_str(), ".csv") == 0 ) { MatrixType matrix(xsize, ysize); matrix.Fill(0); unsigned int imagecount = 0; for( unsigned int j = argct; j < argc; j++ ) { std::string fn = std::string(argv[j]); ReadImage(image2, fn.c_str() ); // std::cout << " image " << j << " is " << fn << std::endl; unsigned long xx = 0, yy = 0, tvoxct = 0; if( rowcoloption == 0 ) { yy = imagecount; } if( rowcoloption == 1 ) { xx = imagecount; } for( Iterator mIter( mask, mask->GetLargestPossibleRegion() ); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() == mv ) { if( rowcoloption == 0 ) { xx = tvoxct; } if( rowcoloption == 1 ) { yy = tvoxct; } matrix[xx][yy] = image2->GetPixel(mIter.GetIndex() ); tvoxct++; } } imagecount++; } typedef double Scalar; typedef itk::ants::antsMatrixUtilities matrixOpType; typename matrixOpType::Pointer matrixOps = matrixOpType::New(); typedef vnl_vector ScalarVectorType; MatrixType evec_matrix(xsize, n_evecs + 1); evec_matrix.Fill(0); ScalarVectorType avg = matrixOps->AverageColumns(matrix); avg_matrix.set_column(mv, avg); evec_matrix.set_column(0, avg); MatrixType tempm = matrixOps->GetCovMatEigenvectors(matrix); for( unsigned int i = 0; i < n_evecs; i++ ) { /** the GetCovMatEigenvector function normalizes the matrix & computes the covariance matrix internally */ // VectorType evec=matrixOps->GetCovMatEigenvector(matrix,i); evec_matrix.set_column(i + 1, tempm.get_column(i) ); } // write out the array2D object typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); std::string num = std::string("evecs_for_label") + to_string(mv); std::string eoutname = outname + num + ext; writer->SetFileName( eoutname ); writer->SetInput( &evec_matrix ); try { writer->Write(); } catch( itk::ExceptionObject& /* exp */) { // std::cout << "Exception caught!" << std::endl; // std::cout << exp << std::endl; return EXIT_FAILURE; } } else { // std::cout << " can only write out csv files " << std::endl; } } // end loop over mv variable { // write out the array2D object typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); std::string num = std::string("avg_for_labels"); std::string eoutname = outname + num + ext; writer->SetFileName( eoutname ); writer->SetInput( &avg_matrix ); try { writer->Write(); } catch( itk::ExceptionObject& /* exp */) { // std::cout << "Exception caught!" << std::endl; // std::cout << exp << std::endl; return EXIT_FAILURE; } } return 0; } template int ConvertImageToFile( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); } argct++; typename ImageType::Pointer image = ITK_NULLPTR; ReadImage(image, fn1.c_str() ); typename ImageType::Pointer mask = ITK_NULLPTR; if( fn2.length() > 3 ) { ReadImage(mask, fn2.c_str() ); } // std::cout << " read Image" << fn1 << " mask? " << fn2 << std::endl; std::ofstream logfile; logfile.open(outname.c_str() ); if( logfile.good() ) { typedef itk::ImageRegionIteratorWithIndex ImageIterator; ImageIterator vfIter( image, image->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { bool getval = true; if( mask->GetPixel(vfIter.GetIndex() ) < 0.5 ) { getval = false; } if( getval ) { float val = image->GetPixel(vfIter.GetIndex() ); logfile << " " << val; } } logfile << std::endl; } logfile.close(); return 0; } template int CorrelationUpdate( int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); argct++; } else { // std::cout << " Not enough inputs " << std::endl; return 1; } unsigned int radius = 2; if( argc > argct ) { radius = atoi(argv[argct]); } argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typename ImageType::Pointer imageout = ITK_NULLPTR; ReadImage(imageout, fn1.c_str() ); imageout->FillBuffer(0); typename ImageType::Pointer image2 = ITK_NULLPTR; ReadImage(image2, fn2.c_str() ); typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for( unsigned int j = 0; j < ImageDimension; j++ ) { rad[j] = radius; } iteratorType GHood(rad, image1, image1->GetLargestPossibleRegion() ); float Gsz = (float)GHood.Size(); GHood.GoToBegin(); while( !GHood.IsAtEnd() ) { typename ImageType::IndexType ind = GHood.GetIndex(); bool isinside = true; for( unsigned int j = 0; j < ImageDimension; j++ ) { float shifted = ind[j]; if( shifted < (radius + 1) || shifted > image1->GetLargestPossibleRegion().GetSize()[j] - radius - 1 ) { isinside = false; } } if( isinside ) { if( image1->GetPixel(ind) > 0 || image2->GetPixel(ind) > 0 ) { // compute mean difference float diff = 0.0; for( unsigned int i = 0; i < GHood.Size(); i++ ) { const typename ImageType::IndexType & ind2 = GHood.GetIndex(i); diff += (image1->GetPixel(ind2) - image2->GetPixel(ind2) ); } diff /= Gsz; float upd = (image1->GetPixel(ind) - image2->GetPixel(ind) ) - diff; imageout->SetPixel(ind, upd); } } ++GHood; } WriteImage(imageout, outname.c_str() ); return 0; } template int MajorityVoting( int argc, char *argv[] ) { typedef int PixelType; typedef itk::Image ImageType; typedef itk::MinimumMaximumImageCalculator CalculatorType; typedef itk::ImageRegionIteratorWithIndex IteratorType; if( argc < 5 ) { // std::cout << " Not enough inputs " << std::endl; return 1; } std::string outputName = std::string( argv[2] ); // Read input segmentations const unsigned long nImages = argc - 4; typename std::vector images(argc - 4); for( int i = 4; i < argc; i++ ) { ReadImage( images[i - 4], argv[i] ); } // Find maximum label int maxLabel = 0; for( unsigned int i = 0; i < nImages; i++ ) { typename CalculatorType::Pointer calc = CalculatorType::New(); calc->SetImage( images[i] ); calc->ComputeMaximum(); if( calc->GetMaximum() > maxLabel ) { maxLabel = calc->GetMaximum(); } } unsigned long nLabels = maxLabel + 1; // account for label=0 typename ImageType::Pointer output = AllocImage(images[0], 0); IteratorType it( output, output->GetLargestPossibleRegion() ); itk::Array votes; votes.SetSize( nLabels ); while( !it.IsAtEnd() ) { votes.Fill(0); unsigned long maxVotes = 0; unsigned long votedLabel = 0; for( unsigned long i = 0; i < nImages; i++ ) { unsigned long label = images[i]->GetPixel( it.GetIndex() ); votes.SetElement(label, votes.GetElement(label) + 1 ); if( votes.GetElement(label) > maxVotes ) { maxVotes = votes.GetElement(label); votedLabel = label; } } it.Set( votedLabel ); ++it; } WriteImage( output, outputName.c_str() ); return 0; } template int MostLikely( int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image LabeledImageType; typedef itk::ImageRegionIteratorWithIndex IteratorType; if( argc < 5 ) { // std::cout << " Not enough inputs " << std::endl; return 1; } std::string outputName = std::string( argv[2] ); float sigma = atof( argv[4] ); typename LabeledImageType::Pointer output = LabeledImageType::New(); typename ImageType::Pointer prob = ImageType::New(); // Read input segmentations for( int i = 5; i < argc; i++ ) { typename ImageType::Pointer iLabel = ImageType::New(); ReadImage( iLabel, argv[i] ); if( i == 5 ) { output->SetRegions( iLabel->GetLargestPossibleRegion() ); output->SetSpacing( iLabel->GetSpacing() ); output->SetOrigin( iLabel->GetOrigin() ); output->SetDirection( iLabel->GetDirection() ); output->Allocate(); output->FillBuffer( 0 ); prob->SetRegions( iLabel->GetLargestPossibleRegion() ); prob->SetSpacing( iLabel->GetSpacing() ); prob->SetOrigin( iLabel->GetOrigin() ); prob->SetDirection( iLabel->GetDirection() ); prob->Allocate(); prob->FillBuffer( 0 ); } IteratorType it( output, output->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { if( ( iLabel->GetPixel( it.GetIndex() ) > sigma ) && ( iLabel->GetPixel( it.GetIndex() ) > prob->GetPixel( it.GetIndex() ) ) ) { prob->SetPixel( it.GetIndex(), iLabel->GetPixel( it.GetIndex() ) ); output->SetPixel( it.GetIndex(), i - 4 ); } ++it; } } WriteImage( output, outputName.c_str() ); return 0; } template int STAPLE( int argc, char *argv[] ) { typedef int PixelType; typedef itk::Image ImageType; typedef itk::Image OutputImageType; typedef itk::MinimumMaximumImageCalculator CalculatorType; typedef itk::STAPLEImageFilter StapleFilterType; if( argc < 5 ) { // std::cout << " Not enough inputs " << std::endl; return 1; } std::string outputName = std::string( argv[2] ); typename StapleFilterType::Pointer stapler = StapleFilterType::New(); std::string::size_type idx; idx = outputName.find_first_of('.'); std::string tempname = outputName.substr(0, idx); std::string extension = outputName.substr(idx, outputName.length() ); float confidence = atof( argv[4] ); // = 0.5 // stapler->SetForegroundValue( foreground ); stapler->SetConfidenceWeight( confidence ); // Read input segmentations typename ImageType::Pointer nullimage( ITK_NULLPTR ); std::vector images(argc - 5, nullimage ); typename CalculatorType::Pointer calc = CalculatorType::New(); int maxLabel = 0; for( int i = 5; i < argc; i++ ) { images[i - 5] = ImageType::New(); ReadImage( images[i - 5], argv[i] ); stapler->SetInput( i - 5, images[i - 5] ); // std::cout << "Input image " << i - 5 << " " << argv[i] << std::endl; calc->SetImage( images[i - 5] ); calc->ComputeMaximum(); if( calc->GetMaximum() > maxLabel ) { maxLabel = calc->GetMaximum(); } } // std::cout << "Examining " << maxLabel << " labels" << std::endl; for( int label = 1; label <= maxLabel; label++ ) { char num[5]; sprintf( num, "%04d", label ); std::string oname = tempname + num + extension; stapler->SetForegroundValue( label ); stapler->Update(); WriteImage( stapler->GetOutput(), oname.c_str() ); } return 0; } template int AverageLabels( int argc, char *argv[] ) { typedef int PixelType; typedef itk::Image ImageType; typedef itk::Image OutputImageType; typedef itk::MinimumMaximumImageCalculator CalculatorType; typedef itk::ImageRegionIteratorWithIndex IteratorType; if( argc < 5 ) { // std::cout << " Not enough inputs " << std::endl; return 1; } std::string outputName = std::string( argv[2] ); std::string::size_type idx; idx = outputName.find_first_of('.'); std::string tempname = outputName.substr(0, idx); std::string extension = outputName.substr(idx, outputName.length() ); // Read input segmentations typename ImageType::Pointer nullimage( ITK_NULLPTR ); std::vector images(argc - 4, nullimage ); typename CalculatorType::Pointer calc = CalculatorType::New(); int maxLabel = 0; for( int i = 4; i < argc; i++ ) { images[i - 4] = ImageType::New(); ReadImage( images[i - 4], argv[i] ); // std::cout << "Input image " << i - 4 << " " << argv[i] << std::endl; calc->SetImage( images[i - 4] ); calc->ComputeMaximum(); if( calc->GetMaximum() > maxLabel ) { maxLabel = calc->GetMaximum(); } } // std::cout << "Examining " << maxLabel << " labels" << std::endl; typename OutputImageType::Pointer nullout( ITK_NULLPTR ); std::vector outimages; for( int label = 0; label < maxLabel; label++ ) { typename OutputImageType::Pointer img = OutputImageType::New(); img->SetRegions( images[0]->GetLargestPossibleRegion() ); img->SetDirection( images[0]->GetDirection() ); img->SetOrigin( images[0]->GetOrigin() ); img->SetSpacing( images[0]->GetSpacing() ); img->Allocate(); outimages.push_back( img ); } for( unsigned int i = 0; i < images.size(); i++ ) { IteratorType it( images[i], images[i]->GetLargestPossibleRegion() ); for( it.GoToBegin(); !it.IsAtEnd(); ++it ) { if( it.Value() > 0 ) { outimages[it.Value() - 1]->SetPixel( it.GetIndex(), outimages[it.Value() - 1]->GetPixel( it.GetIndex() ) + 1.0 / images.size() ); } } } for( int label = 1; label <= maxLabel; label++ ) { char num[5]; sprintf( num, "%04d", label ); std::string oname = tempname + num + extension; WriteImage( outimages[label - 1], oname.c_str() ); } return 0; } template int CorrelationVoting( int argc, char *argv[] ) { typedef float PixelType; typedef int LabelType; typedef itk::Image ImageType; typedef itk::Image LabelImageType; typedef itk::MinimumMaximumImageCalculator CalculatorType; typedef itk::ImageRegionIteratorWithIndex IteratorType; typedef itk::NeighborhoodIterator NeighborhoodIteratorType; if( argc < 6 ) { // std::cout << " Not enough inputs " << std::endl; return 1; } std::string outputName = std::string( argv[2] ); // Read input images and segmentations const int nImages = (argc - 5) / 2; int radius = 5; if( argc > ( 5 + 2 * nImages ) ) { radius = atoi( argv[5 + 2 * nImages] ); } typename ImageType::Pointer target; ReadImage( target, argv[4] ); typename std::vector images(nImages); typename std::vector labels(nImages); for( int i = 5; i < (5 + nImages); i++ ) { ReadImage( images[i - 5], argv[i] ); } for( int i = 5 + nImages; i < (5 + 2 * nImages); i++ ) { ReadImage( labels[i - 5 - nImages], argv[i] ); } // Find maximum label int maxLabel = 0; for( int i = 0; i < nImages; i++ ) { typename CalculatorType::Pointer calc = CalculatorType::New(); calc->SetImage( labels[i] ); calc->ComputeMaximum(); if( calc->GetMaximum() > maxLabel ) { maxLabel = calc->GetMaximum(); } } unsigned long nLabels = maxLabel + 1; // account for label=0 typename LabelImageType::Pointer output = AllocImage(images[0], 0); IteratorType it( output, output->GetLargestPossibleRegion() ); itk::Array votes; votes.SetSize( nLabels ); typename NeighborhoodIteratorType::RadiusType rad; for( unsigned int j = 0; j < ImageDimension; j++ ) { rad[j] = radius; } NeighborhoodIteratorType metricIt(rad, target, target->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { votes.Fill(0); float maxVotes = 0; int votedLabel = 0; for( int i = 0; i < nImages; i++ ) { int label = labels[i]->GetPixel( it.GetIndex() ); votes.SetElement(label, votes.GetElement(label) + 1 ); if( votes.GetElement(label) > maxVotes ) { maxVotes = votes.GetElement(label); votedLabel = label; } } // If all agree, assign label immediately if( maxVotes == nImages ) { it.Set( votedLabel ); } else { votes.Fill( 0.0 ); maxVotes = 0.0; votedLabel = 0; itk::VariableLengthVector weights( nImages ); for( int i = 0; i < nImages; i++ ) { metricIt.SetLocation( it.GetIndex() ); float targetMean = 0.0; float targetVar = 0.0; float imageMean = 0.0; float imageVar = 0.0; float k = 0; float product = 0.0; for( unsigned int j = 0; j < metricIt.Size(); j++ ) { typename NeighborhoodIteratorType::OffsetType internal; typename NeighborhoodIteratorType::OffsetType offset; if( metricIt.IndexInBounds( j, internal, offset ) ) { k++; typename ImageType::IndexType idx = metricIt.GetIndex( j ); if( k == 1 ) { targetMean = target->GetPixel( idx ); imageMean = images[i]->GetPixel( idx ); targetVar = 0.0; imageVar = 0.0; } else { float oldMean = targetMean; float value = target->GetPixel( idx ); targetMean = targetMean + (value - targetMean) / k; targetVar = targetVar + (value - oldMean) * ( value - targetMean ); oldMean = imageMean; float iValue = images[i]->GetPixel( idx ); imageMean = imageMean + ( iValue - imageMean ) / k; imageVar = imageVar + ( iValue - oldMean) * ( iValue - imageMean ); product += value * iValue; } } // metricIt.IndexInBounds() } // j >= metricIt.Size() targetVar /= (k - 1); imageVar /= (k - 1); float pearson = ( product - k * targetMean * imageMean ) / ( (k - 1) * std::sqrt(targetVar) * std::sqrt(imageVar) ); weights.SetElement( i, std::fabs(pearson) ); } // i >= nImages for( int i = 0; i < nImages; i++ ) { int label = labels[i]->GetPixel( it.GetIndex() ); votes.SetElement(label, votes.GetElement(label) + weights.GetElement(i) ); if( votes.GetElement(label) > maxVotes ) { maxVotes = votes.GetElement(label); votedLabel = label; } } it.Set(votedLabel); } // else find weighted vote winner ++it; } // iterate over output image WriteImage( output, outputName.c_str() ); return 0; } template int ImageMetrics( int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; // typedef itk::ANTSNeighborhoodCorrelationImageToImageMetricv4 // MetricType; if( argc < 5 ) { // std::cout << "ERROR: Not enough inputs " << std::endl; return 1; } typename ImageType::Pointer img1; ReadImage( img1, argv[4] ); typename ImageType::Pointer img2; ReadImage( img2, argv[5] ); float value = 0.0; if( strcmp(argv[3], "NeighborhoodCorrelation") == 0 ) { typedef itk::ANTSNeighborhoodCorrelationImageToImageMetricv4 MetricType; typedef typename itk::ImageMaskSpatialObject MaskType; typedef typename MaskType::ImageType MaskImageType; int r = 5; typename MetricType::Pointer metric = MetricType::New(); metric->SetFixedImage( img1 ); metric->SetMovingImage( img2 ); if( argc > 6 ) { r = atoi( argv[6] ); } if( argc > 7 ) { typename MaskType::Pointer mask = MaskType::New(); typename MaskImageType::Pointer maskImage = MaskImageType::New(); ReadImage( maskImage, argv[7] ); mask->SetImage( maskImage ); metric->SetFixedImageMask( mask ); metric->SetMovingImageMask( mask ); } typename MetricType::RadiusType radius; radius.Fill( r ); metric->SetRadius( radius ); metric->Initialize(); value = metric->GetValue(); } else if( strcmp(argv[3], "NormalizedCorrelation") == 0 ) { typedef itk::CorrelationImageToImageMetricv4 MetricType; typedef typename itk::ImageMaskSpatialObject MaskType; typedef typename MaskType::ImageType MaskImageType; typename MetricType::Pointer metric = MetricType::New(); metric->SetFixedImage( img1 ); metric->SetMovingImage( img2 ); if( argc > 6 ) { typename MaskType::Pointer mask = MaskType::New(); typename MaskImageType::Pointer maskImage = MaskImageType::New(); ReadImage( maskImage, argv[6] ); mask->SetImage( maskImage ); metric->SetFixedImageMask( mask ); metric->SetMovingImageMask( mask ); } metric->Initialize(); value = metric->GetValue(); } else if( strcmp(argv[3], "Demons") == 0 ) { typedef itk::DemonsImageToImageMetricv4 MetricType; typedef typename itk::ImageMaskSpatialObject MaskType; typedef typename MaskType::ImageType MaskImageType; typename MetricType::Pointer metric = MetricType::New(); metric->SetFixedImage( img1 ); metric->SetMovingImage( img2 ); if( argc > 6 ) { typename MaskType::Pointer mask = MaskType::New(); typename MaskImageType::Pointer maskImage = MaskImageType::New(); ReadImage( maskImage, argv[6] ); mask->SetImage( maskImage ); metric->SetFixedImageMask( mask ); metric->SetMovingImageMask( mask ); } // FIXME - Calling initialize on demons causes seg fault // std::cout << "Demons is currently broken" << std::endl; return 1; metric->Initialize(); value = metric->GetValue(); } else if( strcmp(argv[3], "Mattes") == 0 ) { typedef itk::MattesMutualInformationImageToImageMetricv4 MetricType; typedef typename itk::ImageMaskSpatialObject MaskType; typedef typename MaskType::ImageType MaskImageType; int bins = 32; typename MetricType::Pointer metric = MetricType::New(); metric->SetFixedImage( img1 ); metric->SetMovingImage( img2 ); if( argc > 6 ) { bins = atoi( argv[6] ); } if( argc > 7 ) { typename MaskType::Pointer mask = MaskType::New(); typename MaskImageType::Pointer maskImage = MaskImageType::New(); ReadImage( maskImage, argv[7] ); mask->SetImage( maskImage ); metric->SetFixedImageMask( mask ); metric->SetMovingImageMask( mask ); } metric->SetNumberOfHistogramBins( bins ); metric->Initialize(); value = metric->GetValue(); } std::cout << value << std::endl; return 0; } template int PearsonCorrelation( int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex IteratorType; if( argc < 5 ) { // std::cout << "ERROR: Not enough inputs " << std::endl; return 1; } typename ImageType::Pointer img1; ReadImage( img1, argv[4] ); typename ImageType::Pointer img2; ReadImage( img2, argv[5] ); typename ImageType::Pointer mask; if( argc > 6 ) { ReadImage( mask, argv[6] ); } else { mask = AllocImage(img1, 1); } float xmean = 0.0; float ymean = 0.0; float xsquared = 0.0; float ysquared = 0.0; float product = 0.0; float k = 0; IteratorType it( mask, mask->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { if( it.Value() > 0 ) { k++; typename ImageType::IndexType idx = it.GetIndex(); float value = img1->GetPixel( idx ); xmean += value; xsquared += (value * value); value = img2->GetPixel( idx ); ymean += value; ysquared += (value * value); product += img1->GetPixel( idx ) * img2->GetPixel( idx ); } ++it; } xmean /= k; ymean /= k; float pearson = ( product - k * xmean * ymean ) / ( std::sqrt(xsquared - (k * xmean * xmean)) * std::sqrt(ysquared - (k * ymean * ymean)) ); std::cout << pearson << std::endl; return 0; } template int Project( int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; if( argc < 5 ) { // std::cout << "Not enough input parameters" << std::endl; return EXIT_FAILURE; } int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; unsigned int axis = atoi(argv[argct]); argct++; unsigned int which = atoi(argv[argct]); argct++; typename ImageType::Pointer imagein; typename ImageType::Pointer imageout; ReadImage( imagein, fn1.c_str() ); if( which == 0 ) { typedef itk::SumProjectionImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetProjectionDimension( axis ); filter->SetInput( imagein ); filter->Update(); imageout = filter->GetOutput(); WriteImage( imageout, outname.c_str() ); } if( which == 1 ) { typedef itk::MaximumProjectionImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetProjectionDimension( axis ); filter->SetInput( imagein ); filter->Update(); imageout = filter->GetOutput(); WriteImage( imageout, outname.c_str() ); } if( which == 2 ) { typedef itk::MinimumProjectionImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetProjectionDimension( axis ); filter->SetInput( imagein ); filter->Update(); imageout = filter->GetOutput(); WriteImage( imageout, outname.c_str() ); } return EXIT_SUCCESS; } template int Translate( int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ResampleImageFilter FilterType; typedef itk::TranslationTransform TransformType; typedef typename TransformType::OutputVectorType OffsetType; if( argc < (int)(ImageDimension + 4 ) ) { // std::cout << "Not enough input parameters" << std::endl; return EXIT_FAILURE; } typename ImageType::Pointer input = ImageType::New(); ReadImage( input, argv[4] ); // std::cout << "Input image: " << argv[4] << std::endl; typename TransformType::Pointer translate = TransformType::New(); OffsetType offset; for( unsigned int i = 0; i < ImageDimension; i++ ) { offset[i] = atof( argv[i + 5] ); } translate->SetOffset( offset ); // std::cout << "Translation: " << offset << std::endl; typename FilterType::Pointer resample = FilterType::New(); resample->SetInput( input ); resample->SetTransform( translate ); resample->SetOutputParametersFromImage( input ); resample->SetDefaultPixelValue( 0 ); resample->Update(); WriteImage( resample->GetOutput(), argv[2] ); return EXIT_SUCCESS; } template int MinMaxMean( int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::MinimumMaximumImageCalculator CalculatorType; typedef itk::ImageMomentsCalculator MomentsCalculatorType; if( argc < 5 ) { // std::cout << " Not enough inputs " << std::endl; return 1; } typename ImageType::Pointer image = ImageType::New(); ReadImage( image, argv[4] ); typename CalculatorType::Pointer calc = CalculatorType::New(); calc->SetImage( image ); calc->ComputeMaximum(); calc->ComputeMinimum(); typename MomentsCalculatorType::Pointer calc2 = MomentsCalculatorType::New(); calc2->SetImage( image ); calc2->Compute(); float mean = calc2->GetTotalMass(); for( unsigned int i = 0; i < ImageDimension; i++ ) { mean /= image->GetLargestPossibleRegion().GetSize()[i]; } // std::cout << calc->GetMinimum() << " " << calc->GetMaximum() << " " << mean << std::endl; return 0; } template TRealType PatchCorrelation( itk::NeighborhoodIterator GHood, itk::NeighborhoodIterator GHood2, std::vector activeindex, std::vector weight, typename TGImageType::Pointer gimage, typename TGImageType::Pointer gimage2, TInterp interp2 ) { typedef TRealType RealType; typedef typename TImageType::PointType PointType; typedef itk::CovariantVector GradientPixelType; typedef vnl_vector VectorType; typedef typename TImageType::IndexType IndexType; unsigned int Gsz = activeindex.size(); std::vector imagepatch1; std::vector imagepatch2; VectorType sample1( Gsz, 0); VectorType sample2( Gsz, 0); vnl_matrix grad1mat( Gsz, ImageDimension); grad1mat.fill( 0 ); vnl_matrix grad2mat( Gsz, ImageDimension); grad2mat.fill( 0 ); // compute mean difference PointType avgpoint1; PointType avgpoint2; avgpoint1.Fill( 0 ); avgpoint2.Fill( 0 ); RealType wt = 1.0 / ( RealType ) Gsz; for( unsigned int ii = 0; ii < Gsz; ii++ ) { sample1[ii] = GHood.GetPixel( activeindex[ii] ); sample2[ii] = GHood2.GetPixel( activeindex[ii] ); IndexType gind = GHood.GetIndex( activeindex[ii] ); IndexType gind2 = GHood2.GetIndex( activeindex[ii] ); if( ( IsInside( gimage, gind ) ) && ( IsInside( gimage2, gind2 ) ) ) { GradientPixelType grad1 = gimage->GetPixel( gind ) * weight[ii]; GradientPixelType grad2 = gimage2->GetPixel( gind2 ) * weight[ii]; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { grad1mat( ii, jj ) = grad1[jj]; grad2mat( ii, jj ) = grad2[jj]; } PointType point1; PointType point2; gimage->TransformIndexToPhysicalPoint( gind, point1 ); gimage2->TransformIndexToPhysicalPoint( gind2, point2 ); for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { avgpoint1[dd] = avgpoint1[dd] + point1[dd] * wt; avgpoint2[dd] = avgpoint2[dd] + point2[dd] * wt; } imagepatch1.push_back( point1 ); imagepatch2.push_back( point2 ); } else { return 0; } } RealType mean1 = sample1.mean(); RealType mean2 = sample2.mean(); sample1 = ( sample1 - mean1 ); sample2 = ( sample2 - mean2 ); RealType sd1 = sqrt( sample1.squared_magnitude() ); RealType sd2 = sqrt( sample2.squared_magnitude() ); RealType correlation = inner_product( sample1, sample2 ) / ( sd1 * sd2 ); bool ok = true; /** compute patch orientation */ vnl_matrix cov1 = grad1mat.transpose() * grad1mat; vnl_matrix cov2 = grad2mat.transpose() * grad2mat; vnl_symmetric_eigensystem eig1( cov1 ); vnl_symmetric_eigensystem eig2( cov2 ); unsigned int eigind0 = 1; // biggest eigenvalue unsigned int eigind1 = 0; if( ImageDimension == 3 ) { eigind0 = 2; // biggest eigenvalue eigind1 = 1; } vnl_vector evec1_2ndary = eig1.get_eigenvector( eigind1 ); vnl_vector evec1_primary = eig1.get_eigenvector( eigind0 ); vnl_vector evec2_2ndary = eig2.get_eigenvector( eigind1 ); vnl_vector evec2_primary = eig2.get_eigenvector( eigind0 ); /** Solve Wahba's problem --- http://en.wikipedia.org/wiki/Wahba%27s_problem */ // NOT USED RealType wt0 = fabs( eig1.get_eigenvalue( eigind0 ) ) + fabs( eig2.get_eigenvalue( eigind0 ) ); // NOT USED RealType wt1 = fabs( eig1.get_eigenvalue( eigind1 ) ) + fabs( eig2.get_eigenvalue( eigind1 ) ); // NOT USED RealType evsum = wt0 + wt1; vnl_matrix B = outer_product( evec2_primary, evec1_primary ); if( ImageDimension == 3 ) { // B = outer_product( evec2_2ndary , evec1_2ndary ) * wt1 / evsum + // outer_product( evec2_primary , evec1_primary ) * wt0 / evsum; B = outer_product( evec2_2ndary, evec1_2ndary ) + outer_product( evec2_primary, evec1_primary ); } vnl_svd wahba( B ); vnl_matrix A_solution = wahba.V() * wahba.U().transpose(); // " << vnl_determinant( wahba.U()) << std::endl; // now rotate the points to the same frame and sample the neighborhoods again for( unsigned int ii = 0; ii < Gsz; ii++ ) { PointType ptran = imagepatch2[ii]; vnl_vector ptran2( ptran.Size(), 0 ); for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { ptran[dd] -= avgpoint2[dd]; ptran2[dd] = ptran[dd]; } // rotate ptran ptran2 = ( A_solution ) * ptran2; for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { ptran[dd] = ptran2[dd] + avgpoint2[dd]; } if( interp2->IsInsideBuffer( ptran ) ) { sample2[ii] = interp2->Evaluate( ptran ); } else { ok = false; } } if( ok ) { mean2 = sample2.mean(); sample2 = ( sample2 - mean2 ); sd2 = sqrt( sample2.squared_magnitude() ); correlation = inner_product( sample1, sample2 ) / ( sd1 * sd2 ); } // done applying wahba solution else { correlation = 0; } if( vnl_math_isnan( correlation ) || vnl_math_isinf( correlation ) ) { return 0; } else { return correlation; } } template void Sinkhorn( vnl_matrix& correspondencematrix ) { // std::cout << " SH begin " << std::endl; typedef TRealType RealType; // now that we have the correspondence matrix we convert it to a "probability" matrix for( unsigned int loop = 0; loop < 2; loop++ ) { for( unsigned int ii = 0; ii < correspondencematrix.cols(); ii++ ) { vnl_vector correspondencematrixcol = correspondencematrix.get_column( ii ); RealType csum = correspondencematrixcol.sum(); if( csum > 0 ) { correspondencematrix.set_column( ii, correspondencematrixcol / csum ); } } for( unsigned int ii = 0; ii < correspondencematrix.rows(); ii++ ) { vnl_vector correspondencematrixrow = correspondencematrix.get_row( ii ); RealType rsum = correspondencematrixrow.sum(); if( rsum > 0 ) { correspondencematrix.set_row( ii, correspondencematrixrow / rsum ); } } } // std::cout << " SH done " << std::endl; } template typename TImage::Pointer ComputeLaplacianImage( typename TImage::Pointer input ) { typedef itk::LaplacianRecursiveGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetSigma( 1.0 ); filter->SetInput( input ); filter->Update(); return filter->GetOutput(); } template int PureTissueN4WeightMask( int argc, char *argv[] ) { // This function is used to create the weight mask for N4. // The basic idea is that we want a weight mask which emphasizes // those voxels which are comprised of "pure" tissue types, e.g. // 100% prob. that it is gray matter. In words, suppose I want a // pure tissue weight mask comprised of gray matter and white matter. // To do this, I calculate the probability that something is gray // matter and not white matter or that something is white matter and // not gray matter. Generalized, this formula is // // weightMask = \sum_{i=1}^N P_i ( \Prod_{j=1,j!=i}^N (1 - P_j ) ) // // where P_i is the i^th probability if( argc < 5 ) { // std::cout << "Usage: " << argv[0] << " ImageDimension"; // std::cout << " outputWeightImage PureTissueN4WeightMask"; // std::cout << " probImage1 probImage2 ... probImageN" << std::endl; return EXIT_FAILURE; } typedef float PixelType; typedef itk::Image ImageType; std::vector images; for( int n = 4; n < argc; n++ ) { typename ImageType::Pointer image; ReadImage( image, argv[n] ); images.push_back( image ); } typename ImageType::Pointer output = ImageType::New(); output->CopyInformation( images[0] ); output->SetRegions( images[0]->GetLargestPossibleRegion() ); output->Allocate(); output->FillBuffer( 0 ); itk::ImageRegionIteratorWithIndex ItO( output, output->GetLargestPossibleRegion() ); for( ItO.GoToBegin(); !ItO.IsAtEnd(); ++ItO ) { PixelType probability = 0.0; for( unsigned int i = 0; i < images.size(); i++ ) { PixelType negation = 1.0; for( unsigned int j = 0; j < images.size(); j++ ) { if( i == j ) { continue; } negation *= ( 1.0 - images[j]->GetPixel( ItO.GetIndex() ) ); } probability += negation * images[i]->GetPixel( ItO.GetIndex() ); } ItO.Set( probability ); } WriteImage( output, argv[2] ); return EXIT_SUCCESS; } template int PMSmoothImage(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float sigma = 1.0; if( argc > argct ) { sigma = atof(argv[argct]); argct++; } float conductance = 0.25; if( argc > argct ) { conductance = atof(argv[argct]); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer varimage = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); PixelType spacingsize = 0; for( unsigned int d = 0; d < ImageDimension; d++ ) { PixelType sp = image1->GetSpacing()[d]; spacingsize += sp * sp; } spacingsize = sqrt( spacingsize ); typedef itk::GradientAnisotropicDiffusionImageFilter< ImageType, ImageType > FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image1 ); filter->SetNumberOfIterations( sigma ); PixelType mytimestep = spacingsize / std::pow( 2.0 , static_cast(ImageDimension+1) ); PixelType reftimestep = 0.4 / std::pow( 2.0 , static_cast(ImageDimension+1) ); if ( mytimestep > reftimestep ) mytimestep = reftimestep; filter->SetTimeStep( mytimestep ); filter->SetConductanceParameter( conductance ); // might need to change this filter->Update(); varimage = filter->GetOutput(); WriteImage( varimage, outname.c_str() ); return EXIT_SUCCESS; } template int ConvolveImage( int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; int argct = 2; const std::string outputFileName = std::string( argv[argct] ); argct += 2; std::string inputFileName = std::string( argv[argct] ); argct++; std::string kernelFileName = std::string( argv[argct] ); argct++; bool normalize = true; if( argc > argct ) { normalize = static_cast( atoi( argv[argct] ) ); } typename ImageType::Pointer inputImage = ITK_NULLPTR; typename ImageType::Pointer kernelImage = ITK_NULLPTR; ReadImage( inputImage, inputFileName.c_str() ); ReadImage( kernelImage, kernelFileName.c_str() ); typedef itk::ConvolutionImageFilter FilterType; typename FilterType::Pointer convoluter = FilterType::New(); convoluter->SetInput( inputImage ); convoluter->SetKernelImage( kernelImage ); convoluter->SetNormalize( normalize ); convoluter->Update(); typename ImageType::Pointer outputImage = convoluter->GetOutput(); outputImage->Update(); outputImage->DisconnectPipeline(); WriteImage( outputImage, outputFileName.c_str() ); return EXIT_SUCCESS; } template int InPaint(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float sigma = 1.0; if( argc > argct ) { sigma = atof(argv[argct]); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer varimage = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); PixelType spacingsize = 0; PixelType minsp = image1->GetSpacing()[0]; for( unsigned int d = 0; d < ImageDimension; d++ ) { PixelType sp = image1->GetSpacing()[d]; if ( sp < minsp ) minsp = sp; spacingsize += sp * sp; } spacingsize = sqrt( spacingsize ); float inpow = spacingsize * 2.0; if( argc > argct ) { inpow = atof(argv[argct]); argct++; } // # 1 job - create a kernel typename ImageType::Pointer kernel = ImageType::New(); typename ImageType::IndexType start; typename ImageType::SizeType size; typename ImageType::RegionType region; start.Fill(0); size.Fill(3); region.SetSize(size); region.SetIndex(start); kernel->SetRegions(region); kernel->Allocate(); kernel->SetSpacing( image1->GetSpacing() ); unsigned long kernelsize = region.GetNumberOfPixels(); itk::ImageRegionIterator imageIterator(kernel, region); unsigned int ct = 0; typename ImageType::PointType centerPoint; centerPoint.Fill( 0 ); typename ImageType::PointType locPoint; locPoint.Fill( 0 ); while(!imageIterator.IsAtEnd()) { if ( ct == static_cast( std::floor( (PixelType) kernelsize / 2.0 ) ) ) { kernel->TransformIndexToPhysicalPoint( imageIterator.GetIndex(), centerPoint ); } ++ct; ++imageIterator; } unsigned int ct2 = 0; PixelType totalval = 0; imageIterator.GoToBegin(); while(!imageIterator.IsAtEnd()) { kernel->TransformIndexToPhysicalPoint( imageIterator.GetIndex(), locPoint ); PixelType val = 0; for ( unsigned int d = 0; d < ImageDimension; d++ ) { PixelType delt = ( locPoint[d] - centerPoint[d] ); val += delt * delt; } PixelType distval = sqrt( val ); if ( distval > inpow ) val = 1.e8; if ( val > 0 ) { imageIterator.Set( 1.0 / val ); totalval += imageIterator.Get( ); } if ( ct2 == static_cast( std::floor( (PixelType) ct / 2.0 ) ) ) imageIterator.Set( 1.e-8 ); ++ct2; ++imageIterator; } imageIterator.GoToBegin(); while(!imageIterator.IsAtEnd()) { imageIterator.Set( imageIterator.Get() / totalval ); ++imageIterator; } typedef itk::ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( image1 ); duplicator->Update(); varimage = duplicator->GetOutput(); for ( unsigned int i = 0; i < sigma; i++ ) { typedef itk::ConvolutionImageFilter< ImageType, ImageType > FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( varimage ); filter->SetKernelImage(kernel); filter->Update(); varimage = filter->GetOutput(); Iterator vfIter( varimage, varimage->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { PixelType pixval = image1->GetPixel( vfIter.GetIndex() ); if ( pixval > 0 ) vfIter.Set( pixval ); } } WriteImage( varimage, outname.c_str() ); return EXIT_SUCCESS; } template int InPaint2(int argc, char *argv[]) { // I_t = \nabla ( \Delta I ) \cdot N where N is \perpto the normal direction typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; float sigma = 1.0; if( argc > argct ) { sigma = atof(argv[argct]); argct++; } typename ImageType::Pointer image1 = NULL; typename ImageType::Pointer varimage = NULL; ReadImage(image1, fn1.c_str() ); PixelType spacingsize = 0; PixelType minsp = image1->GetSpacing()[0]; for( unsigned int d = 0; d < ImageDimension; d++ ) { PixelType sp = image1->GetSpacing()[d]; if ( sp < minsp ) minsp = sp; spacingsize += sp * sp; } spacingsize = sqrt( spacingsize ); float inpow = spacingsize * 2.0; if( argc > argct ) { inpow = atof(argv[argct]); argct++; } // # 1 job - create a kernel typename ImageType::Pointer kernel = ImageType::New(); typename ImageType::IndexType start; typename ImageType::SizeType size; typename ImageType::RegionType region; start.Fill(0); size.Fill(3); region.SetSize(size); region.SetIndex(start); kernel->SetRegions(region); kernel->Allocate(); kernel->SetSpacing( image1->GetSpacing() ); unsigned long kernelsize = region.GetNumberOfPixels(); itk::ImageRegionIterator imageIterator(kernel, region); unsigned int ct = 0; typename ImageType::PointType centerPoint; typename ImageType::PointType locPoint; while(!imageIterator.IsAtEnd()) { if ( ct == static_cast( std::floor( (PixelType) kernelsize / 2.0 ) ) ) { kernel->TransformIndexToPhysicalPoint( imageIterator.GetIndex(), centerPoint ); } ++ct; ++imageIterator; } unsigned int ct2 = 0; PixelType totalval = 0; imageIterator.GoToBegin(); while(!imageIterator.IsAtEnd()) { kernel->TransformIndexToPhysicalPoint( imageIterator.GetIndex(), locPoint ); PixelType val = 0; for ( unsigned int d = 0; d < ImageDimension; d++ ) { PixelType delt = ( locPoint[d] - centerPoint[d] ); val += delt * delt; } PixelType distval = sqrt( val ); if ( distval > inpow ) val = 1.e8; if ( val > 0 ) { imageIterator.Set( 1.0 / val ); totalval += imageIterator.Get( ); } if ( ct2 == static_cast( std::floor( (PixelType) ct / 2.0 ) ) ) imageIterator.Set( 1.e-8 ); ++ct2; ++imageIterator; } imageIterator.GoToBegin(); while(!imageIterator.IsAtEnd()) { imageIterator.Set( imageIterator.Get() / totalval ); ++imageIterator; } typedef itk::ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( image1 ); duplicator->Update(); varimage = duplicator->GetOutput(); for ( unsigned int i = 0; i < sigma; i++ ) { typedef itk::ConvolutionImageFilter< ImageType, ImageType > FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( varimage ); filter->SetKernelImage(kernel); filter->Update(); varimage = filter->GetOutput(); Iterator vfIter( varimage, varimage->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { PixelType pixval = image1->GetPixel( vfIter.GetIndex() ); if ( pixval > 0 ) vfIter.Set( pixval ); } } WriteImage( varimage, outname.c_str() ); return EXIT_SUCCESS; } template int Check3TissueLabeling( int argc, char *argv[] ) { // This function is used for quality control in the abp.sh pipeline. // On the UVa cluster, the labels after the segmentation step on // random data sets would be of some odd permutation. Under expected // conditions the labels should be CSF -> 1, GM -> 2, WM -> 3 but for // some reason which we can't reproduce, they'd be some other ordering. // The warped priors are correct so we use those images and the segmentation // to reorder the labels and move the posteriors where appropriate. if( argc < 8 ) { // std::cout << "Usage: " << argv[0] << " ImageDimension"; // std::cout << " segmentationImage Check3TissueLabeling"; // std::cout << " priorWarped1 priorWarped2 priorWarped3"; // std::cout << " posteriorWarped1 posteriorWarped2 posteriorWarped3" << std::endl; return EXIT_FAILURE; } typedef double PixelType; typedef unsigned int LabelType; const unsigned int NumberOfLabels = 3; typedef itk::Image ImageType; typedef itk::Image LabelImageType; typename LabelImageType::Pointer labelImage; ReadImage( labelImage, argv[2] ); typename ImageType::Pointer priors[NumberOfLabels]; typename ImageType::Pointer posteriors[NumberOfLabels]; for( unsigned int d = 0; d < NumberOfLabels; d++ ) { ReadImage( priors[d], argv[d + 4] ); ReadImage( posteriors[d], argv[d + 7] ); } typename LabelImageType::Pointer maxPriorLabelImage = LabelImageType::New(); maxPriorLabelImage->CopyInformation( labelImage ); maxPriorLabelImage->SetRegions( labelImage->GetRequestedRegion() ); maxPriorLabelImage->Allocate(); maxPriorLabelImage->FillBuffer( 0 ); itk::ImageRegionIteratorWithIndex ItL( labelImage, labelImage->GetRequestedRegion() ); itk::ImageRegionIterator ItM( maxPriorLabelImage, maxPriorLabelImage->GetRequestedRegion() ); for( ItL.GoToBegin(), ItM.GoToBegin(); !ItL.IsAtEnd(); ++ItM, ++ItL ) { if( ItL.Get() == 0 ) { continue; } LabelType maxLabel = 1; PixelType maxPrior = priors[0]->GetPixel( ItL.GetIndex() ); for( LabelType d = 2; d <= 3; d++ ) { PixelType prior = priors[d - 1]->GetPixel( ItL.GetIndex() ); if( prior > maxPrior ) { maxPrior = prior; maxLabel = d; } } ItM.Set( maxLabel ); } itk::Matrix permutations; unsigned int which = 0; permutations(which, 0) = 1; permutations(which, 1) = 2; permutations(which, 2) = 3; permutations(++which, 0) = 1; permutations(which, 1) = 3; permutations(which, 2) = 2; permutations(++which, 0) = 2; permutations(which, 1) = 1; permutations(which, 2) = 3; permutations(++which, 0) = 2; permutations(which, 1) = 3; permutations(which, 2) = 1; permutations(++which, 0) = 3; permutations(which, 1) = 1; permutations(which, 2) = 2; permutations(++which, 0) = 3; permutations(which, 1) = 2; permutations(which, 2) = 1; PixelType maxDice = 0.0; int maxPermutationRow = -1; for( unsigned r = 0; r < 6; r++ ) { typedef itk::ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( labelImage ); duplicator->Update(); typename LabelImageType::Pointer permutedLabelImage = duplicator->GetModifiableOutput(); itk::ImageRegionIterator ItP( permutedLabelImage, permutedLabelImage->GetRequestedRegion() ); for( ItP.GoToBegin(); !ItP.IsAtEnd(); ++ItP ) { LabelType permutedLabel = ItP.Get(); if( permutedLabel != 0 ) { unsigned int whichColumn = permutedLabel - 1; ItP.Set( permutations( r, whichColumn ) ); } } typedef itk::LabelOverlapMeasuresImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetSourceImage( permutedLabelImage ); filter->SetTargetImage( maxPriorLabelImage ); filter->Update(); PixelType dice = filter->GetMeanOverlap(); // std::cout << r << ": " << dice << std::endl; if( dice > maxDice ) { maxPermutationRow = r; maxDice = dice; } } if( maxPermutationRow == -1 ) { std::cerr << "Unexpected problem." << std::endl; return EXIT_FAILURE; } LabelType movingLabels[3]; LabelType fixedLabels[3]; for( unsigned int d = 0; d < 3; d++ ) { // std::cout << d + 1 << " -> " << permutations( maxPermutationRow, d ) << std::endl; movingLabels[d] = permutations( maxPermutationRow, d ); fixedLabels[d] = d + 1; } if( maxPermutationRow == 0 ) { // std::cout << "No need to change labels/posteriors." << std::endl; } else { for( unsigned int d = 0; d < 3; d++ ) { LabelType movingLabel = movingLabels[d]; LabelType fixedLabel = fixedLabels[d]; if( movingLabel == fixedLabel ) { continue; } else { WriteImage( posteriors[movingLabels[d] - 1], argv[7 + d] ); WriteImage( posteriors[movingLabels[movingLabels[d] - 1] - 1], argv[7 + movingLabels[d] - 1] ); LabelType tmp = movingLabels[d]; movingLabels[d] = movingLabels[tmp - 1]; movingLabels[tmp - 1] = tmp; } if( d == 0 ) { typedef itk::ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( labelImage ); duplicator->Update(); typename LabelImageType::Pointer permutedLabelImage = duplicator->GetModifiableOutput(); itk::ImageRegionIteratorWithIndex ItP( permutedLabelImage, permutedLabelImage->GetRequestedRegion() ); for( ItP.GoToBegin(); !ItP.IsAtEnd(); ++ItP ) { LabelType permutedLabel = ItP.Get(); if( permutedLabel != 0 ) { unsigned int whichColumn = permutedLabel - 1; ItP.Set( permutations( maxPermutationRow, whichColumn ) ); } } // std::cout << "Relabeling segmentation image." << std::endl; WriteImage( permutedLabelImage, argv[2] ); } } } return EXIT_SUCCESS; } template struct blob_index_cmp { blob_index_cmp(const T arr) : barr(arr) { } bool operator()(const size_t a, const size_t b) const { return barr[a] < barr[b]; } const T barr; }; template void getBlobCorrespondenceMatrix( unsigned int radval, typename TImage::Pointer image, typename TImage::Pointer image2, vnl_matrix& correspondencematrix, BlobsListType blobs1, BlobsListType blobs2, float gradsig, bool dosinkhorn ) { typedef float PixelType; typedef float RealType; typedef itk::Image ImageType; typedef itk::CovariantVector GradientPixelType; typedef itk::Image GradientImageType; typedef itk::SmartPointer GradientImagePointer; typedef itk::GradientRecursiveGaussianImageFilter GradientImageFilterType; typedef typename GradientImageFilterType::Pointer GradientImageFilterPointer; typedef itk::MultiScaleLaplacianBlobDetectorImageFilter BlobFilterType; typedef typename BlobFilterType::BlobPointer BlobPointer; GradientImageFilterPointer gfilter = GradientImageFilterType::New(); gfilter->SetInput( image ); gfilter->SetSigma( gradsig ); gfilter->Update(); GradientImagePointer gimage = gfilter->GetOutput(); GradientImageFilterPointer gfilter2 = GradientImageFilterType::New(); gfilter2->SetInput( image2 ); gfilter2->SetSigma( gradsig ); gfilter2->Update(); GradientImagePointer gimage2 = gfilter2->GetOutput(); // now compute some feature characteristics in each blob correspondencematrix.set_size( blobs1.size(), blobs2.size() ); correspondencematrix.fill( 0 ); typedef typename ImageType::IndexType IndexType; typedef itk::NeighborhoodIterator niteratorType; typename niteratorType::RadiusType rad; rad.Fill( radval ); niteratorType GHood( rad, image, image->GetLargestPossibleRegion() ); niteratorType GHood2( rad, image2, image2->GetLargestPossibleRegion() ); IndexType zeroind; zeroind.Fill( radval ); GHood.SetLocation( zeroind ); // get indices within a ND-sphere std::vector activeindex; std::vector weights; RealType weightsum = 0; for( unsigned int ii = 0; ii < GHood.Size(); ii++ ) { IndexType ind = GHood.GetIndex( ii ); RealType dist = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { dist += ( ind[jj] - zeroind[jj] ) * ( ind[jj] - zeroind[jj] ); } dist = sqrt( dist ); if( dist <= radval ) { activeindex.push_back( ii ); RealType wt = exp( -1.0 * dist / ( radval * radval ) ); weights.push_back( wt ); weightsum += ( wt ); } } for( unsigned int ii = 0; ii < weights.size(); ii++ ) { weights[ii] = weights[ii] / weightsum; } BlobPointer bestblob = ITK_NULLPTR; if( ( !blobs2.empty() ) && ( !blobs1.empty() ) ) { unsigned int count2; RealType smallval = 1.e-4; typedef itk::LinearInterpolateImageFunction ScalarInterpolatorType; typedef typename ScalarInterpolatorType::Pointer InterpPointer; InterpPointer interp1 = ScalarInterpolatorType::New(); interp1->SetInputImage(image); InterpPointer interp2 = ScalarInterpolatorType::New(); interp2->SetInputImage(image2); unsigned int count1 = 0; for( unsigned int i = 0; i < blobs1.size(); i++ ) { BlobPointer blob1 = blobs1[i]; IndexType indexi = blob1->GetCenter(); if( image->GetPixel( indexi ) > smallval ) { GHood.SetLocation( indexi ); bestblob = ITK_NULLPTR; count2 = 0; for( unsigned int j = 0; j < blobs2.size(); j++ ) { const BlobPointer & blob2 = blobs2[j]; const IndexType & indexj = blob2->GetCenter(); if( image2->GetPixel( indexj ) > smallval ) { GHood2.SetLocation( indexj ); RealType correlation = PatchCorrelation( GHood, GHood2, activeindex, weights, gimage, gimage2, interp2 ); if( correlation < 0 ) { correlation = 0; } correspondencematrix( i, j ) = correlation; count2++; } } count1++; } // imagei GetPixel gt 0 if( i % 100 == 0 ) { // std::cout << " Progress : " << (float ) i / (float) blobs1.size() * 100.0 << std::endl; } } if( dosinkhorn ) { Sinkhorn( correspondencematrix ); } } return; } template int BlobDetector( int argc, char *argv[] ) { typedef float PixelType; typedef float RealType; typedef itk::Image ImageType; if( argc < 5 ) { // std::cout << " Not enough inputs " << std::endl; return 1; } // sensitive parameters are set here - begin RealType gradsig = 1.0; // sigma for gradient filter unsigned int stepsperoctave = 10; // number of steps between doubling of scale RealType minscale = std::pow( 1.0, 1.0 ); RealType maxscale = std::pow( 2.0, 10.0 ); RealType uniqfeat_thresh = 0.01; RealType smallval = 1.e-2; // assumes images are normalizes in [ 0, 1 ] bool dosinkhorn = false; RealType maxradiusdiffallowed = 0.25; // IMPORTANT feature size difference RealType kneighborhoodval = 3; // IMPORTANT - defines how many nhood nodes to use in k-hood definition unsigned int radval = 20; // IMPORTANT radius for correlation RealType dthresh = 0.02; // IMPORTANT distance preservation threshold // sensitive parameters are set here - end int argct = 2; const std::string outname = std::string(argv[argct]); std::string outname2 = std::string("temp.nii.gz"); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; unsigned int nblobs = atoi( argv[argct] ); argct++; std::string fn2 = ""; if( argc > argct ) { fn2 = std::string(argv[argct]); argct++; } if( argc > argct ) { outname2 = std::string(argv[argct]); argct++; } RealType corrthresh = 0; if( argc > argct ) { corrthresh = atof(argv[argct]); argct++; } if( argc > argct ) { radval = atoi(argv[argct]); argct++; } if( argc > argct ) { dthresh = atof(argv[argct]); argct++; } typename ImageType::Pointer image; typename ImageType::Pointer image2; ReadImage( image, fn1.c_str() ); typedef itk::MultiScaleLaplacianBlobDetectorImageFilter BlobFilterType; typename BlobFilterType::Pointer blobFilter = BlobFilterType::New(); typedef typename BlobFilterType::BlobPointer BlobPointer; blobFilter->SetStartT( minscale ); blobFilter->SetEndT( maxscale ); blobFilter->SetStepsPerOctave( stepsperoctave ); blobFilter->SetNumberOfBlobs( nblobs ); blobFilter->SetInput( image ); /*ComputeLaplacianImage( image ) ); */ blobFilter->Update(); typedef typename BlobFilterType::BlobRadiusImageType BlobRadiusImageType; typename BlobRadiusImageType::Pointer labimg = blobFilter->GetBlobRadiusImage(); typename BlobRadiusImageType::Pointer labimg2; WriteImage( labimg, outname.c_str() ); typedef typename BlobFilterType::BlobsListType BlobsListType; BlobsListType blobs1 = blobFilter->GetBlobs(); vnl_matrix correspondencematrix1; correspondencematrix1.set_size( blobs1.size(), blobs1.size() ); correspondencematrix1.fill( 1 ); vnl_matrix correspondencematrix2; vnl_matrix correspondencematrix; // getBlobCorrespondenceMatrix( radval, image, image, correspondencematrix1, // blobs1, blobs1, gradsig , dosinkhorn ); BlobsListType blobs2; if( fn2.length() > 3 ) { ReadImage( image2, fn2.c_str() ); typename BlobFilterType::Pointer blobFilter2 = BlobFilterType::New(); blobFilter2->SetStartT( minscale ); blobFilter2->SetEndT( maxscale ); blobFilter2->SetStepsPerOctave( stepsperoctave ); blobFilter2->SetNumberOfBlobs( nblobs ); blobFilter2->SetInput( image2 ); blobFilter2->Update(); labimg2 = blobFilter2->GetBlobRadiusImage(); WriteImage( labimg2, outname2.c_str() ); labimg->FillBuffer( 0 ); labimg2->FillBuffer( 0 ); blobs2 = blobFilter2->GetBlobs(); correspondencematrix2.set_size( blobs2.size(), blobs2.size() ); correspondencematrix2.fill( 1 ); // getBlobCorrespondenceMatrix ( radval, image2, image2, // correspondencematrix2, blobs2, blobs2, gradsig, dosinkhorn ); } else { return EXIT_SUCCESS; } // std::cout << " Blob1Length " << blobs1.size() << " Blob2Length " << blobs2.size() << std::endl; // now compute some feature characteristics in each blob typedef typename ImageType::IndexType IndexType; IndexType zeroind; zeroind.Fill( radval ); BlobPointer bestblob = ITK_NULLPTR; if( ( !blobs2.empty() ) && ( !blobs1.empty() ) ) { getBlobCorrespondenceMatrix ( radval, image, image2, correspondencematrix, blobs1, blobs2, gradsig, dosinkhorn ); // vnl_matrix diagcorr = outer_product( correspondencematrix1.get_diagonal(), // correspondencematrix2.get_diagonal() ); // Sinkhorn( diagcorr ); // for ( unsigned int row = 0; row < correspondencematrix.rows(); row++ ) // for ( unsigned int col = 0; col < correspondencematrix.cols(); col++ ) // correspondencematrix( row, col ) *= diagcorr( row, col ); // Sinkhorn( correspondencematrix ); unsigned int matchpt = 1; std::cout << " now compute pairwise matching " << correspondencematrix.max_value() << " reducing to " << corrthresh << std::endl; unsigned int count1 = 0; typedef std::pair BlobPairType; std::vector blobpairs; while( ( matchpt < ( corrthresh + 1 ) ) && ( count1 < blobs1.size() ) ) { unsigned int maxpair = correspondencematrix.arg_max(); unsigned int maxrow = ( unsigned int ) maxpair / correspondencematrix.cols(); unsigned int maxcol = maxpair - maxrow * correspondencematrix.cols(); BlobPointer blob1 = blobs1[maxrow]; bestblob = blobs2[maxcol]; if( bestblob && bestblob->GetObjectRadius() > 1 ) { if( fabs( bestblob->GetObjectRadius() - blob1->GetObjectRadius() ) < maxradiusdiffallowed ) { if( bestblob && ( image->GetPixel( blob1->GetCenter() ) > smallval ) && ( image2->GetPixel( bestblob->GetCenter() ) > smallval ) && ( correspondencematrix1(maxrow, maxrow) > uniqfeat_thresh ) && ( correspondencematrix2(maxcol, maxcol) > uniqfeat_thresh ) ) { BlobPairType blobpairing = std::make_pair( blob1, bestblob ); blobpairs.push_back( blobpairing ); std::cout << " best correlation " << correspondencematrix.absolute_value_max() << " rad1 " << blob1->GetObjectRadius() << " rad2 " << bestblob->GetObjectRadius() << " : " << matchpt << std::endl; labimg->SetPixel( blob1->GetCenter(), matchpt ); // ( int ) ( 0.5 + ( *i )->GetObjectRadius() ) ); labimg2->SetPixel( bestblob->GetCenter(), matchpt ); // ( int ) ( 0.5 + bestblob->GetObjectRadius() ) ); matchpt++; } } } correspondencematrix.set_row( maxrow, correspondencematrix.get_row( 0 ).fill( 0 ) ); correspondencematrix.set_column( maxcol, correspondencematrix.get_column( 0 ).fill( 0 ) ); count1++; } /** For every blob, compute the distance to its neighbors before and after matching */ vnl_matrix distmatpre( blobpairs.size(), blobpairs.size() ); distmatpre.fill( 0 ); vnl_matrix distmatpost( blobpairs.size(), blobpairs.size() ); distmatpost.fill( 0 ); vnl_matrix distratiomat( blobpairs.size(), blobpairs.size() ); distratiomat.fill( 0 ); if( true ) { for( unsigned int bp = 0; bp < blobpairs.size(); bp++ ) { IndexType blobind = blobpairs[bp].first->GetCenter(); IndexType blobpairind = blobpairs[bp].second->GetCenter(); std::vector distspre; std::vector distspost; std::vector distspreind; std::vector distspostind; for( unsigned int bp2 = 0; bp2 < blobpairs.size(); bp2++ ) { IndexType blobneighborind = blobpairs[bp2].first->GetCenter(); IndexType blobpairneighborind = blobpairs[bp2].second->GetCenter(); RealType dist1 = 0; RealType dist2 = 0; for( unsigned int dim = 0; dim < ImageDimension; dim++ ) { RealType delta1 = blobind[dim] - blobneighborind[dim]; RealType delta2 = blobpairind[dim] - blobpairneighborind[dim]; dist1 += delta1 * delta1; dist2 += delta2 * delta2; } RealType drat = 0; if( dist1 > 0 ) { drat = dist2 / dist1; } distspre.push_back( dist1 ); distspost.push_back( dist2 ); distspreind.push_back( bp2 ); distspostind.push_back( bp2 ); // // std::cout << " blob " << bp << " vs " << bp2 << sqrt( dist1 ) << " v " << sqrt( dist2 ) << // std::endl; distmatpre( bp, bp2 ) = distmatpre( bp2, bp ) = dist1; distmatpost( bp, bp2 ) = distmatpost( bp2, bp ) = dist2; distratiomat( bp, bp2 ) = distratiomat( bp2, bp ) = drat; } } // now we have the distance ratio matrix - let's find a cluster of nodes with values near 1 // count the k neighborhood for each blobpair possibility for( unsigned int bp = 0; bp < blobpairs.size(); bp++ ) { IndexType blobind = blobpairs[bp].first->GetCenter(); IndexType blobpairind = blobpairs[bp].second->GetCenter(); unsigned int kct = 0; for( unsigned int bp2 = 0; bp2 < blobpairs.size(); bp2++ ) { if( ( bp2 != bp ) && ( vnl_math_abs( distratiomat( bp2, bp ) - 1 ) < dthresh ) ) { kct++; } } if( kct < kneighborhoodval ) { labimg->SetPixel( blobind, 0 ); // ( int ) ( 0.5 + ( *i )->GetObjectRadius() ) ); labimg2->SetPixel( blobpairind, 0 ); // ( int ) ( 0.5 + bestblob->GetObjectRadius() ) ); } else { // std::cout << " blob " << bp << " keep " << distratiomat.get_row( bp ) << std::endl; } } } // if false if( ( correspondencematrix1.rows() > 0 ) && ( false ) ) { typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( "corrmat1.csv" ); writer->SetInput( &correspondencematrix1 ); writer->SetInput( &distmatpre ); writer->Write(); } if( ( correspondencematrix2.rows() > 0 ) && ( false ) ) { typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( "corrmat2.csv" ); writer->SetInput( &correspondencematrix2 ); writer->SetInput( &distratiomat ); writer->Write(); } WriteImage( labimg, outname.c_str() ); WriteImage( labimg2, outname2.c_str() ); // std::cout << " Matched " << matchpt << " blobs " << std::endl; } return EXIT_SUCCESS; } template int MatchBlobs( int argc, char *argv[] ) { typedef float PixelType; typedef float RealType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex IteratorType; typedef itk::MultiScaleLaplacianBlobDetectorImageFilter BlobFilterType; typedef typename BlobFilterType::BlobPointer BlobPointer; typedef typename BlobFilterType::BlobRadiusImageType BlobRadiusImageType; typedef typename BlobFilterType::BlobsListType BlobsListType; typedef typename BlobFilterType::BlobType BlobType; typedef typename ImageType::IndexType IndexType; if( argc < 5 ) { // std::cout << " Not enough inputs " << std::endl; return 1; } // sensitive parameters are set here - begin RealType gradsig = 1.0; // sigma for gradient filter RealType smallval = 1.e-2; // assumes images are normalizes in [ 0, 1 ] bool dosinkhorn = false; RealType maxradiusdiffallowed = 0.25; // IMPORTANT feature size difference unsigned int radval = 5; // IMPORTANT radius for correlation RealType dthresh = 0.02; // IMPORTANT distance preservation threshold // sensitive parameters are set here - end int argct = 2; const std::string outname = std::string(argv[argct]); argct += 2; std::string fn1 = std::string(argv[argct]); argct++; std::string landmarks1 = std::string(argv[argct]); argct++; std::string fn2 = std::string(argv[argct]); argct++; typename ImageType::Pointer image; typename ImageType::Pointer imageLM; typename ImageType::Pointer image2; ReadImage( image, fn1.c_str() ); ReadImage( imageLM, landmarks1.c_str() ); ReadImage( image2, fn2.c_str() ); vnl_matrix correspondencematrix; typedef itk::MinimumMaximumImageCalculator LabelCalculatorType; typename LabelCalculatorType::Pointer calc = LabelCalculatorType::New(); calc->SetImage( imageLM ); calc->ComputeMaximum(); typename ImageType::Pointer labimg = MakeNewImage( image, 0 ); typename ImageType::Pointer labimg2 = MakeNewImage( image2, 0 ); typename ImageType::Pointer confimg2 = MakeNewImage( image2, 0 ); float maximum = calc->GetMaximum(); unsigned int corrthresh = ( (unsigned int) maximum ) * 100; if( argc > argct ) { corrthresh = atof(argv[argct]); argct++; } if( argc > argct ) { radval = atof(argv[argct]); argct++; } if( argc > argct ) { dthresh = atof(argv[argct]); argct++; } RealType kneighborhoodval = maximum; // IMPORTANT - defines how many nhood nodes // to use in k-hood definition IteratorType cIter( imageLM, imageLM->GetLargestPossibleRegion() ); BlobsListType blobs1; itk::Point zeroPoint; zeroPoint.Fill(0); // std::cout << " N-Landmarks " << maximum << std::endl; for( cIter.GoToBegin(); !cIter.IsAtEnd(); ++cIter ) { RealType val = imageLM->GetPixel( cIter.GetIndex() ); if( val > 0 ) { typename BlobType::PointType centerPoint; image->TransformIndexToPhysicalPoint( cIter.GetIndex(), centerPoint ); BlobPointer blob = BlobType::New(); blob->SetSigma( 1 ); blob->SetScaleSpaceValue( val ); blob->SetCenter( cIter.GetIndex() ); const typename BlobType::VectorType centerVector = centerPoint - zeroPoint; blob->GetObjectToParentTransform()->SetOffset(centerVector); blob->ComputeBoundingBox(); blobs1.push_back( blob ); } } typedef itk::ImageRandomConstIteratorWithIndex randIterator; randIterator mIter( image2, image2->GetLargestPossibleRegion() ); unsigned long numpx = image2->GetBufferedRegion().GetNumberOfPixels(); unsigned int n_samples = ( unsigned int ) ( ( float ) numpx ) * 0.1; mIter.SetNumberOfSamples( n_samples ); BlobsListType blobs2; for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { // transform the center index into offset vector typename BlobType::PointType centerPoint; image2->TransformIndexToPhysicalPoint( mIter.GetIndex(), centerPoint ); BlobPointer blob = BlobType::New(); blob->SetSigma( 1 ); blob->SetScaleSpaceValue( image2->GetPixel( mIter.GetIndex() ) ); blob->SetCenter( mIter.GetIndex() ); const typename BlobType::VectorType centerVector = centerPoint - zeroPoint; blob->GetObjectToParentTransform()->SetOffset(centerVector); blob->ComputeBoundingBox(); blobs2.push_back( blob ); } getBlobCorrespondenceMatrix ( radval, image, image2, correspondencematrix, blobs1, blobs2, gradsig, dosinkhorn ); unsigned int matchpt = 1; std::cout << " now compute pairwise matching " << correspondencematrix.max_value() << " reducing to " << corrthresh << std::endl; unsigned int count1 = 0; typedef std::pair BlobPairType; std::vector blobpairs; vnl_matrix correspondencematrix_hard( correspondencematrix ); vnl_matrix correspondencematrix_soft( correspondencematrix ); while( ( matchpt < ( corrthresh + 1 ) ) ) { unsigned int maxpair = correspondencematrix_hard.arg_max(); if( maxpair < 1.e-9 ) { correspondencematrix_hard.update( correspondencematrix_soft ); maxpair = correspondencematrix_hard.arg_max(); } unsigned int maxrow = ( unsigned int ) maxpair / correspondencematrix.cols(); unsigned int maxcol = maxpair - maxrow * correspondencematrix.cols(); BlobPointer blob1 = blobs1[maxrow]; BlobPointer bestblob( blobs2[maxcol] ); if( bestblob && bestblob->GetObjectRadius() > 1 ) { if( fabs( bestblob->GetObjectRadius() - blob1->GetObjectRadius() ) < maxradiusdiffallowed ) { if( bestblob && ( image->GetPixel( blob1->GetCenter() ) > smallval ) && ( image2->GetPixel( bestblob->GetCenter() ) > smallval ) ) { bestblob->SetScaleSpaceValue( correspondencematrix( maxrow, maxcol ) ); BlobPairType blobpairing = std::make_pair( blob1, bestblob ); blobpairs.push_back( blobpairing ); matchpt++; } } } correspondencematrix_hard.set_row( maxrow, correspondencematrix_hard.get_row( maxrow ).fill( 0 ) ); correspondencematrix_hard.set_column( maxcol, correspondencematrix_hard.get_column( maxcol ).fill( 0 ) ); correspondencematrix_soft( maxrow, maxcol ) = 0; count1++; } /** For every blob, compute the distance to its neighbors before and after matching */ vnl_matrix distmatpre( blobpairs.size(), blobpairs.size() ); distmatpre.fill( 0 ); vnl_matrix distmatpost( blobpairs.size(), blobpairs.size() ); distmatpost.fill( 0 ); vnl_matrix distratiomat( blobpairs.size(), blobpairs.size() ); distratiomat.fill( 0 ); if( true ) { for( unsigned int bp = 0; bp < blobpairs.size(); bp++ ) { IndexType blobind = blobpairs[bp].first->GetCenter(); IndexType blobpairind = blobpairs[bp].second->GetCenter(); std::vector distspre; std::vector distspost; std::vector distspreind; std::vector distspostind; for( unsigned int bp2 = 0; bp2 < blobpairs.size(); bp2++ ) { IndexType blobneighborind = blobpairs[bp2].first->GetCenter(); IndexType blobpairneighborind = blobpairs[bp2].second->GetCenter(); RealType dist1 = 0; RealType dist2 = 0; for( unsigned int dim = 0; dim < ImageDimension; dim++ ) { RealType delta1 = blobind[dim] - blobneighborind[dim]; RealType delta2 = blobpairind[dim] - blobpairneighborind[dim]; dist1 += delta1 * delta1; dist2 += delta2 * delta2; } RealType drat = 0; if( dist1 > 0 ) { drat = dist2 / dist1; } distspre.push_back( dist1 ); distspost.push_back( dist2 ); distspreind.push_back( bp2 ); distspostind.push_back( bp2 ); distmatpre( bp, bp2 ) = distmatpre( bp2, bp ) = dist1; distmatpost( bp, bp2 ) = distmatpost( bp2, bp ) = dist2; distratiomat( bp, bp2 ) = distratiomat( bp2, bp ) = drat; } } // now we have the distance ratio matrix - let's find a cluster of nodes with values near 1 // count the k neighborhood for each blobpair possibility for( unsigned int bp = 0; bp < blobpairs.size(); bp++ ) { IndexType blobind = blobpairs[bp].first->GetCenter(); IndexType blobpairind = blobpairs[bp].second->GetCenter(); unsigned int kct = 0; typedef vnl_vector kVectorType; kVectorType kLog1( kneighborhoodval, 0 ); for( unsigned int bp2 = 0; bp2 < blobpairs.size(); bp2++ ) { if( ( bp2 != bp ) && ( vnl_math_abs( distratiomat( bp2, bp ) - 1 ) < dthresh ) // && ( blobpairs[bp2].first->GetScaleSpaceValue() != blobpairs[bp].first->GetScaleSpaceValue() ) // && ( blobpairs[bp2].second->GetScaleSpaceValue() != blobpairs[bp].second->GetScaleSpaceValue() ) ) { kct++; kLog1( blobpairs[bp2].first->GetScaleSpaceValue() - 1 ) = 1; } } // if ( ( kLog1.sum() >= ( kneighborhoodval / 2 ) ) && ( kct >= kneighborhoodval ) ) if( ( kct >= kneighborhoodval ) ) { labimg->SetPixel( blobind, blobpairs[bp].first->GetScaleSpaceValue() ); // ( int ) ( 0.5 + ( *i // )->GetObjectRadius() ) ); labimg2->SetPixel( blobpairind, blobpairs[bp].first->GetScaleSpaceValue() ); confimg2->SetPixel( blobpairind, blobpairs[bp].second->GetScaleSpaceValue() ); // // std::cout << " blob " << bp << " keep " << distratiomat.get_row( bp ) << " LM " << // blobpairs[bp].first->GetScaleSpaceValue() << std::endl; } } } // if false if( true ) { typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( "temp_corrmat.csv" ); writer->SetInput( &correspondencematrix ); writer->Write(); } if( true ) { typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( "temp_distmat2.csv" ); writer->SetInput( &distratiomat ); writer->Write(); } std::string outname1 = outname + std::string("lm1.nii.gz"); WriteImage( labimg, outname1.c_str() ); std::string outname2 = outname + std::string("lm2.nii.gz"); WriteImage( labimg2, outname2.c_str() ); std::string outname3 = outname + std::string("conf2.nii.gz"); WriteImage( confimg2, outname3.c_str() ); return EXIT_SUCCESS; } // // ImageMath was a gigantic switch statement that had 3 duplicated // lists of 'if (operation == )' clauses for 2d, 3d, and 4d. I // figured out which functions were 2D only, 3D only and 4D Only, // which were valid for all dimensions, which were 2d and 3d, and // which were 3d and 4d. // So there's a template method for each case, and they're assembled // for each dimension in an Explicit Template Function below. template int ImageMathHelper2DOnly(int argc, char **argv) { std::string operation = std::string(argv[3]); if( operation == "TileImages" ) { TileImages(argc, argv); return EXIT_SUCCESS; } if( operation == "TimeSeriesRegionCorr" ) { TimeSeriesRegionCorr(argc, argv); return EXIT_SUCCESS; } if( operation == "TimeSeriesRegionSCCA" ) { TimeSeriesRegionSCCA(argc, argv); return EXIT_SUCCESS; } return EXIT_FAILURE; } template int ImageMathHelper2DOr3D(int argc, char **argv) { std::string operation = std::string(argv[3]); if( operation == "AverageLabels" ) { AverageLabels(argc, argv); return EXIT_SUCCESS; } if( operation == "Check3TissueLabeling" ) { Check3TissueLabeling(argc, argv); return EXIT_SUCCESS; } if( operation == "oldPropagateLabelsThroughMask" ) { PropagateLabelsThroughMask(argc, argv); return EXIT_SUCCESS; } if( operation == "SetOrGetPixel" ) { SetOrGetPixel(argc, argv); return EXIT_SUCCESS; } if( operation == "STAPLE" ) { STAPLE(argc, argv); return EXIT_SUCCESS; } return EXIT_FAILURE; } template int ImageMathHelper3DOr4D(int argc, char **argv) { std::string operation = std::string(argv[3]); if( operation == "ConvertLandmarkFile" ) { ConvertLandmarkFile(argc, argv); return EXIT_SUCCESS; } if( operation == "PASLQuantifyCBF" ) { PASLQuantifyCBF(argc, argv); return EXIT_SUCCESS; } if( operation == "PASL" ) { PASL(argc, argv); return EXIT_SUCCESS; } if( operation == "pCASL" ) { pCASL(argc, argv); return EXIT_SUCCESS; } if( operation == "TriPlanarView" ) { TriPlanarView(argc, argv); return EXIT_SUCCESS; } return EXIT_FAILURE; } template int ImageMathHelper3DOnly(int argc, char **argv) { std::string operation = std::string(argv[3]); if( operation == "4DTensorTo3DTensor" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "ComponentTo3DTensor" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "FuseNImagesIntoNDVectorField" ) { FuseNImagesIntoNDVectorField(argc, argv); return EXIT_SUCCESS; } if( operation == "ExtractComponentFrom3DTensor" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "MTR" ) { MTR(argc, argv); return EXIT_SUCCESS; } if( operation == "SmoothTensorImage" ) { SmoothTensorImage(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorAxialDiffusion" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorColor" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorEigenvalue" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorFA" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorFANumerator" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorFADenominator" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorIOTest" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorMask" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorMeanDiffusion" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorRadialDiffusion" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorToLocalSpace" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorToPhysicalSpace" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorToVectorComponent" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "TensorToVector" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } if( operation == "ValidTensor" ) { TensorFunctions(argc, argv); return EXIT_SUCCESS; } return EXIT_FAILURE; } template int ImageMathHelper4DOnly(int argc, char **argv) { std::string operation = std::string(argv[3]); if( operation == "AverageOverDimension" ) { AverageOverDimension(argc, argv); return EXIT_SUCCESS; } if( operation == "CompCorrAuto" ) { CompCorrAuto(argc, argv); return EXIT_SUCCESS; } if( operation == "ComputeTimeSeriesLeverage" ) { ComputeTimeSeriesLeverage(argc, argv); return EXIT_SUCCESS; } if( operation == "nvols" ) { PrintHeader(argc, argv); return EXIT_SUCCESS; } if( operation == "PCASLQuantifyCBF" ) { // PCASLQuantifyCBF<4>(argc, argv); return EXIT_SUCCESS; } if( operation == "SliceTimingCorrection" ) { SliceTimingCorrection(argc, argv); return EXIT_SUCCESS; } if( operation == "SplitAlternatingTimeSeries" ) { SplitAlternatingTimeSeries(argc, argv); return EXIT_SUCCESS; } if( operation == "ThreeTissueConfounds" ) { ThreeTissueConfounds(argc, argv); return EXIT_SUCCESS; } if( operation == "TimeSeriesMask" ) { TimeSeriesMask(argc, argv); return EXIT_SUCCESS; } if( operation == "TimeSeriesAssemble" ) { TimeSeriesAssemble(argc, argv); return EXIT_SUCCESS; } if( operation == "TimeSeriesDisassemble" ) { TimeSeriesDisassemble(argc, argv); return EXIT_SUCCESS; } if( operation == "TimeSeriesInterpolationSubtraction" ) { TimeSeriesInterpolationSubtraction(argc, argv); return EXIT_SUCCESS; } if( operation == "TimeSeriesSimpleSubtraction" ) { TimeSeriesSimpleSubtraction(argc, argv); return EXIT_SUCCESS; } if( operation == "TimeSeriesSubset" ) { TimeSeriesSubset(argc, argv); return EXIT_SUCCESS; } if( operation == "TimeSeriesToMatrix" ) { TimeSeriesToMatrix(argc, argv); return EXIT_SUCCESS; } return EXIT_FAILURE; } template int ImageMathHelperAll(int argc, char **argv) { std::string operation = std::string(argv[3]); if( operation == "m") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "mresample") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "+") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "-") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "vm") { VImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "vmresample") { VImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "v+") { VImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "v-") { VImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "/") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "^") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "exp") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "max") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "abs") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "addtozero") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "overadd") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "total") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "vtotal") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "mean") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "Decision") { ImageMath(argc, argv); return EXIT_SUCCESS; } if( operation == "Neg") { NegativeImage(argc, argv); return EXIT_SUCCESS; } if( operation == "G") { SmoothImage(argc, argv); return EXIT_SUCCESS; } if( operation == "Convolve") { ConvolveImage(argc, argv); return EXIT_SUCCESS; } if( operation == "PeronaMalik") { PMSmoothImage(argc, argv); return EXIT_SUCCESS; } if( operation == "InPaint") { InPaint(argc, argv); return EXIT_SUCCESS; } if( operation == "MD" || operation == "ME" ) { MorphImage(argc, argv); return EXIT_SUCCESS; } if( operation == "MO" || operation == "MC" ) { MorphImage(argc, argv); return EXIT_SUCCESS; } if( operation == "GD" || operation == "GE" ) { MorphImage(argc, argv); return EXIT_SUCCESS; } if( operation == "GO" || operation == "GC" ) { MorphImage(argc, argv); return EXIT_SUCCESS; } if( operation == "D" ) { DistanceMap(argc, argv); return EXIT_SUCCESS; } if( operation == "MaurerDistance" ) { GenerateMaurerDistanceImage(argc, argv); return EXIT_SUCCESS; } if( operation == "Normalize" ) { NormalizeImage(argc, argv); return EXIT_SUCCESS; } if( operation == "Grad" ) { GradientImage(argc, argv); return EXIT_SUCCESS; } if( operation == "Laplacian" ) { LaplacianImage(argc, argv); return EXIT_SUCCESS; } if( operation == "Canny" ) { CannyImage(argc, argv); return EXIT_SUCCESS; } if( operation == "LabelSurfaceArea" ) { LabelSurfaceArea(argc, argv); return EXIT_SUCCESS; } if( operation == "PH" ) { PrintHeader(argc, argv); return EXIT_SUCCESS; } if( operation == "CenterImage2inImage1" ) { CenterImage2inImage1(argc, argv); return EXIT_SUCCESS; } if( operation == "Byte" ) { ByteImage(argc, argv); return EXIT_SUCCESS; } if( operation == "ReflectionMatrix" ) { ReflectionMatrix(argc, argv); return EXIT_SUCCESS; } if( operation == "MakeAffineTransform" ) { MakeAffineTransform(argc, argv); return EXIT_SUCCESS; } if( operation == "ClosestSimplifiedHeaderMatrix" ) { ClosestSimplifiedHeaderMatrix(argc, argv); return EXIT_SUCCESS; } if( operation == "LabelStats" ) { LabelStats(argc, argv); return EXIT_SUCCESS; } if( operation == "ROIStatistics" ) { ROIStatistics(argc, argv); return EXIT_SUCCESS; } if( operation == "LabelThickness" ) { LabelThickness(argc, argv); return EXIT_SUCCESS; } if( operation == "LabelThickness2" ) { LabelThickness2(argc, argv); return EXIT_SUCCESS; } if( operation == "DiceAndMinDistSum" ) { DiceAndMinDistSum(argc, argv); return EXIT_SUCCESS; } if( operation == "Lipschitz" ) { Lipschitz(argc, argv); return EXIT_SUCCESS; } if( operation == "InvId" ) { InvId(argc, argv); return EXIT_SUCCESS; } if( operation == "ShiftImageSlicesInTime" ) { ShiftImageSlicesInTime(argc, argv); return EXIT_SUCCESS; } if( operation == "ReplicateImage" ) { ReplicateImage(argc, argv); return EXIT_SUCCESS; } if( operation == "ReplicateDisplacement" ) { ReplicateDisplacement(argc, argv); return EXIT_SUCCESS; } if( operation == "GetLargestComponent" ) { GetLargestComponent(argc, argv); return EXIT_SUCCESS; } if( operation == "ExtractVectorComponent" ) { ExtractVectorComponent(argc, argv); return EXIT_SUCCESS; } if( operation == "ThresholdAtMean" ) { ThresholdAtMean(argc, argv); return EXIT_SUCCESS; } if( operation == "SetTimeSpacing" ) { SetTimeSpacing(argc, argv); return EXIT_SUCCESS; } if( operation == "SetTimeSpacingWarp" ) { SetTimeSpacingWarp(argc, argv); return EXIT_SUCCESS; } if( operation == "FlattenImage" ) { FlattenImage(argc, argv); return EXIT_SUCCESS; } if( operation == "CorruptImage" ) { CorruptImage(argc, argv); return EXIT_SUCCESS; } if( operation == "Where" ) { Where(argc, argv); return EXIT_SUCCESS; } if( operation == "Finite" ) { Finite( argc, argv ); return EXIT_SUCCESS; } if( operation == "FillHoles" ) { FillHoles(argc, argv); return EXIT_SUCCESS; } if( operation == "HistogramMatch" ) { HistogramMatching(argc, argv); return EXIT_SUCCESS; } if( operation == "RescaleImage" ) { RescaleImage(argc, argv); return EXIT_SUCCESS; } if( operation == "WindowImage" ) { WindowImage(argc, argv); return EXIT_SUCCESS; } if( operation == "WindowImage" ) { WindowImage(argc, argv); return EXIT_SUCCESS; } if( operation == "NeighborhoodStats" ) { NeighborhoodStats(argc, argv); return EXIT_SUCCESS; } if( operation == "PadImage" ) { PadImage(argc, argv); return EXIT_SUCCESS; } if( operation == "SigmoidImage" ) { SigmoidImage(argc, argv); return EXIT_SUCCESS; } if( operation == "Sharpen" ) { SharpenImage(argc, argv); return EXIT_SUCCESS; } if( operation == "MakeImage" ) { MakeImage(argc, argv); return EXIT_SUCCESS; } if( operation == "stack" ) { StackImage(argc, argv); return EXIT_SUCCESS; } if( operation == "stack2" ) { Stack2Images(argc, argv); return EXIT_SUCCESS; } if( operation == "CompareHeadersAndImages" ) { CompareHeadersAndImages(argc, argv); return EXIT_SUCCESS; } if( operation == "CountVoxelDifference" ) { CountVoxelDifference(argc, argv); return EXIT_SUCCESS; } if( operation == "RemoveLabelInterfaces" ) { RemoveLabelInterfaces(argc, argv); return EXIT_SUCCESS; } if( operation == "ReplaceVoxelValue" ) { ReplaceVoxelValue(argc, argv); return EXIT_SUCCESS; } if( operation == "PoissonDiffusion" ) { PoissonDiffusion(argc, argv); return EXIT_SUCCESS; } if( operation == "EnumerateLabelInterfaces" ) { EnumerateLabelInterfaces(argc, argv); return EXIT_SUCCESS; } if( operation == "ConvertImageToFile" ) { ConvertImageToFile(argc, argv); return EXIT_SUCCESS; } if( operation == "PValueImage" ) { PValueImage(argc, argv); return EXIT_SUCCESS; } if( operation == "CorrelationUpdate" ) { CorrelationUpdate(argc, argv); return EXIT_SUCCESS; } if( operation == "ConvertImageSetToMatrix" ) { ConvertImageSetToMatrix(argc, argv); return EXIT_SUCCESS; } if( operation == "RandomlySampleImageSetToCSV" ) { RandomlySampleImageSetToCSV(argc, argv); return EXIT_SUCCESS; } if( operation == "ConvertImageSetToEigenvectors" ) { ConvertImageSetToEigenvectors(argc, argv); return EXIT_SUCCESS; } if( operation == "ConvertVectorToImage" ) { ConvertVectorToImage(argc, argv); return EXIT_SUCCESS; } if( operation == "PropagateLabelsThroughMask" ) { itkPropagateLabelsThroughMask(argc, argv); return EXIT_SUCCESS; } if( operation == "FastMarchingExtension" ) { FastMarchingExtension(argc, argv); return EXIT_SUCCESS; } if( operation == "FastMarchingSegmentation" ) { FastMarchingSegmentation(argc, argv); return EXIT_SUCCESS; } if( operation == "TruncateImageIntensity" ) { TruncateImageIntensity(argc, argv); return EXIT_SUCCESS; } if( operation == "ExtractSlice" ) { ExtractSlice(argc, argv); return EXIT_SUCCESS; } if( operation == "ClusterThresholdVariate" ) { ClusterThresholdVariate(argc, argv); return EXIT_SUCCESS; } if( operation == "MajorityVoting" ) { MajorityVoting(argc, argv); return EXIT_SUCCESS; } if( operation == "MostLikely" ) { MostLikely(argc, argv); return EXIT_SUCCESS; } if( operation == "CorrelationVoting" ) { CorrelationVoting(argc, argv); return EXIT_SUCCESS; } if( operation == "PearsonCorrelation" ) { PearsonCorrelation(argc, argv); return EXIT_SUCCESS; } if( operation == "Translate" ) { Translate(argc, argv); return EXIT_SUCCESS; } if( operation == "NeighborhoodCorrelation" ) { ImageMetrics(argc, argv); return EXIT_SUCCESS; } if( operation == "NormalizedCorrelation" ) { ImageMetrics(argc, argv); return EXIT_SUCCESS; } if( operation == "Demons" ) { ImageMetrics(argc, argv); return EXIT_SUCCESS; } if( operation == "Mattes" ) { return ImageMetrics(argc, argv); } if( operation == "MinMaxMean" ) { MinMaxMean(argc, argv); return EXIT_SUCCESS; } if( operation == "PureTissueN4WeightMask" ) { PureTissueN4WeightMask(argc, argv); return EXIT_SUCCESS; } if( operation == "BlobDetector" ) { BlobDetector(argc, argv); return EXIT_SUCCESS; } if( operation == "MatchBlobs" ) { MatchBlobs(argc, argv); return EXIT_SUCCESS; } if( operation == "Project" ) { Project(argc, argv); return EXIT_SUCCESS; } return EXIT_FAILURE; } } // namespace ants ants-2.2.0/Examples/ImageSetStatistics.cxx000066400000000000000000000712541311104306400205350ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copriyght (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include #include #include #include "ReadWriteData.h" #include "itkMersenneTwisterRandomVariateGenerator.h" #include "itkHistogramMatchingImageFilter.h" #include "itkMinimumMaximumImageFilter.h" #include "itkConnectedComponentImageFilter.h" #include "itkRelabelComponentImageFilter.h" #include "itkLabelStatisticsImageFilter.h" #include "itkNeighborhoodIterator.h" // RecursiveAverageImages img1 img2 weight // We divide the 2nd input image by its mean and add it to the first // input image with weight 1/n. // The output overwrites the 1st img with the sum. // Note: could easily add variance computation // http://people.revoledu.com/kardi/tutorial/RecursiveStatistic/Time-Variance.htm #include "itkDiscreteGaussianImageFilter.h" namespace ants { template void ReadImage(itk::SmartPointer & target, const char *file, bool copy) { // std::cout << " reading b " << std::string(file) << std::endl; typedef itk::ImageFileReader readertype; typename readertype::Pointer reader = readertype::New(); reader->SetFileName(file); reader->Update(); if( !copy ) { target = (reader->GetOutput() ); } else { typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter2( target, target->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { vfIter2.Set( reader->GetOutput()->GetPixel(vfIter2.GetIndex() ) ); } } } double TProb(double t, int df) { if( t == 0 ) { return 0; } double a = 0.36338023; double w = atan(t / sqrt( (double)df) ); double s = sin(w); double c = cos(w); double t1, t2; int j1, j2, k2; if( df % 2 == 0 ) // even { t1 = s; if( df == 2 ) // special case df=2 { return 0.5 * (1 + t1); } t2 = s; j1 = -1; j2 = 0; k2 = (df - 2) / 2; } else { t1 = w; if( df == 1 ) // special case df=1 { return 1 - (0.5 * (1 + (t1 * (1 - a) ) ) ); } t2 = s * c; t1 = t1 + t2; if( df == 3 ) // special case df=3 { return 1 - (0.5 * (1 + (t1 * (1 - a) ) ) ); } j1 = 0; j2 = 1; k2 = (df - 3) / 2; } for( int i = 1; i >= k2; i++ ) { j1 = j1 + 2; j2 = j2 + 2; t2 = t2 * c * c * j1 / j2; t1 = t1 + t2; } return 1 - (0.5 * (1 + (t1 * (1 - a * (df % 2) ) ) ) ); } template typename TImage::Pointer SmoothImage(typename TImage::Pointer image, float sig) { typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(sig); filter->SetUseImageSpacingOn(); filter->SetMaximumError(.01f); filter->SetInput(image); filter->Update(); return filter->GetOutput(); } template // typename TInputImage::Pointer void HistogramMatch(typename TInputImage::Pointer m_InputFixedImage, typename TInputImage::Pointer m_InputMovingImage) { std::cout << " MATCHING INTENSITIES " << std::endl; typedef itk::HistogramMatchingImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( m_InputMovingImage ); filter->SetReferenceImage( m_InputFixedImage ); filter->SetNumberOfHistogramLevels( 256 ); filter->SetNumberOfMatchPoints( 10 ); filter->ThresholdAtMeanIntensityOn(); filter->ThresholdAtMeanIntensityOff(); filter->Update(); typename TInputImage::Pointer img = filter->GetOutput(); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( img, img->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { m_InputMovingImage->SetPixel(vfIter.GetIndex(), vfIter.Get() ); } return; } template void LocalMean(typename TImage::Pointer image, unsigned int nhood, typename TImage::Pointer meanimage ) { typename TImage::Pointer localmean = MakeNewImage(image, 0); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator outIter(image, image->GetLargestPossibleRegion() ); typename TImage::SizeType imagesize = image->GetLargestPossibleRegion().GetSize(); const unsigned int ImageDimension = 3; typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for( unsigned int j = 0; j < ImageDimension; j++ ) { rad[j] = nhood; } for( outIter.GoToBegin(); !outIter.IsAtEnd(); ++outIter ) { itk::NeighborhoodIterator hoodIt( rad, image, image->GetLargestPossibleRegion() ); typename TImage::IndexType oindex = outIter.GetIndex(); hoodIt.SetLocation(oindex); double fixedMean = 0; // double movingMean=0; unsigned int hoodlen = hoodIt.Size(); // unsigned int inct=0; bool takesample = true; // double sumj=0; // double sumi=0; if( takesample ) { // double sumj=0; double sumi = 0; unsigned int cter = 0; for( unsigned int indct = 0; indct < hoodlen; indct++ ) { typename TImage::IndexType index = hoodIt.GetIndex(indct); bool inimage = true; for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( index[dd] < 0 || index[dd] > static_cast(imagesize[dd] - 1) ) { inimage = false; } } if( inimage ) { sumi += image->GetPixel(index); cter++; } } if( cter > 0 ) { fixedMean = sumi / (float)cter; } } float val = image->GetPixel(oindex) - fixedMean; meanimage->SetPixel( oindex, meanimage->GetPixel(oindex) + fixedMean); localmean->SetPixel( oindex, val ); } typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( image, image->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { vfIter.Set( localmean->GetPixel( vfIter.GetIndex() ) ); } return; // localmean; } template // std::vector float GetClusterStat(typename TImage::Pointer image, float Tthreshold, unsigned int minSize, unsigned int whichstat, std::string outfn, bool TRUTH) { typedef float InternalPixelType; typedef TImage InternalImageType; typedef TImage OutputImageType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::ConnectedComponentImageFilter FilterType; typedef itk::RelabelComponentImageFilter RelabelType; typename ThresholdFilterType::Pointer threshold = ThresholdFilterType::New(); typename FilterType::Pointer filter = FilterType::New(); typename RelabelType::Pointer relabel = RelabelType::New(); InternalPixelType threshold_low, threshold_hi; threshold_low = Tthreshold; threshold_hi = 1.e9; threshold->SetInput(image); threshold->SetInsideValue(itk::NumericTraits::OneValue()); threshold->SetOutsideValue(itk::NumericTraits::ZeroValue()); threshold->SetLowerThreshold(threshold_low); threshold->SetUpperThreshold(threshold_hi); threshold->Update(); filter->SetInput(threshold->GetOutput() ); // if (argc > 5) { int fullyConnected = 1; // atoi( argv[5] ); filter->SetFullyConnected( fullyConnected ); } relabel->SetInput( filter->GetOutput() ); relabel->SetMinimumObjectSize( minSize ); // relabel->SetUseHistograms(true); try { relabel->Update(); } catch( itk::ExceptionObject & excep ) { std::cout << "Relabel: exception caught !" << std::endl; std::cout << excep << std::endl; } typename TImage::Pointer Clusters = MakeNewImage(relabel->GetOutput(), 0); // typename TImage::Pointer Clusters=relabel->GetOutput(); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); /* typename itk::LabelStatisticsImageFilter::Pointer labstat= itk::LabelStatisticsImageFilter::New(); labstat->SetInput(image); labstat->SetLabelImage(Clusters); labstat->SetUseHistograms(true); labstat->Update(); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( Clusters, Clusters->GetLargestPossibleRegion() ); float maximum=0; // Relabel the Clusters image with the right statistic for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if (relabel->GetOutput()->GetPixel(vfIter.GetIndex()) > 0 ) { float pix = relabel->GetOutput()->GetPixel(vfIter.GetIndex()); float val; if (whichstat == 0) val=pix; else if (whichstat == 1) val = labstat->GetSum(pix); else if (whichstat == 2) val = labstat->GetMean(pix); else if (whichstat == 3) val = labstat->GetMaximum(pix); if (val > maximum) maximum=val; vfIter.Set(val); } } */ float maximum = relabel->GetNumberOfObjects(); float maxtstat = 0; std::vector histogram( (int)maximum + 1); std::vector clustersum( (int)maximum + 1); for( int i = 0; i <= maximum; i++ ) { histogram[i] = 0; clustersum[i] = 0; } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 0 ) { float vox = image->GetPixel(vfIter.GetIndex() ); histogram[(unsigned int)vfIter.Get()] = histogram[(unsigned int)vfIter.Get()] + 1; clustersum[(unsigned int)vfIter.Get()] += vox; if( vox > maxtstat ) { maxtstat = vox; } } } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 0 ) { if( whichstat == 0 ) // size { Clusters->SetPixel( vfIter.GetIndex(), histogram[(unsigned int)vfIter.Get()] ); } if( whichstat == 1 ) // sum { Clusters->SetPixel( vfIter.GetIndex(), clustersum[(unsigned int)vfIter.Get()] ); } if( whichstat == 2 ) // mean { Clusters->SetPixel( vfIter.GetIndex(), clustersum[(unsigned int)vfIter.Get()] / (float)histogram[(unsigned int)vfIter.Get()] ); } if( whichstat == 3 ) // max { Clusters->SetPixel( vfIter.GetIndex(), histogram[(unsigned int)vfIter.Get()] ); } } else { Clusters->SetPixel(vfIter.GetIndex(), 0); } } // for (int i=0; i<=maximum; i++) // std::cout << " label " << i << " ct is: " << histogram[i] << std::endl; if( TRUTH ) { typedef itk::ImageFileWriter writertype; typename writertype::Pointer writer = writertype::New(); writer->SetFileName( (outfn + std::string("Clusters.nii") ).c_str() ); writer->SetInput( Clusters ); writer->Write(); } if( whichstat == 0 ) { return histogram[1]; } else if( whichstat == 1 ) { float mx = 0; for( int i = 1; i <= maximum; i++ ) { if( clustersum[i] > mx ) { mx = clustersum[i]; } } return mx; } else if( whichstat == 2 ) { float mx = 0; for( int i = 1; i <= maximum; i++ ) { if( clustersum[i] / (float)histogram[i] > mx ) { mx = clustersum[i] / (float)histogram[i] * 1000.0; } } return mx; } else if( whichstat == 3 ) { return maxtstat * 1000.0; } else { return histogram[1]; } } float median(std::vector vec) { typedef std::vector::size_type vec_sz; vec_sz size = vec.size(); if( size == 0 ) { return 0; } // throw domain_error("median of an empty vector"); sort(vec.begin(), vec.end() ); vec_sz mid = size / 2; return size % 2 == 0 ? (vec[mid] + vec[mid - 1]) / 2 : vec[mid]; } float npdf(std::vector vec, bool opt, float www) { typedef std::vector::size_type vec_sz; vec_sz size = vec.size(); if( size == 0 ) { return 0; } // throw domain_error("median of an empty vector"); float mean = 0, var = 0; float max = -1.e9, min = 1.e9; for( unsigned int i = 0; i < size; i++ ) { float val = vec[i]; if( val > max ) { max = val; } else if( val < min ) { min = val; } float n = (float) (i + 1); float wt1 = 1.0 / (float)n; float wt2 = 1.0 - wt1; mean = mean * wt2 + val * wt1; if( i > 0 ) { float wt3 = 1.0 / ( (float) n - 1.0 ); var = var * wt2 + ( val - mean ) * ( val - mean) * wt3; } } if( var == 0 ) { return mean; } // else std::cout << " Mean " << mean << " var " << var << std::endl; // eval parzen probability std::vector prob(size); float maxprob = 0; // float maxprobval=0; float weightedmean = 0; float weighttotal = 0; unsigned int maxprobind = 0; // float sample=0.0; float width; if( www > 0 ) { width = www; } else { width = sqrt(var) / 2.0; } // std::cout << " using width " << width << std::endl; // float N=(float)size; for( unsigned int j = 0; j < size; j++ ) { float sample = vec[j]; float total = 0.0; for( unsigned int i = 0; i < size; i++ ) { float delt = vec[i] - sample; delt *= delt; prob[i] = 1.0 / (2.0 * 3.1214 * width) * exp(-0.5 * delt / (width * width) ); total += prob[i]; // maxprobval+=prob[i] } if( total > maxprob ) { maxprob = total; maxprobind = j; } weightedmean += sample * total; weighttotal += total; // for (unsigned int i=0; i vec) { typedef std::vector::size_type vec_sz; vec_sz size = vec.size(); if( size == 0 ) { return 0; } // throw domain_error("median of an empty vector"); sort(vec.begin(), vec.end() ); const unsigned int lo = 0; const unsigned int hi = size; const unsigned int ct = hi - lo; float total = 0; for( unsigned int i = lo; i < hi; i++ ) { total += vec[i]; } return total / (float)ct; } float myantsmax(std::vector vec) { typedef std::vector::size_type vec_sz; vec_sz size = vec.size(); if( size == 0 ) { return 0; } float max = -1.e9; for( unsigned int i = 0; i < size; i++ ) { float val = vec[i]; if( val > max ) { max = val; } } return max; } float myantssimilaritymaxlabel(std::vector labelvec, std::vector similarityvec, bool opt) { typedef std::vector::size_type vec_sz; vec_sz size = labelvec.size(); if( size == 0 ) { return 0; } unsigned int max = 0; float maxsim = -1.e9; float totalsim = 0; for( unsigned int i = 0; i < size; i++ ) { totalsim += similarityvec[i]; } if( fabs(totalsim) <= 0 ) { return 0; } for( unsigned int i = 0; i < size; i++ ) { float simval = similarityvec[i]; if( simval > maxsim ) { maxsim = simval; max = i; } } if( opt == true ) { return labelvec[max]; } else { return max; } } template int ImageSetStatistics(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::ImageFileReader readertype; typedef typename ImageType::IndexType IndexType; typedef itk::ImageRegionIteratorWithIndex Iterator; unsigned int mch = 0; int argct = 2; std::string fn1 = std::string(argv[argct]); argct++; std::string outfn = std::string(argv[argct]); argct++; unsigned int whichstat = atoi(argv[argct]); argct++; std::string roifn = ""; if( argc > argct ) { roifn = std::string(argv[argct]); argct++; } std::string simimagelist = std::string(""); if( argc > argct ) { simimagelist = std::string(argv[argct]); argct++; } float www = 0; // if (argc > argct) { www=atof(argv[argct]);argct++;} // unsigned int mchmax= 0; // if (argc > argct) { mchmax=atoi(argv[argct]); argct++;} unsigned int localmeanrad = 0; // if (argc > argct) { localmeanrad=atoi(argv[argct]);argct++;} // std::cout <<" roifn " << roifn << " fn1 " << fn1 << " whichstat " << whichstat << std::endl; typename ImageType::Pointer outimage = ITK_NULLPTR; typename ImageType::Pointer ROIimg = ITK_NULLPTR; if( roifn.length() > 4 ) { std::cout << " reading roi image " << roifn << std::endl; typename readertype::Pointer reader2 = readertype::New(); reader2->SetFileName(roifn.c_str() ); reader2->UpdateLargestPossibleRegion(); try { ROIimg = reader2->GetOutput(); } catch( ... ) { ROIimg = ITK_NULLPTR; std::cout << " Error reading ROI image " << std::endl; // return 0; } } // now do the recursive average const unsigned int maxChar = 512; char lineBuffer[maxChar]; char filenm[maxChar]; unsigned int filecount1 = 0; { std::ifstream inputStreamA( fn1.c_str(), std::ios::in ); if( !inputStreamA.is_open() ) { std::cout << "Can't open parameter file: " << fn1 << std::endl; return EXIT_FAILURE; } while( !inputStreamA.eof() ) { inputStreamA.getline( lineBuffer, maxChar, '\n' ); if( sscanf( lineBuffer, "%s ", filenm) != 1 ) { // std::cout << "Done. read " << lineBuffer << " n " << ct1 << " files " << std::endl; // std::cout << std::endl; continue; } else { filecount1++; } } inputStreamA.close(); } std::cout << " NFiles1 " << filecount1 << std::endl; unsigned int filecount2 = 0; if( simimagelist.length() > 2 && ( whichstat == 5 || whichstat == 6 ) ) { std::ifstream inputStreamA( simimagelist.c_str(), std::ios::in ); if( !inputStreamA.is_open() ) { std::cout << "Can't open parameter file: " << fn1 << std::endl; return EXIT_FAILURE; } while( !inputStreamA.eof() ) { inputStreamA.getline( lineBuffer, maxChar, '\n' ); if( sscanf( lineBuffer, "%s ", filenm) != 1 ) { // std::cout << "Done. read " << lineBuffer << " n " << ct1 << " files " << std::endl; // std::cout << std::endl; continue; } else { filecount2++; } } inputStreamA.close(); if( filecount1 != filecount2 ) { std::cout << " the number of similarity images does not match the number of label images --- thus, we have to get out of here !! i.e. something's wrong. " << std::endl; return EXIT_FAILURE; } } // fi simimagelist std::cout << " NFiles2 " << filecount2 << std::endl; typename ImageType::Pointer meanimage; std::vector imagestack; imagestack.resize(filecount1); // imagestack.fill(NULL); std::vector filenames(filecount1); typename ImageType::Pointer StatImage; unsigned int ct = 0; std::ifstream inputStreamA( fn1.c_str(), std::ios::in ); if( !inputStreamA.is_open() ) { std::cout << "Can't open parameter file: " << fn1 << std::endl; return EXIT_FAILURE; } while( !inputStreamA.eof() ) { inputStreamA.getline( lineBuffer, maxChar, '\n' ); if( sscanf( lineBuffer, "%s ", filenm) != 1 ) { // std::cout << "Done. read " << lineBuffer << " n " << ct1 << " files " << std::endl; // std::cout << std::endl; continue; } else { filenames[ct] = std::string(filenm); ReadImage(imagestack[ct], filenm, false); if( ct == 0 ) { meanimage = MakeNewImage(imagestack[ct], 0); } if( localmeanrad > 0 ) { LocalMean(imagestack[ct], localmeanrad, meanimage); } std::cout << " done reading " << (float) ct / (float ) filecount1 << std::endl; ct++; } } inputStreamA.close(); // read similarity images, if needed std::vector simimagestack; simimagestack.resize(filecount2); ct = 0; if( simimagelist.length() > 2 && ( whichstat == 5 || whichstat == 6 ) ) { inputStreamA.open( simimagelist.c_str() ); if( !inputStreamA.is_open() ) { std::cout << "Can't open parameter file: " << fn1 << std::endl; return -1; } while( !inputStreamA.eof() ) { inputStreamA.getline( lineBuffer, maxChar, '\n' ); if( sscanf( lineBuffer, "%s ", filenm) != 1 ) { continue; } else { ReadImage(simimagestack[ct], filenm, false); ct++; } } inputStreamA.close(); } // fi read similarity images ReadImage( StatImage, filenames[0].c_str(), false); Iterator vfIter(StatImage, StatImage->GetLargestPossibleRegion() ); std::vector voxels(filecount1); std::vector similarities(filecount2); unsigned long nvox = 1; for( unsigned int i = 0; i < ImageDimension; i++ ) { nvox *= StatImage->GetLargestPossibleRegion().GetSize()[i]; } ct = 0; unsigned long prog = nvox / 15; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( ct % prog == 0 ) { std::cout << " % " << (float) ct / (float) nvox << std::endl; } ct++; IndexType ind = vfIter.GetIndex(); unsigned int maxval = 0; bool takesample = true; if( ROIimg ) { if( ROIimg->GetPixel(ind) < 0.5 ) { takesample = false; } else { maxval = (unsigned int)(ROIimg->GetPixel(ind) - 1); } } if( takesample ) { if( mch == 0 ) { meanimage->SetPixel(ind, meanimage->GetPixel(ind) / filecount1 ); } for( unsigned int j = 0; j < filecount1; j++ ) { voxels[j] = imagestack[j]->GetPixel(ind); } for( unsigned int j = 0; j < filecount2; j++ ) { similarities[j] = simimagestack[j]->GetPixel(ind); } float stat = 0; switch( whichstat ) { case 1: { stat = npdf(voxels, true, www); if( ct == 1 ) { std::cout << "the max prob appearance \n"; } } break; case 2: { stat = npdf(voxels, false, www); if( ct == 1 ) { std::cout << "the probabilistically weighted appearance " << www << " \n"; } } break; case 3: { stat = trimmean(voxels); if( ct == 1 ) { std::cout << "the trimmed mean appearance \n"; } } break; case 4: { stat = myantsmax(voxels); if( ct == 1 ) { std::cout << "the maximum appearance \n"; } } break; case 5: { stat = myantssimilaritymaxlabel(voxels, similarities, true); if( ct == 1 ) { std::cout << "the maximum similarity-based label \n"; } } break; case 6: { stat = myantssimilaritymaxlabel(voxels, similarities, false); if( ct == 1 ) { std::cout << "which image provides the maximum similarity-based label \n"; } } break; case 7: { stat = voxels[maxval]; if( ct == 1 ) { std::cout << "which image provides the maximum similarity-based label \n"; } } break; default: { stat = median(voxels); if( ct == 1 ) { std::cout << "the median appearance \n"; } } break; } float sval = stat; if( localmeanrad > 0 ) { sval += meanimage->GetPixel(ind); } StatImage->SetPixel(ind, sval); } else { StatImage->SetPixel(ind, 0); } } WriteImage(StatImage, outfn.c_str() ); std::cout << " Done " << std::endl; return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ImageSetStatistics( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ImageSetStatistics" ); const int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Usage: " << std::endl; std::cout << argv[0] << " ImageDimension controlslist.txt outimage.nii whichstat {roi.nii} {imagelist2forsimilarityweightedstats.txt}" << std::endl; std::cout << " whichstat = 0: median, 1: max prob appearance , 2: weighted mean appearance , 3: trimmed mean , 4 : max value , option 5 : similarity-weighted (must pass imagelist2 as well) else median , option 6 : same as similarity-weighted option 5 but the label corresponds to the image that provides the best local match ... useful if you want to MRF smooth these indices , option 7 : similar to 5 but expects the max-value to be stored in the ROI image and uses it to get the intensity ... " << std::endl; std::cout << " example: ImageSetStatistics 3 imagelist.txt maxvalueimage.nii.gz 4 " << std::endl; std::cout << " similarity weighted --- pass in a list of similarity images here which will be used to select the best label --- thus, number of similarity images must match the number of label images . " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension switch( atoi(argv[1]) ) { case 2: { return ImageSetStatistics<2>(argc, argv); } break; case 3: { return ImageSetStatistics<3>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/KellyKapowski.cxx000066400000000000000000000646761311104306400175670ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include "antsCommandLineParser.h" #include "itkDiReCTImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkImage.h" #include "ReadWriteData.h" #include "itkTimeProbe.h" #include #include #include namespace ants { template class CommandIterationUpdate : public itk::Command { public: typedef CommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); protected: CommandIterationUpdate() { }; public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { const TFilter * filter = dynamic_cast( object ); if( typeid( event ) != typeid( itk::IterationEvent ) ) { return; } std::cout << " Iteration " << filter->GetElapsedIterations() << " (of " << filter->GetMaximumNumberOfIterations() << "). "; std::cout << "Current energy = " << filter->GetCurrentEnergy() << ". "; if( filter->GetElapsedIterations() >= filter->GetConvergenceWindowSize() ) { std::cout << "(convergence value = " << filter->GetCurrentConvergenceMeasurement() << ", threshold = " << filter->GetConvergenceThreshold() << ")"; } std::cout << std::endl; } }; template int DiReCT( itk::ants::CommandLineParser *parser ) { typedef float RealType; typedef unsigned int LabelType; typedef itk::Image LabelImageType; typename LabelImageType::Pointer segmentationImage; typedef itk::Image ImageType; typename ImageType::Pointer grayMatterProbabilityImage; typename ImageType::Pointer whiteMatterProbabilityImage; typename ImageType::Pointer thicknessPriorImage; typedef itk::DiReCTImageFilter DiReCTFilterType; typename DiReCTFilterType::Pointer direct = DiReCTFilterType::New(); typedef typename DiReCTFilterType::LabelType DirectLabelType; bool verbose = false; typename itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } if( verbose ) { std::cout << "Running DiReCT for " << ImageDimension << "-dimensional images." << std::endl << std::endl; } // // debugging information // typename itk::ants::CommandLineParser::OptionType::Pointer debugOption = parser->GetOption( "print-debug-information" ); if( debugOption && debugOption->GetNumberOfFunctions() ) { std::string value = debugOption->GetFunction()->GetName(); ConvertToLowerCase( value ); if( std::strcmp( value.c_str(), "true" ) || parser->Convert( value ) != 0 ) { direct->DebugOn(); } } // // segmentation image // typename itk::ants::CommandLineParser::OptionType::Pointer segmentationImageOption = parser->GetOption( "segmentation-image" ); if( segmentationImageOption && segmentationImageOption->GetNumberOfFunctions() ) { if( segmentationImageOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { std::string inputFile = segmentationImageOption->GetFunction( 0 )->GetName(); ReadImage( segmentationImage, inputFile.c_str() ); } else if( segmentationImageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { std::string inputFile = segmentationImageOption->GetFunction( 0 )->GetParameter( 0 ); ReadImage( segmentationImage, inputFile.c_str() ); if( segmentationImageOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { DirectLabelType gmval = parser->Convert( segmentationImageOption->GetFunction( 0 )->GetParameter( 1 ) ); direct->SetGrayMatterLabel( gmval ); } if( segmentationImageOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { DirectLabelType wmval = parser->Convert( segmentationImageOption->GetFunction( 0 )->GetParameter( 2 ) ); direct->SetWhiteMatterLabel( wmval ); } } } else { if( verbose ) { std::cerr << "Segmentation image not specified." << std::endl; } return EXIT_FAILURE; } direct->SetSegmentationImage( segmentationImage ); // // gray matter probability image // typename itk::ants::CommandLineParser::OptionType::Pointer grayMatterOption = parser->GetOption( "gray-matter-probability-image" ); if( grayMatterOption && grayMatterOption->GetNumberOfFunctions() ) { std::string gmFile = grayMatterOption->GetFunction()->GetName(); ReadImage( grayMatterProbabilityImage, gmFile.c_str() ); } else { if( verbose ) { std::cout << " Grey matter probability image not specified. " << "Creating one from the segmentation image." << std::endl; } typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( segmentationImage ); thresholder->SetLowerThreshold( direct->GetGrayMatterLabel() ); thresholder->SetUpperThreshold( direct->GetGrayMatterLabel() ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); typedef itk::DiscreteGaussianImageFilter SmootherType; typename SmootherType::Pointer smoother = SmootherType::New(); smoother->SetVariance( 1.0 ); smoother->SetUseImageSpacingOn(); smoother->SetMaximumError( 0.01 ); smoother->SetInput( thresholder->GetOutput() ); smoother->Update(); grayMatterProbabilityImage = smoother->GetOutput(); } direct->SetGrayMatterProbabilityImage( grayMatterProbabilityImage ); // // white matter probability image // typename itk::ants::CommandLineParser::OptionType::Pointer whiteMatterOption = parser->GetOption( "white-matter-probability-image" ); if( whiteMatterOption && whiteMatterOption->GetNumberOfFunctions() ) { std::string wmFile = whiteMatterOption->GetFunction( 0 )->GetName(); ReadImage( whiteMatterProbabilityImage, wmFile.c_str() ); } else { if( verbose ) { std::cout << " White matter probability image not specified. " << "Creating one from the segmentation image." << std::endl << std::endl; } typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( segmentationImage ); thresholder->SetLowerThreshold( direct->GetWhiteMatterLabel() ); thresholder->SetUpperThreshold( direct->GetWhiteMatterLabel() ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); typedef itk::DiscreteGaussianImageFilter SmootherType; typename SmootherType::Pointer smoother = SmootherType::New(); smoother->SetVariance( 1.0 ); smoother->SetUseImageSpacingOn(); smoother->SetMaximumError( 0.01 ); smoother->SetInput( thresholder->GetOutput() ); smoother->Update(); whiteMatterProbabilityImage = smoother->GetOutput(); } direct->SetWhiteMatterProbabilityImage( whiteMatterProbabilityImage ); // // label priors // typename itk::ants::CommandLineParser::OptionType::Pointer tpOption = parser->GetOption( "thickness-prior-image" ); if( tpOption && tpOption->GetNumberOfFunctions() ) { std::string labFile = tpOption->GetFunction( 0 )->GetName(); ReadImage( thicknessPriorImage, labFile.c_str() ); direct->SetThicknessPriorImage( thicknessPriorImage ); } // // convergence options // typename itk::ants::CommandLineParser::OptionType::Pointer convergenceOption = parser->GetOption( "convergence" ); if( convergenceOption && convergenceOption->GetNumberOfFunctions() ) { if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { direct->SetMaximumNumberOfIterations( parser->Convert( convergenceOption->GetFunction( 0 )->GetParameter( 0 ) ) ); } if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { direct->SetConvergenceThreshold( parser->Convert( convergenceOption->GetFunction( 0 )->GetParameter( 1 ) ) ); } if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { direct->SetConvergenceWindowSize( parser->Convert( convergenceOption->GetFunction( 0 )->GetParameter( 2 ) ) ); } } // // thickness prior estimate // typename itk::ants::CommandLineParser::OptionType::Pointer thicknessPriorOption = parser->GetOption( "thickness-prior-estimate" ); if( thicknessPriorOption && thicknessPriorOption->GetNumberOfFunctions() ) { direct->SetThicknessPriorEstimate( parser->Convert( thicknessPriorOption->GetFunction( 0 )->GetName() ) ); } // // gradient step // typename itk::ants::CommandLineParser::OptionType::Pointer gradientStepOption = parser->GetOption( "gradient-step" ); if( gradientStepOption && gradientStepOption->GetNumberOfFunctions() ) { direct->SetInitialGradientStep( parser->Convert( gradientStepOption->GetFunction( 0 )->GetName() ) ); } // // do B-spline smoothing? // typename itk::ants::CommandLineParser::OptionType::Pointer bsplineSmoothingOption = parser->GetOption( "use-bspline-smoothing" ); if( bsplineSmoothingOption && bsplineSmoothingOption->GetNumberOfFunctions() ) { direct->SetUseBSplineSmoothing( parser->Convert( bsplineSmoothingOption->GetFunction( 0 )->GetName() ) ); } // // smoothing parameter for the velocity field // typename itk::ants::CommandLineParser::OptionType::Pointer smoothingVelocityFieldParameterOption = parser->GetOption( "smoothing-velocity-field-parameter" ); if( smoothingVelocityFieldParameterOption && smoothingVelocityFieldParameterOption->GetNumberOfFunctions() ) { if( direct->GetUseBSplineSmoothing() ) { direct->SetBSplineSmoothingIsotropicMeshSpacing( parser->Convert( smoothingVelocityFieldParameterOption->GetFunction( 0 )->GetName() ) ); } else { direct->SetSmoothingVelocityFieldVariance( parser->Convert( smoothingVelocityFieldParameterOption->GetFunction( 0 )->GetName() ) ); } } // // smoothing variance for the hit and total images // typename itk::ants::CommandLineParser::OptionType::Pointer smoothingVarianceOption = parser->GetOption( "smoothing-variance" ); if( smoothingVarianceOption && smoothingVarianceOption->GetNumberOfFunctions() ) { direct->SetSmoothingVariance( parser->Convert( smoothingVarianceOption->GetFunction( 0 )->GetName() ) ); } // // number of integration points // typename itk::ants::CommandLineParser::OptionType::Pointer numberOfIntegrationPointsOption = parser->GetOption( "number-of-integration-points" ); if( numberOfIntegrationPointsOption && numberOfIntegrationPointsOption->GetNumberOfFunctions() ) { direct->SetNumberOfIntegrationPoints( parser->Convert( numberOfIntegrationPointsOption->GetFunction( 0 )->GetName() ) ); } // // number of invert displacement field iterations // typename itk::ants::CommandLineParser::OptionType::Pointer numberOfInvertDisplacementFieldIterationsOption = parser->GetOption( "maximum-number-of-invert-displacement-field-iterations" ); if( numberOfInvertDisplacementFieldIterationsOption && numberOfInvertDisplacementFieldIterationsOption->GetNumberOfFunctions() ) { direct->SetMaximumNumberOfInvertDisplacementFieldIterations( parser->Convert( numberOfInvertDisplacementFieldIterationsOption->GetFunction( 0 )->GetName() ) ); } if( verbose ) { typedef CommandIterationUpdate CommandType; typename CommandType::Pointer observer = CommandType::New(); direct->AddObserver( itk::IterationEvent(), observer ); } itk::TimeProbe timer; timer.Start(); try { direct->Update(); // causes problems with ANTsR , unknown reason } catch( itk::ExceptionObject & e ) { if( verbose ) { std::cerr << "Exception caught: " << e << std::endl; } return EXIT_FAILURE; } timer.Stop(); if( verbose ) { direct->Print( std::cout, 3 ); std::cout << "DiReCT elapsed time: " << timer.GetMean() << std::endl; } /** * output */ typename itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() > 0 ) { if( outputOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { WriteImage( direct->GetOutput(), ( outputOption->GetFunction( 0 )->GetName() ).c_str() ); } else if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { WriteImage( direct->GetOutput(), ( outputOption->GetFunction( 0 )->GetParameter() ).c_str() ); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { WriteImage( direct->GetOutput( 1 ), ( outputOption->GetFunction( 0 )->GetParameter( 1 ) ).c_str() ); } } } if( segmentationImageOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { std::string inputFile = segmentationImageOption->GetFunction( 0 )->GetName(); ReadImage( segmentationImage, inputFile.c_str() ); } else if( segmentationImageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { } if( outputOption && outputOption->GetNumberOfFunctions() > 1 ) { WriteImage( direct->GetOutput( 1 ), ( outputOption->GetFunction( 1 )->GetName() ).c_str() ); } return EXIT_SUCCESS; } void KellyKapowskiInitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, DiReCT tries to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "image-dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "A segmentation image must be supplied labeling the gray" ) + std::string( "and white matters. Ddefault values = 2 and 3, respectively." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "segmentation-image" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "imageFilename" ); option->SetUsageOption( 1, "[imageFilename,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "In addition to the segmentation image, a gray matter " ) + std::string( "probability image can be used. If no such image is " ) + std::string( "supplied, one is created using the segmentation image " ) + std::string( "and a variance of 1.0 mm." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "gray-matter-probability-image" ); option->SetShortName( 'g' ); option->SetUsageOption( 0, "imageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "In addition to the segmentation image, a white matter " ) + std::string( "probability image can be used. If no such image is " ) + std::string( "supplied, one is created using the segmentation image " ) + std::string( "and a variance of 1.0 mm." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "white-matter-probability-image" ); option->SetShortName( 'w' ); option->SetUsageOption( 0, "imageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Convergence is determined by fitting a line to the normalized energy " ) + std::string( "profile of the last N iterations (where N is specified by " ) + std::string( "the window size) and determining the slope which is then " ) + std::string( "compared with the convergence threshold." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "convergence" ); option->SetShortName( 'c' ); option->SetUsageOption( 0, "[,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Provides a prior constraint on the final thickness measurement. Default = 10 mm." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "thickness-prior-estimate" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "thicknessPriorEstimate" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "An image containing spatially varying prior thickness values." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "thickness-prior-image" ); option->SetShortName( 'a' ); option->SetUsageOption( 0, "thicknessPriorFileName" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Gradient step size for the optimization. Default = 0.025." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "gradient-step" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "stepSize" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Defines the Gaussian smoothing of the hit and total images. Default = 1.0." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "smoothing-variance" ); option->SetShortName( 'l' ); option->SetUsageOption( 0, "variance" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Defines the Gaussian smoothing of the velocity field (default = 1.5)." ) + std::string( "If the b-spline smoothing option is chosen, then this " ) + std::string( "defines the isotropic mesh spacing for the smoothing spline (default = 15)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "smoothing-velocity-field-parameter" ); option->SetShortName( 'm' ); option->SetUsageOption( 0, "variance" ); option->SetUsageOption( 1, "isotropicMeshSpacing" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( " Sets the option for B-spline smoothing of the velocity field." ) + std::string( "Default = false." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-bspline-smoothing" ); option->SetShortName( 'b' ); option->SetUsageOption( 0, "1/(0)" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Number of compositions of the diffeomorphism per iteration. Default = 10." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "number-of-integration-points" ); option->SetShortName( 'n' ); option->SetUsageOption( 0, "numberOfPoints" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Maximum number of iterations for estimating the invert displacement field. Default = 20." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "maximum-number-of-invert-displacement-field-iterations" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "numberOfIterations" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The output consists of a thickness map defined in the " ) + std::string( "segmented gray matter. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "imageFileName" ); option->SetUsageOption( 1, "[imageFileName,warpedWhiteMatterImageFileName]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int KellyKapowski( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "KellyKapowski" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "DiReCT is a registration based estimate of cortical " ) + std::string( "thickness. It was published in S. R. Das, B. B. " ) + std::string( "Avants, M. Grossman, and J. C. Gee, Registration based " ) + std::string( "cortical thickness measurement, Neuroimage 2009, " ) + std::string( "45:867--879." ); parser->SetCommandDescription( commandDescription ); KellyKapowskiInitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc == 1 ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Get dimensionality unsigned int dimension = 3; itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "image-dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() > 0 ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { // Read in the first intensity image to get the image dimension. std::string filename; itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "input-image" ); if( imageOption && imageOption->GetNumberOfFunctions() > 0 ) { if( imageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { filename = imageOption->GetFunction( 0 )->GetParameter( 0 ); } else { filename = imageOption->GetFunction( 0 )->GetName(); } } else { std::cout << "No input images were specified. Specify an input " << " segmentation image with the -s option" << std::endl; return EXIT_FAILURE; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); dimension = imageIO->GetNumberOfDimensions(); } switch( dimension ) { case 2: { return DiReCT<2>( parser ); } break; case 3: { return DiReCT<3>( parser ); } break; case 4: { return DiReCT<4>( parser ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/KellySlater.cxx000066400000000000000000001113671311104306400172170ustar00rootroot00000000000000 #include "antsUtilities.h" #include "antsAllocImage.h" #include #include "itkVectorIndexSelectionCastImageFilter.h" #include "itkImageRegionIteratorWithIndex.h" #include "vnl/algo/vnl_determinant.h" #include "itkWarpImageFilter.h" #include "itkWarpImageMultiTransformFilter.h" #include "itkDisplacementFieldFromMultiTransformFilter.h" #include "itkFastMarchingUpwindGradientImageFilter.h" #include "itkFastMarchingUpwindGradientImageFilter.h" #include "itkImageFileWriter.h" #include "itkANTSImageRegistrationOptimizer.h" #include "vnl/algo/vnl_determinant.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkCovariantVector.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkVectorCurvatureAnisotropicDiffusionImageFilter.h" #include "ReadWriteData.h" #include "itkSignedMaurerDistanceMapImageFilter.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkCentralDifferenceImageFunction.h" #include "itkSurfaceCurvatureBase.h" #include "itkSurfaceImageCurvature.h" namespace ants { template typename TImage::Pointer GetVectorComponent(typename TField::Pointer field, unsigned int index) { // Initialize the Moving to the displacement field typedef TImage ImageType; typename ImageType::Pointer sfield = AllocImage(field); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( field, field->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { typename TField::PixelType v1 = vfIter.Get(); sfield->SetPixel(vfIter.GetIndex(), v1[index]); } return sfield; } template typename TImage::Pointer MaurerDistanceMap( typename TImage::PixelType pixlo, typename TImage::PixelType pixhi, typename TImage::Pointer input) { // std::cout << " DDMap " << std::endl; typedef TImage ImageType; typedef itk::SignedMaurerDistanceMapImageFilter< ImageType, ImageType> FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetSquaredDistance(false); // filter->InputIsBinaryOn(); filter->SetUseImageSpacing(true); filter->SetBackgroundValue(0); filter->SetInput(BinaryThreshold(pixlo, pixhi, pixhi, input) ); filter->Update(); // WriteImage(filter->GetOutput(),"temp1.nii"); return filter->GetOutput(); } template typename TImage::Pointer SmoothImage(typename TImage::Pointer image, double sig) { // find min value typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter(image, image->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { typename TImage::PixelType v1 = vfIter.Get(); if( vnl_math_isnan(v1) ) { vfIter.Set(0); } } typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(sig); filter->SetUseImageSpacingOn(); filter->SetMaximumError(.01f); filter->SetInput(image); filter->Update(); typename TImage::Pointer out = filter->GetOutput(); return out; } template void SmoothDeformation(typename TImage::Pointer vectorimage, double sig) { enum { ImageDimension = TImage::ImageDimension }; typedef typename TImage::PixelType RealType; typedef itk::Vector VectorType; typedef itk::Image ImageType; typename ImageType::Pointer subimgx = GetVectorComponent(vectorimage, 0); subimgx = SmoothImage(subimgx, sig); typename ImageType::Pointer subimgy = GetVectorComponent(vectorimage, 1); subimgy = SmoothImage(subimgy, sig); typename ImageType::Pointer subimgz = GetVectorComponent(vectorimage, 2); subimgz = SmoothImage(subimgz, sig); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType Iterator( vectorimage, vectorimage->GetLargestPossibleRegion().GetSize() ); Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { VectorType vec; vec[0] = subimgx->GetPixel(Iterator.GetIndex() ); vec[1] = subimgy->GetPixel(Iterator.GetIndex() ); vec[2] = subimgz->GetPixel(Iterator.GetIndex() ); Iterator.Set(vec); ++Iterator; } return; } // this has to have never been called because it doesn't actually // copy anything template typename TImage::Pointer CopyImage(TDisplacementField* field ) { enum { ImageDimension = TImage::ImageDimension }; // unsigned int row=0; // unsigned int col=0; typedef typename TImage::PixelType PixelType; typedef itk::Image RealImageType; typename RealImageType::RegionType m_JacobianRegion; typename RealImageType::Pointer m_RealImage = ITK_NULLPTR; m_RealImage = AllocImage(field, 0); return m_RealImage; } template typename TImage::Pointer LabelSurface(typename TImage::PixelType foreground, typename TImage::PixelType newval, typename TImage::Pointer input, double distthresh ) { std::cout << " Label Surf " << std::endl; typedef TImage ImageType; enum { ImageDimension = ImageType::ImageDimension }; // ORIENTATION ALERT: Original code set origin & spacing from // examplar without also setting directions. typename ImageType::Pointer Image = AllocImage(input, 0.0); typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for( int j = 0; j < ImageDimension; j++ ) { rad[j] = (unsigned int)(distthresh + 0.5); } iteratorType GHood(rad, input, input->GetLargestPossibleRegion() ); GHood.GoToBegin(); // std::cout << " foreg " << (int) foreground; while( !GHood.IsAtEnd() ) { typename TImage::PixelType p = GHood.GetCenterPixel(); typename TImage::IndexType ind = GHood.GetIndex(); typename TImage::IndexType ind2; if( p == foreground ) { bool atedge = false; for( unsigned int i = 0; i < GHood.Size(); i++ ) { ind2 = GHood.GetIndex(i); double dist = 0.0; for( int j = 0; j < ImageDimension; j++ ) { dist += (double)(ind[j] - ind2[j]) * (double)(ind[j] - ind2[j]); } dist = sqrt(dist); if( GHood.GetPixel(i) != foreground && dist < distthresh ) { atedge = true; } } if( atedge && p == foreground ) { Image->SetPixel(ind, newval); } else { Image->SetPixel(ind, 0); } } ++GHood; } return Image; } template typename TField::Pointer LaplacianGrad(typename TImage::Pointer wm, typename TImage::Pointer gm, double sig) { typedef typename TImage::IndexType IndexType; IndexType ind; typedef TImage ImageType; typedef TField GradientImageType; typedef itk::GradientRecursiveGaussianImageFilter GradientImageFilterType; typedef typename GradientImageFilterType::Pointer GradientImageFilterPointer; typename TField::Pointer sfield = AllocImage(wm); typename TImage::Pointer laplacian = SmoothImage(wm, 3); laplacian->FillBuffer(0); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType Iterator( wm, wm->GetLargestPossibleRegion().GetSize() ); Iterator.GoToBegin(); // initialize L(wm)=1, L(gm)=0.5, else 0 while( !Iterator.IsAtEnd() ) { ind = Iterator.GetIndex(); if( wm->GetPixel(ind) ) { laplacian->SetPixel(ind, 1); } else { laplacian->SetPixel(ind, 0.); } ++Iterator; } // smooth and then reset the values for( unsigned int iterations = 0; iterations < 100; iterations++ ) { laplacian = SmoothImage(laplacian, sqrt(sig) ); Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { ind = Iterator.GetIndex(); if( wm->GetPixel(ind) ) { laplacian->SetPixel(ind, 1); } else if( gm->GetPixel(ind) == 0 && wm->GetPixel(ind) == 0 ) { laplacian->SetPixel(ind, 0.); } ++Iterator; } } GradientImageFilterPointer filter = GradientImageFilterType::New(); filter->SetInput( laplacian ); filter->SetSigma(sig); filter->Update(); return filter->GetOutput(); } template typename TField::Pointer ExpDiffMap(typename TField::Pointer velofield, typename TImage::Pointer wm, double sign, unsigned int numtimepoints ) { typedef TImage ImageType; typedef TField DisplacementFieldType; typedef typename TField::PixelType PixelType; typename TField::PixelType zero, disp; enum { ImageDimension = TImage::ImageDimension }; disp.Fill(0); zero.Fill(0); typename DisplacementFieldType::Pointer incrfield = AllocImage(velofield, zero); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType Iterator( wm, wm->GetLargestPossibleRegion().GetSize() ); Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { incrfield->SetPixel(Iterator.GetIndex(), velofield->GetPixel(Iterator.GetIndex() ) * sign); ++Iterator; } // generate phi typedef itk::MatrixOffsetTransformBase AffineTransformType; typedef itk::DisplacementFieldFromMultiTransformFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); warper->SetOutputParametersFromImage(velofield ); warper->DetermineFirstDeformNoInterp(); unsigned int ttiter = 0; while( ttiter < numtimepoints ) // 10 time integration points { ttiter++; warper->PushBackDisplacementFieldTransform(incrfield); } warper->Update(); return warper->GetOutput(); } template typename TField::Pointer DiReCTCompose(typename TField::Pointer velofield, typename TField::Pointer diffmap ) { typedef typename TField::PixelType PixelType; typename TField::PixelType zero, disp; enum { ImageDimension = TImage::ImageDimension }; disp.Fill(0); zero.Fill(0); typedef itk::MatrixOffsetTransformBase AffineTransformType; typedef itk::DisplacementFieldFromMultiTransformFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); warper->SetOutputParametersFromImage( velofield ); warper->DetermineFirstDeformNoInterp(); warper->PushBackDisplacementFieldTransform(diffmap); warper->PushBackDisplacementFieldTransform(velofield); warper->Update(); return warper->GetOutput(); } template void InvertField( typename TField::Pointer field, typename TField::Pointer inverseFieldIN, double weight = 1.0, double toler = 0.1, int maxiter = 20, bool /* print */ = false) { enum { ImageDimension = TImage::ImageDimension }; typedef TField DisplacementFieldType; typedef typename TField::Pointer DisplacementFieldPointer; typedef typename TField::PixelType VectorType; typedef TImage ImageType; typedef typename TImage::Pointer ImagePointer; double mytoler = toler; unsigned int mymaxiter = maxiter; VectorType zero; zero.Fill(0); // if (this->GetElapsedIterations() < 2 ) maxiter=10; ImagePointer realImage = AllocImage(field); typedef typename DisplacementFieldType::PixelType VectorType; typedef typename DisplacementFieldType::IndexType IndexType; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::ANTSImageRegistrationOptimizer ROType; typename ROType::Pointer m_MFR = ROType::New(); DisplacementFieldPointer inverseField = AllocImage(field, zero); DisplacementFieldPointer lagrangianInitCond = AllocImage(field); DisplacementFieldPointer eulerianInitCond = AllocImage(field); typedef typename DisplacementFieldType::SizeType SizeType; SizeType size = field->GetLargestPossibleRegion().GetSize(); typename ImageType::SpacingType spacing = field->GetSpacing(); unsigned long npix = 1; for( int j = 0; j < ImageDimension; j++ ) // only use in-plane spacing { npix *= field->GetLargestPossibleRegion().GetSize()[j]; } double max = 0; Iterator iter( field, field->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { IndexType index = iter.GetIndex(); VectorType vec1 = iter.Get(); VectorType newvec = vec1 * weight; lagrangianInitCond->SetPixel(index, newvec); inverseField->SetPixel(index, inverseFieldIN->GetPixel(index) ); double mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += newvec[jj] * newvec[jj]; } mag = sqrt(mag); if( mag > max ) { max = mag; } } eulerianInitCond->FillBuffer(zero); double scale = (1.) / max; if( scale > 1. ) { scale = 1.0; } // double initscale=scale; Iterator vfIter( inverseField, inverseField->GetLargestPossibleRegion() ); // int num=10; // for (int its=0; its subpix && meandif > subpix*0.1 && badct < 2 )//&& ct < 20 && denergy > 0) // double length=0.0; double stepl = 2.; double epsilon = (double)size[0] / 256; if( epsilon > 1 ) { epsilon = 1; } while( difmag > mytoler && ct 0.001 ) { meandif = 0.0; // this field says what position the eulerian field should contain in the E domain m_MFR->ComposeDiffs(inverseField, lagrangianInitCond, eulerianInitCond, 1); difmag = 0.0; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { IndexType index = vfIter.GetIndex(); VectorType update = eulerianInitCond->GetPixel(index); double mag = 0; for( int j = 0; j < ImageDimension; j++ ) { update[j] *= (-1.0); mag += (update[j] / spacing[j]) * (update[j] / spacing[j]); } mag = sqrt(mag); meandif += mag; if( mag > difmag ) { difmag = mag; } // if (mag < 1.e-2) update.Fill(0); eulerianInitCond->SetPixel(index, update); realImage->SetPixel(index, mag); } meandif /= (double)npix; if( ct == 0 ) { epsilon = 0.75; } else { epsilon = 0.5; } stepl = difmag * epsilon; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { double val = realImage->GetPixel(vfIter.GetIndex() ); VectorType update = eulerianInitCond->GetPixel(vfIter.GetIndex() ); if( val > stepl ) { update = update * (stepl / val); } VectorType upd = vfIter.Get() + update * (epsilon); vfIter.Set(upd); } ct++; } for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { IndexType index = iter.GetIndex(); inverseFieldIN->SetPixel(index, inverseField->GetPixel(index) ); } // std::cout <<" difmag " << difmag << ": its " << ct << " len " << m_MFR->MeasureDeformation(inverseField ) << // std::endl; return; } template int LaplacianThicknessExpDiff2(int argc, char *argv[]) { int argct = 2; std::string segfn = std::string(argv[argct]); argct++; std::string wfn = std::string(argv[argct]); argct++; std::string gfn = std::string(argv[argct]); argct++; std::string outname = std::string(argv[argct]); argct++; unsigned int numtimepoints = 10; typedef double RealType; RealType gradstep = (RealType)(-1.0) * 0.5; // (ImageDimension-1); if( argc > argct ) { gradstep = atof(argv[argct]) * (-1.0); } gradstep *= 1.0 / (RealType)numtimepoints * 10; argct++; unsigned int alltheits = 50; if( argc > argct ) { alltheits = atoi(argv[argct]); } argct++; RealType thickprior = 6.0; if( argc > argct ) { thickprior = atof(argv[argct]); } argct++; // bool useCurvaturePrior = false; // if( argc > argct ) // { // useCurvaturePrior = atoi(argv[argct]); // } argct++; RealType smoothingsigma = 1.5; if( argc > argct ) { smoothingsigma = atof(argv[argct]); } argct++; // bool useEuclidean = true; // if( argc > argct ) // { // useEuclidean = atoi(argv[argct]); // } argct++; std::cout << " smooth " << smoothingsigma << " thp " << thickprior << " gs " << gradstep << std::endl; typedef RealType PixelType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::Image ImageType; typedef typename ImageType::IndexType IndexType; typedef itk::ANTSImageRegistrationOptimizer ROType; typename ROType::Pointer m_MFR = ROType::New(); typename ImageType::Pointer segmentationimage; ReadImage(segmentationimage, segfn.c_str() ); typename ImageType::Pointer wm; ReadImage(wm, wfn.c_str() ); typename ImageType::DirectionType omat = wm->GetDirection(); typename ImageType::DirectionType fmat = wm->GetDirection(); fmat.SetIdentity(); std::cout << " Setting Identity Direction " << fmat << std::endl; wm->SetDirection(fmat); typename ImageType::Pointer totalimage; ReadImage(totalimage, wfn.c_str() ); totalimage->SetDirection(fmat); typename ImageType::Pointer hitimage; ReadImage(hitimage, wfn.c_str() ); hitimage->SetDirection(fmat); typename ImageType::Pointer gm; ReadImage(gm, gfn.c_str() ); gm->SetDirection(fmat); wm->SetDirection(fmat); segmentationimage->SetDirection(fmat); typename DisplacementFieldType::Pointer lapgrad; typename ImageType::Pointer gmb = BinaryThreshold(2, 2, 1, segmentationimage); // fixme typename ImageType::Pointer wmb = BinaryThreshold(3, 3, 1, segmentationimage); // fixme typename ImageType::Pointer laplacian = SmoothImage(wm, smoothingsigma); lapgrad = LaplacianGrad(wmb, gmb, 1); VectorType zero; zero.Fill(0); typename DisplacementFieldType::Pointer corrfield = AllocImage(wm, zero); typename DisplacementFieldType::Pointer incrfield = AllocImage(wm, zero); typename DisplacementFieldType::Pointer invfield = AllocImage(wm, zero); typename DisplacementFieldType::Pointer incrinvfield = AllocImage(wm, zero); typename DisplacementFieldType::Pointer velofield = AllocImage(wm, zero); // LabelSurface(typename TImage::PixelType foreground, // typename TImage::PixelType newval, typename TImage::Pointer input, RealType distthresh ) RealType distthresh = 1.5; typename ImageType::Pointer wmgrow = Morphological(wmb, 0, 1, 1); typename ImageType::Pointer bsurf = LabelSurface(1, 1, wmgrow, distthresh); // or wmb ? typename ImageType::Pointer speedprior = ITK_NULLPTR; WriteImage(bsurf, "surf.nii.gz"); // typename RealTypeImageType::Pointer distfromboundary = // typename ImageType::Pointer surf=MaurerDistanceMap(0.5,1.e9,bsurf); // surf= SmoothImage(surf,3); typename ImageType::Pointer finalthickimage = BinaryThreshold(3, 3, 1, segmentationimage); // fixme gmb = BinaryThreshold(2, 3, 1, segmentationimage); // fixme typename ImageType::Pointer gmgrow = Morphological(gmb, 1, 1, 1); typename ImageType::Pointer gmsurf = LabelSurface(1, 1, gmgrow, distthresh); // or wmb ? // WriteImage(gmsurf,"surfdefgm.nii.gz"); // WriteImage(bsurf,"surfdefwm.nii.gz"); typedef DisplacementFieldType TimeVaryingVelocityFieldType; typedef typename DisplacementFieldType::PointType DPointType; typedef itk::VectorLinearInterpolateImageFunction DefaultInterpolatorType; typename DefaultInterpolatorType::Pointer vinterp = DefaultInterpolatorType::New(); vinterp->SetInputImage(lapgrad); typedef itk::LinearInterpolateImageFunction ScalarInterpolatorType; typename ScalarInterpolatorType::Pointer ginterp = ScalarInterpolatorType::New(); typename ScalarInterpolatorType::Pointer winterp = ScalarInterpolatorType::New(); winterp->SetInputImage(wm); ginterp->SetInputImage(gm); DPointType pointIn1; DPointType pointIn2; typename DefaultInterpolatorType::ContinuousIndexType vcontind; DPointType pointIn3; typedef itk::ImageRegionIteratorWithIndex IteratorType; typedef itk::ImageRegionIteratorWithIndex VIteratorType; VIteratorType VIterator( lapgrad, lapgrad->GetLargestPossibleRegion().GetSize() ); VIterator.GoToBegin(); while( !VIterator.IsAtEnd() ) { // the velocity field solution value VectorType vec = VIterator.Get(); RealType mag = 0; for( unsigned dd = 0; dd < ImageDimension; dd++ ) { mag += vec[dd] * vec[dd]; } mag = sqrt(mag); // if (mag > 0) vec=vec/mag; VIterator.Set( (vec) * gradstep); ++VIterator; } // m_MFR->SmoothDisplacementFieldGauss(lapgrad,1.7); std::cout << " Scaling done " << std::endl; typename ImageType::Pointer thickimage = laplacian; VectorType disp; VectorType incdisp; disp.Fill(0.0); incdisp.Fill(0.0); IteratorType Iterator( wm, wm->GetLargestPossibleRegion().GetSize() ); RealType totalerr = 1.e8, lasterr = 1.e10; unsigned its = 0; wmgrow->FillBuffer(0); RealType dmag = 0; RealType thicknesserror = 0; unsigned long thickerrct = 0; unsigned int badct = 0; RealType thickoffset = 0; bool checknans = true; while( its < alltheits && badct < 4 ) { its++; if( totalerr > lasterr ) { badct++; std::cout << " badct " << badct << std::endl; } else { badct = 0; } lasterr = totalerr; // Optimization Error initialized for this iteration totalerr = 0; incrfield->FillBuffer(zero); incrfield->FillBuffer(zero); incrinvfield->FillBuffer(zero); // generate phi // corrfield->FillBuffer(zero); invfield->FillBuffer(zero); unsigned int ttiter = 0; thickimage->FillBuffer(0); hitimage->FillBuffer(0); totalimage->FillBuffer(0); thicknesserror = 0; thickerrct = 1; bool debug = false; bool spatprior = false; typename ImageType::Pointer priorim = ITK_NULLPTR; if( speedprior ) { spatprior = true; priorim = speedprior; } typename ImageType::Pointer wpriorim = ITK_NULLPTR; RealType origthickprior = thickprior; while( ttiter < numtimepoints ) // N time integration points { // m_MFR->Compose(incrinvfield,invfield,NULL); m_MFR->ComposeDiffs(invfield, incrinvfield, invfield, 1); if( debug ) { std::cout << " exp " << std::endl; } // Integrate the negative velocity field to generate diffeomorphism corrfield step 3(a) // corrfield=ExpDiffMap( velofield, wm, -1, numtimepoints-ttiter); // std::cout << " corrf len " << m_MFR->MeasureDeformation( corrfield ) << std::endl; if( debug ) { std::cout << " gmdef " << std::endl; } typename ImageType::Pointer gmdef = gm; // m_MFR->WarpImageBackward(gm,corrfield); totalerr = 0; typename ImageType::Pointer surfdef = m_MFR->WarpImageBackward(wm, invfield); if( debug ) { std::cout << " thkdef " << std::endl; } typename ImageType::Pointer thkdef = m_MFR->WarpImageBackward(thickimage, invfield); if( debug ) { std::cout << " thindef " << std::endl; } typename ImageType::Pointer thindef = m_MFR->WarpImageBackward(bsurf, invfield); if( spatprior ) { wpriorim = m_MFR->WarpImageBackward(priorim, invfield); } typedef DisplacementFieldType GradientImageType; typedef itk::GradientRecursiveGaussianImageFilter GradientImageFilterType; typedef typename GradientImageFilterType::Pointer GradientImageFilterPointer; GradientImageFilterPointer gfilter = GradientImageFilterType::New(); gfilter->SetInput( surfdef ); gfilter->SetSigma( smoothingsigma ); gfilter->Update(); typename DisplacementFieldType::Pointer lapgrad2 = gfilter->GetOutput(); // this is the "speed" image typename ImageType::Pointer speed_image = CopyImage(invfield); IteratorType xxIterator( speed_image, speed_image->GetLargestPossibleRegion().GetSize() ); xxIterator.GoToBegin(); RealType maxlapgrad2mag = 0; while( !xxIterator.IsAtEnd() ) { typename ImageType::IndexType speedindex = xxIterator.GetIndex(); if( segmentationimage->GetPixel(speedindex) == 2 ) // fixme { thickprior = origthickprior; VectorType wgradval = lapgrad2->GetPixel(speedindex); RealType wmag = 0; for( unsigned kq = 0; kq < ImageDimension; kq++ ) { wmag += wgradval[kq] * wgradval[kq]; } if( fabs(wmag) < 1.e-6 ) { wmag = 0; } wmag = sqrt(wmag); if( checknans ) { if( vnl_math_isnan(wmag) || vnl_math_isinf(wmag) || wmag == 0 ) { wgradval.Fill(0); lapgrad2->SetPixel(speedindex, wgradval); wmag = 0; } else { lapgrad2->SetPixel(speedindex, wgradval / wmag); } } totalerr += fabs(surfdef->GetPixel(speedindex) - gmdef->GetPixel(speedindex) ); // RealType thkval=thkdef->GetPixel(speedindex); // RealType thkval=finalthickimage->GetPixel(speedindex); // RealType fval=1; //(thickprior-thkval); // if ( fval > 0 ) fval=1; else fval=-1; // speed function here IMPORTANT!! RealType dd = (surfdef->GetPixel(speedindex) - gmdef->GetPixel(speedindex) ) * gradstep; dd *= gm->GetPixel(speedindex); if( checknans ) { if( vnl_math_isnan(dd) || vnl_math_isinf(dd) ) { dd = 0; } } speed_image->SetPixel(speedindex, dd); if( wmag * dd > maxlapgrad2mag ) { maxlapgrad2mag = wmag * dd; } } else { speed_image->SetPixel(speedindex, 0); } ++xxIterator; } if( maxlapgrad2mag < 1.e-4 ) { maxlapgrad2mag = 1.e9; } if( ttiter == numtimepoints - 1 ) { if( ImageDimension == 2 ) { WriteImage(surfdef, "surfdef.nii.gz"); } if( ImageDimension == 2 ) { WriteImage(thindef, "thindef.nii.gz"); } if( ImageDimension == 2 ) { WriteImage(gmdef, "gmdef.nii.gz"); } if( ImageDimension == 2 ) { WriteImage(thkdef, "thick2.nii.gz"); } } /* Now that we have the gradient image, we need to visit each voxel and compute objective function */ // std::cout << " maxlapgrad2mag " << maxlapgrad2mag << std::endl; Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { typename DisplacementFieldType::IndexType velind = Iterator.GetIndex(); VectorType wgradval = lapgrad2->GetPixel(velind); // *5.0/(maxlapgrad2mag*(RealType)numtimepoints); disp = wgradval * speed_image->GetPixel(velind); incrfield->SetPixel(velind, incrfield->GetPixel(velind) + disp); if( ttiter == 0 ) // make euclidean distance image { dmag = 0; disp = corrfield->GetPixel(velind); for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { dmag += disp[jj] * disp[jj]; } RealType bval = bsurf->GetPixel(velind); if( checknans ) { if( vnl_math_isnan(dmag) || vnl_math_isinf(dmag) ) { dmag = 0; } if( vnl_math_isnan(bval) || vnl_math_isinf(bval) ) { bval = 0; } } /** Change 2-26-2010 = incoporate gm prob in length ... */ dmag = sqrt(dmag) * bval; // *gm->GetPixel(velind); thickimage->SetPixel(velind, dmag); totalimage->SetPixel(velind, dmag); hitimage->SetPixel(velind, bval); } else if( segmentationimage->GetPixel(velind) == 2 ) // fixme { RealType thkval = thkdef->GetPixel(velind); RealType putval = thindef->GetPixel(velind); hitimage->SetPixel(velind, hitimage->GetPixel(velind) + putval); totalimage->SetPixel(velind, totalimage->GetPixel(velind) + thkval); } ++Iterator; } Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { IndexType velind = Iterator.GetIndex(); bool shouldbezero = false; if( segmentationimage->GetPixel(velind) == 0 ) { shouldbezero = true; } if( !shouldbezero ) { if( bsurf->GetPixel(velind) == 0 && gmsurf->GetPixel(velind) == 0 && segmentationimage->GetPixel(velind) != 2 ) { shouldbezero = true; } } if( shouldbezero ) { velofield->SetPixel(velind, zero); corrfield->SetPixel(velind, zero); invfield->SetPixel(velind, zero); } incrinvfield->SetPixel(velind, velofield->GetPixel(velind) ); ++Iterator; } if( ttiter == 0 ) { corrfield->FillBuffer(zero); } InvertField( invfield, corrfield, 1.0, 0.1, 20, true); InvertField( corrfield, invfield, 1.0, 0.1, 20, true); // InvertField( invfield, corrfield, 1.0,0.1,20,true); // InvertField( corrfield, invfield, 1.0,0.1,20,true); ttiter++; } Iterator.GoToBegin(); RealType maxth = 0; while( !Iterator.IsAtEnd() ) { typename DisplacementFieldType::IndexType velind = Iterator.GetIndex(); // increment velocity field at every voxel v = v + u, step 4 velofield->SetPixel(Iterator.GetIndex(), velofield->GetPixel(Iterator.GetIndex() ) + incrfield->GetPixel(Iterator.GetIndex() ) ); RealType hitval = hitimage->GetPixel(velind); RealType thkval = 0; if( hitval > 0.001 ) /** potential source of problem 2 -- this value could be smaller ... */ { thkval = totalimage->GetPixel(velind) / hitval - thickoffset; } if( thkval > 10 ) { std::cout << "thkval " << thkval << " hitval " << hitval << " total " << totalimage->GetPixel(velind) << std::endl; } if( thkval < 0 ) { thkval = 0; } if( segmentationimage->GetPixel(velind) == 2 ) { finalthickimage->SetPixel(velind, thkval); } else { finalthickimage->SetPixel(velind, 0); } if( thkval > maxth ) { maxth = thkval; } if( finalthickimage->GetPixel(velind) > thickprior ) { finalthickimage->SetPixel(velind, thickprior ); } ++Iterator; } if( debug ) { std::cout << " now smooth " << std::endl; } m_MFR->SmoothDisplacementFieldGauss(velofield, smoothingsigma); WriteImage(corrfield, "corrfield.nii.gz"); WriteImage(invfield, "invfield.nii.gz"); // std::string velofieldname = outname + "velofield"; // WriteDisplacementField(velofield,velofieldname.c_str()); // std::string incrfieldname = outname + "incrfield"; // WriteDisplacementField(incrfield,incrfieldname.c_str()); // std::string tname = outname + "dork1.nii.gz"; // WriteImage(hitimage,tname.c_str()); // tname = outname + "dork2.nii.gz"; // WriteImage(totalimage,tname.c_str()); if( thickerrct == 0 ) { thickerrct = 1; } std::cout << " error " << totalerr << " at it " << its << " th-err " << thicknesserror / (RealType)thickerrct << " max thick " << maxth << std::endl; // std::string sulcthickname =outname + "sulcthick.nii"; // if (ImageDimension==2) WriteJpg(finalthickimage,"thick.jpg"); // std::string velofieldname = outname + "velofield"; // WriteDisplacementField(velofield,velofieldname.c_str()); if( debug ) { std::cout << "outside it " << its << std::endl; } // std::cin.get(); } finalthickimage->SetDirection(omat); WriteImage(finalthickimage, outname.c_str() ); finalthickimage->SetDirection(fmat); return 0; thickimage->FillBuffer(0); typename ImageType::Pointer thkdef = m_MFR->WarpImageBackward(finalthickimage, invfield); Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { RealType tt1 = finalthickimage->GetPixel(Iterator.GetIndex() ); RealType tt = thkdef->GetPixel(Iterator.GetIndex() ); if( tt1 > tt ) { tt = tt1; } thickimage->SetPixel(Iterator.GetIndex(), tt); ++Iterator; } std::string thickname = outname; thickimage->SetDirection(omat); WriteImage(thickimage, thickname.c_str() ); return 0; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int KellySlater( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "KellySlater" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 6 ) { std::cout << "Usage: " << argv[0] << " ImageDimension Segmentation.nii.gz WMProb.nii.gz GMProb.nii.gz Out.nii {GradStep-1-2D,2-3D} {#Its-~50} {ThickPriorValue-6} {Bool-use-curvature-prior} {smoothing} {BoolUseEuclidean?}" << std::endl; std::cout << " this is a kind of binary image registration thing with diffeomorphisms " << std::endl; std::cout << " Segmentation.nii.gz -- should contain the value 3 where WM exists and the value 2 where GM exists " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } unsigned int dim = atoi(argv[1]); std::cout << " dim " << dim << std::endl; switch( dim ) { case 2: { LaplacianThicknessExpDiff2<2>(argc, argv); } break; case 3: { LaplacianThicknessExpDiff2<3>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/LabelClustersUniquely.cxx000066400000000000000000000132361311104306400212600ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include "itkDiscreteGaussianImageFilter.h" // RecursiveAverageImages img1 img2 weightonimg2 outputname // We divide the 2nd input image by its mean and add it to the first // input image with weight 1/n. // The output overwrites the 1st img with the sum. #include #include #include #include "vnl/vnl_vector.h" #include "itkMinimumMaximumImageFilter.h" #include "itkConnectedComponentImageFilter.h" #include "itkRelabelComponentImageFilter.h" #include "itkLabelStatisticsImageFilter.h" #include "itkCastImageFilter.h" #include "ReadWriteData.h" namespace ants { template int LabelUniquely(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typedef int ULPixelType; typedef itk::Image labelimagetype; typedef itk::CastImageFilter CastFilterType; typedef itk::CastImageFilter< labelimagetype, ImageType> CastFilterType2; typedef itk::ConnectedComponentImageFilter FilterType; typedef itk::RelabelComponentImageFilter RelabelType; // want the average value in each cluster as defined by the mask and the value thresh and the clust thresh if( argc < 2 ) { std::cout << "missing 1st filename" << std::endl; throw; } if( argc < 3 ) { std::cout << "missing 2nd filename" << std::endl; throw; } if( argc < 4 ) { std::cout << "missing cluster thresholod" << std::endl; throw; } bool fullyConnected = false; if( argc > 5 ) { fullyConnected = static_cast< bool >( atoi( argv[4] ) ); } std::string fn1 = std::string(argv[1]); float clusterthresh = atof(argv[3]); typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typename FilterType::Pointer filter = FilterType::New(); typename RelabelType::Pointer relabel = RelabelType::New(); typename CastFilterType::Pointer castInput = CastFilterType::New(); castInput->SetInput(image1); filter->SetInput( castInput->GetOutput() ); filter->SetFullyConnected( fullyConnected ); // old default was false relabel->SetInput( filter->GetOutput() ); relabel->SetMinimumObjectSize( (unsigned int) clusterthresh ); try { relabel->Update(); } catch( itk::ExceptionObject & excep ) { std::cout << "Relabel: exception caught !" << std::endl; std::cout << excep << std::endl; } // float maximum=relabel->GetNumberOfObjects(); typename CastFilterType2::Pointer castRegions = CastFilterType2::New(); castRegions->SetInput( relabel->GetOutput() ); castRegions->Update(); WriteImage( castRegions->GetOutput() , argv[2] ); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int LabelClustersUniquely( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "LabelClustersUniquely" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << std::endl; std::cout << argv[0] << " ImageDimension clustersin.hdr labeledclustersout.hdr sizethresh optionalBoolFullyConnected" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi(argv[1]) ) { case 2: { return LabelUniquely<2>(argc, argv + 1); } break; case 3: { return LabelUniquely<3>(argc, argv + 1); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/LabelGeometryMeasures.cxx000066400000000000000000000331351311104306400212200ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include #include "ReadWriteData.h" #include "itkAffineTransform.h" #include "itkCSVArray2DDataObject.h" #include "itkCSVArray2DFileReader.h" #include "itkCSVNumericObjectFileWriter.h" #include "itkImage.h" #include "itkLabelGeometryImageFilter.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkResampleImageFilter.h" #include "itkTransformFileWriter.h" #include "itkLabelPerimeterEstimationCalculator.h" #include "itkLabelMap.h" #include "itkLabelImageToLabelMapFilter.h" #include "itkShapeLabelMapFilter.h" #include "itkShapeLabelObject.h" #include #include #include #include #include #include #include namespace ants { template int LabelGeometryMeasures( int argc, char * argv[] ) { typedef unsigned int LabelType; typedef itk::Image LabelImageType; typedef float RealType; typedef itk::Image RealImageType; typename LabelImageType::Pointer labelImage = LabelImageType::New(); ReadImage( labelImage, argv[2] ); typename RealImageType::Pointer intensityImage = RealImageType::New(); bool intensityImageUsed = false; if( argc > 3 ) { try { ReadImage( intensityImage, argv[3] ); intensityImageUsed = true; } catch( ... ) { } } bool outputCSVFormat = false; if( argc > 4 ) { outputCSVFormat = true; } typedef itk::LabelGeometryImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( labelImage ); if( intensityImageUsed ) { filter->SetIntensityInput( intensityImage ); } filter->CalculatePixelIndicesOff(); filter->CalculateOrientedBoundingBoxOff(); filter->CalculateOrientedLabelRegionsOff(); // These generate optional outputs. // filter->CalculatePixelIndicesOn(); // filter->CalculateOrientedBoundingBoxOn();; // filter->CalculateOrientedLabelRegionsOn(); filter->Update(); typedef itk::ShapeLabelObject LabelObjectType; typedef itk::LabelMap< LabelObjectType > LabelMapType; // convert the image in a collection of objects typedef itk::LabelImageToLabelMapFilter ConverterType; typename ConverterType::Pointer converter = ConverterType::New(); converter->SetInput( labelImage ); typedef itk::ShapeLabelMapFilter< LabelMapType > ValuatorType; typename ValuatorType::Pointer valuator = ValuatorType::New(); valuator->SetInput( converter->GetOutput() ); valuator->Update(); typename LabelMapType::Pointer labelMap = valuator->GetOutput(); // typedef itk::LabelPerimeterEstimationCalculator AreaFilterType; // typename AreaFilterType::Pointer areafilter = AreaFilterType::New(); // areafilter->SetImage( labelImage ); // areafilter->SetFullyConnected( false ); // areafilter->Compute(); typename FilterType::LabelsType allLabels = filter->GetLabels(); std::sort( allLabels.begin(), allLabels.end() ); if( outputCSVFormat ) { typename FilterType::LabelsType::iterator allLabelsIt; std::vector columnHeaders; columnHeaders.push_back( std::string( "Label" ) ); columnHeaders.push_back( std::string( "VolumeInVoxels" ) ); columnHeaders.push_back( std::string( "SurfaceAreaInMillimetersSquared" ) ); columnHeaders.push_back( std::string( "Eccentricity" ) ); columnHeaders.push_back( std::string( "Elongation" ) ); columnHeaders.push_back( std::string( "Orientation" ) ); columnHeaders.push_back( std::string( "Centroid_x" ) ); columnHeaders.push_back( std::string( "Centroid_y" ) ); if( ImageDimension == 3 ) { columnHeaders.push_back( std::string( "Centroid_z" ) ); } columnHeaders.push_back( std::string( "AxesLength_x" ) ); columnHeaders.push_back( std::string( "AxesLength_y" ) ); if( ImageDimension == 3 ) { columnHeaders.push_back( std::string( "AxesLength_z" ) ); } columnHeaders.push_back( std::string( "BoundingBoxLower_x" ) ); columnHeaders.push_back( std::string( "BoundingBoxLower_y" ) ); if( ImageDimension == 3 ) { columnHeaders.push_back( std::string( "BoundingBoxLower_z" ) ); } columnHeaders.push_back( std::string( "BoundingBoxUpper_x" ) ); columnHeaders.push_back( std::string( "BoundingBoxUpper_y" ) ); if( ImageDimension == 3 ) { columnHeaders.push_back( std::string( "BoundingBoxUpper_z" ) ); } if( filter->GetIntensityInput() ) { columnHeaders.push_back( std::string( "IntegratedIntensity" ) ); columnHeaders.push_back( std::string( "WeightedCentroid_x" ) ); columnHeaders.push_back( std::string( "WeightedCentroid_y" ) ); if( ImageDimension == 3 ) { columnHeaders.push_back( std::string( "WeightedCentroid_z" ) ); } } std::vector rowHeaders; std::ostringstream convert;// stream used for the conversion for( allLabelsIt = allLabels.begin(); allLabelsIt != allLabels.end(); allLabelsIt++ ) { if( *allLabelsIt == 0 ) { continue; } convert << *allLabelsIt; // insert the textual representation of 'Number' in the characters in the stream rowHeaders.push_back( convert.str() ); // set 'Result' to the contents of the stream } vnl_matrix measures( allLabels.size() - 1, columnHeaders.size() - 1 ); unsigned int rowIndex = 0; for( allLabelsIt = allLabels.begin(); allLabelsIt != allLabels.end(); allLabelsIt++ ) { if( *allLabelsIt == 0 ) { continue; } unsigned int columnIndex = 0; // measures( rowIndex, columnIndex ) = static_cast< double >( *allLabelsIt ); measures( rowIndex, columnIndex++ ) = filter->GetVolume( *allLabelsIt ); const LabelObjectType * labelObject = labelMap->GetLabelObject( *allLabelsIt ); measures( rowIndex, columnIndex++ ) = labelObject->GetPerimeter(); measures( rowIndex, columnIndex++ ) = filter->GetEccentricity( *allLabelsIt ); measures( rowIndex, columnIndex++ ) = filter->GetElongation( *allLabelsIt ); measures( rowIndex, columnIndex++ ) = filter->GetOrientation( *allLabelsIt ); measures( rowIndex, columnIndex++ ) = filter->GetCentroid( *allLabelsIt )[0]; measures( rowIndex, columnIndex++ ) = filter->GetCentroid( *allLabelsIt )[1]; if( ImageDimension == 3 ) { measures( rowIndex, columnIndex++ ) = filter->GetCentroid( *allLabelsIt )[2]; } measures( rowIndex, columnIndex++ ) = filter->GetAxesLength( *allLabelsIt )[0]; measures( rowIndex, columnIndex++ ) = filter->GetAxesLength( *allLabelsIt )[1]; if( ImageDimension == 3 ) { measures( rowIndex, columnIndex++ ) = filter->GetAxesLength( *allLabelsIt )[2]; } unsigned int arrayIndex = 0; measures( rowIndex, columnIndex++ ) = filter->GetBoundingBox( *allLabelsIt )[arrayIndex++]; measures( rowIndex, columnIndex++ ) = filter->GetBoundingBox( *allLabelsIt )[arrayIndex++]; if( ImageDimension == 3 ) { measures( rowIndex, columnIndex++ ) = filter->GetBoundingBox( *allLabelsIt )[arrayIndex++]; } measures( rowIndex, columnIndex++ ) = filter->GetBoundingBox( *allLabelsIt )[arrayIndex++]; measures( rowIndex, columnIndex++ ) = filter->GetBoundingBox( *allLabelsIt )[arrayIndex++]; if( ImageDimension == 3 ) { measures( rowIndex, columnIndex++ ) = filter->GetBoundingBox( *allLabelsIt )[arrayIndex++]; } if( filter->GetIntensityInput() ) { measures( rowIndex, columnIndex++ ) = filter->GetIntegratedIntensity( *allLabelsIt ); measures( rowIndex, columnIndex++ ) = filter->GetWeightedCentroid( *allLabelsIt )[0]; measures( rowIndex, columnIndex++ ) = filter->GetWeightedCentroid( *allLabelsIt )[1]; if( ImageDimension == 3 ) { measures( rowIndex, columnIndex++ ) = filter->GetWeightedCentroid( *allLabelsIt )[2]; } } rowIndex++; } typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( argv[4] ); writer->SetColumnHeaders( columnHeaders ); writer->SetRowHeaders( rowHeaders ); writer->SetInput( &measures ); try { writer->Write(); } catch( itk::ExceptionObject& exp ) { std::cerr << "Exception caught!" << std::endl; std::cerr << exp << std::endl; return EXIT_FAILURE; } } else { typename FilterType::LabelsType::iterator allLabelsIt; // std::cout << "Number of labels: " << labelGeometryFilter->GetNumberOfLabels() << std::endl; // std::cout << "Label geometry measures." << std::endl; std::cout << std::left << std::setw( 7 ) << "Label" << std::left << std::setw( 10 ) << "Volume(voxels)" << std::left << std::setw( 15 ) << "SurfArea(mm^2)" << std::left << std::setw( 15 ) << "Eccentricity" << std::left << std::setw( 15 ) << "Elongation" << std::left << std::setw( 15 ) << "Orientation" << std::left << std::setw( 30 ) << "Centroid" << std::left << std::setw( 30 ) << "Axes Length" << std::left << std::setw( 30 ) << "Bounding Box"; if( filter->GetIntensityInput() ) { std::cout << std::left << std::setw( 20 ) << "Integrated Int." << std::left << std::setw( 30 ) << "Weighted Centroid"; } std::cout << std::endl; for( allLabelsIt = allLabels.begin(); allLabelsIt != allLabels.end(); allLabelsIt++ ) { if( *allLabelsIt == 0 ) { continue; } std::cout << std::setw( 7 ) << *allLabelsIt; std::cout << std::setw( 10 ) << filter->GetVolume( *allLabelsIt ); const LabelObjectType * labelObject = labelMap->GetLabelObject( *allLabelsIt ); std::cout << std::setw( 15 ) << labelObject->GetPerimeter(); std::cout << std::setw( 15 ) << filter->GetEccentricity( *allLabelsIt ); std::cout << std::setw( 15 ) << filter->GetElongation( *allLabelsIt ); std::cout << std::setw( 15 ) << filter->GetOrientation( *allLabelsIt ); std::stringstream oss; oss << filter->GetCentroid( *allLabelsIt ); std::cout << std::setw( 30 ) << ( oss.str() ).c_str(); oss.str( "" ); oss << filter->GetAxesLength( *allLabelsIt ); std::cout << std::setw( 30 ) << ( oss.str() ).c_str(); oss.str( "" ); oss << filter->GetBoundingBox( *allLabelsIt ); std::cout << std::setw( 30 ) << ( oss.str() ).c_str(); oss.str( "" ); // std::cout << filter->GetMajorAxisLength( *allLabelsIt ) << "\t"; // std::cout << filter->GetMinorAxisLength( *allLabelsIt ) << "\t"; if( filter->GetIntensityInput() ) { oss << filter->GetIntegratedIntensity( *allLabelsIt ); std::cout << std::setw( 20 ) << ( oss.str() ).c_str(); oss.str( "" ); oss << filter->GetWeightedCentroid( *allLabelsIt ); std::cout << std::setw( 30 ) << ( oss.str() ).c_str(); oss.str( "" ); } std::cout << std::endl; } } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int LabelGeometryMeasures( std::vector args, std::ostream* itkNotUsed( out_stream ) ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "LabelGeometryMeasures" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage 1: " << argv[0] << " imageDimension labelImage [intensityImage=none] [csvFile]" << std::endl; // std::cout << "Usage 2: " << argv[0] << " X singleLabelImage outputTransform " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { LabelGeometryMeasures<2>( argc, argv ); } break; case 3: { LabelGeometryMeasures<3>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/LabelOverlapMeasures.cxx000066400000000000000000000206401311104306400210320ustar00rootroot00000000000000#include "antsUtilities.h" #include #include "itkCSVArray2DDataObject.h" #include "itkCSVArray2DFileReader.h" #include "itkCSVNumericObjectFileWriter.h" #include "itkHausdorffDistanceImageFilter.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkLabelOverlapMeasuresImageFilter.h" #include "itkSignedMaurerDistanceMapImageFilter.h" #include #include namespace ants { template int LabelOverlapMeasures( int argc, char * argv[] ) { if( argc < 3 ) { std::cout << "missing 1st filename" << std::endl; throw; } if( argc < 4 ) { std::cout << "missing 2nd filename" << std::endl; throw; } bool outputCSVFormat = false; if( argc > 4 ) { outputCSVFormat = true; } typedef unsigned int PixelType; typedef itk::Image ImageType; typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader1 = ReaderType::New(); reader1->SetFileName( argv[2] ); typename ReaderType::Pointer reader2 = ReaderType::New(); reader2->SetFileName( argv[3] ); typedef itk::LabelOverlapMeasuresImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetSourceImage( reader1->GetOutput() ); filter->SetTargetImage( reader2->GetOutput() ); filter->Update(); typename FilterType::MapType labelMap = filter->GetLabelSetMeasures(); std::vector allLabels; allLabels.clear(); for( typename FilterType::MapType::const_iterator it = labelMap.begin(); it != labelMap.end(); ++it ) { if( (*it).first == 0 ) { continue; } const int label = (*it).first; allLabels.push_back( label ); } std::sort( allLabels.begin(), allLabels.end() ); if( outputCSVFormat ) { std::vector columnHeaders; columnHeaders.push_back( std::string( "Label" ) ); columnHeaders.push_back( std::string( "Total/Target" ) ); columnHeaders.push_back( std::string( "Jaccard" ) ); columnHeaders.push_back( std::string( "Dice" ) ); columnHeaders.push_back( std::string( "VolumeSimilarity" ) ); columnHeaders.push_back( std::string( "FalseNegative" ) ); columnHeaders.push_back( std::string( "FalsePositive" ) ); std::vector rowHeaders; rowHeaders.push_back( std::string( "All" ) ); std::vector::const_iterator itL = allLabels.begin(); while( itL != allLabels.end() ) { std::ostringstream convert; // stream used for the conversion convert << *itL; // insert the textual representation of 'Number' in the characters in the stream rowHeaders.push_back( convert.str() ); ++itL; } vnl_matrix measures( allLabels.size() + 1, 6 ); measures( 0, 0 ) = filter->GetTotalOverlap(); measures( 0, 1 ) = filter->GetUnionOverlap(); measures( 0, 2 ) = filter->GetMeanOverlap(); measures( 0, 3 ) = filter->GetVolumeSimilarity(); measures( 0, 4 ) = filter->GetFalseNegativeError(); measures( 0, 5 ) = filter->GetFalsePositiveError(); unsigned int rowIndex = 1; for( itL = allLabels.begin(); itL != allLabels.end(); ++itL ) { measures( rowIndex, 0 ) = filter->GetTargetOverlap( *itL ); measures( rowIndex, 1 ) = filter->GetUnionOverlap( *itL ); measures( rowIndex, 2 ) = filter->GetMeanOverlap( *itL ); measures( rowIndex, 3 ) = filter->GetVolumeSimilarity( *itL ); measures( rowIndex, 4 ) = filter->GetFalseNegativeError( *itL ); measures( rowIndex, 5 ) = filter->GetFalsePositiveError( *itL ); rowIndex++; } typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( argv[4] ); writer->SetColumnHeaders( columnHeaders ); writer->SetRowHeaders( rowHeaders ); writer->SetInput( &measures ); try { writer->Write(); } catch( itk::ExceptionObject& exp ) { std::cerr << "Exception caught!" << std::endl; std::cerr << exp << std::endl; return EXIT_FAILURE; } } else { std::cout << " " << "************ All Labels *************" << std::endl; std::cout << std::setw( 10 ) << " " << std::setw( 17 ) << "Total" << std::setw( 17 ) << "Union (jaccard)" << std::setw( 17 ) << "Mean (dice)" << std::setw( 17 ) << "Volume sim." << std::setw( 17 ) << "False negative" << std::setw( 17 ) << "False positive" << std::endl; std::cout << std::setw( 10 ) << " "; std::cout << std::setw( 17 ) << filter->GetTotalOverlap(); std::cout << std::setw( 17 ) << filter->GetUnionOverlap(); std::cout << std::setw( 17 ) << filter->GetMeanOverlap(); std::cout << std::setw( 17 ) << filter->GetVolumeSimilarity(); std::cout << std::setw( 17 ) << filter->GetFalseNegativeError(); std::cout << std::setw( 17 ) << filter->GetFalsePositiveError(); std::cout << std::endl; std::cout << " " << "************ Individual Labels *************" << std::endl; std::cout << std::setw( 10 ) << "Label" << std::setw( 17 ) << "Target" << std::setw( 17 ) << "Union (jaccard)" << std::setw( 17 ) << "Mean (dice)" << std::setw( 17 ) << "Volume sim." << std::setw( 17 ) << "False negative" << std::setw( 17 ) << "False positive" << std::endl; for( unsigned int i = 0; i < allLabels.size(); i++ ) { int label = allLabels[i]; std::cout << std::setw( 10 ) << label; std::cout << std::setw( 17 ) << filter->GetTargetOverlap( label ); std::cout << std::setw( 17 ) << filter->GetUnionOverlap( label ); std::cout << std::setw( 17 ) << filter->GetMeanOverlap( label ); std::cout << std::setw( 17 ) << filter->GetVolumeSimilarity( label ); std::cout << std::setw( 17 ) << filter->GetFalseNegativeError( label ); std::cout << std::setw( 17 ) << filter->GetFalsePositiveError( label ); std::cout << std::endl; } } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int LabelOverlapMeasures( std::vector args, std::ostream * /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "LabelOverlapMeasures" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Usage: " << argv[0] << " imageDimension sourceImage " << "targetImage [outputCSVFile]" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { return LabelOverlapMeasures<2>( argc, argv ); } break; case 3: { return LabelOverlapMeasures<3>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/LaplacianThickness.cxx000066400000000000000000001031051311104306400205130ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include #include "itkDanielssonDistanceMapImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkFastMarchingUpwindGradientImageFilter.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkImageFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkLaplacianRecursiveGaussianImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkVectorCurvatureAnisotropicDiffusionImageFilter.h" #include "itkVectorIndexSelectionCastImageFilter.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkWarpImageFilter.h" #include "vnl/algo/vnl_determinant.h" #include "ReadWriteData.h" namespace ants { template typename TImage::Pointer GetVectorComponent(typename TField::Pointer field, unsigned int index) { // Initialize the Moving to the displacement field typedef TImage ImageType; typename ImageType::Pointer sfield = AllocImage(field); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( field, field->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { typename TField::PixelType v1 = vfIter.Get(); sfield->SetPixel(vfIter.GetIndex(), v1[index]); } return sfield; } template typename TImage::Pointer SmoothImage(typename TImage::Pointer image, float sig) { // find min value typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter(image, image->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { typename TImage::PixelType v1 = vfIter.Get(); if( vnl_math_isnan(v1) ) { vfIter.Set(0); } } typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(sig); filter->SetUseImageSpacingOn(); filter->SetMaximumError(.01f); filter->SetInput(image); filter->Update(); typename TImage::Pointer out = filter->GetOutput(); return out; } template void SmoothDeformation(typename TImage::Pointer vectorimage, float sig) { typedef itk::Vector VectorType; typedef itk::Image ImageType; typename ImageType::Pointer subimgx = GetVectorComponent(vectorimage, 0); subimgx = SmoothImage(subimgx, sig); typename ImageType::Pointer subimgy = GetVectorComponent(vectorimage, 1); subimgy = SmoothImage(subimgy, sig); typename ImageType::Pointer subimgz = GetVectorComponent(vectorimage, 2); subimgz = SmoothImage(subimgz, sig); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType Iterator( vectorimage, vectorimage->GetLargestPossibleRegion().GetSize() ); Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { VectorType vec; vec[0] = subimgx->GetPixel(Iterator.GetIndex() ); vec[1] = subimgy->GetPixel(Iterator.GetIndex() ); vec[2] = subimgz->GetPixel(Iterator.GetIndex() ); Iterator.Set(vec); ++Iterator; } return; } template typename TImage::Pointer LabelSurface(typename TImage::PixelType foreground, typename TImage::PixelType newval, typename TImage::Pointer input, float distthresh ) { std::cout << " Label Surf " << std::endl; typedef TImage ImageType; enum { ImageDimension = ImageType::ImageDimension }; // ORIENTATION ALERT -- original code set spacing & origin without // also setting orientation. typename ImageType::Pointer Image = AllocImage(input); typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for( int j = 0; j < ImageDimension; j++ ) { rad[j] = (unsigned int)(distthresh + 0.5); } iteratorType GHood(rad, input, input->GetLargestPossibleRegion() ); GHood.GoToBegin(); // std::cout << " foreg " << (int) foreground; while( !GHood.IsAtEnd() ) { typename TImage::PixelType p = GHood.GetCenterPixel(); typename TImage::IndexType ind = GHood.GetIndex(); typename TImage::IndexType ind2; if( p == foreground ) { bool atedge = false; for( unsigned int i = 0; i < GHood.Size(); i++ ) { ind2 = GHood.GetIndex(i); float dist = 0.0; for( int j = 0; j < ImageDimension; j++ ) { dist += (float)(ind[j] - ind2[j]) * (float)(ind[j] - ind2[j]); } dist = sqrt(dist); if( GHood.GetPixel(i) != foreground && dist < distthresh ) { atedge = true; } } if( atedge && p == foreground ) { Image->SetPixel(ind, newval); } else { Image->SetPixel(ind, 0); } } ++GHood; } return Image; } template typename TImage::Pointer Morphological( typename TImage::Pointer input, float rad, bool option) { typedef TImage ImageType; enum { ImageDimension = TImage::ImageDimension }; typedef typename TImage::PixelType PixelType; if( !option ) { std::cout << " eroding the image " << std::endl; } else { std::cout << " dilating the image " << std::endl; } typedef itk::BinaryBallStructuringElement< PixelType, ImageDimension> StructuringElementType; // Software Guide : BeginCodeSnippet typedef itk::BinaryErodeImageFilter< TImage, TImage, StructuringElementType> ErodeFilterType; typedef itk::BinaryDilateImageFilter< TImage, TImage, StructuringElementType> DilateFilterType; typename ErodeFilterType::Pointer binaryErode = ErodeFilterType::New(); typename DilateFilterType::Pointer binaryDilate = DilateFilterType::New(); StructuringElementType structuringElement; structuringElement.SetRadius( (unsigned long) rad ); // 3x3x3 structuring element structuringElement.CreateStructuringElement(); binaryErode->SetKernel( structuringElement ); binaryDilate->SetKernel( structuringElement ); // It is necessary to define what could be considered objects on the binary // images. This is specified with the methods \code{SetErodeValue()} and // \code{SetDilateValue()}. The value passed to these methods will be // considered the value over which the dilation and erosion rules will apply binaryErode->SetErodeValue( 1 ); binaryDilate->SetDilateValue( 1 ); typename TImage::Pointer temp; if( option ) { binaryDilate->SetInput( input ); binaryDilate->Update(); temp = binaryDilate->GetOutput(); } else { binaryErode->SetInput( input ); // binaryDilate->GetOutput() ); binaryErode->Update(); temp = binaryErode->GetOutput(); typedef itk::ImageRegionIteratorWithIndex ImageIteratorType; ImageIteratorType o_iter( temp, temp->GetLargestPossibleRegion() ); o_iter.GoToBegin(); while( !o_iter.IsAtEnd() ) { if( o_iter.Get() > 0.5 && input->GetPixel(o_iter.GetIndex() ) > 0.5 ) { o_iter.Set(1); } else { o_iter.Set(0); } ++o_iter; } } return temp; } template typename TField::Pointer FMMGrad(typename TImage::Pointer wm, typename TImage::Pointer gm ) { typedef TImage ImageType; enum { ImageDimension = TImage::ImageDimension }; typename TField::Pointer sfield = AllocImage(wm); typename ImageType::Pointer surf = LabelSurface(1, 1, wm, 1.9 ); typedef itk::FastMarchingUpwindGradientImageFilter FloatFMType; typename FloatFMType::Pointer marcher = FloatFMType::New(); typedef typename FloatFMType::NodeType NodeType; typedef typename FloatFMType::NodeContainer NodeContainer; // setup alive points typename NodeContainer::Pointer alivePoints = NodeContainer::New(); typename NodeContainer::Pointer targetPoints = NodeContainer::New(); typename NodeContainer::Pointer trialPoints = NodeContainer::New(); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType thIt( wm, wm->GetLargestPossibleRegion().GetSize() ); thIt.GoToBegin(); unsigned long bb = 0, cc = 0, dd = 0; while( !thIt.IsAtEnd() ) { if( thIt.Get() > 0.1 && surf->GetPixel(thIt.GetIndex() ) == 0 ) { NodeType node; node.SetValue( 0 ); node.SetIndex(thIt.GetIndex() ); alivePoints->InsertElement(bb, node); bb++; } if( gm->GetPixel(thIt.GetIndex() ) == 0 && wm->GetPixel(thIt.GetIndex() ) == 0 ) { NodeType node; node.SetValue( 0 ); node.SetIndex(thIt.GetIndex() ); targetPoints->InsertElement(cc, node); cc++; } if( surf->GetPixel(thIt.GetIndex() ) == 1 ) { NodeType node; node.SetValue( 0 ); node.SetIndex(thIt.GetIndex() ); trialPoints->InsertElement(cc, node); dd++; } ++thIt; } marcher->SetTargetReachedModeToAllTargets(); marcher->SetAlivePoints( alivePoints ); marcher->SetTrialPoints( trialPoints ); marcher->SetTargetPoints( targetPoints ); marcher->SetInput( gm ); double stoppingValue = 1000.0; marcher->SetStoppingValue( stoppingValue ); marcher->GenerateGradientImageOn(); marcher->Update(); WriteImage(marcher->GetOutput(), "marcher.nii.gz"); thIt.GoToBegin(); while( !thIt.IsAtEnd() ) { typename TField::PixelType vec; for( dd = 0; dd < ImageDimension; dd++ ) { vec[dd] = marcher->GetGradientImage()->GetPixel(thIt.GetIndex() )[dd]; } ++thIt; } return sfield; } template typename TField::Pointer LaplacianGrad(typename TImage::Pointer wm, typename TImage::Pointer gm, float sig, unsigned int numits, float tolerance) { typedef typename TImage::IndexType IndexType; IndexType ind; typedef TImage ImageType; typedef TField GradientImageType; typedef itk::GradientRecursiveGaussianImageFilter GradientImageFilterType; typedef typename GradientImageFilterType::Pointer GradientImageFilterPointer; typename TField::Pointer sfield = AllocImage(wm); typename TImage::Pointer laplacian = SmoothImage(wm, 1); laplacian->FillBuffer(0); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType Iterator( wm, wm->GetLargestPossibleRegion().GetSize() ); Iterator.GoToBegin(); // initialize L(wm)=1, L(gm)=0.5, else 0 while( !Iterator.IsAtEnd() ) { ind = Iterator.GetIndex(); if( wm->GetPixel(ind) >= 0.5 ) { laplacian->SetPixel(ind, 1); } else { laplacian->SetPixel(ind, 2.); } ++Iterator; } // smooth and then reset the values float meanvalue = 0, lastmean = 1; unsigned int iterations = 0; while( fabs(meanvalue - lastmean) > tolerance && iterations < numits ) { iterations++; std::cout << " % " << (float) iterations / (float)(numits + 1) << " delta-mean " << fabs(meanvalue - lastmean) << std::endl; laplacian = SmoothImage(laplacian, sqrt(sig) ); Iterator.GoToBegin(); unsigned int ct = 0; lastmean = meanvalue; while( !Iterator.IsAtEnd() ) { ind = Iterator.GetIndex(); if( wm->GetPixel(ind) >= 0.5 ) { laplacian->SetPixel(ind, 1); } else if( gm->GetPixel(ind) < 0.5 && wm->GetPixel(ind) < 0.5 ) { laplacian->SetPixel(ind, 2.); } else { meanvalue += laplacian->GetPixel(ind); ct++; } ++Iterator; } meanvalue /= (float)ct; } // / WriteImage(laplacian, "laplacian.hdr"); GradientImageFilterPointer filter = GradientImageFilterType::New(); filter->SetInput( laplacian ); filter->SetSigma(sig * 0.5); filter->Update(); return filter->GetOutput(); } template float IntegrateLength( typename TImage::Pointer /* gmsurf */, typename TImage::Pointer /* thickimage */, typename TImage::IndexType velind, typename TField::Pointer lapgrad, float itime, float starttime, float /* finishtime */, bool timedone, float deltaTime, typename TInterp::Pointer vinterp, typename TInterp2::Pointer sinterp, unsigned int /* task */, bool /* propagate */, bool domeasure, unsigned int m_NumberOfTimePoints, typename TImage::SpacingType spacing, float vecsign, float timesign, float gradsign, unsigned int ct, typename TImage::Pointer wm, typename TImage::Pointer gm, float priorthickval, typename TImage::Pointer smooththick, bool printprobability, typename TImage::Pointer /* sulci */ ) { typedef typename TField::PixelType VectorType; typedef typename TField::PointType DPointType; typedef itk::VectorLinearInterpolateImageFunction DefaultInterpolatorType; VectorType zero; zero.Fill(0); VectorType disp; disp.Fill(0); ct = 0; DPointType pointIn1; DPointType pointIn2; typename DefaultInterpolatorType::ContinuousIndexType vcontind; DPointType pointIn3; enum { ImageDimension = TImage::ImageDimension }; typedef typename TImage::IndexType IndexType; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { IndexType index; index[jj] = velind[jj]; pointIn1[jj] = velind[jj] * lapgrad->GetSpacing()[jj]; } // if( task == 0 ) // { // propagate = false; // } // else // { // propagate = true; // } itime = starttime; timedone = false; float totalmag = 0; if( domeasure ) { while( !timedone ) { float scale = 1; // *m_DT[timeind]/m_DS[timeind]; // std::cout << " scale " << scale << std::endl; double itimetn1 = itime - timesign * deltaTime * scale; double itimetn1h = itime - timesign * deltaTime * 0.5 * scale; if( itimetn1h < 0 ) { itimetn1h = 0; } if( itimetn1h > m_NumberOfTimePoints - 1 ) { itimetn1h = m_NumberOfTimePoints - 1; } if( itimetn1 < 0 ) { itimetn1 = 0; } if( itimetn1 > m_NumberOfTimePoints - 1 ) { itimetn1 = m_NumberOfTimePoints - 1; } // first get current position of particle for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { IndexType index; index[jj] = velind[jj]; pointIn1[jj] = velind[jj] * lapgrad->GetSpacing()[jj]; } // std::cout << " ind " << index << std::endl; // now index the time varying field at that position. typename DefaultInterpolatorType::OutputType f1; f1.Fill(0); typename DefaultInterpolatorType::OutputType f2; f2.Fill(0); typename DefaultInterpolatorType::OutputType f3; f3.Fill(0); typename DefaultInterpolatorType::OutputType f4; f4.Fill(0); typename DefaultInterpolatorType::ContinuousIndexType Y1; typename DefaultInterpolatorType::ContinuousIndexType Y2; typename DefaultInterpolatorType::ContinuousIndexType Y3; typename DefaultInterpolatorType::ContinuousIndexType Y4; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { pointIn2[jj] = disp[jj] + pointIn1[jj]; vcontind[jj] = pointIn2[jj] / lapgrad->GetSpacing()[jj]; Y1[jj] = vcontind[jj]; Y2[jj] = vcontind[jj]; Y3[jj] = vcontind[jj]; Y4[jj] = vcontind[jj]; } // Y1[ImageDimension]=itimetn1; // Y2[ImageDimension]=itimetn1h; // Y3[ImageDimension]=itimetn1h; // Y4[ImageDimension]=itime; f1 = vinterp->EvaluateAtContinuousIndex( Y1 ); for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { Y2[jj] += f1[jj] * deltaTime * 0.5; } bool isinside = true; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { if( Y2[jj] < 1 || Y2[jj] > lapgrad->GetLargestPossibleRegion().GetSize()[jj] - 2 ) { isinside = false; } } if( isinside ) { f2 = vinterp->EvaluateAtContinuousIndex( Y2 ); } for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { Y3[jj] += f2[jj] * deltaTime * 0.5; } isinside = true; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { if( Y3[jj] < 1 || Y3[jj] > lapgrad->GetLargestPossibleRegion().GetSize()[jj] - 2 ) { isinside = false; } } if( isinside ) { f3 = vinterp->EvaluateAtContinuousIndex( Y3 ); } for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { Y4[jj] += f3[jj] * deltaTime; } isinside = true; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { if( Y4[jj] < 1 || Y4[jj] > lapgrad->GetLargestPossibleRegion().GetSize()[jj] - 2 ) { isinside = false; } } if( isinside ) { f4 = vinterp->EvaluateAtContinuousIndex( Y4 ); } for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { pointIn3[jj] = pointIn2[jj] + gradsign * vecsign * deltaTime / 6.0 * ( f1[jj] + 2.0 * f2[jj] + 2.0 * f3[jj] + f4[jj] ); } VectorType out; float mag = 0, dmag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { out[jj] = pointIn3[jj] - pointIn1[jj]; mag += (pointIn3[jj] - pointIn2[jj]) * (pointIn3[jj] - pointIn2[jj]); dmag += (pointIn3[jj] - pointIn1[jj]) * (pointIn3[jj] - pointIn1[jj]); disp[jj] = out[jj]; } dmag = sqrt(dmag); totalmag += sqrt(mag); ct++; // if (!propagate) //thislength=dmag;// // thislength += totalmag; itime = itime + deltaTime * timesign; IndexType myind; for( unsigned int qq = 0; qq < ImageDimension; qq++ ) { myind[qq] = (unsigned long)(pointIn3[qq] / spacing[qq] + 0.5); } if( (gm->GetPixel(myind) < 0.5 && wm->GetPixel(myind) < 0.5) || (wm->GetPixel(myind) >= 0.5 && gm->GetPixel(myind) < 0.5) || mag < 1.e-1 * deltaTime ) { timedone = true; } if( gm->GetPixel(myind) < 0.5 ) { timedone = true; } if( ct > 2.0 / deltaTime ) { timedone = true; } if( totalmag > priorthickval ) { timedone = true; } if( smooththick ) { if( (totalmag - smooththick->GetPixel(velind) ) > 1 ) { timedone = true; } } if( printprobability ) { std::cout << " ind " << Y1 << " prob " << sinterp->EvaluateAtContinuousIndex(Y1) << " t " << itime << std::endl; } } } return totalmag; } template int LaplacianThickness(int argc, char *argv[]) { float gradstep = -50.0; // atof(argv[3])*(-1.0); unsigned int nsmooth = 2; float smoothparam = 1; float priorthickval = 500; double dT = 0.01; std::string wfn = std::string(argv[1]); std::string gfn = std::string(argv[2]); int argct = 3; std::string outname = std::string(argv[argct]); argct++; if( argc > argct ) { smoothparam = atof(argv[argct]); } argct++; if( argc > argct ) { priorthickval = atof(argv[argct]); } argct++; if( argc > argct ) { dT = atof(argv[argct]); } argct++; float dosulc = 0; if( argc > argct ) { dosulc = atof(argv[argct]); } argct++; float tolerance = 0.001; if( argc > argct ) { tolerance = atof(argv[argct]); } argct++; std::cout << " using tolerance " << tolerance << std::endl; typedef float PixelType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::Image ImageType; typedef typename ImageType::SpacingType SpacingType; // typename tvt::Pointer gWarp; // ReadImage( gWarp, ifn.c_str() ); typename ImageType::Pointer thickimage; ReadImage(thickimage, wfn.c_str() ); thickimage->FillBuffer(0); typename ImageType::Pointer thickimage2; ReadImage(thickimage2, wfn.c_str() ); thickimage2->FillBuffer(0); typename ImageType::Pointer wm; ReadImage(wm, wfn.c_str() ); typename ImageType::Pointer gm; ReadImage(gm, gfn.c_str() ); SpacingType spacing = wm->GetSpacing(); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType Iterator( wm, wm->GetLargestPossibleRegion().GetSize() ); typename ImageType::Pointer wmb = BinaryThreshold(0.5, 1.e9, 1, wm); typename DisplacementFieldType::Pointer lapgrad = ITK_NULLPTR; typename DisplacementFieldType::Pointer lapgrad2 = ITK_NULLPTR; typename ImageType::Pointer gmb = BinaryThreshold(0.5, 1.e9, 1, gm); /** get sulcal priors */ typename ImageType::Pointer sulci = ITK_NULLPTR; if( dosulc > 0 ) { std::cout << " using sulcal prior " << std::endl; typedef itk::DanielssonDistanceMapImageFilter FilterType; typename FilterType::Pointer distmap = FilterType::New(); distmap->InputIsBinaryOn(); distmap->SetUseImageSpacing(true); distmap->SetInput(wmb); distmap->Update(); typename ImageType::Pointer distwm = distmap->GetOutput(); typedef itk::LaplacianRecursiveGaussianImageFilter dgf; typename dgf::Pointer lfilter = dgf::New(); lfilter->SetSigma(smoothparam); lfilter->SetInput(distwm); lfilter->Update(); typename ImageType::Pointer image2 = lfilter->GetOutput(); typedef itk::RescaleIntensityImageFilter RescaleFilterType; typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( 0 ); rescaler->SetOutputMaximum( 1 ); rescaler->SetInput( image2 ); rescaler->Update(); sulci = rescaler->GetOutput(); WriteImage(sulci, "sulci.nii"); Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { // std::cout << " a good value for use sulcus prior is 0.002 -- in a function : // 1/(1.+exp(-0.1*(sulcprob-0.275)/use-sulcus-prior)) " << std::endl; // float gmprob = gm->GetPixel(Iterator.GetIndex() ); if( gmprob == 0 ) { gmprob = 0.05; } float sprob = sulci->GetPixel(Iterator.GetIndex() ); sprob = 1 / (1. + exp(-0.1 * (sprob - 0.5) / dosulc) ); sulci->SetPixel(Iterator.GetIndex(), sprob ); // if (gmprob > 0) std::cout << " gmp " << gmprob << std::endl; ++Iterator; } std::cout << " modified gm prior by sulcus prior " << std::endl; WriteImage(sulci, "sulcigm.nii"); typedef itk::GradientRecursiveGaussianImageFilter GradientImageFilterType; typedef typename GradientImageFilterType::Pointer GradientImageFilterPointer; GradientImageFilterPointer filter = GradientImageFilterType::New(); filter->SetInput( distwm ); filter->SetSigma(smoothparam); filter->Update(); lapgrad2 = filter->GetOutput(); // return 0; /** sulc priors done */ } lapgrad = LaplacianGrad(wmb, gmb, smoothparam, 500, tolerance); // lapgrad=FMMGrad(wmb,gmb); // LabelSurface(typename TImage::PixelType foreground, // typename TImage::PixelType newval, typename TImage::Pointer input, float distthresh ) float distthresh = 1.9; typename ImageType::Pointer wmgrow = Morphological(wmb, 1, true); typename ImageType::Pointer surf = LabelSurface(1, 1, wmgrow, distthresh); typename ImageType::Pointer gmsurf = LabelSurface(1, 1, gmb, distthresh); // now integrate // double timezero = 0; // 1 double timeone = 1; // (s[ImageDimension]-1-timezero); // unsigned int m_NumberOfTimePoints = s[ImageDimension]; float starttime = timezero; // timezero; float finishtime = timeone; // s[ImageDimension]-1;//timeone; // std::cout << " MUCKING WITH START FINISH TIME " << finishtime << std::endl; typename DisplacementFieldType::IndexType velind; typename ImageType::Pointer smooththick = ITK_NULLPTR; float timesign = 1.0; if( starttime > finishtime ) { timesign = -1.0; } unsigned int m_NumberOfTimePoints = 2; typedef DisplacementFieldType TimeVaryingVelocityFieldType; typedef typename DisplacementFieldType::PointType DPointType; typedef itk::VectorLinearInterpolateImageFunction DefaultInterpolatorType; typename DefaultInterpolatorType::Pointer vinterp = DefaultInterpolatorType::New(); typedef itk::LinearInterpolateImageFunction ScalarInterpolatorType; typename ScalarInterpolatorType::Pointer sinterp = ScalarInterpolatorType::New(); sinterp->SetInputImage(gm); if( sulci ) { sinterp->SetInputImage(sulci); } VectorType zero; zero.Fill(0); DPointType pointIn1; DPointType pointIn2; typename DefaultInterpolatorType::ContinuousIndexType vcontind; DPointType pointIn3; typedef itk::ImageRegionIteratorWithIndex VIteratorType; VIteratorType VIterator( lapgrad, lapgrad->GetLargestPossibleRegion().GetSize() ); VIterator.GoToBegin(); while( !VIterator.IsAtEnd() ) { VectorType vec = VIterator.Get(); float mag = 0; for( unsigned int qq = 0; qq < ImageDimension; qq++ ) { mag += vec[qq] * vec[qq]; } mag = sqrt(mag); if( mag > 0 ) { vec = vec / mag; } VIterator.Set(vec * gradstep); if( lapgrad2 ) { vec = lapgrad2->GetPixel(VIterator.GetIndex() ); mag = 0; for( unsigned int qq = 0; qq < ImageDimension; qq++ ) { mag += vec[qq] * vec[qq]; } mag = sqrt(mag); if( mag > 0 ) { vec = vec / mag; } lapgrad2->SetPixel(VIterator.GetIndex(), vec * gradstep); } ++VIterator; } bool propagate = false; for( unsigned int smoothit = 0; smoothit < nsmooth; smoothit++ ) { std::cout << " smoothit " << smoothit << std::endl; Iterator.GoToBegin(); unsigned int cter = 0; while( !Iterator.IsAtEnd() ) { velind = Iterator.GetIndex(); // float thislength=0; for( unsigned int task = 0; task < 1; task++ ) { float itime = starttime; unsigned long ct = 0; bool timedone = false; VectorType disp; disp.Fill(0.0); double deltaTime = dT, vecsign = 1.0; bool domeasure = false; float gradsign = 1.0; bool printprobability = false; // std::cout << " wmb " << wmb->GetPixel(velind) << " gm " << gm->GetPixel(velind) << std::endl; // if (surf->GetPixel(velind) != 0) printprobability=true; if( gm->GetPixel(velind) > 0.25 ) // && wmb->GetPixel(velind) < 1 ) { cter++; domeasure = true; } vinterp->SetInputImage(lapgrad); gradsign = -1.0; vecsign = -1.0; float len1 = IntegrateLength (gmsurf, thickimage, velind, lapgrad, itime, starttime, finishtime, timedone, deltaTime, vinterp, sinterp, task, propagate, domeasure, m_NumberOfTimePoints, spacing, vecsign, gradsign, timesign, ct, wm, gm, priorthickval, smooththick, printprobability, sulci ); gradsign = 1.0; vecsign = 1; float len2 = IntegrateLength (gmsurf, thickimage, velind, lapgrad, itime, starttime, finishtime, timedone, deltaTime, vinterp, sinterp, task, propagate, domeasure, m_NumberOfTimePoints, spacing, vecsign, gradsign, timesign, ct, wm, gm, priorthickval - len1, smooththick, printprobability, sulci ); float len3 = 1.e9, len4 = 1.e9; if( lapgrad2 ) { vinterp->SetInputImage(lapgrad2); gradsign = -1.0; vecsign = -1.0; len3 = IntegrateLength (gmsurf, thickimage, velind, lapgrad2, itime, starttime, finishtime, timedone, deltaTime, vinterp, sinterp, task, propagate, domeasure, m_NumberOfTimePoints, spacing, vecsign, gradsign, timesign, ct, wm, gm, priorthickval, smooththick, printprobability, sulci ); gradsign = 1.0; vecsign = 1; len4 = IntegrateLength (gmsurf, thickimage, velind, lapgrad2, itime, starttime, finishtime, timedone, deltaTime, vinterp, sinterp, task, propagate, domeasure, m_NumberOfTimePoints, spacing, vecsign, gradsign, timesign, ct, wm, gm, priorthickval - len3, smooththick, printprobability, sulci ); } float totalength = len1 + len2; // if (totalength > 5 && totalength < 8) std::cout<< " t1 " << len3+len4 << " t2 " << len1+len2 << std::endl; if( len3 + len4 < totalength ) { totalength = len3 + len4; } if( smoothit == 0 ) { if( thickimage2->GetPixel(velind) == 0 ) { thickimage2->SetPixel(velind, totalength); } else if( (totalength) > 0 && thickimage2->GetPixel(velind) < (totalength) ) { thickimage2->SetPixel(velind, totalength); } } if( smoothit > 0 && smooththick ) { thickimage2->SetPixel(velind, (totalength) * 0.5 + smooththick->GetPixel(velind) * 0.5 ); } if( domeasure && (totalength) > 0 && cter % 10000 == 0 ) { std::cout << " len1 " << len1 << " len2 " << len2 << " ind " << velind << std::endl; } } ++Iterator; } smooththick = SmoothImage(thickimage2, 1.0); // set non-gm voxels to zero IteratorType gIterator( gm, gm->GetLargestPossibleRegion().GetSize() ); gIterator.GoToBegin(); while( !gIterator.IsAtEnd() ) { if( gm->GetPixel(gIterator.GetIndex() ) < 0.25 ) { thickimage2->SetPixel(gIterator.GetIndex(), 0); } ++gIterator; } std::cout << " writing " << outname << std::endl; WriteImage(thickimage2, outname.c_str() ); } // WriteImage(thickimage,"turd.hdr"); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int LaplacianThickness( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "LaplacianThickness" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Usage: " << argv[0] << " WM.nii GM.nii Out.nii {smoothparam=3} {priorthickval=5} {dT=0.01} use-sulcus-prior optional-laplacian-tolerance=0.001" << std::endl; std::cout << " a good value for use sulcus prior is 0.15 -- in a function : 1/(1.+exp(-0.1*(laplacian-img-value-sulcprob)/0.01)) " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } std::string ifn = std::string(argv[1]); // std::cout << " image " << ifn << std::endl; // Get the image dimension itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(ifn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(ifn.c_str() ); imageIO->ReadImageInformation(); unsigned int dim = imageIO->GetNumberOfDimensions(); // std::cout << " dim " << dim << std::endl; switch( dim ) { case 2: { return LaplacianThickness<2>(argc, argv); } break; case 3: { return LaplacianThickness<3>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/LesionFilling.cxx000066400000000000000000000247351311104306400175240ustar00rootroot00000000000000#include #include #include #include #include #include "antsUtilities.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkCastImageFilter.h" #include "itkSubtractImageFilter.h" #include "itkBinaryDilateImageFilter.h" #include "itkBinaryBallStructuringElement.h" #include "itkMaskImageFilter.h" #include "itkConnectedComponentImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkImageRegionIterator.h" //LesionFilling dimension t1.nii.gz lesionmask output.nii.gz namespace ants { template int LesionFilling( int argc, char * argv[] ) { const char * T1FileName = argv[2]; const char * LesionMapFileName = argv[3]; const char * OutputFileName = argv[4]; if( argc < 3 ) { std::cout << "Missing arguments, see usage." << std::endl; throw; } if( argc < 4 ) { std::cout << "2 more arguments are necessary, see usage." << std::endl; throw; } if( argc < 5 ) { std::cout << "Missing output filename." << std::endl; throw; } typedef itk::Image< double, ImageDimension> T1ImageType; typedef itk::Image< unsigned char, ImageDimension> LesionImageType; typedef itk::ImageFileReader T1ImageReaderType; typedef itk::ImageFileReader LesionImageReaderType; typename LesionImageReaderType::Pointer LesionReader = LesionImageReaderType::New(); LesionReader->SetFileName( LesionMapFileName ); try { LesionReader->Update(); } catch( itk::ExceptionObject & excp ) { std::cout << "no lesion mask that can be read" << std::endl; return 0; } typename T1ImageReaderType::Pointer T1Reader = T1ImageReaderType::New(); T1Reader->SetFileName( T1FileName); try { T1Reader->Update(); } catch( itk::ExceptionObject & excp ) { std::cout << "no T1 image that can be read" << std::endl; return 0; } typename T1ImageType::Pointer outImage = NULL ; outImage = T1Reader->GetOutput() ; typedef itk::ImageRegionIterator< T1ImageType> IteratorType; typedef itk::BinaryThresholdImageFilter BinaryThresholdImageFilterType; typedef itk::BinaryBallStructuringElement< double, ImageDimension> StructuringElementType; typedef itk::BinaryDilateImageFilter< T1ImageType, T1ImageType, StructuringElementType > DilateFilterType; typedef itk::ConnectedComponentImageFilter ConnectedComponentFilterType; typename ConnectedComponentFilterType::Pointer connected = ConnectedComponentFilterType::New(); connected->SetInput( LesionReader->GetOutput() ); connected->Update(); const int LesionNumber = connected->GetObjectCount() ; std::cout << "Number of lesions: " << LesionNumber << std::endl; for ( int i = 1; i < LesionNumber + 1 ; i++) { typedef itk::CastImageFilter< LesionImageType, T1ImageType> FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( connected->GetOutput() ); filter->Update() ; typename BinaryThresholdImageFilterType::Pointer thresholdFilter = BinaryThresholdImageFilterType::New(); thresholdFilter->SetInput(filter->GetOutput()); thresholdFilter->SetLowerThreshold( (double) i - 0.1); thresholdFilter->SetUpperThreshold( (double) i + 0.1); thresholdFilter->SetInsideValue ( 1 ); thresholdFilter->SetOutsideValue ( 0 ); thresholdFilter->Update() ; //Neighbouring voxel //filling lesions with the voxels surrounding them //first finding the edges of lesions //by subtracting dilated lesion map from lesion map itself typename DilateFilterType::Pointer binaryDilate = DilateFilterType::New(); StructuringElementType structuringElement; structuringElement.SetRadius( 1 ); // 3x3 structuring element structuringElement.CreateStructuringElement(); binaryDilate->SetKernel( structuringElement ); binaryDilate->SetInput( thresholdFilter->GetOutput() ); binaryDilate->SetDilateValue( 1 ); binaryDilate->Update() ; // subtract dilated image form non-dilated one typedef itk::SubtractImageFilter SubtractImageFilterType; typename SubtractImageFilterType::Pointer subtractFilter = SubtractImageFilterType::New (); //output = image1 - image2 subtractFilter->SetInput1( binaryDilate->GetOutput() ); subtractFilter->SetInput2( thresholdFilter->GetOutput() ); subtractFilter->Update(); //multiply the outer lesion mask with T1 to get only the neighbouring voxels typedef itk::MaskImageFilter< T1ImageType, T1ImageType > MaskFilterType; typename MaskFilterType::Pointer maskFilter = MaskFilterType::New(); maskFilter->SetInput( outImage ) ; maskFilter->SetMaskImage( subtractFilter->GetOutput() ); maskFilter->Update() ; typename T1ImageType::Pointer LesionEdge= NULL ; LesionEdge = maskFilter->GetOutput() ; //calculating mean lesion intesity //Note: lesions should not be filled with values //less than their originial values, this is a //trick to exclude any CSF voxels in surronding voxels (if any) typename MaskFilterType::Pointer maskFilterLesion = MaskFilterType::New(); maskFilterLesion->SetInput( T1Reader->GetOutput() ); //do we need to pass a double lesion maskFilterLesion->SetMaskImage(thresholdFilter->GetOutput() ); maskFilterLesion->Update() ; IteratorType it( maskFilterLesion->GetOutput(), maskFilterLesion->GetOutput()->GetLargestPossibleRegion() ); it.GoToBegin(); /** Walk over the image. */ int counter = 0; double meanInsideLesion = 0; while ( !it.IsAtEnd() ) { if( it.Value() != 0) { //coutning number of voxels inside lesion counter++; meanInsideLesion += it.Get(); } ++it; } meanInsideLesion /= (double) counter; //check that all outer voxels are more than the mean //intensity of the lesion, i.e. not including CSF voxels IteratorType itNoCSF( maskFilter->GetOutput(), maskFilter->GetOutput()->GetLargestPossibleRegion() ); itNoCSF.GoToBegin(); std::vector outerWMVoxels; while ( !itNoCSF.IsAtEnd() ) { if ( itNoCSF.Get() >= meanInsideLesion ) { outerWMVoxels.push_back( itNoCSF.Get() ); }//end if ++itNoCSF; }//end while //walk through original T1 //and change inside the lesion with a random pick from //collected normal appearing WM voxels (outerWMVoxels) IteratorType it4( outImage, outImage->GetLargestPossibleRegion() ); IteratorType itL( thresholdFilter->GetOutput(), thresholdFilter->GetOutput()->GetLargestPossibleRegion() ); int max = outerWMVoxels.size(); int min = 0; it4.GoToBegin(); itL.GoToBegin(); while ( !it4.IsAtEnd() ) { if (itL.Get() == 1) { int index = min + (rand() % (int)(max - min )) ; it4.Set( outerWMVoxels[ index ] ); }//end if ++it4; ++itL; }//end while }//end of loop for lesions typedef itk::ImageFileWriter< T1ImageType> WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( outImage); writer->SetFileName( OutputFileName ); try { writer->Update(); } catch( itk::ExceptionObject & err ) { std::cerr << "ExceptionObject caught !" << std::endl; std::cerr << err << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; }//main int int LesionFilling( std::vector args, std::ostream* itkNotUsed( out_stream ) ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "LesionFilling" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); //LesionFilling dimension t1.nii.gz lesionmask output.nii.gz if( argc < 3 ) { std::cout << "Example usage: " << argv[0] << " imageDimension T1_image.nii.gz lesion_mask.nii.gz output_lesion_filled.nii.gz" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { return LesionFilling<2>( argc, argv ); } break; case 3: { return LesionFilling<3>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; }//int LesionFilling std::vector }//namespace ants ants-2.2.0/Examples/MeasureImageSimilarity.cxx000066400000000000000000000311301311104306400213640ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt for 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. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include "ReadWriteData.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkAvantsMutualInformationRegistrationFunction.h" #include "itkSpatialMutualInformationRegistrationFunction.h" #include "itkProbabilisticRegistrationFunction.h" #include "itkCrossCorrelationRegistrationFunction.h" namespace ants { template int MeasureImageSimilarity(unsigned int argc, char *argv[]) { typedef float PixelType; typedef itk::Vector VectorType; typedef itk::Image FieldType; typedef itk::Image ImageType; typedef itk::ImageFileWriter writertype; typedef typename ImageType::IndexType IndexType; typedef itk::ImageRegionIteratorWithIndex Iterator; // get command line params unsigned int argct = 2; unsigned int whichmetric = atoi(argv[argct]); argct++; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = std::string(argv[argct]); argct++; std::string logfilename = ""; if( argc > argct ) { logfilename = std::string(argv[argct]); } argct++; std::string imgfilename = ""; if( argc > argct ) { imgfilename = std::string(argv[argct]); } argct++; double targetvalue = 0; if( argc > argct ) { targetvalue = atof(argv[argct]); } argct++; double epsilontolerance = 1.e20; if( argc > argct ) { epsilontolerance = atof(argv[argct]); } argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typename ImageType::Pointer image2 = ITK_NULLPTR; ReadImage(image2, fn2.c_str() ); /* typedef itk::ImageRegionIteratorWithIndex VIterator; typename FieldType::Pointer field=FieldType::New(); field->SetLargestPossibleRegion( image1->GetLargestPossibleRegion() ); field->SetBufferedRegion( image1->GetLargestPossibleRegion() ); field->SetLargestPossibleRegion( image1->GetLargestPossibleRegion() ); field->Allocate(); field->SetSpacing(image1->GetSpacing()); field->SetOrigin(image1->GetOrigin()); VectorType zero; zero.Fill(0); VIterator vfIter2( field, field->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { IndexType index=vfIter2.GetIndex(); vfIter2.Set(zero); } */ typename ImageType::Pointer metricimg = AllocImage(image1, 0); typedef ImageType FixedImageType; typedef ImageType MovingImageType; typedef FieldType DisplacementFieldType; // Choose the similarity metric typedef itk::AvantsMutualInformationRegistrationFunction MIMetricType; typedef itk::SpatialMutualInformationRegistrationFunction SMIMetricType; typedef itk::CrossCorrelationRegistrationFunction CCMetricType; // typedef itk::LandmarkCrossCorrelationRegistrationFunction // MetricType; // typename typename MIMetricType::Pointer mimet = MIMetricType::New(); typename SMIMetricType::Pointer smimet = SMIMetricType::New(); typename CCMetricType::Pointer ccmet = CCMetricType::New(); // int nbins=32; typename CCMetricType::RadiusType hradius; typename CCMetricType::RadiusType ccradius; ccradius.Fill(4); typename MIMetricType::RadiusType miradius; miradius.Fill(0); // mimet->SetDisplacementField(field); mimet->SetFixedImage(image1); mimet->SetMovingImage(image2); mimet->SetRadius(miradius); mimet->SetGradientStep(1.e2); mimet->SetNormalizeGradient(false); // ccmet->SetDisplacementField(field); ccmet->SetFixedImage(image1); ccmet->SetMovingImage(image2); ccmet->SetRadius(ccradius); ccmet->SetGradientStep(1.e2); ccmet->SetNormalizeGradient(false); double metricvalue = 0; std::string metricname = ""; if( whichmetric == 0 ) { hradius = miradius; unsigned long ct = 0; Iterator iter( metricimg, metricimg->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { IndexType index = iter.GetIndex(); double fval = image1->GetPixel(index); double mval = image2->GetPixel(index); metricvalue += fabs(fval - mval); metricimg->SetPixel(index, fabs(fval - mval) ); ct++; } metricvalue /= (float)ct; metricname = "MSQ "; } else if( whichmetric == 1 ) // imagedifference { double ccval = 0; hradius = ccradius; metricname = "CC "; typedef itk::ConstNeighborhoodIterator ScanIteratorType; typename FixedImageType::RegionType region = image1->GetLargestPossibleRegion(); ScanIteratorType asamIt( hradius, image1, region); unsigned long ct = 0; Iterator iter( metricimg, metricimg->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { IndexType index = iter.GetIndex(); double val = 0; double fmip = 0, mmip = 0, ffip = 0; asamIt.SetLocation(index); for( unsigned int i = 0; i < asamIt.Size(); i++ ) { IndexType locind = asamIt.GetIndex(i); if( region.IsInside( locind ) ) { double f = image1->GetPixel(locind); double m = image2->GetPixel(locind); fmip += (f * m); mmip += (m * m); ffip += (f * f); } } double denom = mmip * ffip; if( denom == 0 ) { val = 1; } else { val = fmip / sqrt(denom); } metricimg->SetPixel(index, val); // if (ct % 10000 == 0) // std::cout << val << " index " << index << std::endl; // asamIt.SetLocation(index); // totval+=met->localProbabilistic; ccval += val; ct++; } if( ct > 0 ) { metricvalue = ccval / ct; } else { metricvalue = 0; } /* std::cout << metricname << std::endl; ccmet->InitializeIteration(); metricimg=ccmet->MakeImage(); metricvalue=ccmet->ComputeCrossCorrelation()*(1.0); */ } else if( whichmetric == 2 ) { hradius = miradius; mimet->InitializeIteration(); metricvalue = mimet->ComputeMutualInformation(); metricname = "MI "; } else if( whichmetric == 3 ) { hradius = miradius; smimet->InitializeIteration(); metricvalue = smimet->ComputeSpatialMutualInformation(); metricname = "SMI "; } std::cout << fn1 << " : " << fn2 << " => " << metricname << metricvalue << std::endl; if( logfilename.length() > 3 ) { std::ofstream logfile; logfile.open(logfilename.c_str(), std::ofstream::app); if( logfile.good() ) { logfile << fn1 << " : " << fn2 << " => " << metricname << metricvalue << std::endl; } else { std::cout << " cant open file "; } logfile.close(); } if( imgfilename.length() > 3 ) { std::cout << "Only Implemented for MSQ and CC " << std::endl; typename writertype::Pointer w = writertype::New(); w->SetInput(metricimg); w->SetFileName(imgfilename.c_str() ); w->Write(); // met->WriteImages(); /* typename MetricType::NeighborhoodType asamIt( hradius, field,field->GetLargestPossibleRegion()); unsigned long ct = 0; double totval = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { IndexType index=vfIter2.GetIndex(); double val=0; asamIt.SetLocation(index); // met->ComputeUpdate( asamIt, gd); met->ComputeMetricAtPairB(index, zero); metricimg->SetPixel(index, val); //if (ct % 10000 == 0) // std::cout << val << " index " << index << std::endl; // asamIt.SetLocation(index); // totval+=met->localProbabilistic; ct++; } std::cout << " AvantsMI : " << totval/(double)ct << " E " << met->GetEnergy() << std::endl; std::cout << " write begin " << std::endl; std::cout << " write end " << std::endl; */ } double diff = ( (double)metricvalue - (double) targetvalue); std::cout << " targetvalue " << targetvalue << " metricvalue " << metricvalue << " diff " << diff << " toler " << epsilontolerance << std::endl; if( diff < epsilontolerance ) { return EXIT_SUCCESS; } else { return EXIT_FAILURE; } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int MeasureImageSimilarity( std::vector args, std::ostream* /*out_stream = NULL */ ) { try { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "MeasureImageSimilarity" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Basic useage ex: " << std::endl; std::cout << argv[0] << " ImageDimension whichmetric image1.ext image2.ext {logfile} {outimage.ext} {target-value} {epsilon-tolerance}" << std::endl; std::cout << " outimage (Not Implemented for MI yet) and logfile are optional " << std::endl; std::cout << " target-value and epsilon-tolerance set goals for the metric value -- if the metric value is within epsilon-tolerance of the target-value, then the test succeeds " << std::endl; std::cout << " Metric 0 - MeanSquareDifference, 1 - Cross-Correlation, 2-Mutual Information , 3-SMI " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } int metricsuccess = EXIT_FAILURE; // Get the image dimension switch( atoi(argv[1]) ) { case 2: { metricsuccess = MeasureImageSimilarity<2>(argc, argv); } break; case 3: { metricsuccess = MeasureImageSimilarity<3>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } std::cout << " Failure? " << metricsuccess << std::endl; return metricsuccess; } catch( const itk::ExceptionObject & e ) { e.Print( std::cerr ); return EXIT_FAILURE; } catch( const std::exception & e ) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch( ... ) { std::cerr << "UNKNOWN FAILURE" << std::endl; return EXIT_FAILURE; } } } // namespace ants ants-2.2.0/Examples/MeasureMinMaxMean.cxx000066400000000000000000000171771311104306400203040ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include "ReadWriteData.h" namespace ants { template int MeasureMinMaxMean(int argc, char *argv[]) { typedef itk::Vector PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; typename ImageType::Pointer image = ITK_NULLPTR; typename ImageType::Pointer mask = ITK_NULLPTR; PixelType mean; mean.Fill(0); PixelType max; max.Fill(0); PixelType min; min.Fill(9.e9); unsigned long ct = 0; ReadImage(image, argv[2]); bool takeabsval = false; if( argc > 4 ) { takeabsval = atoi(argv[4]); } if( argc > 5 ) { ReadImage(mask, argv[5]); } Iterator vfIter2( image, image->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { bool isinside = true; if( mask ) { if( mask->GetPixel(vfIter2.GetIndex() ).GetNorm() < 0.5 ) { isinside = false; } } if( isinside ) { PixelType val = vfIter2.Get(); if( takeabsval ) { for( unsigned int k = 0; k < NVectorComponents; k++ ) { val[k] = fabs(val[k]); } } mean = mean + val; if( val.GetSquaredNorm() > max.GetSquaredNorm() ) { max = val; } else if( val.GetSquaredNorm() < min.GetSquaredNorm() ) { min = val; } ct++; } } if( ct > 0 ) { mean = mean / (float)ct; } float variance = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { bool isinside = true; if( mask ) { if( mask->GetPixel(vfIter2.GetIndex() ).GetNorm() < 0.5 ) { isinside = false; } } if( isinside ) { PixelType val = vfIter2.Get(); if( takeabsval ) { for( unsigned int k = 0; k < NVectorComponents; k++ ) { val[k] = fabs(val[k]); } } variance += (val - mean).GetSquaredNorm(); } } float temp = (1.0 / (float)ct) * variance; std::cout << argv[2] << " Max : " << max << " Min : " << min << " Mean : " << mean << " Var : " << temp << " SD : " << sqrt(1.0 / (float)(ct - 1) * variance) << std::endl; if( argc > 3 ) { std::ofstream logfile; logfile.open(argv[3], std::ofstream::app); if( logfile.good() ) { logfile << argv[2] << " Max : " << max << " Min : " << min << " Mean : " << mean << std::endl; } else { std::cout << " cant open file " << argv[3] << std::endl; } logfile.close(); } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int MeasureMinMaxMean( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "MeasureMinMaxMean" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Basic useage ex: " << std::endl; std::cout << argv[0] << " ImageDimension image.nii {log.txt} {take-absolute-value} {mask-name} " << std::endl; std::cout << " log.txt is optional - take-abs-val reports min-max-mean of abs val image " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } int dim = atoi( argv[1] ); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(argv[2], itk::ImageIOFactory::ReadMode); imageIO->SetFileName(argv[2]); imageIO->ReadImageInformation(); unsigned int ncomponents = imageIO->GetNumberOfComponents(); int returnValue = EXIT_FAILURE; // Get the image dimension switch( atoi(argv[1]) ) { case 2: { switch( ncomponents ) { case 3: { returnValue = MeasureMinMaxMean<2, 3>(argc, argv); } break; case 2: { returnValue = MeasureMinMaxMean<2, 2>(argc, argv); } break; default: { returnValue = MeasureMinMaxMean<2, 1>(argc, argv); } break; } } break; case 3: { switch( ncomponents ) { case 7: { returnValue = MeasureMinMaxMean<3, 7>(argc, argv); } break; case 6: { returnValue = MeasureMinMaxMean<3, 6>(argc, argv); } break; case 3: { returnValue = MeasureMinMaxMean<3, 3>(argc, argv); } break; default: { returnValue = MeasureMinMaxMean<3, 1>(argc, argv); } break; } } break; case 4: { switch( ncomponents ) { case 7: { returnValue = MeasureMinMaxMean<4, 7>(argc, argv); } break; case 6: { returnValue = MeasureMinMaxMean<4, 6>(argc, argv); } break; case 4: { returnValue = MeasureMinMaxMean<4, 4>(argc, argv); } break; case 3: { returnValue = MeasureMinMaxMean<4, 3>(argc, argv); } break; case 2: { returnValue = MeasureMinMaxMean<4, 2>(argc, argv); } break; default: { returnValue = MeasureMinMaxMean<4, 1>(argc, argv); } break; } } break; default: std::cout << " not supported " << dim << std::endl; return EXIT_FAILURE; } return returnValue; } } // namespace ants ants-2.2.0/Examples/MemoryTest.cxx000066400000000000000000000145051311104306400170700ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt for 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. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include "ReadWriteData.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkAvantsMutualInformationRegistrationFunction.h" #include "itkProbabilisticRegistrationFunction.h" #include "itkCrossCorrelationRegistrationFunction.h" namespace ants { template int MemoryTest(unsigned int argc, char *argv[]) { typedef float PixelType; typedef itk::Vector VectorType; typedef itk::Image FieldType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; // get command line params unsigned int argct = 2; unsigned int whichmetric = atoi(argv[argct]); argct++; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = std::string(argv[argct]); argct++; unsigned int numberoffields = 11; if( argc > argct ) { numberoffields = atoi(argv[argct]); } argct++; typename ImageType::Pointer image1 = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); typename ImageType::Pointer image2 = ITK_NULLPTR; ReadImage(image2, fn2.c_str() ); std::vector fieldvec; for( unsigned int i = 0; i < numberoffields; i++ ) { std::cout << " NFields " << i << " of " << numberoffields << std::endl; VectorType zero; zero.Fill(0); // ORIENTATION ALERT: Original code set spacing/origin without // also setting directions. typename FieldType::Pointer field = AllocImage(image1, zero); fieldvec.push_back(field); } typename ImageType::Pointer metricimg = AllocImage(image1, 0); Iterator iter( metricimg, metricimg->GetLargestPossibleRegion() ); typedef ImageType FixedImageType; typedef ImageType MovingImageType; typedef FieldType DisplacementFieldType; // Choose the similarity metric typedef itk::AvantsMutualInformationRegistrationFunction MIMetricType; typedef itk::CrossCorrelationRegistrationFunction CCMetricType; // typedef itk::LandmarkCrossCorrelationRegistrationFunction // MetricType; // typename typename MIMetricType::Pointer mimet = MIMetricType::New(); typename CCMetricType::Pointer ccmet = CCMetricType::New(); // int nbins=32; typename CCMetricType::RadiusType ccradius; ccradius.Fill(4); typename MIMetricType::RadiusType miradius; miradius.Fill(0); // mimet->SetDisplacementField(field); mimet->SetFixedImage(image1); mimet->SetMovingImage(image2); mimet->SetRadius(miradius); mimet->SetGradientStep(1.e2); mimet->SetNormalizeGradient(false); // ccmet->SetDisplacementField(field); ccmet->SetFixedImage(image1); ccmet->SetMovingImage(image2); ccmet->SetRadius(ccradius); ccmet->SetGradientStep(1.e2); ccmet->SetNormalizeGradient(false); if( whichmetric == 1 ) // imagedifference { ccmet->InitializeIteration(); } else if( whichmetric != 0 ) { mimet->InitializeIteration(); } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int MemoryTest( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "MemoryTest" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Basic useage ex: " << std::endl; std::cout << argv[0] << " ImageDimension whichmetric image1.ext image2.ext NumberOfFieldsToAllocate " << std::endl; std::cout << " outimage and logfile are optional " << std::endl; std::cout << " Metric 0 - MeanSquareDifference, 1 - Cross-Correlation, 2-Mutual Information " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension switch( atoi(argv[1]) ) { case 2: { return MemoryTest<2>(argc, argv); } break; case 3: { return MemoryTest<3>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/MultiplyImages.cxx000066400000000000000000000201461311104306400177230ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include "itkDiscreteGaussianImageFilter.h" // RecursiveAverageImages img1 img2 weightonimg2 outputname // We divide the 2nd input image by its mean and add it to the first // input image with weight 1/n. // The output overwrites the 1st img with the sum. #include "ReadWriteData.h" namespace ants { template int MultiplyImages(int argc, char *argv[]) { typedef itk::Vector PixelType; typedef itk::Image ImageType; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::ImageFileReader readertype; typedef itk::ImageFileWriter writertype; if( argc < 3 ) { std::cout << "missing 1st filename" << std::endl; throw; } if( argc < 4 ) { std::cout << "missing 2nd filename" << std::endl; throw; } if( argc < 5 ) { std::cout << "missing output filename" << std::endl; throw; } std::string fn1 = std::string(argv[2]); std::string fn2 = std::string(argv[3]); typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename ImageType::Pointer varimage = ITK_NULLPTR; typename readertype::Pointer reader2 = readertype::New(); typename readertype::Pointer reader1 = readertype::New(); reader2->SetFileName(fn2.c_str() ); bool isfloat = false; try { reader2->UpdateLargestPossibleRegion(); } catch( ... ) { // std::cout << " Rather than opening " << fn2 // << // " as an image file, this program has decided, in its great wisdom, to consider it to be a floating point numerical value, and has acted accordingly -- i.e. read this as a number. " // << std::endl; isfloat = true; } float floatval = 1.0; if( isfloat ) { floatval = atof(argv[3]); } else { image2 = reader2->GetOutput(); } reader1->SetFileName(fn1.c_str() ); try { reader1->UpdateLargestPossibleRegion(); image1 = reader1->GetOutput(); } catch( ... ) { std::cout << " read 1 error "; } varimage = AllocImage(image1); Iterator vfIter2( varimage, varimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { typename ImageType::IndexType ind = vfIter2.GetIndex(); PixelType pix1 = image1->GetPixel(ind); if( isfloat ) { vfIter2.Set(pix1 * floatval); } else { vfIter2.Set(pix1 * image2->GetPixel(ind) ); } } typename writertype::Pointer writer = writertype::New(); writer->SetFileName(argv[4]); writer->SetInput( varimage ); writer->Write(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int MultiplyImages( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "MultiplyImages" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Usage: " << std::endl; std::cout << argv[0] << " ImageDimension img1.nii img2.nii product.nii {smoothing}" << std::endl; std::cout << " 2nd image file may also be floating point numerical value, and program will act accordingly -- i.e. read as a number. " << std::endl; std::cout << " Program handles vector and tensor images as well " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } int dim = atoi( argv[1] ); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(argv[2], itk::ImageIOFactory::ReadMode); imageIO->SetFileName(argv[2]); imageIO->ReadImageInformation(); unsigned int ncomponents = imageIO->GetNumberOfComponents(); int returnValue = EXIT_FAILURE; // Get the image dimension switch( atoi(argv[1]) ) { case 1: { switch( ncomponents ) { case 3: { returnValue = MultiplyImages<1, 3>(argc, argv); } break; case 2: { returnValue = MultiplyImages<1, 2>(argc, argv); } break; default: { returnValue = MultiplyImages<1, 1>(argc, argv); } break; } } break; case 2: { switch( ncomponents ) { case 3: { returnValue = MultiplyImages<2, 3>(argc, argv); } break; case 2: { returnValue = MultiplyImages<2, 2>(argc, argv); } break; default: { returnValue = MultiplyImages<2, 1>(argc, argv); } break; } } break; case 3: { switch( ncomponents ) { case 7: { returnValue = MultiplyImages<3, 7>(argc, argv); } break; case 6: { returnValue = MultiplyImages<3, 6>(argc, argv); } break; case 3: { returnValue = MultiplyImages<3, 3>(argc, argv); } break; default: { returnValue = MultiplyImages<3, 1>(argc, argv); } break; } } break; case 4: { switch( ncomponents ) { case 7: { returnValue = MultiplyImages<4, 7>(argc, argv); } break; case 6: { returnValue = MultiplyImages<4, 6>(argc, argv); } break; case 4: { returnValue = MultiplyImages<4, 4>(argc, argv); } break; case 3: { returnValue = MultiplyImages<4, 3>(argc, argv); } break; case 2: { returnValue = MultiplyImages<4, 2>(argc, argv); } break; default: { returnValue = MultiplyImages<4, 1>(argc, argv); } break; } } break; default: std::cout << " not supported " << dim << std::endl; return EXIT_FAILURE; } return returnValue; } } // namespace ants ants-2.2.0/Examples/N3BiasFieldCorrection.cxx000066400000000000000000000205471311104306400210360ustar00rootroot00000000000000 #include "antsUtilities.h" #include "antsAllocImage.h" #include #include "ReadWriteData.h" #include "itkBSplineControlPointImageFilter.h" #include "itkExpImageFilter.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIterator.h" #include "itkN3MRIBiasFieldCorrectionImageFilter.h" #include "itkOtsuThresholdImageFilter.h" #include "itkShrinkImageFilter.h" namespace ants { template class CommandIterationUpdate : public itk::Command { public: typedef CommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); protected: CommandIterationUpdate() { }; public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { const TFilter * filter = dynamic_cast( object ); if( typeid( event ) != typeid( itk::IterationEvent ) ) { return; } std::cout << "Iteration " << filter->GetElapsedIterations() << " (of " << filter->GetMaximumNumberOfIterations() << "). "; std::cout << " Current convergence value = " << filter->GetCurrentConvergenceMeasurement() << " (threshold = " << filter->GetConvergenceThreshold() << ")" << std::endl; } }; template int N3BiasFieldCorrection( int argc, char *argv[] ) { typedef float RealType; typedef itk::Image ImageType; typedef itk::Image MaskImageType; typename ImageType::Pointer image; ReadImage( image, argv[2] ); typedef itk::ShrinkImageFilter ShrinkerType; typename ShrinkerType::Pointer shrinker = ShrinkerType::New(); shrinker->SetInput( image ); shrinker->SetShrinkFactors( 1 ); typename MaskImageType::Pointer maskImage = ITK_NULLPTR; if( argc > 5 ) { ReadImage( maskImage, argv[5] ); } if( !maskImage ) { typedef itk::OtsuThresholdImageFilter ThresholderType; typename ThresholderType::Pointer otsu = ThresholderType::New(); otsu->SetInput( image ); otsu->SetNumberOfHistogramBins( 200 ); otsu->SetInsideValue( 0 ); otsu->SetOutsideValue( 1 ); otsu->Update(); maskImage = otsu->GetOutput(); } typedef itk::ShrinkImageFilter MaskShrinkerType; typename MaskShrinkerType::Pointer maskshrinker = MaskShrinkerType::New(); maskshrinker->SetInput( maskImage ); maskshrinker->SetShrinkFactors( 1 ); if( argc > 4 ) { shrinker->SetShrinkFactors( atoi( argv[4] ) ); maskshrinker->SetShrinkFactors( atoi( argv[4] ) ); } shrinker->Update(); maskshrinker->Update(); typedef itk::N3MRIBiasFieldCorrectionImageFilter CorrecterType; typename CorrecterType::Pointer correcter = CorrecterType::New(); correcter->SetInput( shrinker->GetOutput() ); correcter->SetMaskImage( maskshrinker->GetOutput() ); if( argc > 6 ) { correcter->SetMaximumNumberOfIterations( atoi( argv[6] ) ); } if( argc > 7 ) { correcter->SetNumberOfFittingLevels( atoi( argv[7] ) ); } bool verbose = false; if( argc > 7 ) { verbose = atoi( argv[8] ); } typedef CommandIterationUpdate CommandType; typename CommandType::Pointer observer = CommandType::New(); if ( verbose ) { correcter->AddObserver( itk::IterationEvent(), observer ); } try { correcter->Update(); } catch( ... ) { std::cout << "Exception caught." << std::endl; return EXIT_FAILURE; } // correcter->Print( std::cout, 3 ); /** * Reconstruct the bias field at full image resolution. Divide * the original input image by the bias field to get the final * corrected image. */ typedef itk::BSplineControlPointImageFilter BSplinerType; typename BSplinerType::Pointer bspliner = BSplinerType::New(); bspliner->SetInput( correcter->GetLogBiasFieldControlPointLattice() ); bspliner->SetSplineOrder( correcter->GetSplineOrder() ); bspliner->SetSize( image->GetLargestPossibleRegion().GetSize() ); bspliner->SetOrigin( image->GetOrigin() ); bspliner->SetDirection( image->GetDirection() ); bspliner->SetSpacing( image->GetSpacing() ); bspliner->Update(); typename ImageType::Pointer logField = AllocImage(bspliner->GetOutput() ); itk::ImageRegionIterator ItB( bspliner->GetOutput(), bspliner->GetOutput()->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItF( logField, logField->GetLargestPossibleRegion() ); for( ItB.GoToBegin(), ItF.GoToBegin(); !ItB.IsAtEnd(); ++ItB, ++ItF ) { ItF.Set( ItB.Get()[0] ); } typedef itk::ExpImageFilter ExpFilterType; typename ExpFilterType::Pointer expFilter = ExpFilterType::New(); expFilter->SetInput( logField ); expFilter->Update(); typedef itk::DivideImageFilter DividerType; typename DividerType::Pointer divider = DividerType::New(); divider->SetInput1( image ); divider->SetInput2( expFilter->GetOutput() ); divider->Update(); typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); if( argc < 4 ) { std::cout << "missing divider image filename" << std::endl; throw; } WriteImage( divider->GetOutput(), argv[3] ); if( argc > 8 ) { WriteImage( expFilter->GetOutput(), argv[8] ); } return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int N3BiasFieldCorrection( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "N3BiasFieldCorrection" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Usage: " << argv[0] << " imageDimension inputImage " << "outputImage [shrinkFactor] [maskImage] [numberOfIterations] " << "[numberOfFittingLevels] [outputBiasField] [verbose]" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { return N3BiasFieldCorrection<2>( argc, argv ); } break; case 3: { return N3BiasFieldCorrection<3>( argc, argv ); } break; case 4: { return N3BiasFieldCorrection<4>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/N4BiasFieldCorrection.cxx000066400000000000000000001071371311104306400210400ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include "antsCommandLineParser.h" #include "ReadWriteData.h" #include "itkBinaryThresholdImageFilter.h" #include "itkBSplineControlPointImageFilter.h" #include "itkConstantPadImageFilter.h" #include "itkExpImageFilter.h" #include "itkExtractImageFilter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkLabelStatisticsImageFilter.h" #include "itkN4BiasFieldCorrectionImageFilter.h" #include "itkShrinkImageFilter.h" #include "itkTimeProbe.h" #include #include #include #include "ANTsVersion.h" namespace ants { template class CommandIterationUpdate : public itk::Command { public: typedef CommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); protected: CommandIterationUpdate() { }; public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { const TFilter * filter = dynamic_cast( object ); if( typeid( event ) != typeid( itk::IterationEvent ) ) { return; } if( filter->GetElapsedIterations() == 1 ) { std::cout << "Current level = " << filter->GetCurrentLevel() + 1 << std::endl; } std::cout << " Iteration " << filter->GetElapsedIterations() << " (of " << filter->GetMaximumNumberOfIterations()[filter->GetCurrentLevel()] << "). "; std::cout << " Current convergence value = " << filter->GetCurrentConvergenceMeasurement() << " (threshold = " << filter->GetConvergenceThreshold() << ")" << std::endl; } }; template int N4( itk::ants::CommandLineParser *parser ) { typedef float RealType; typedef itk::Image ImageType; typename ImageType::Pointer inputImage = ITK_NULLPTR; typedef itk::Image MaskImageType; typename MaskImageType::Pointer maskImage = ITK_NULLPTR; bool verbose = false; typename itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } if( verbose ) { std::cout << std::endl << "Running N4 for " << ImageDimension << "-dimensional images." << std::endl << std::endl; } typedef itk::N4BiasFieldCorrectionImageFilter CorrecterType; typename CorrecterType::Pointer correcter = CorrecterType::New(); typename itk::ants::CommandLineParser::OptionType::Pointer inputImageOption = parser->GetOption( "input-image" ); if( inputImageOption && inputImageOption->GetNumberOfFunctions() ) { std::string inputFile = inputImageOption->GetFunction( 0 )->GetName(); ReadImage( inputImage, inputFile.c_str() ); } else { if( verbose ) { std::cerr << "Input image not specified." << std::endl; } return EXIT_FAILURE; } /** * handle the mask image */ bool isMaskImageSpecified = false; typename itk::ants::CommandLineParser::OptionType::Pointer maskImageOption = parser->GetOption( "mask-image" ); if( maskImageOption && maskImageOption->GetNumberOfFunctions() ) { std::string inputFile = maskImageOption->GetFunction( 0 )->GetName(); ReadImage( maskImage, inputFile.c_str() ); isMaskImageSpecified = true; } if( !maskImage ) { if( verbose ) { std::cout << "Mask not read. Using the entire image as the mask." << std::endl << std::endl; } maskImage = MaskImageType::New(); maskImage->CopyInformation( inputImage ); maskImage->SetRegions( inputImage->GetRequestedRegion() ); maskImage->Allocate( false ); maskImage->FillBuffer( itk::NumericTraits::OneValue() ); } typename ImageType::Pointer weightImage = ITK_NULLPTR; typename itk::ants::CommandLineParser::OptionType::Pointer weightImageOption = parser->GetOption( "weight-image" ); if( weightImageOption && weightImageOption->GetNumberOfFunctions() ) { std::string inputFile = weightImageOption->GetFunction( 0 )->GetName(); ReadImage( weightImage, inputFile.c_str() ); } /** * convergence options */ typename itk::ants::CommandLineParser::OptionType::Pointer convergenceOption = parser->GetOption( "convergence" ); if( convergenceOption && convergenceOption->GetNumberOfFunctions() ) { if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { std::vector numIters = parser->ConvertVector( convergenceOption->GetFunction( 0 )->GetParameter( 0 ) ); typename CorrecterType::VariableSizeArrayType maximumNumberOfIterations( numIters.size() ); for( unsigned int d = 0; d < numIters.size(); d++ ) { maximumNumberOfIterations[d] = numIters[d]; } correcter->SetMaximumNumberOfIterations( maximumNumberOfIterations ); typename CorrecterType::ArrayType numberOfFittingLevels; numberOfFittingLevels.Fill( numIters.size() ); correcter->SetNumberOfFittingLevels( numberOfFittingLevels ); correcter->SetConvergenceThreshold( 0.0 ); } if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { correcter->SetConvergenceThreshold( parser->Convert( convergenceOption->GetFunction( 0 )->GetParameter( 1 ) ) ); } } else // set default values { typename CorrecterType::VariableSizeArrayType maximumNumberOfIterations( 4 ); maximumNumberOfIterations.Fill( 50 ); correcter->SetMaximumNumberOfIterations( maximumNumberOfIterations ); correcter->SetNumberOfFittingLevels( 4 ); correcter->SetConvergenceThreshold( 0.0 ); } /** * B-spline options -- we place this here to take care of the case where * the user wants to specify things in terms of the spline distance. */ typename ImageType::IndexType inputImageIndex = inputImage->GetLargestPossibleRegion().GetIndex(); typename ImageType::SizeType inputImageSize = inputImage->GetLargestPossibleRegion().GetSize(); typename ImageType::PointType newOrigin = inputImage->GetOrigin(); typename itk::ants::CommandLineParser::OptionType::Pointer bsplineOption = parser->GetOption( "bspline-fitting" ); if( bsplineOption && bsplineOption->GetNumberOfFunctions() ) { if( bsplineOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { correcter->SetSplineOrder( parser->Convert( bsplineOption->GetFunction( 0 )->GetParameter( 1 ) ) ); } if( bsplineOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { std::vector array = parser->ConvertVector( bsplineOption->GetFunction( 0 )->GetParameter( 0 ) ); typename CorrecterType::ArrayType numberOfControlPoints; if( array.size() == 1 ) { // the user wants to specify things in terms of spline distance. // 1. need to pad the images to get as close to possible to the // requested domain size. float splineDistance = array[0]; typename ImageType::SizeType originalImageSize = inputImage->GetLargestPossibleRegion().GetSize(); itk::Size lowerBound; itk::Size upperBound; for( unsigned int d = 0; d < ImageDimension; d++ ) { float domain = static_cast( originalImageSize[d] - 1 ) * inputImage->GetSpacing()[d]; unsigned int numberOfSpans = static_cast( std::ceil( domain / splineDistance ) ); unsigned long extraPadding = static_cast( ( numberOfSpans * splineDistance - domain ) / inputImage->GetSpacing()[d] + 0.5 ); lowerBound[d] = static_cast( 0.5 * extraPadding ); upperBound[d] = extraPadding - lowerBound[d]; newOrigin[d] -= ( static_cast( lowerBound[d] ) * inputImage->GetSpacing()[d] ); numberOfControlPoints[d] = numberOfSpans + correcter->GetSplineOrder(); } typedef itk::ConstantPadImageFilter PadderType; typename PadderType::Pointer padder = PadderType::New(); padder->SetInput( inputImage ); padder->SetPadLowerBound( lowerBound ); padder->SetPadUpperBound( upperBound ); padder->SetConstant( 0 ); padder->Update(); inputImage = padder->GetOutput(); inputImage->DisconnectPipeline(); typedef itk::ConstantPadImageFilter MaskPadderType; typename MaskPadderType::Pointer maskPadder = MaskPadderType::New(); maskPadder->SetInput( maskImage ); maskPadder->SetPadLowerBound( lowerBound ); maskPadder->SetPadUpperBound( upperBound ); maskPadder->SetConstant( 0 ); maskPadder->Update(); maskImage = maskPadder->GetOutput(); maskImage->DisconnectPipeline(); if( weightImage ) { typename PadderType::Pointer weightPadder = PadderType::New(); weightPadder->SetInput( weightImage ); weightPadder->SetPadLowerBound( lowerBound ); weightPadder->SetPadUpperBound( upperBound ); weightPadder->SetConstant( 0 ); weightPadder->Update(); weightImage = weightPadder->GetOutput(); weightImage->DisconnectPipeline(); } if( verbose ) { std::cout << "Specified spline distance: " << splineDistance << "mm" << std::endl; std::cout << " original image size: " << originalImageSize << std::endl; std::cout << " padded image size: " << inputImage->GetLargestPossibleRegion().GetSize() << std::endl; std::cout << " number of control points: " << numberOfControlPoints << std::endl; std::cout << std::endl; } } else if( array.size() == ImageDimension ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { numberOfControlPoints[d] = static_cast( array[d] ) + correcter->GetSplineOrder(); } } else { if( verbose ) { std::cerr << "Incorrect mesh resolution" << std::endl; } return EXIT_FAILURE; } correcter->SetNumberOfControlPoints( numberOfControlPoints ); } } typedef itk::ShrinkImageFilter ShrinkerType; typename ShrinkerType::Pointer shrinker = ShrinkerType::New(); shrinker->SetInput( inputImage ); shrinker->SetShrinkFactors( 1 ); typedef itk::ShrinkImageFilter MaskShrinkerType; typename MaskShrinkerType::Pointer maskshrinker = MaskShrinkerType::New(); maskshrinker->SetInput( maskImage ); maskshrinker->SetShrinkFactors( 1 ); typename itk::ants::CommandLineParser::OptionType::Pointer shrinkFactorOption = parser->GetOption( "shrink-factor" ); int shrinkFactor = 4; if( shrinkFactorOption && shrinkFactorOption->GetNumberOfFunctions() ) { shrinkFactor = parser->Convert( shrinkFactorOption->GetFunction( 0 )->GetName() ); } shrinker->SetShrinkFactors( shrinkFactor ); maskshrinker->SetShrinkFactors( shrinkFactor ); if( ImageDimension == 4 ) { shrinker->SetShrinkFactor( 3, 1 ); maskshrinker->SetShrinkFactor( 3, 1 ); } shrinker->Update(); maskshrinker->Update(); itk::TimeProbe timer; timer.Start(); correcter->SetInput( shrinker->GetOutput() ); correcter->SetMaskImage( maskshrinker->GetOutput() ); typedef itk::ShrinkImageFilter WeightShrinkerType; typename WeightShrinkerType::Pointer weightshrinker = WeightShrinkerType::New(); if( weightImage ) { weightshrinker->SetInput( weightImage ); weightshrinker->SetShrinkFactors( shrinker->GetShrinkFactors() ); weightshrinker->Update(); correcter->SetConfidenceImage( weightshrinker->GetOutput() ); } if( verbose ) { typedef CommandIterationUpdate CommandType; typename CommandType::Pointer observer = CommandType::New(); correcter->AddObserver( itk::IterationEvent(), observer ); } /** * histogram sharpening options */ typename itk::ants::CommandLineParser::OptionType::Pointer histOption = parser->GetOption( "histogram-sharpening" ); if( histOption && histOption->GetNumberOfFunctions() ) { if( histOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { correcter->SetBiasFieldFullWidthAtHalfMaximum( parser->Convert( histOption->GetFunction( 0 )->GetParameter( 0 ) ) ); } if( histOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { correcter->SetWienerFilterNoise( parser->Convert( histOption->GetFunction( 0 )->GetParameter( 1 ) ) ); } if( histOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { correcter->SetNumberOfHistogramBins( parser->Convert( histOption->GetFunction( 0 )->GetParameter( 2 ) ) ); } } try { // correcter->DebugOn(); correcter->Update(); } catch( itk::ExceptionObject & e ) { if( verbose ) { std::cerr << "Exception caught: " << e << std::endl; } return EXIT_FAILURE; } if( verbose ) { correcter->Print( std::cout, 3 ); } timer.Stop(); if( verbose ) { std::cout << "Elapsed time: " << timer.GetMean() << std::endl; } /** * output */ typename itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { /** * Reconstruct the bias field at full image resolution. Divide * the original input image by the bias field to get the final * corrected image. */ typedef itk::BSplineControlPointImageFilter BSplinerType; typename BSplinerType::Pointer bspliner = BSplinerType::New(); bspliner->SetInput( correcter->GetLogBiasFieldControlPointLattice() ); bspliner->SetSplineOrder( correcter->GetSplineOrder() ); bspliner->SetSize( inputImage->GetLargestPossibleRegion().GetSize() ); bspliner->SetOrigin( newOrigin ); bspliner->SetDirection( inputImage->GetDirection() ); bspliner->SetSpacing( inputImage->GetSpacing() ); bspliner->Update(); typename ImageType::Pointer logField = AllocImage( inputImage ); itk::ImageRegionIterator ItB( bspliner->GetOutput(), bspliner->GetOutput()->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItF( logField, logField->GetLargestPossibleRegion() ); for( ItB.GoToBegin(), ItF.GoToBegin(); !ItB.IsAtEnd(); ++ItB, ++ItF ) { ItF.Set( ItB.Get()[0] ); } typedef itk::ExpImageFilter ExpFilterType; typename ExpFilterType::Pointer expFilter = ExpFilterType::New(); expFilter->SetInput( logField ); expFilter->Update(); typedef itk::DivideImageFilter DividerType; typename DividerType::Pointer divider = DividerType::New(); divider->SetInput1( inputImage ); divider->SetInput2( expFilter->GetOutput() ); divider->Update(); if( maskImageOption && maskImageOption->GetNumberOfFunctions() > 0 ) { itk::ImageRegionIteratorWithIndex ItD( divider->GetOutput(), divider->GetOutput()->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItI( inputImage, inputImage->GetLargestPossibleRegion() ); for( ItD.GoToBegin(), ItI.GoToBegin(); !ItD.IsAtEnd(); ++ItD, ++ItI ) { if( maskImage->GetPixel( ItD.GetIndex() ) == itk::NumericTraits::ZeroValue() ) { ItD.Set( ItI.Get() ); } } } bool doRescale = true; typename itk::ants::CommandLineParser::OptionType::Pointer rescaleOption = parser->GetOption( "rescale-intensities" ); if( ! isMaskImageSpecified || ( rescaleOption && rescaleOption->GetNumberOfFunctions() && ! parser->Convert( rescaleOption->GetFunction()->GetName() ) ) ) { doRescale = false; } if( doRescale ) { typedef itk::Image ShortImageType; typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInsideValue( itk::NumericTraits::ZeroValue() ); thresholder->SetOutsideValue( itk::NumericTraits::OneValue() ); thresholder->SetLowerThreshold( itk::NumericTraits::ZeroValue() ); thresholder->SetUpperThreshold( itk::NumericTraits::ZeroValue() ); thresholder->SetInput( maskImage ); typedef itk::LabelStatisticsImageFilter StatsType; typename StatsType::Pointer stats = StatsType::New(); stats->SetInput( inputImage ); stats->SetLabelInput( thresholder->GetOutput() ); stats->UseHistogramsOff(); stats->Update(); typedef typename StatsType::LabelPixelType StatsLabelType; StatsLabelType maskLabel = itk::NumericTraits::OneValue(); RealType minOriginal = stats->GetMinimum( maskLabel ); RealType maxOriginal = stats->GetMaximum( maskLabel ); typename StatsType::Pointer stats2 = StatsType::New(); stats2->SetInput( divider->GetOutput() ); stats2->SetLabelInput( thresholder->GetOutput() ); stats2->UseHistogramsOff(); stats2->Update(); RealType minBiasCorrected = stats2->GetMinimum( maskLabel ); RealType maxBiasCorrected = stats2->GetMaximum( maskLabel ); RealType slope = ( maxOriginal - minOriginal ) / ( maxBiasCorrected - minBiasCorrected ); itk::ImageRegionIteratorWithIndex ItD( divider->GetOutput(), divider->GetOutput()->GetLargestPossibleRegion() ); for( ItD.GoToBegin(); !ItD.IsAtEnd(); ++ItD ) { if( maskImage->GetPixel( ItD.GetIndex() ) == maskLabel ) { RealType originalIntensity = ItD.Get(); RealType rescaledIntensity = maxOriginal - slope * ( maxBiasCorrected - originalIntensity ); ItD.Set( rescaledIntensity ); } } } typename ImageType::RegionType inputRegion; inputRegion.SetIndex( inputImageIndex ); inputRegion.SetSize( inputImageSize ); typedef itk::ExtractImageFilter CropperType; typename CropperType::Pointer cropper = CropperType::New(); cropper->SetInput( divider->GetOutput() ); cropper->SetExtractionRegion( inputRegion ); cropper->SetDirectionCollapseToSubmatrix(); cropper->Update(); typename CropperType::Pointer biasFieldCropper = CropperType::New(); biasFieldCropper->SetInput( expFilter->GetOutput() ); biasFieldCropper->SetExtractionRegion( inputRegion ); biasFieldCropper->SetDirectionCollapseToSubmatrix(); biasFieldCropper->Update(); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { WriteImage( cropper->GetOutput(), ( outputOption->GetFunction( 0 )->GetName() ).c_str() ); } else if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { WriteImage( cropper->GetOutput(), ( outputOption->GetFunction( 0 )->GetParameter( 0 ) ).c_str() ); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { WriteImage( biasFieldCropper->GetOutput(), ( outputOption->GetFunction( 0 )->GetParameter( 1 ) ).c_str() ); } } } return EXIT_SUCCESS; } void N4InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, N4 tries to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "image-dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3/4" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "A scalar image is expected as input for bias correction. " ) + std::string( "Since N4 log transforms the intensities, negative values " ) + std::string( "or values close to zero should be processed prior to " ) + std::string( "correction." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "input-image" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "inputImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "If a mask image is specified, the final bias correction is " ) + std::string( "only performed in the mask region. If a weight image is not " ) + std::string( "specified, only intensity values inside the masked region are " ) + std::string( "used during the execution of the algorithm. If a weight " ) + std::string( "image is specified, only the non-zero weights are used in the " ) + std::string( "execution of the algorithm although the mask region defines " ) + std::string( "where bias correction is performed in the final output. " ) + std::string( "Otherwise bias correction occurs over the entire image domain. " ) + std::string( "See also the option description for the weight image. " ) + std::string( "If a mask image is *not* specified then the entire image region " ) + std::string( "will be used as the mask region. Note that this is different than " ) + std::string( "the N3 implementation which uses the results of Otsu thresholding " ) + std::string( "to define a mask. However, this leads to unknown anatomical regions being " ) + std::string( "included and excluded during the bias correction." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask-image" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "maskImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "At each iteration, a new intensity mapping is calculated " ) + std::string( "and applied but there is nothing which constrains the " ) + std::string( "new intensity range to be within certain values. The " ) + std::string( "result is that the range can \"drift\" from the original " ) + std::string( "at each iteration. This option rescales to the [min,max] " ) + std::string( "range of the original image intensities within the user-specified mask." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "rescale-intensities" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "0/(1)" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The weight image allows the user to perform a relative " ) + std::string( "weighting of specific voxels during the B-spline fitting. " ) + std::string( "For example, some studies have shown that N3 performed on " ) + std::string( "white matter segmentations improves performance. If one " ) + std::string( "has a spatial probability map of the white matter, one can " ) + std::string( "use this map to weight the b-spline fitting towards those " ) + std::string( "voxels which are more probabilistically classified as white " ) + std::string( "matter. See also the option description for the mask image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "weight-image" ); option->SetUsageOption( 0, "weightImageFilename" ); option->SetShortName( 'w' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Running N4 on large images can be time consuming. " ) + std::string( "To lessen computation time, the input image can be resampled. " ) + std::string( "The shrink factor, specified as a single integer, describes " ) + std::string( "this resampling. Shrink factors <= 4 are commonly used." ) + std::string( "Note that the shrink factor is only applied to the first two or " ) + std::string( "three dimensions which we assume are spatial. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "shrink-factor" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "1/2/3/(4)/..." ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Convergence is determined by calculating the coefficient of " ) + std::string( "variation between subsequent iterations. When this value " ) + std::string( "is less than the specified threshold " ) + std::string( "from the previous iteration or the maximum number of " ) + std::string( "iterations is exceeded the program terminates. Multiple " ) + std::string( "resolutions can be specified by using 'x' between the number " ) + std::string( "of iterations at each resolution, e.g. 100x50x50." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "convergence" ); option->SetShortName( 'c' ); option->SetUsageOption( 0, "[,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "These options describe the b-spline fitting parameters. " ) + std::string( "The initial b-spline mesh at the coarsest resolution is " ) + std::string( "specified either as the number of elements in each dimension, " ) + std::string( "e.g. 2x2x3 for 3-D images, or it can be specified as a " ) + std::string( "single scalar parameter which describes the isotropic sizing " ) + std::string( "of the mesh elements. The latter option is typically preferred. " ) + std::string( "For each subsequent level, the spline distance decreases in " ) + std::string( "half, or equivalently, the number of mesh elements doubles " ) + std::string( "Cubic splines (order = 3) are typically used. The default setting " ) + std::string( "is to employ a single mesh element over the entire domain, i.e., " ) + std::string( "-b [1x1x1,3]." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "bspline-fitting" ); option->SetShortName( 'b' ); option->SetUsageOption( 0, "[splineDistance,]" ); option->SetUsageOption( 1, "[initialMeshResolution,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "These options describe the histogram sharpening parameters, " ) + std::string( "i.e. the deconvolution step parameters described in the " ) + std::string( "original N3 algorithm. The default values have been shown " ) + std::string( "to work fairly well." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "histogram-sharpening" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "[,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The output consists of the bias corrected version of the " ) + std::string( "input image. Optionally, one can also output the estimated " ) + std::string( "bias field." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "correctedImage" ); option->SetUsageOption( 1, "[correctedImage,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Get Version Information." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "version" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int N4BiasFieldCorrection( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "N4BiasFieldCorrection" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "N4 is a variant of the popular N3 (nonparameteric nonuniform " ) + std::string( "normalization) retrospective bias correction algorithm. Based " ) + std::string( "on the assumption that the corruption of the low frequency bias " ) + std::string( "field can be modeled as a convolution of the intensity histogram " ) + std::string( "by a Gaussian, the basic algorithmic protocol is to iterate " ) + std::string( "between deconvolving the intensity histogram by a Gaussian, " ) + std::string( "remapping the intensities, and then spatially smoothing this " ) + std::string( "result by a B-spline modeling of the bias field itself. " ) + std::string( "The modifications from and improvements obtained over " ) + std::string( "the original N3 algorithm are described in the following paper: " ) + std::string( "N. Tustison et al., N4ITK: Improved N3 Bias Correction, " ) + std::string( "IEEE Transactions on Medical Imaging, 29(6):1310-1320, June 2010." ); parser->SetCommandDescription( commandDescription ); N4InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc == 1 ) { parser->PrintMenu( std::cerr, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Show automatic version itk::ants::CommandLineParser::OptionType::Pointer versionOption = parser->GetOption( "version" ); if( versionOption && versionOption->GetNumberOfFunctions() ) { std::string versionFunction = versionOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( versionFunction ); if( versionFunction.compare( "1" ) == 0 || versionFunction.compare( "true" ) == 0 ) { //Print Version Information std::cout << ANTs::Version::ExtendedVersionString() << std::endl; return EXIT_SUCCESS; } } // Get dimensionality unsigned int dimension = 3; itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "image-dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { // Read in the first intensity image to get the image dimension. std::string filename; itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "input-image" ); if( imageOption && imageOption->GetNumberOfFunctions() > 0 ) { if( imageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { filename = imageOption->GetFunction( 0 )->GetParameter( 0 ); } else { filename = imageOption->GetFunction( 0 )->GetName(); } } else { std::cerr << "No input images were specified. Specify an input image" << " with the -i option" << std::endl; return EXIT_FAILURE; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); dimension = imageIO->GetNumberOfDimensions(); } int returnValue = EXIT_FAILURE; switch( dimension ) { case 2: { returnValue = N4<2>( parser ); } break; case 3: { returnValue = N4<3>( parser ); } break; case 4: { returnValue = N4<4>( parser ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return returnValue; } } // namespace ants ants-2.2.0/Examples/NonLocalSuperResolution.cxx000066400000000000000000000577301311104306400215770ustar00rootroot00000000000000#include "antsAllocImage.h" #include "antsCommandLineParser.h" #include "antsUtilities.h" #include "ReadWriteData.h" #include "itkNonLocalSuperresolutionImageFilter.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkGaussianInterpolateImageFunction.h" #include "itkInterpolateImageFunction.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "itkLabelImageGaussianInterpolateImageFunction.h" #include "itkLabelImageGenericInterpolateImageFunction.h" #include "itkTimeProbe.h" #include "ANTsVersion.h" namespace ants { template class CommandProgressUpdate : public itk::Command { public: typedef CommandProgressUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( CommandProgressUpdate ); protected: CommandProgressUpdate() : m_CurrentProgress( 0 ) {}; typedef TFilter FilterType; unsigned int m_CurrentProgress; public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { const TFilter * filter = dynamic_cast( caller ); if( typeid( event ) == typeid( itk::IterationEvent ) ) { if( filter->GetCurrentIteration() > 0 ) { std::cout << "(epsilon value = " << filter->GetCurrentEpsilon() << ")." << std::endl; } std::cout << "Level " << filter->GetCurrentIteration() << ": " << std::flush; this->m_CurrentProgress = 0; } itk::ProcessObject *po = dynamic_cast( caller ); if (! po) return; // std::cout << po->GetProgress() << std::endl; if( typeid( event ) == typeid ( itk::ProgressEvent ) ) { if( this->m_CurrentProgress < 99 ) { this->m_CurrentProgress++; if( this->m_CurrentProgress % 10 == 0 ) { std::cout << this->m_CurrentProgress << std::flush; } else { std::cout << "*" << std::flush; } } } } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { const TFilter * filter = dynamic_cast( object ); if( typeid( event ) == typeid( itk::IterationEvent ) ) { if( filter->GetCurrentIteration() > 0 ) { std::cout << "(epsilon value = " << filter->GetCurrentEpsilon() << ")." << std::endl; } std::cout << "Level " << filter->GetCurrentIteration() << ": " << std::flush; this->m_CurrentProgress = 0; } itk::ProcessObject *po = dynamic_cast( const_cast( object ) ); if (! po) return; if( typeid( event ) == typeid ( itk::ProgressEvent ) ) { if( this->m_CurrentProgress < 99 ) { this->m_CurrentProgress++; if( this->m_CurrentProgress % 10 == 0 ) { std::cout << this->m_CurrentProgress << std::flush; } else { std::cout << "*" << std::flush; } } } } }; template int NonLocalSuperResolution( itk::ants::CommandLineParser *parser ) { typedef float RealType; typedef typename itk::ants::CommandLineParser::OptionType OptionType; bool verbose = false; typename itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } if( verbose ) { std::cout << std::endl << "Running for " << ImageDimension << "-dimensional images." << std::endl << std::endl; } typedef itk::Image ImageType; typename ImageType::Pointer inputImage = ITK_NULLPTR; typename OptionType::Pointer inputImageOption = parser->GetOption( "input-image" ); if( inputImageOption && inputImageOption->GetNumberOfFunctions() ) { std::string inputFile = inputImageOption->GetFunction( 0 )->GetName(); ReadImage( inputImage, inputFile.c_str() ); } else { if( verbose ) { std::cerr << "Input image not specified." << std::endl; } return EXIT_FAILURE; } typename ImageType::Pointer referenceImage = ITK_NULLPTR; typename OptionType::Pointer referenceImageOption = parser->GetOption( "reference-image" ); typename ImageType::Pointer interpolatedImage = ITK_NULLPTR; typename OptionType::Pointer interpolatedImageOption = parser->GetOption( "interpolated-image" ); if( referenceImageOption && referenceImageOption->GetNumberOfFunctions() ) { std::string inputFile = referenceImageOption->GetFunction( 0 )->GetName(); typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( inputFile.c_str() ); referenceImage = reader->GetOutput(); referenceImage->Update(); referenceImage->DisconnectPipeline(); } else if( interpolatedImageOption && interpolatedImageOption->GetNumberOfFunctions() ) { std::string inputFile = interpolatedImageOption->GetFunction( 0 )->GetName(); typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( inputFile.c_str() ); interpolatedImage = reader->GetOutput(); interpolatedImage->Update(); interpolatedImage->DisconnectPipeline(); } else { if( verbose ) { std::cerr << "Reference image or interpolated image not specified." << std::endl; } return EXIT_FAILURE; } typedef itk::NonLocalSuperresolutionImageFilter SuperresoluterType; typename SuperresoluterType::Pointer superresoluter = SuperresoluterType::New(); superresoluter->SetLowResolutionInputImage( inputImage ); if( referenceImage ) { superresoluter->SetHighResolutionReferenceImage( referenceImage ); superresoluter->SetPerformInitialMeanCorrection( false ); } else if( interpolatedImage ) { superresoluter->SetHighResolutionReferenceImage( interpolatedImage ); superresoluter->SetPerformInitialMeanCorrection( true ); } typename SuperresoluterType::NeighborhoodRadiusType neighborhoodPatchRadius; typename SuperresoluterType::NeighborhoodRadiusType neighborhoodSearchRadius; neighborhoodPatchRadius.Fill( 1 ); neighborhoodSearchRadius.Fill( 3 ); // Get the search and patch radii typename OptionType::Pointer searchRadiusOption = parser->GetOption( "search-radius" ); if( searchRadiusOption && searchRadiusOption->GetNumberOfFunctions() ) { std::string searchRadiusString = searchRadiusOption->GetFunction( 0 )->GetName(); std::vector searchRadius; searchRadius.push_back( 3 ); if( searchRadiusOption && searchRadiusOption->GetNumberOfFunctions() ) { searchRadius = parser->ConvertVector( searchRadiusString ); } if( searchRadius.size() == 1 ) { for( unsigned int d = 1; d < ImageDimension; d++ ) { searchRadius.push_back( searchRadius[0] ); } } if( searchRadius.size() != ImageDimension ) { if( verbose ) { std::cerr << "Search radius specified incorrectly. Please see usage options." << std::endl; } return EXIT_FAILURE; } for( unsigned int d = 0; d < ImageDimension; d++ ) { neighborhoodSearchRadius[d] = searchRadius[d]; } } superresoluter->SetNeighborhoodSearchRadius( neighborhoodSearchRadius ); typename OptionType::Pointer patchRadiusOption = parser->GetOption( "patch-radius" ); if( patchRadiusOption && patchRadiusOption->GetNumberOfFunctions() ) { std::vector patchRadius; patchRadius.push_back( 1 ); patchRadius = parser->ConvertVector( patchRadiusOption->GetFunction( 0 )->GetName() ); if( patchRadius.size() == 1 ) { for( unsigned int d = 1; d < ImageDimension; d++ ) { patchRadius.push_back( patchRadius[0] ); } } if( patchRadius.size() != ImageDimension ) { if( verbose ) { std::cerr << "Patch radius specified incorrectly. Please see usage options." << std::endl; } return EXIT_FAILURE; } for( unsigned int d = 0; d < ImageDimension; d++ ) { neighborhoodPatchRadius[d] = patchRadius[d]; } } superresoluter->SetNeighborhoodPatchRadius( neighborhoodPatchRadius ); RealType intensitySigma = 1.0; typename OptionType::Pointer intensitySigmaOption = parser->GetOption( "intensity-difference-sigma" ); if( intensitySigmaOption && intensitySigmaOption->GetNumberOfFunctions() ) { intensitySigma = parser->Convert( intensitySigmaOption->GetFunction( 0 )->GetName() ); } superresoluter->SetIntensityDifferenceSigma( intensitySigma ); RealType patchSimilaritySigma = 1.0; typename OptionType::Pointer patchSimilaritySigmaOption = parser->GetOption( "patch-similarity-sigma" ); if( patchSimilaritySigmaOption && patchSimilaritySigmaOption->GetNumberOfFunctions() ) { patchSimilaritySigma = parser->Convert( patchSimilaritySigmaOption->GetFunction( 0 )->GetName() ); } superresoluter->SetPatchSimilaritySigma( patchSimilaritySigma ); std::vector scaleLevels; scaleLevels.push_back( 32.0 ); scaleLevels.push_back( 16.0 ); scaleLevels.push_back( 8.0 ); scaleLevels.push_back( 4.0 ); scaleLevels.push_back( 2.0 ); scaleLevels.push_back( 1.0 ); typename OptionType::Pointer scaleLevelsOption = parser->GetOption( "scale-levels" ); if( scaleLevelsOption && scaleLevelsOption->GetNumberOfFunctions() ) { scaleLevels = parser->ConvertVector( scaleLevelsOption->GetFunction( 0 )->GetName() ); } superresoluter->SetScaleLevels( scaleLevels ); // Get the interpolator and possible parameters std::string whichInterpolator( "linear" ); typename itk::ants::CommandLineParser::OptionType::Pointer interpolationOption = parser->GetOption( "interpolation" ); if( interpolationOption && interpolationOption->GetNumberOfFunctions() ) { whichInterpolator = interpolationOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( whichInterpolator ); } if( !std::strcmp( whichInterpolator.c_str(), "multilabel" ) || !std::strcmp( whichInterpolator.c_str(), "genericlabel" ) ) { if( verbose ) { std::cerr << "A label-based interpolator is not appropriate for this application." << std::endl; } return EXIT_FAILURE; } const size_t VImageDimension = ImageDimension; typename ImageType::SpacingType cache_spacing_for_smoothing_sigmas(itk::NumericTraits::ZeroValue()); if( !std::strcmp( whichInterpolator.c_str(), "gaussian" ) ) { cache_spacing_for_smoothing_sigmas = referenceImage->GetSpacing(); } #include "make_interpolator_snip.tmpl" superresoluter->SetInterpolator( interpolator ); itk::TimeProbe timer; timer.Start(); if( verbose ) { typedef CommandProgressUpdate CommandType; typename CommandType::Pointer observer = CommandType::New(); superresoluter->AddObserver( itk::ProgressEvent(), observer ); superresoluter->AddObserver( itk::IterationEvent(), observer ); } try { // superresoluter->DebugOn(); superresoluter->Update(); } catch( itk::ExceptionObject & e ) { if( verbose ) { std::cerr << "Exception caught: " << e << std::endl; } return EXIT_FAILURE; } if( verbose ) { std::cout << std::endl << std::endl; superresoluter->Print( std::cout, 3 ); } timer.Stop(); if( verbose ) { std::cout << "Elapsed time: " << timer.GetMean() << std::endl; } /** * output */ typename itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { WriteImage( superresoluter->GetOutput(), ( outputOption->GetFunction( 0 )->GetName() ).c_str() ); } return EXIT_SUCCESS; } void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, the program tries to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "image-dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3/4" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "A low-resolution image input image to be superresoluted. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "input-image" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "inputImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "An interpolated version of the low-resolution image (such as B-spline). " ) + std::string( "One should specify either this option as a secondary input or a high-resolution " ) + std::string( "multi-modal counterpart (cf the -k option)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "interpolated-image" ); option->SetShortName( 'j' ); option->SetUsageOption( 0, "inputImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "A high resolution reference multi-modal image. Assumed to be in the same " ) + std::string( "space as the low-resolution input image (i.e., registered)." ) + std::string( "One should specify either this option as a secondary input or an interpolated " ) + std::string( "version (cf the -j option)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "reference-image" ); option->SetShortName( 'k' ); option->SetUsageOption( 0, "inputImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Patch radius. Default = 1x1x1" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "patch-radius" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "1" ); option->SetUsageOption( 1, "1x1x1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Search radius. Default = 3x3x3." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "search-radius" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "3" ); option->SetUsageOption( 1, "3x3x3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Intensity difference sigma. Default = 1.0" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "intensity-difference-sigma" ); option->SetShortName( 'g' ); option->SetUsageOption( 0, "1.0" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Patch similarity sigma. Default = 1.0" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "patch-similarity-sigma" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "1.0" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Scale levels. Default = 32x16x8x2x1" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "scale-levels" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "32x16x8x2x1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Several interpolation options are available in ITK. " ) + std::string( "These have all been made available." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "interpolation" ); option->SetShortName( 'n' ); option->SetUsageOption( 0, "Linear" ); option->SetUsageOption( 1, "NearestNeighbor" ); option->SetUsageOption( 2, "Gaussian[,]" ); option->SetUsageOption( 3, "BSpline[]" ); option->SetUsageOption( 4, "CosineWindowedSinc" ); option->SetUsageOption( 5, "WelchWindowedSinc" ); option->SetUsageOption( 6, "HammingWindowedSinc" ); option->SetUsageOption( 7, "LanczosWindowedSinc" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The output consists of the noise corrected version of the " ) + std::string( "input image. Optionally, one can also output the estimated " ) + std::string( "noise image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "outputImage" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Get Version Information." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "version" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int NonLocalSuperResolution( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "NonLocalSuperResolution" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "Non-local super resolution described in the following papers: " ) + std::string( "1) JV Manjon, P Coupe, A Buades, V Fonov, DL Collins, and Montserrat Robles. " ) + std::string( "Non-local MRI Upsampling." ) + std::string( "Medical Image Analysis, 14:784-792, 2010 and" ) + std::string( "2) JV Manjon, P Coupe, A Buades, DL Collins, and Montserrat Robles. " ) + std::string( "MRI Superresolution Using Self-Similarity and Image Priors." ) + std::string( "International Journal of Biomedical Imaging, 2010." ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc == 1 ) { parser->PrintMenu( std::cerr, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Show automatic version itk::ants::CommandLineParser::OptionType::Pointer versionOption = parser->GetOption( "version" ); if( versionOption && versionOption->GetNumberOfFunctions() ) { std::string versionFunction = versionOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( versionFunction ); if( versionFunction.compare( "1" ) == 0 || versionFunction.compare( "true" ) == 0 ) { //Print Version Information std::cout << ANTs::Version::ExtendedVersionString() << std::endl; return EXIT_SUCCESS; } } // Get dimensionality unsigned int dimension = 3; itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "image-dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { // Read in the first intensity image to get the image dimension. std::string filename; itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "input-image" ); itk::ants::CommandLineParser::OptionType::Pointer interpolatedImageOption = parser->GetOption( "interpolated-image" ); itk::ants::CommandLineParser::OptionType::Pointer referenceImageOption = parser->GetOption( "reference-image" ); if( imageOption && imageOption->GetNumberOfFunctions() > 0 && ( ( interpolatedImageOption && interpolatedImageOption->GetNumberOfFunctions() > 0 ) || ( referenceImageOption && referenceImageOption->GetNumberOfFunctions() > 0 ) ) ) { if( imageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { filename = imageOption->GetFunction( 0 )->GetParameter( 0 ); } else { filename = imageOption->GetFunction( 0 )->GetName(); } } else { std::cerr << "Not enough input images were specified. Specify an input image" << " with the -i option and a corresponding high-resoution image. Either" << " an interpolated version (-j) or multi-modal counterpart (-k)." << std::endl; return EXIT_FAILURE; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); dimension = imageIO->GetNumberOfDimensions(); } switch( dimension ) { case 2: { return NonLocalSuperResolution<2>( parser ); } break; case 3: { return NonLocalSuperResolution<3>( parser ); } break; case 4: { return NonLocalSuperResolution<4>( parser ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/PasteImageIntoImage.cxx000066400000000000000000000123311311104306400205670ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include #include namespace ants { template int PasteImageIntoImage( unsigned int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; std::vector startIndex = ConvertVector( std::string( argv[5] ) ); typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader1 = ReaderType::New(); reader1->SetFileName( argv[2] ); reader1->Update(); typename ReaderType::Pointer reader2 = ReaderType::New(); reader2->SetFileName( argv[3] ); reader2->Update(); PixelType backgroundValue = 0; if( argc > 6 ) { backgroundValue = static_cast( atof( argv[6] ) ); } unsigned int writeOver = 1; if( argc > 7 ) { writeOver = static_cast( atoi( argv[7] ) ); } PixelType conflictLabel = -1; if( argc > 8 ) { conflictLabel = static_cast( atof( argv[8] ) ); } itk::ImageRegionIteratorWithIndex It( reader2->GetOutput(), reader2->GetOutput()->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { typename ImageType::IndexType index = It.GetIndex(); PixelType paintValue = It.Get(); for( unsigned int d = 0; d < ImageDimension; d++ ) { index[d] += startIndex[d]; } if( paintValue != backgroundValue ) { if( reader1->GetOutput()->GetLargestPossibleRegion().IsInside( index ) ) { PixelType canvasValue = reader1->GetOutput()->GetPixel( index ); if( canvasValue == backgroundValue || writeOver == 1 ) { reader1->GetOutput()->SetPixel( index, paintValue ); } else if( canvasValue != backgroundValue && writeOver == 0 ) { continue; } else { reader1->GetOutput()->SetPixel( index, conflictLabel ); } } } } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( argv[4] ); writer->SetInput( reader1->GetOutput() ); writer->Update(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int PasteImageIntoImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "PasteImageIntoImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 6 ) { std::cout << argv[0] << " imageDimension inputCanvasImage inputImage " << "outputImage startIndex [backgroundLabel=0] [paintOverNonBackgroundVoxels=0] [conflictLabel=-1]" << std::endl; std::cout << " If the current painting image voxel is nonbackground and corresponds to a background voxel in the canvas image " << std::endl; std::cout << " paintOverNonBackgroundVoxels = 0 -> leave the canvas voxel as is." << std::endl; std::cout << " paintOverNonBackgroundVoxels = 1 -> replace canvas voxel value with painting image voxel value" << std::endl; std::cout << " paintOverNonBackgroundVoxels = 2 -> replace canvas voxel walue with conflictLabel" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { return PasteImageIntoImage<2>( argc, argv ); } break; case 3: { return PasteImageIntoImage<3>( argc, argv ); } break; case 4: { return PasteImageIntoImage<4>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/PermuteFlipImageOrientationAxes.cxx000066400000000000000000000144761311104306400232230ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include "itkImage.h" #include "itkConstantPadImageFilter.h" #include "itkIdentityTransform.h" #include "itkLinearInterpolateImageFunction.h" #include "itkRecursiveGaussianImageFilter.h" #include "itkPermuteAxesImageFilter.h" #include "itkFlipImageFilter.h" #include "ReadWriteData.h" namespace ants { template int PermuteFlipImageOrientationAxes( int argc, char * argv[] ) { typedef float InputPixelType; typedef float OutputPixelType; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typename InputImageType::Pointer inputImage = ITK_NULLPTR; ReadImage(inputImage, argv[1]); // Create a filter typedef OutputImageType ShortImage; typename itk::PermuteAxesImageFilter::Pointer permute; permute = itk::PermuteAxesImageFilter::New(); permute->SetInput( inputImage ); unsigned int upperFactors[Dimension]; unsigned int lowerFactors[Dimension]; for( unsigned int q = 0; q < Dimension; ++q ) { upperFactors[q] = 0; lowerFactors[q] = 0; } bool flipaboutorigin = false; if( Dimension == 2 ) { if( argc > 3 ) { upperFactors[0] = atoi(argv[3]); } if( argc > 4 ) { upperFactors[1] = atoi(argv[4]); } if( argc > 5 ) { lowerFactors[0] = atoi(argv[5]); } if( argc > 6 ) { lowerFactors[1] = atoi(argv[6]); } if( argc > 7 ) { flipaboutorigin = atoi(argv[7]); } } else if( Dimension == 3 ) { if( argc > 3 ) { upperFactors[0] = atoi(argv[3]); } if( argc > 4 ) { upperFactors[1] = atoi(argv[4]); } if( argc > 5 ) { upperFactors[2] = atoi(argv[5]); } if( argc > 6 ) { lowerFactors[0] = atoi(argv[6]); } if( argc > 7 ) { lowerFactors[1] = atoi(argv[7]); } if( argc > 8 ) { lowerFactors[2] = atoi(argv[8]); } if( argc > 9 ) { flipaboutorigin = atoi(argv[9]); } } permute->SetOrder( upperFactors ); permute->Update(); typedef itk::FlipImageFilter FlipType; typename FlipType::FlipAxesArrayType flip; for( unsigned int i = 0; i < Dimension; i++ ) { flip[i] = lowerFactors[i]; } typename FlipType::Pointer flipper = FlipType::New(); flipper->SetFlipAboutOrigin(flipaboutorigin); flipper->SetFlipAxes(flip); flipper->SetInput( permute->GetOutput() ); flipper->Update(); typename InputImageType::Pointer image = flipper->GetOutput(); WriteImage(image, argv[2]); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int PermuteFlipImageOrientationAxes( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "PermuteFlipImageOrientationAxes" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << std::endl; std::cout << argv[0] << " ImageDimension inputImageFile outputImageFile xperm yperm {zperm} xflip yflip {zflip} {FlipAboutOrigin}" << std::endl; std::cout << " for 3D: " << argv[0] << " 3 in.nii out.nii 2 0 1 1 1 1 \n would map z=>x, x=>y, y=>z and flip each " << std::endl; std::cout << " for 2D: " << argv[0] << " 2 in.nii out.nii 1 0 1 0 \n would map x=>y, y=>x and flip x " << std::endl; std::cout << std::endl; std::cout << " 0 1 2 for permute factors gives no axis permutation " << std::endl; std::cout << " 1 2 0 maps y to x, z to y and x to z " << std::endl; std::cout << " the flip values are boolean - 0 1 0 would flip the y-axis only " << std::endl; std::cout << std::endl << " The FlipAboutOrigin boolean lets you flip about the coordinate set in the origin " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension switch( atoi(argv[1]) ) { case 2: { return PermuteFlipImageOrientationAxes<2>(argc - 1, argv + 1); } break; case 3: { return PermuteFlipImageOrientationAxes<3>(argc - 1, argv + 1); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/PrintHeader.cxx000066400000000000000000000426171311104306400171720ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include #include #include #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "itkCastImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkSpatialOrientation.h" namespace ants { using namespace std; /** below code from Paul Yushkevich's c3d */ template bool try_print_metadata(itk::MetaDataDictionary & mdd, std::string key) { AnyType value = 0; if( itk::ExposeMetaData(mdd, key, value) ) { cout << " " << key << " = " << value << endl; return true; } else { return false; } } string get_rai_code(itk::SpatialOrientation::ValidCoordinateOrientationFlags code) { std::map m_CodeToString; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIP] = "RIP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIP] = "LIP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSP] = "RSP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSP] = "LSP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIA] = "RIA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIA] = "LIA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSA] = "RSA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSA] = "LSA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRP] = "IRP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILP] = "ILP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRP] = "SRP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLP] = "SLP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRA] = "IRA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILA] = "ILA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRA] = "SRA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLA] = "SLA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI] = "RPI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPI] = "LPI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAI] = "RAI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAI] = "LAI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPS] = "RPS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPS] = "LPS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAS] = "RAS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAS] = "LAS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRI] = "PRI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLI] = "PLI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARI] = "ARI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALI] = "ALI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRS] = "PRS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLS] = "PLS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARS] = "ARS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALS] = "ALS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPR] = "IPR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPR] = "SPR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAR] = "IAR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAR] = "SAR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPL] = "IPL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPL] = "SPL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAL] = "IAL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAL] = "SAL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIR] = "PIR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSR] = "PSR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIR] = "AIR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASR] = "ASR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIL] = "PIL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSL] = "PSL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIL] = "AIL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASL] = "ASL"; return m_CodeToString[code]; } template int PrintHeader(int argc, char *argv[]) { typedef float inPixelType; typedef itk::Image ImageType; typedef itk::ImageFileReader readertype; typename readertype::Pointer reader = readertype::New(); if( argc < 2 ) { std::cout << "missing input image name" << std::endl; throw; } reader->SetFileName(argv[1]); reader->Update(); // Print only specific header information if( argc > 2 ) { switch( atoi( argv[2] ) ) { case 0: { for( int d = 0; d < static_cast( ImageDimension )-1; d++ ) { std::cout << reader->GetOutput()->GetOrigin()[d] << 'x'; } std::cout << reader->GetOutput()->GetOrigin()[static_cast( ImageDimension )-1] << std::endl; break; } case 1: { for( int d = 0; d < static_cast( ImageDimension )-1; d++ ) { std::cout << reader->GetOutput()->GetSpacing()[d] << 'x'; } std::cout << reader->GetOutput()->GetSpacing()[static_cast( ImageDimension )-1] << std::endl; break; } case 2: { for( int d = 0; d < static_cast( ImageDimension )-1; d++ ) { std::cout << reader->GetOutput()->GetLargestPossibleRegion().GetSize()[d] << 'x'; } std::cout << reader->GetOutput()->GetLargestPossibleRegion().GetSize()[static_cast( ImageDimension )-1] << std::endl; break; } case 3: { for( int d = 0; d < static_cast( ImageDimension )-1; d++ ) { std::cout << reader->GetOutput()->GetLargestPossibleRegion().GetIndex()[d] << 'x'; } std::cout << reader->GetOutput()->GetLargestPossibleRegion().GetIndex()[static_cast( ImageDimension )-1] << std::endl; break; } case 4: { for( int di = 0; di < static_cast( ImageDimension ); di++ ) { for( int dj = 0; dj < static_cast( ImageDimension ); dj++ ) { std::cout << reader->GetOutput()->GetDirection()[di][dj]; if( di == dj && di == static_cast( ImageDimension )-1 ) { std::cout << std::endl; } else { std::cout << 'x'; } } } break; } } return EXIT_SUCCESS; } // else print out entire header information std::cout << " Spacing " << reader->GetOutput()->GetSpacing() << std::endl; std::cout << " Origin " << reader->GetOutput()->GetOrigin() << std::endl; std::cout << " Direction " << std::endl << reader->GetOutput()->GetDirection() << std::endl; if( ImageDimension == 1 ) { std::cout << " Size : " << reader->GetOutput()->GetLargestPossibleRegion().GetSize()[0] << " " << std::endl; } else if( ImageDimension == 2 ) { std::cout << " Size : " << reader->GetOutput()->GetLargestPossibleRegion().GetSize()[0] << " " << reader->GetOutput()->GetLargestPossibleRegion().GetSize()[1] << " " << std::endl; } else if( ImageDimension == 3 ) { std::cout << " Size : " << reader->GetOutput()->GetLargestPossibleRegion().GetSize()[0] << " " << reader->GetOutput()->GetLargestPossibleRegion().GetSize()[1] << " " << " " << reader->GetOutput()->GetLargestPossibleRegion().GetSize()[2] << std::endl; } else { std::cout << " Size : " << reader->GetOutput()->GetLargestPossibleRegion().GetSize() << std::endl; } // std::cout << " Orientation " << reader->GetOutput()->GetOrientation() << std::endl; unsigned int VDim = ImageDimension; // Get the input image typename ImageType::Pointer image = reader->GetOutput(); // Compute the bounding box vnl_vector bb0, bb1, ospm; bb0.set_size(VDim); bb1.set_size(VDim); ospm.set_size(VDim); for( size_t i = 0; i < VDim; i++ ) { bb0[i] = image->GetOrigin()[i]; bb1[i] = bb0[i] + image->GetSpacing()[i] * image->GetBufferedRegion().GetSize()[i]; ospm[i] = -image->GetOrigin()[i] / image->GetSpacing()[i]; } // Compute the intensity range of the image size_t n = image->GetBufferedRegion().GetNumberOfPixels(); float *vox = image->GetBufferPointer(); double iMax = vox[0], iMin = vox[0], iMean = vox[0]; for( size_t i = 1; i < n; i++ ) { iMax = (iMax > vox[i]) ? iMax : vox[i]; iMin = (iMin < vox[i]) ? iMin : vox[i]; iMean += vox[i]; } iMean /= n; // Short or long? bool full = true; if( !full ) { cout << " dim = " << image->GetBufferedRegion().GetSize() << "; "; cout << " bb = {[" << bb0 << "], [" << bb1 << "]}; "; cout << " vox = " << image->GetSpacing() << "; "; cout << " range = [" << iMin << ", " << iMax << "]; "; cout << endl; } else { cout << endl; cout << " Image Dimensions : " << image->GetBufferedRegion().GetSize() << endl; cout << " Bounding Box : " << "{[" << bb0 << "], [" << bb1 << "]}" << endl; cout << " Voxel Spacing : " << image->GetSpacing() << endl; cout << " Intensity Range : [" << iMin << ", " << iMax << "]" << endl; cout << " Mean Intensity : " << iMean << endl; cout << " Direction Cos Mtx. : " << endl; std::cout << image->GetDirection().GetVnlMatrix() << std::endl; // Print NIFTI s-form matrix (check against freesurfer's MRIinfo) cout << " Voxel->RAS x-form : " << endl; // image->GetVoxelSpaceToRASPhysicalSpaceMatrix().GetVnlMatrix(); // std::cout << image->GetVoxelSpaceToRASPhysicalSpaceMatrix().GetVnlMatrix() << std::endl; // // Print metadata cout << " Image Metadata: " << endl; itk::MetaDataDictionary & mdd = image->GetMetaDataDictionary(); itk::MetaDataDictionary::ConstIterator itMeta; for( itMeta = mdd.Begin(); itMeta != mdd.End(); ++itMeta ) { // Get the metadata as a generic object string key = itMeta->first, v_string; itk::SpatialOrientation::ValidCoordinateOrientationFlags v_oflags = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_INVALID; if( itk::ExposeMetaData(mdd, key, v_string) ) { // For some weird reason, some of the strings returned by this method // contain '\0' characters. We will replace them by spaces std::ostringstream sout(""); for( unsigned int i = 0; i < v_string.length(); i++ ) { if( v_string[i] >= ' ' ) { sout << v_string[i]; } } v_string = sout.str(); // Make sure the value has more than blanks if( v_string.find_first_not_of(" ") != v_string.npos ) { cout << " " << key << " = " << v_string << endl; } } else if( itk::ExposeMetaData(mdd, key, v_oflags) ) { cout << " " << key << " = " << get_rai_code(v_oflags) << endl; } else { bool rc = false; if( !rc ) { rc |= try_print_metadata(mdd, key); } if( !rc ) { rc |= try_print_metadata(mdd, key); } if( !rc ) { rc |= try_print_metadata(mdd, key); } if( !rc ) { rc |= try_print_metadata(mdd, key); } if( !rc ) { rc |= try_print_metadata(mdd, key); } if( !rc ) { rc |= try_print_metadata(mdd, key); } if( !rc ) { rc |= try_print_metadata(mdd, key); } if( !rc ) { rc |= try_print_metadata(mdd, key); } if( !rc ) { rc |= try_print_metadata(mdd, key); } if( !rc ) { rc |= try_print_metadata(mdd, key); } if( !rc ) { cout << " " << key << " of unsupported type " << itMeta->second->GetMetaDataObjectTypeName() << endl; } } } } return EXIT_FAILURE; } bool FileExists(string strFilename) { struct stat stFileInfo; bool blnReturn; int intStat; // Attempt to get the file attributes intStat = stat(strFilename.c_str(), &stFileInfo); if( intStat == 0 ) { // We were able to get the file attributes // so the file obviously exists. blnReturn = true; } else { // We were not able to get the file attributes. // This may mean that we don't have permission to // access the folder which contains this file. If you // need to do that level of checking, lookup the // return values of stat which will give you // more details on why stat failed. blnReturn = false; } return blnReturn; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int PrintHeader( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "PrintHeader" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 2 || ( (argc == 2) && ( strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 ) ) ) { std::cout << "Usage: " << argv[0] << " image.ext [whatInformation]" << std::endl; std::cout << " whatInformation: " << std::endl; std::cout << " 0 = origin" << std::endl; std::cout << " 1 = spacing" << std::endl; std::cout << " 2 = size" << std::endl; std::cout << " 3 = index" << std::endl; std::cout << " 4 = direction" << std::endl; if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } // Get the image dimension std::string fn = std::string(argv[1]); if( !FileExists(fn) ) { std::cout << " file " << fn << " does not exist . " << std::endl; return EXIT_FAILURE; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); try { imageIO->ReadImageInformation(); } catch( ... ) { std::cout << " cant read " << fn << std::endl; return EXIT_FAILURE; } switch( imageIO->GetNumberOfDimensions() ) { case 1: { PrintHeader<1>(argc, argv); } break; case 2: { PrintHeader<2>(argc, argv); } break; case 3: { PrintHeader<3>(argc, argv); } break; case 4: { PrintHeader<4>(argc, argv); } break; default: std::cout << "Unsupported dimension " << imageIO->GetNumberOfDimensions() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/RebaseTensorImage.cxx000066400000000000000000000140431311104306400203140ustar00rootroot00000000000000/*=========================================================================1 Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include "ReadWriteData.h" #include "itkPreservationOfPrincipalDirectionTensorReorientationImageFilter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkWarpTensorImageMultiTransformFilter.h" #include "itkTransformFileReader.h" #include "itkTransformFactory.h" namespace ants { // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int RebaseTensorImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "RebaseTensorImage" ); const int argc = args.size(); char * * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 5 ) { std::cout << "Usage: " << argv[0] << " Dimension infile.nii outfile.nii " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } const int dim = atoi(argv[1]); const char * const moving_image_filename = argv[2]; const char * const output_image_filename = argv[3]; if( dim != 3 ) { std::cout << "RebaseTensorImage only supports 3D image volumes" << std::endl; return EXIT_FAILURE; } typedef itk::DiffusionTensor3D PixelType; typedef itk::Image TensorImageType; typedef itk::Image ImageType; // No reason to use log-euclidean space TensorImageType::Pointer img_mov; ReadTensorImage(img_mov, moving_image_filename, false); TensorImageType::DirectionType::InternalMatrixType direction = img_mov->GetDirection().GetVnlMatrix(); direction.set_identity(); std::cout << "Transforming space of " << moving_image_filename; // std::cout << i << " = " << argv[i-1] << std::endl; char * convert = argv[4]; if( strcmp( convert, "PHYSICAL" ) == 0 ) { std::cout << " -> physical space"; direction = img_mov->GetDirection().GetVnlMatrix(); } else if( strcmp( convert, "LOCAL" ) == 0 ) { std::cout << " -> local space"; direction = img_mov->GetDirection().GetTranspose(); } else { std::cout << " -> " << convert << " space"; ImageType::Pointer target; ReadImage( target, convert ); direction = img_mov->GetDirection().GetTranspose() * target->GetDirection().GetVnlMatrix(); } // direction = direction.transpose(); // to accomodate for how // eigenvectors are stored std::cout << std::endl; std::cout << "Final rebasing matrix: " << std::endl << direction << std::endl; if( !direction.is_identity(0.00001) ) { itk::ImageRegionIteratorWithIndex it( img_mov, img_mov->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { /* PixelType dt = it.Value(); PixelType::EigenValuesArrayType evalues; PixelType::EigenVectorsMatrixType evectors; dt.ComputeEigenAnalysis( evalues, evectors ); evectors = evectors * direction; PixelType::EigenVectorsMatrixType emat; emat.Fill( 0.0 ); for (unsigned int i=0; i<3; i++) { emat(i,i) = evalues[i]; } PixelType::EigenVectorsMatrixType::InternalMatrixType matrixDT = evectors.GetTranspose() * emat.GetVnlMatrix() * evectors.GetVnlMatrix(); */ PixelType::EigenVectorsMatrixType::InternalMatrixType dt; dt(0, 0) = it.Value()[0]; dt(0, 1) = dt(1, 0) = it.Value()[1]; dt(0, 2) = dt(2, 0) = it.Value()[2]; dt(1, 1) = it.Value()[3]; dt(1, 2) = dt(2, 1) = it.Value()[4]; dt(2, 2) = it.Value()[5]; if( ( it.Value()[0] + it.Value()[3] + it.Value()[5] ) > 0.00001 ) { PixelType::EigenVectorsMatrixType::InternalMatrixType matrixDT = direction * dt * direction.transpose(); PixelType outDT; outDT[0] = matrixDT(0, 0); outDT[1] = matrixDT(1, 0); outDT[2] = matrixDT(2, 0); outDT[3] = matrixDT(1, 1); outDT[4] = matrixDT(2, 1); outDT[5] = matrixDT(2, 2); it.Set( outDT ); } ++it; } } else { std::cout << "Identity transform detected.. image unmodified" << std::endl; } // No reason to use log-euclidean space here WriteTensorImage(img_mov, output_image_filename, false); return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ReorientTensorImage.cxx000066400000000000000000000176551311104306400207160ustar00rootroot00000000000000/*=========================================================================1 Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include "antsUtilities.h" #include "ReadWriteData.h" #include "itkPreservationOfPrincipalDirectionTensorReorientationImageFilter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkWarpTensorImageMultiTransformFilter.h" #include "itkTransformFileReader.h" #include "itkTransformFactory.h" namespace ants { static bool ReorientTensorImage_ParseInput(int argc, char * *argv, char *& moving_image_filename, char *& output_image_filename, TRAN_OPT_QUEUE & opt_queue) { opt_queue.clear(); opt_queue.reserve(argc - 2); moving_image_filename = argv[0]; output_image_filename = argv[1]; int ind = 2; while( ind < argc ) { TRAN_OPT opt; opt.filename = argv[ind]; opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; if( strcmp(argv[ind], "-i") == 0 ) { std::cout << "ERROR - inverse transforms not yet supported\n" << std::endl; return false; } else { bool set_current_affine_inv = false; if( opt.file_type == AFFINE_FILE ) { SetAffineInvFlag(opt, set_current_affine_inv); } else { if( opt.file_type == DEFORMATION_FILE && set_current_affine_inv ) { std::cout << "Ignore inversion of non-affine file type! " << std::endl; std::cout << "opt.do_affine_inv:" << opt.do_affine_inv << std::endl; } } opt_queue.push_back(opt); DisplayOpt(opt); } ++ind; } return true; } template void ReorientTensorImage(char *moving_image_filename, char *output_image_filename, TRAN_OPT_QUEUE & opt_queue) { typedef itk::DiffusionTensor3D PixelType; typedef itk::Image TensorImageType; typedef itk::Image ImageType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::MatrixOffsetTransformBase AffineTransformType; itk::TransformFactory::RegisterTransform(); typedef itk::ImageFileReader ImageFileReaderType; typename TensorImageType::Pointer img_mov; // No reason to use log-euclidean space ReadTensorImage(img_mov, moving_image_filename, false); typename ImageType::Pointer img_ref = ITK_NULLPTR; typename ImageFileReaderType::Pointer reader_img_ref = ImageFileReaderType::New(); typedef itk::TransformFileReader TranReaderType; typedef itk::ImageFileReader FieldReaderType; typename DisplacementFieldType::Pointer field = ITK_NULLPTR; typename AffineTransformType::Pointer aff = ITK_NULLPTR; const int kOptQueueSize = opt_queue.size(); if( kOptQueueSize > 1 ) { std::cout << "ERROR: Only 1 input transform is permitted" << std::endl; return; } typedef itk::PreservationOfPrincipalDirectionTensorReorientationImageFilter PPDReorientType; typename PPDReorientType::Pointer reo = PPDReorientType::New(); reo->SetInput( img_mov ); const TRAN_OPT & opt = opt_queue[0]; switch( opt.file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); aff = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); reo->SetAffineTransform( aff ); std::cout << "Affine transform" << std::endl; } break; case DEFORMATION_FILE: { typename FieldReaderType::Pointer field_reader = FieldReaderType::New(); field_reader->SetFileName( opt.filename ); field_reader->Update(); // field = field_reader->GetOutput(); reo->SetDisplacementField( field_reader->GetOutput() ); std::cout << "Warp transform" << std::endl; } break; default: { std::cout << "Unknown file type!" << std::endl; } } reo->Update(); typename TensorImageType::Pointer img_output = reo->GetOutput(); // No reason to use log-euclidean space here WriteTensorImage(img_output, output_image_filename, false); } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ReorientTensorImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ReorientTensorImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Usage: " << argv[0] << " Dimension infile.nii outfile.nii " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } TRAN_OPT_QUEUE opt_queue; char * moving_image_filename = ITK_NULLPTR; char * output_image_filename = ITK_NULLPTR; bool is_parsing_ok = false; int dim = atoi(argv[1]); if( dim != 3 ) { std::cout << "ReorientTensorImage only supports 3D image volumes" << std::endl; return EXIT_FAILURE; } is_parsing_ok = ReorientTensorImage_ParseInput(argc - 2, argv + 2, moving_image_filename, output_image_filename, opt_queue); if( is_parsing_ok ) { std::cout << "moving_image_filename: " << moving_image_filename << std::endl; std::cout << "output_image_filename: " << output_image_filename << std::endl; DisplayOptQueue(opt_queue); ReorientTensorImage<3>(moving_image_filename, output_image_filename, opt_queue); } else { std::cout << "Input error!" << std::endl; return EXIT_FAILURE; } // ReorientTensorImage<3>(argc,argv); // WarpImageForward(argc,argv); return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ResampleImage.cxx000066400000000000000000000257531311104306400175020ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkResampleImageFilter.h" #include "itkConstantBoundaryCondition.h" #include "itkIdentityTransform.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkGaussianInterpolateImageFunction.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "ReadWriteData.h" #include #include namespace ants { template int ResampleImage( int argc, char *argv[] ) { typedef double RealType; typedef double PixelType; typedef itk::Image ImageType; typename ImageType::Pointer image = ITK_NULLPTR; ReadImage( image, argv[2] ); typedef itk::IdentityTransform TransformType; typename TransformType::Pointer transform = TransformType::New(); transform->SetIdentity(); typedef itk::LinearInterpolateImageFunction LinearInterpolatorType; typename LinearInterpolatorType::Pointer interpolator = LinearInterpolatorType::New(); interpolator->SetInputImage( image ); typedef itk::NearestNeighborInterpolateImageFunction NearestNeighborInterpolatorType; typename NearestNeighborInterpolatorType::Pointer nn_interpolator = NearestNeighborInterpolatorType::New(); nn_interpolator->SetInputImage( image ); typedef itk::BSplineInterpolateImageFunction BSplineInterpolatorType; typename BSplineInterpolatorType::Pointer bs_interpolator = BSplineInterpolatorType::New(); bs_interpolator->SetInputImage( image ); typedef itk::GaussianInterpolateImageFunction GaussianInterpolatorType; typename GaussianInterpolatorType::Pointer g_interpolator = GaussianInterpolatorType::New(); g_interpolator->SetInputImage( image ); typedef itk::WindowedSincInterpolateImageFunction HammingInterpolatorType; typename HammingInterpolatorType::Pointer sh_interpolator = HammingInterpolatorType::New(); sh_interpolator->SetInputImage( image ); typedef itk::WindowedSincInterpolateImageFunction > Sinc1InterpolatorType; typename Sinc1InterpolatorType::Pointer sc_interpolator = Sinc1InterpolatorType::New(); sc_interpolator->SetInputImage( image ); typedef itk::WindowedSincInterpolateImageFunction > Sinc2InterpolatorType; typename Sinc2InterpolatorType::Pointer sw_interpolator = Sinc2InterpolatorType::New(); sw_interpolator->SetInputImage( image ); typedef itk::WindowedSincInterpolateImageFunction > Sinc3InterpolatorType; typename Sinc3InterpolatorType::Pointer sl_interpolator = Sinc3InterpolatorType::New(); sl_interpolator->SetInputImage( image ); typename Sinc3InterpolatorType::Pointer sb_interpolator = Sinc3InterpolatorType::New(); sb_interpolator->SetInputImage( image ); typedef itk::ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); typename ResamplerType::SpacingType spacing; typename ResamplerType::SizeType size; typename ImageType::IndexType oldStartIndex = image->GetLargestPossibleRegion().GetIndex(); typename ImageType::IndexType newStartIndex; newStartIndex.Fill(0); // should be "same" as original start index but in new physical space std::vector sp = ConvertVector( std::string( argv[4] ) ); if( argc <= 5 || atoi( argv[5] ) == 0 ) { if( sp.size() == 1 ) { spacing.Fill( sp[0] ); } else if( sp.size() == ImageDimension ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { spacing[d] = sp[d]; } } else { std::cout << "Invalid spacing." << std::endl; } for( unsigned int i = 0; i < ImageDimension; i++ ) { RealType spacing_old = image->GetSpacing()[i]; RealType size_old = image->GetLargestPossibleRegion().GetSize()[i]; size[i] = static_cast( ( spacing_old * size_old ) / spacing[i] + 0.5 ); RealType oldstart = static_cast( oldStartIndex[i] ); newStartIndex[i] = static_cast( ( spacing_old * oldstart ) / spacing[i] + 0.5 ); } } else { if( sp.size() == 1 ) { size.Fill( static_cast( sp[0] ) ); } else if( sp.size() == ImageDimension ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { size[d] = static_cast( sp[d] ); } } else { std::cout << "Invalid size." << std::endl; } for( unsigned int i = 0; i < ImageDimension; i++ ) { RealType spacing_old = image->GetSpacing()[i]; RealType size_old = image->GetLargestPossibleRegion().GetSize()[i]; float ratio = static_cast( size_old - 1.0 ) / static_cast( size[i] - 1.0 ); spacing[i] = spacing_old * ratio; RealType oldstart = static_cast( oldStartIndex[i] ); newStartIndex[i] = static_cast( oldstart * ratio + 0.5 ); } } char arg7 = '\0'; if( argc > 7 ) { arg7 = *argv[7]; } resampler->SetTransform( transform ); resampler->SetInterpolator( interpolator ); if( argc > 6 && atoi( argv[6] ) ) { switch( atoi( argv[6] ) ) { case 0: default: { resampler->SetInterpolator( interpolator ); } break; case 1: { resampler->SetInterpolator( nn_interpolator ); } break; case 2: { double sigma[ImageDimension]; for( unsigned int d = 0; d < ImageDimension; d++ ) { sigma[d] = image->GetSpacing()[d]; } double alpha = 1.0; if( argc > 7 ) { std::vector sg = ConvertVector( std::string( argv[7] ) ); for( unsigned int d = 0; d < ImageDimension; d++ ) { sigma[d] = sg[d]; } } if( argc > 8 ) { alpha = static_cast( atof( argv[8] ) ); } g_interpolator->SetParameters( sigma, alpha ); resampler->SetInterpolator( g_interpolator ); } break; case 3: { switch( arg7 ) { case 'h': default: { resampler->SetInterpolator( sh_interpolator ); } break; case 'c': { resampler->SetInterpolator( sc_interpolator ); } break; case 'l': { resampler->SetInterpolator( sl_interpolator ); } break; case 'w': { resampler->SetInterpolator( sw_interpolator ); } break; case 'b': { resampler->SetInterpolator( sb_interpolator ); } break; } } case 4: { if( argc > 7 && atoi( argv[7] ) >= 0 && atoi( argv[7] ) <= 5 ) { bs_interpolator->SetSplineOrder( atoi( argv[7] ) ); } else { bs_interpolator->SetSplineOrder( 3 ); } resampler->SetInterpolator( bs_interpolator ); } break; } } resampler->SetInput( image ); resampler->SetSize( size ); resampler->SetOutputOrigin( image->GetOrigin() ); resampler->SetOutputDirection( image->GetDirection() ); resampler->SetOutputSpacing( spacing ); // resampler->SetOutputStartIndex( newStartIndex ); resampler->SetDefaultPixelValue( 0 ); resampler->Update(); typename ImageType::Pointer outimage = resampler->GetOutput(); // typename ImageType::RegionType region = outimage->GetLargestPossibleRegion(); // region.SetIndex( newStartIndex ); // outimage->SetLargestPossibleRegion( region ); WriteImage( outimage , argv[3] ); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ResampleImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ResampleImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 5 ) { std::cout << "Usage: " << argv[0] << " imageDimension inputImage " << "outputImage MxNxO [size=1,spacing=0] [interpolate type]" << std::endl; std::cout << " Interpolation type: " << std::endl; std::cout << " 0. linear (default)" << std::endl; std::cout << " 1. nn " << std::endl; std::cout << " 2. gaussian [sigma=imageSpacing] [alpha=1.0]" << std::endl; std::cout << " 3. windowedSinc [type = 'c'osine, 'w'elch, 'b'lackman, 'l'anczos, 'h'amming]" << std::endl; std::cout << " 4. B-Spline [order=3]" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: { return ResampleImage<2>( argc, argv ); } break; case 3: { return ResampleImage<3>( argc, argv ); } break; case 4: { return ResampleImage<4>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ResampleImageBySpacing.cxx000066400000000000000000000354731311104306400213020ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include "itkImage.h" #include "ReadWriteData.h" #include "itkResampleImageFilter.h" #include "itkIdentityTransform.h" #include "itkLinearInterpolateImageFunction.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkRecursiveGaussianImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" namespace ants { // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ResampleImageBySpacing( std::vector args, std::ostream* /*out_stream = NULL */ ) { args.insert( args.begin(), "ResampleImageBySpacing" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 5 ) { std::cout << "Usage: " << std::endl; std::cout << argv[0] << " ImageDimension inputImageFile outputImageFile outxspc outyspc {outzspacing} {dosmooth?} {addvox} {nn-interp?}" << std::endl; std::cout << " addvox pads each dimension by addvox " << std::endl; std::cout << " " << std::endl; // std::cout << " interp 0 = linear, 1 = nn " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } unsigned int Dimension = atoi(argv[1]); if( Dimension == 2 ) { typedef float InputPixelType; typedef float InternalPixelType; typedef float OutputPixelType; typedef itk::Image InputImageType; typedef itk::Image InternalImageType; typedef itk::Image OutputImageType; InputImageType::Pointer inputImage; ReadImage( inputImage, argv[2] ); const InputImageType::SpacingType& inputSpacing = inputImage->GetSpacing(); OutputImageType::SpacingType spacing; for( int i = 0; i < 2; i++ ) { spacing[i] = inputSpacing[i]; } std::cout << " spacing " << spacing << " dim " << 2 << std::endl; bool dosmooth = 1; if( argc > 4 ) { spacing[0] = atof(argv[4]); } if( argc > 5 ) { spacing[1] = atof(argv[5]); } if( argc > 6 ) { dosmooth = atoi(argv[6]); } int addvox = 0; if( argc > 7 ) { addvox = atoi(argv[7]); } bool nn = false; if( argc > 8 ) { nn = atoi(argv[7]); } std::cout << " spacing2 " << spacing << std::endl; InternalImageType::Pointer smoothedImage = inputImage; if( dosmooth ) { for( int sm = 0; sm < 2; sm++ ) { typedef itk::RecursiveGaussianImageFilter< OutputImageType, OutputImageType> GaussianFilterType; GaussianFilterType::Pointer smootherX = GaussianFilterType::New(); smootherX->SetInput( smoothedImage ); const float sig = atof(argv[4 + sm]) / inputSpacing[sm] - 1.0; std::cout << " smoothing by : " << sig << " dir " << sm << std::endl; smootherX->SetSigma( sig ); smootherX->SetDirection( sm ); smootherX->SetNormalizeAcrossScale( false ); if( sig > 0 && dosmooth ) { try { smootherX->Update(); } catch( itk::ExceptionObject & excep ) { std::cout << "Exception catched !" << std::endl; std::cout << excep << std::endl; } smoothedImage = smootherX->GetOutput(); } } } // InternalImageType::ConstPointer smoothedImage = smootherY->GetOutput(); // InternalImageType::ConstPointer smoothedImage = reader->GetOutput(); // smoothedImage =SmoothImage(reader->GetOutput() , ); typedef itk::ResampleImageFilter< InternalImageType, OutputImageType> ResampleFilterType; ResampleFilterType::Pointer resampler = ResampleFilterType::New(); typedef itk::IdentityTransform TransformType; typedef itk::LinearInterpolateImageFunction< InternalImageType, double> InterpolatorType; typedef itk::NearestNeighborInterpolateImageFunction< InternalImageType, double> InterpolatorType2; InterpolatorType::Pointer interpolator = InterpolatorType::New(); InterpolatorType2::Pointer interpolator2 = InterpolatorType2::New(); resampler->SetInterpolator( interpolator ); if( nn == 1 ) { resampler->SetInterpolator( interpolator2 ); } InternalImageType::IndexType ind; ind.Fill(1); resampler->SetDefaultPixelValue( inputImage->GetPixel(ind) ); // zero regions without source // Use the inputImage as initial template resampler->SetOutputParametersFromImage( inputImage ); // Reset spacing by explicit specification std::cout << " out space " << spacing << std::endl; resampler->SetOutputSpacing( spacing ); InputImageType::SizeType inputSize = inputImage->GetLargestPossibleRegion().GetSize(); typedef InputImageType::SizeType::SizeValueType SizeValueType; InputImageType::SizeType size; for( int i = 0; i < 2; i++ ) { size[i] = static_cast(inputSize[i] * inputSpacing[i] / spacing[i] + addvox); } std::cout << " output size " << size << " spc " << spacing << std::endl; resampler->SetSize( size ); resampler->SetInput( smoothedImage ); TransformType::Pointer transform = TransformType::New(); transform->SetIdentity(); resampler->SetTransform( transform ); resampler->Update(); WriteImage( resampler->GetOutput(), argv[3] ); } if( Dimension == 3 ) { typedef float InputPixelType; typedef float InternalPixelType; typedef float OutputPixelType; typedef itk::Image InputImageType; typedef itk::Image InternalImageType; typedef itk::Image OutputImageType; InputImageType::Pointer inputImage; ReadImage( inputImage, argv[2] ); const InputImageType::SpacingType& inputSpacing = inputImage->GetSpacing(); OutputImageType::SpacingType spacing; for( int i = 0; i < 3; i++ ) { spacing[i] = inputSpacing[i]; } std::cout << " spacing " << spacing << " dim " << 3 << std::endl; bool dosmooth = 1; if( argc > 4 ) { spacing[0] = atof(argv[4]); } if( argc > 5 ) { spacing[1] = atof(argv[5]); } if( argc > 6 ) { spacing[2] = atof(argv[6]); } if( argc > 7 ) { dosmooth = atoi(argv[7]); } int addvox = 0; if( argc > 8 ) { addvox = atoi(argv[8]); } bool nn = false; if( argc > 9 ) { nn = atoi(argv[9]); } std::cout << " spacing2 " << spacing << std::endl; InternalImageType::Pointer smoothedImage = inputImage; if( dosmooth ) { for( int sm = 0; sm < 3; sm++ ) { typedef itk::RecursiveGaussianImageFilter< OutputImageType, OutputImageType> GaussianFilterType; GaussianFilterType::Pointer smootherX = GaussianFilterType::New(); smootherX->SetInput( smoothedImage ); const float sig = atof(argv[4 + sm]) / inputSpacing[sm] - 1.0; std::cout << " smoothing by : " << sig << " dir " << sm << std::endl; smootherX->SetSigma( sig ); smootherX->SetDirection( sm ); smootherX->SetNormalizeAcrossScale( false ); if( sig > 0 && dosmooth ) { try { smootherX->Update(); } catch( itk::ExceptionObject & excep ) { std::cout << "Exception catched !" << std::endl; std::cout << excep << std::endl; } smoothedImage = smootherX->GetOutput(); } } } // InternalImageType::ConstPointer smoothedImage = smootherY->GetOutput(); // InternalImageType::ConstPointer smoothedImage = reader->GetOutput(); // smoothedImage =SmoothImage(reader->GetOutput() , ); typedef itk::ResampleImageFilter< InternalImageType, OutputImageType> ResampleFilterType; ResampleFilterType::Pointer resampler = ResampleFilterType::New(); typedef itk::IdentityTransform TransformType; typedef itk::LinearInterpolateImageFunction< InternalImageType, double> InterpolatorType; typedef itk::NearestNeighborInterpolateImageFunction< InternalImageType, double> InterpolatorType2; InterpolatorType::Pointer interpolator = InterpolatorType::New(); InterpolatorType2::Pointer interpolator2 = InterpolatorType2::New(); resampler->SetInterpolator( interpolator ); if( nn == 1 ) { resampler->SetInterpolator( interpolator2 ); } InternalImageType::IndexType ind; ind.Fill(1); resampler->SetDefaultPixelValue( inputImage->GetPixel(ind) ); // zero regions without source // Use the inputImage as initial template resampler->SetOutputParametersFromImage( inputImage ); // Reset spacing by explicit specification std::cout << " out space " << spacing << std::endl; resampler->SetOutputSpacing( spacing ); InputImageType::SizeType inputSize = inputImage->GetLargestPossibleRegion().GetSize(); typedef InputImageType::SizeType::SizeValueType SizeValueType; InputImageType::SizeType size; for( int i = 0; i < 3; i++ ) { size[i] = static_cast(inputSize[i] * inputSpacing[i] / spacing[i] + addvox); } std::cout << " output size " << size << " spc " << spacing << std::endl; resampler->SetSize( size ); resampler->SetInput( smoothedImage ); TransformType::Pointer transform = TransformType::New(); transform->SetIdentity(); resampler->SetTransform( transform ); resampler->Update(); WriteImage( resampler->GetOutput(), argv[3] ); } /*ADDING 4-dimensional images */ if( Dimension == 4 ) { typedef float InputPixelType; typedef float InternalPixelType; typedef float OutputPixelType; typedef itk::Image InputImageType; typedef itk::Image InternalImageType; typedef itk::Image OutputImageType; InputImageType::Pointer inputImage; ReadImage( inputImage, argv[2] ); const InputImageType::SpacingType& inputSpacing = inputImage->GetSpacing(); OutputImageType::SpacingType spacing; for( int i = 0; i < 4; i++ ) { spacing[i] = inputSpacing[i]; } std::cout << " spacing " << spacing << " dim " << 4 << std::endl; bool dosmooth = 1; if( argc > 4 ) { spacing[0] = atof(argv[4]); } if( argc > 5 ) { spacing[1] = atof(argv[5]); } if( argc > 6 ) { spacing[2] = atof(argv[6]); } if( argc > 7 ) { spacing[3] = atof(argv[7]); } if( argc > 8 ) { dosmooth = atoi(argv[8]); } int addvox = 0; if( argc > 9 ) { addvox = atoi(argv[9]); } bool nn = false; if( argc > 9 ) { nn = atoi(argv[10]); } std::cout << " spacing2 " << spacing << std::endl; InternalImageType::Pointer smoothedImage = inputImage; if( dosmooth ) { for( int sm = 0; sm < 4; sm++ ) { typedef itk::RecursiveGaussianImageFilter< OutputImageType, OutputImageType> GaussianFilterType; GaussianFilterType::Pointer smootherX = GaussianFilterType::New(); smootherX->SetInput( smoothedImage ); const float sig = atof(argv[4 + sm]) / inputSpacing[sm] - 1.0; std::cout << " smoothing by : " << sig << " dir " << sm << std::endl; smootherX->SetSigma( sig ); smootherX->SetDirection( sm ); smootherX->SetNormalizeAcrossScale( false ); if( sig > 0 && dosmooth ) { try { smootherX->Update(); } catch( itk::ExceptionObject & excep ) { std::cout << "Exception catched !" << std::endl; std::cout << excep << std::endl; } smoothedImage = smootherX->GetOutput(); } } } // InternalImageType::ConstPointer smoothedImage = smootherY->GetOutput(); // InternalImageType::ConstPointer smoothedImage = reader->GetOutput(); // smoothedImage =SmoothImage(reader->GetOutput() , ); typedef itk::ResampleImageFilter< InternalImageType, OutputImageType> ResampleFilterType; ResampleFilterType::Pointer resampler = ResampleFilterType::New(); typedef itk::IdentityTransform TransformType; typedef itk::LinearInterpolateImageFunction< InternalImageType, double> InterpolatorType; typedef itk::NearestNeighborInterpolateImageFunction< InternalImageType, double> InterpolatorType2; InterpolatorType::Pointer interpolator = InterpolatorType::New(); InterpolatorType2::Pointer interpolator2 = InterpolatorType2::New(); resampler->SetInterpolator( interpolator ); if( nn == 1 ) { resampler->SetInterpolator( interpolator2 ); } InternalImageType::IndexType ind; ind.Fill(1); resampler->SetDefaultPixelValue( inputImage->GetPixel(ind) ); // zero regions without source // Use the inputImage as initial template resampler->SetOutputParametersFromImage( inputImage ); // Reset spacing by explicit specification std::cout << " out space " << spacing << std::endl; resampler->SetOutputSpacing( spacing ); InputImageType::SizeType inputSize = inputImage->GetLargestPossibleRegion().GetSize(); typedef InputImageType::SizeType::SizeValueType SizeValueType; InputImageType::SizeType size; for( int i = 0; i < 4; i++ ) { size[i] = static_cast(inputSize[i] * inputSpacing[i] / spacing[i] + addvox); } std::cout << " output size " << size << " spc " << spacing << std::endl; resampler->SetSize( size ); resampler->SetInput( smoothedImage ); TransformType::Pointer transform = TransformType::New(); transform->SetIdentity(); resampler->SetTransform( transform ); resampler->Update(); WriteImage( resampler->GetOutput(), argv[3] ); } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ResetDirection.cxx000066400000000000000000000116761311104306400177110ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include #include #include #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "itkCastImageFilter.h" #include "itkRescaleIntensityImageFilter.h" namespace ants { template int ResetDirection(int argc, char *argv[]) { if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " infile.nii outfile.nii " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } typedef float outPixelType; typedef float inPixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; typedef itk::ImageFileReader readertype; typedef itk::ImageFileWriter writertype; typename readertype::Pointer reader = readertype::New(); reader->SetFileName(argv[1]); reader->Update(); typename OutImageType::Pointer outim = reader->GetOutput(); typename OutImageType::DirectionType direction = outim->GetDirection(); direction.SetIdentity(); typedef itk::ImageRegionIteratorWithIndex Iterator; typename ImageType::Pointer varimage = AllocImage(outim); varimage->SetDirection( direction ); Iterator vfIter2( varimage, varimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { vfIter2.Set(outim->GetPixel(vfIter2.GetIndex() ) ); } typename writertype::Pointer writer = writertype::New(); writer->SetFileName(argv[2]); writer->SetInput( varimage ); writer->Update(); writer->Write(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ResetDirection( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ResetDirection" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " infile.nii outfile.nii " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension std::string fn = std::string(argv[1]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); switch( imageIO->GetNumberOfDimensions() ) { case 2: { return ResetDirection<2>(argc, argv); } break; case 3: { return ResetDirection<3>(argc, argv); } break; case 4: { return ResetDirection<4>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/SetDirectionByMatrix.cxx000066400000000000000000000130211311104306400210240ustar00rootroot00000000000000/* * ResetDirection2.cxx * * Created on: Nov 14, 2008 * Author: songgang */ /*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include #include #include "antsAllocImage.h" #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "itkCastImageFilter.h" #include "itkRescaleIntensityImageFilter.h" namespace ants { template int ResetDirection(int argc, char *argv[]) { if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " infile.nii outfile.nii direction matrix in a row " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } typedef float outPixelType; typedef float inPixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; typedef itk::ImageFileReader readertype; typedef itk::ImageFileWriter writertype; typename readertype::Pointer reader = readertype::New(); reader->SetFileName(argv[1]); reader->Update(); typename OutImageType::Pointer outim = reader->GetOutput(); typename OutImageType::DirectionType direction = outim->GetDirection(); // direction->SetIdentity(); // direction.Fill(0); // for (unsigned int i=0;iset_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " infile.nii outfile.nii d01 d02 d03 d10 .... " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension std::string fn = std::string(argv[1]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); switch( imageIO->GetNumberOfDimensions() ) { case 2: { return ResetDirection<2>(argc, argv); } break; case 3: { return ResetDirection<3>(argc, argv); } break; case 4: { return ResetDirection<4>(argc,argv); } break; default: std::cout << "Unsupported dimension " << imageIO->GetNumberOfDimensions() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/SetOrigin.cxx000066400000000000000000000113101311104306400166520ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include #include #include #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "itkCastImageFilter.h" #include "itkRescaleIntensityImageFilter.h" namespace ants { template int SetOrigin(int argc, char *argv[]) { typedef float outPixelType; typedef float inPixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; typedef itk::ImageFileReader readertype; typedef itk::ImageFileWriter writertype; typename readertype::Pointer reader = readertype::New(); reader->SetFileName(argv[1]); reader->Update(); typename OutImageType::Pointer outim = reader->GetOutput(); typename OutImageType::PointType orig = outim->GetOrigin(); std::cout << " Old Orig " << outim->GetOrigin(); if( argc > 3 ) { orig[0] = atof(argv[3]); } if( argc > 4 ) { orig[1] = atof(argv[4]); } if( argc > 5 ) { orig[2] = atof(argv[5]); } std::cout << " New Orig " << orig << std::endl; outim->SetOrigin(orig); typedef itk::ImageRegionIteratorWithIndex Iterator; typename ImageType::Pointer varimage = AllocImage(outim->GetLargestPossibleRegion(), outim->GetSpacing(), orig, outim->GetDirection() ); Iterator vfIter2( varimage, varimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { vfIter2.Set(outim->GetPixel(vfIter2.GetIndex() ) ); } typename writertype::Pointer writer = writertype::New(); writer->SetFileName(argv[2]); writer->SetInput( varimage ); writer->Update(); writer->Write(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int SetOrigin( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "SetOrigin" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " Dimension infile.hdr outfile.nii OriginX OriginY {OriginZ} " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension switch( atoi(argv[1]) ) { case 2: { return SetOrigin<2>(argc - 1, argv + 1); } break; case 3: { return SetOrigin<3>(argc - 1, argv + 1); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/SetSpacing.cxx000066400000000000000000000112411311104306400170120ustar00rootroot00000000000000/* * SetSpacing.cxx * * Created on: Mar 5, 2009 * Author: songgang */ /*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include #include #include #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "itkCastImageFilter.h" #include "itkRescaleIntensityImageFilter.h" namespace ants { template int SetSpacing(int argc, char *argv[]) { typedef float outPixelType; typedef float inPixelType; typedef itk::Image ImageType; typedef itk::Image OutImageType; typedef itk::ImageFileReader readertype; typedef itk::ImageFileWriter writertype; typename readertype::Pointer reader = readertype::New(); reader->SetFileName(argv[1]); reader->Update(); typename OutImageType::Pointer outim = reader->GetOutput(); typename OutImageType::SpacingType spacing = outim->GetSpacing(); std::cout << " Old Spacing " << outim->GetSpacing(); if( argc > 3 ) { spacing[0] = atof(argv[3]); } if( argc > 4 ) { spacing[1] = atof(argv[4]); } if( argc > 5 ) { spacing[2] = atof(argv[5]); } std::cout << " New Spacing " << spacing << std::endl; typedef itk::ImageRegionIteratorWithIndex Iterator; typename ImageType::Pointer varimage = AllocImage(outim); varimage->SetSpacing(spacing); Iterator vfIter2( varimage, varimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { vfIter2.Set(outim->GetPixel(vfIter2.GetIndex() ) ); } typename writertype::Pointer writer = writertype::New(); writer->SetFileName(argv[2]); writer->SetInput( varimage ); writer->Update(); writer->Write(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int SetSpacing( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "SetSpacing" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " Dimension infile.hdr outfile.nii SpacingX SpacingY {SpacingZ} " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension switch( atoi(argv[1]) ) { case 2: { return SetSpacing<2>(argc - 1, argv + 1); } break; case 3: { return SetSpacing<3>(argc - 1, argv + 1); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/SmoothDisplacementField.cxx000066400000000000000000000234441311104306400215300ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include "ReadWriteData.h" #include "itkDisplacementFieldToBSplineImageFilter.h" #include "itkGaussianOperator.h" #include "itkImage.h" #include "itkImageDuplicator.h" #include "itkTimeProbe.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkVectorNeighborhoodOperatorImageFilter.h" namespace ants { template int SmoothDisplacementField( int argc, char *argv[] ) { typedef float RealType; typedef itk::Image RealImageType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typename DisplacementFieldType::Pointer field = ITK_NULLPTR; ReadImage( field, argv[2] ); typename DisplacementFieldType::Pointer smoothField = DisplacementFieldType::New(); float elapsedTime = 0.0; std::vector var = ConvertVector( std::string( argv[4] ) ); if( var.size() == 1 ) { float variance = var[0]; typedef itk::GaussianOperator GaussianSmoothingOperatorType; typedef itk::VectorNeighborhoodOperatorImageFilter GaussianSmoothingSmootherType; GaussianSmoothingOperatorType gaussianSmoothingOperator; typedef itk::ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( field ); duplicator->Update(); smoothField = duplicator->GetOutput(); itk::TimeProbe timer; timer.Start(); typename GaussianSmoothingSmootherType::Pointer smoother = GaussianSmoothingSmootherType::New(); for( unsigned int dimension = 0; dimension < ImageDimension; ++dimension ) { // smooth along this dimension gaussianSmoothingOperator.SetDirection( dimension ); gaussianSmoothingOperator.SetVariance( variance ); gaussianSmoothingOperator.SetMaximumError( 0.001 ); gaussianSmoothingOperator.SetMaximumKernelWidth( smoothField->GetRequestedRegion().GetSize()[dimension] ); gaussianSmoothingOperator.CreateDirectional(); // todo: make sure we only smooth within the buffered region smoother->SetOperator( gaussianSmoothingOperator ); smoother->SetInput( smoothField ); smoother->Update(); smoothField = smoother->GetOutput(); smoothField->Update(); smoothField->DisconnectPipeline(); } const VectorType zeroVector( 0.0 ); //make sure boundary does not move float weight1 = 1.0; if (variance < 0.5) { weight1 = 1.0 - 1.0 * ( variance / 0.5); } float weight2 = 1.0 - weight1; const typename DisplacementFieldType::RegionType region = field->GetLargestPossibleRegion(); const typename DisplacementFieldType::SizeType size = region.GetSize(); const typename DisplacementFieldType::IndexType startIndex = region.GetIndex(); itk::ImageRegionConstIteratorWithIndex fieldIt( field, field->GetLargestPossibleRegion() ); itk::ImageRegionIteratorWithIndex smoothedFieldIt( smoothField, smoothField->GetLargestPossibleRegion() ); for( fieldIt.GoToBegin(), smoothedFieldIt.GoToBegin(); !fieldIt.IsAtEnd(); ++fieldIt, ++smoothedFieldIt ) { typename DisplacementFieldType::IndexType index = fieldIt.GetIndex(); bool isOnBoundary = false; for ( unsigned int dimension = 0; dimension < ImageDimension; ++dimension ) { if( index[dimension] == startIndex[dimension] || index[dimension] == static_cast( size[dimension] ) - startIndex[dimension] - 1 ) { isOnBoundary = true; break; } } if( isOnBoundary ) { smoothedFieldIt.Set( zeroVector ); } else { smoothedFieldIt.Set( smoothedFieldIt.Get() * weight1 + fieldIt.Get() * weight2 ); } } timer.Stop(); elapsedTime = timer.GetMean(); } else if( var.size() == ImageDimension ) { typedef itk::DisplacementFieldToBSplineImageFilter BSplineFilterType; unsigned int numberOfLevels = 1; if( argc > 5 ) { numberOfLevels = atoi( argv[5] ); } unsigned int splineOrder = 3; if( argc > 6 ) { splineOrder = atoi( argv[6] ); } typename BSplineFilterType::ArrayType ncps; for( unsigned int d = 0; d < ImageDimension; d++ ) { ncps[d] = var[d] + splineOrder; } typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); bspliner->SetDisplacementField( field ); bspliner->SetBSplineDomainFromImage( field ); bspliner->SetNumberOfControlPoints( ncps ); bspliner->SetSplineOrder( splineOrder ); bspliner->SetNumberOfFittingLevels( numberOfLevels ); bspliner->SetEnforceStationaryBoundary( true ); if( argc > 7 ) { typename BSplineFilterType::RealImageType::Pointer confidenceImage = ITK_NULLPTR; ReadImage( confidenceImage, argv[7] ); bspliner->SetConfidenceImage( confidenceImage ); } bspliner->SetEstimateInverse( false ); itk::TimeProbe timer; timer.Start(); bspliner->Update(); timer.Stop(); elapsedTime = timer.GetMean(); smoothField = bspliner->GetOutput(); smoothField->DisconnectPipeline(); } else { std::cerr << "Error: unexpected variance format." << std::endl; return EXIT_FAILURE; } typename RealImageType::Pointer rmseImage = RealImageType::New(); rmseImage->SetOrigin( field->GetOrigin() ); rmseImage->SetDirection( field->GetDirection() ); rmseImage->SetSpacing( field->GetSpacing() ); rmseImage->SetRegions( field->GetLargestPossibleRegion() ); rmseImage->Allocate(); rmseImage->FillBuffer( 0.0 ); itk::ImageRegionConstIterator fieldIt( field, field->GetLargestPossibleRegion() ); itk::ImageRegionConstIterator smoothedFieldIt( smoothField, smoothField->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItR( rmseImage, rmseImage->GetLargestPossibleRegion() ); float rmse = 0.0; vnl_vector rmse_comp( 2 ); rmse_comp.fill( 0.0 ); float N = 0.0; for( fieldIt.GoToBegin(), smoothedFieldIt.GoToBegin(), ItR.GoToBegin(); !fieldIt.IsAtEnd(); ++fieldIt, ++smoothedFieldIt, ++ItR ) { ItR.Set( ( fieldIt.Get() - smoothedFieldIt.Get() ).GetSquaredNorm() ); for( unsigned int d = 0; d < ImageDimension; d++ ) { rmse_comp[d] += vnl_math_sqr( fieldIt.Get()[d] - smoothedFieldIt.Get()[d] ); } rmse += ( fieldIt.Get() - smoothedFieldIt.Get() ).GetSquaredNorm(); N += 1.0; } rmse = std::sqrt( rmse / N ); std::cout << "Elapsed time: " << elapsedTime << std::endl; std::cout << "RMSE = " << rmse << std::endl; for( unsigned int d = 0; d < ImageDimension; d++ ) { std::cout << " rmse[" << d << "] = " << std::sqrt( rmse_comp[d] / N ) << std::endl; } WriteImage( smoothField, argv[3] ); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int SmoothDisplacementField( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "SmoothImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if ( argc < 5 ) { std::cout << argv[0] << " imageDimension inputField outputField variance_or_mesh_size_base_level " << "[numberOfevels=1] [splineOrder=3] [confidenceImage]" << std::endl; return EXIT_FAILURE; } switch( atoi( argv[1] ) ) { case 2: return SmoothDisplacementField<2>( argc, argv ); break; case 3: return SmoothDisplacementField<3>( argc, argv ); break; case 4: return SmoothDisplacementField<4>( argc, argv ); break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/SmoothImage.cxx000066400000000000000000000135771311104306400172040ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include "itkMedianImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" #include "ReadWriteData.h" namespace ants { template int SmoothImage(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; std::vector sigmaVector = ConvertVector( argv[3] ); typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer varimage = ITK_NULLPTR; ReadImage(image1, argv[2]); typedef itk::DiscreteGaussianImageFilter dgf; typedef itk::MedianImageFilter medf; typename dgf::Pointer filter = dgf::New(); typename medf::Pointer filter2 = medf::New(); bool usespacing = false; float gaussianmaxerror = 0.01; int gaussianmaxkernelwidth = 32; if( argc > 5 ) { usespacing = atoi(argv[5]); } bool usemedian = false; if( argc > 6 ) { usemedian = atoi(argv[6]); } if( argc > 7 ) { gaussianmaxerror = atof(argv[7]); } if( argc > 8 ) { gaussianmaxkernelwidth = atoi(argv[8]); } if( !usespacing ) { filter->SetUseImageSpacingOff(); } else { filter->SetUseImageSpacingOn(); } if( !usemedian ) { if( sigmaVector.size() == 1 ) { filter->SetVariance( vnl_math_sqr( sigmaVector[0] ) ); } else if( sigmaVector.size() == ImageDimension ) { typename dgf::ArrayType varianceArray; for( unsigned int d = 0; d < ImageDimension; d++ ) { varianceArray[d] = vnl_math_sqr( sigmaVector[d] ); } filter->SetVariance( varianceArray ); } else { std::cerr << "Incorrect sigma vector size. Must either be of size 1 or ImageDimension." << std::endl; } filter->SetMaximumError( gaussianmaxerror ); filter->SetMaximumKernelWidth( gaussianmaxkernelwidth ); filter->SetInput( image1 ); filter->Update(); varimage = filter->GetOutput(); } else { typename ImageType::SizeType rad; if( sigmaVector.size() == 1 ) { rad.Fill( static_cast( sigmaVector[0] ) ); } else if( sigmaVector.size() == ImageDimension ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { rad[d] = sigmaVector[d]; } } else { std::cerr << "Incorrect sigma vector size. Must either be of size 1 or ImageDimension." << std::endl; } filter2->SetRadius(rad); filter2->SetInput( image1 ); filter2->Update(); varimage = filter2->GetOutput(); } WriteImage( varimage, argv[4] ); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int SmoothImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "SmoothImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << "Usage: " << std::endl; std::cout << argv[0] << " ImageDimension image.ext smoothingsigma outimage.ext {sigma-is-in-spacing-coordinates-0/1} {medianfilter-0/1} {GaussianSetMaximumError=0.01} {GaussianSetMaximumKernelWidth=32}" << std::endl; std::cout << " if median, then sigma means radius of filtering " << std::endl; std::cout << " A separate sigma can be specified for each dimension, e.g., 1.5x1x2 " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } switch( atoi(argv[1]) ) { case 2: { return SmoothImage<2>(argc, argv); } break; case 3: { return SmoothImage<3>(argc, argv); } break; case 4: { return SmoothImage<4>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/StackSlices.cxx000066400000000000000000000260611311104306400171700ustar00rootroot00000000000000/*========================================================================= Program: ITK General Author: Jeffrey T. Duda (jtduda@seas.upenn.edu) Institution: PICSL This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include #include "itkBinaryThresholdImageFilter.h" #include "itkExtractImageFilter.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkLabelStatisticsImageFilter.h" #include "ReadWriteData.h" namespace ants { /* FlipScalarVolume * This program takes a volume and flips it along the * indicated axes */ // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int StackSlices( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "StackSlices" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); // Pixel and Image typedefs typedef float PixelType; typedef itk::Image ImageSeriesType; typedef itk::Image ImageType; typedef itk::Image SliceType; typedef itk::Image LabelSliceType; typedef itk::ImageFileReader ReaderType; typedef itk::ImageFileReader Reader4DType; typedef itk::ExtractImageFilter ExtractFilterType; typedef itk::ExtractImageFilter ExtractFilterType2; typedef itk::ImageRegionIteratorWithIndex SliceIt; // Check for valid input parameters if( argc < 5 ) { std::cout << "Usage: " << argv[0] << " outputvolume x y z inputvolume(s)" << std::endl; std::cout << " The specific slice is chosen by specifying the index for x, y, xor z." << std::endl; std::cout << " For example, an \"x y z\" selection of \"30 -1 -1\" will stack slice 30 " << std::endl; std::cout << " along the first dimension. Also note that input 4-D volumes are treated " << std::endl; std::cout << " as a series of 3-D volumes." << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } char * stackName = argv[1]; int dimVars[3]; dimVars[0] = atoi( argv[2] ); dimVars[1] = atoi( argv[3] ); dimVars[2] = atoi( argv[4] ); int dim = -1; int slice = -1; // which dim to extract slice from for( unsigned int i = 0; i < 3; i++ ) { if( dimVars[i] > -1 ) { if( (dim > -1) || (slice > -1) ) { std::cout << "Can only choose slice from 1 dimension" << std::endl; return EXIT_FAILURE; } dim = i; slice = dimVars[i]; } } SliceType::SizeType size; size.Fill( 0 ); ImageType::RegionType region3D; ImageSeriesType::Pointer imageSeries = ITK_NULLPTR; unsigned long nSlices = 0; bool inputIsA4DImage = true; if( argc > 6 ) { inputIsA4DImage = false; std::cout << " Input is a set of 3-D volumes." << std::endl; } else { std::cout << " Input is a 4-D image." << std::endl; } if( !inputIsA4DImage ) // input is a set of 3-D volumes { nSlices = argc - 5; // std::cout << nSlices << std::endl; ReaderType::Pointer firstReader = ReaderType::New(); firstReader->SetFileName( argv[5] ); firstReader->Update(); if( dim == 0 ) { size[0] = firstReader->GetOutput()->GetLargestPossibleRegion().GetSize()[1]; size[1] = firstReader->GetOutput()->GetLargestPossibleRegion().GetSize()[2]; } if( dim == 1 ) { size[0] = firstReader->GetOutput()->GetLargestPossibleRegion().GetSize()[0]; size[1] = firstReader->GetOutput()->GetLargestPossibleRegion().GetSize()[2]; } if( dim == 2 ) { size[0] = firstReader->GetOutput()->GetLargestPossibleRegion().GetSize()[0]; size[1] = firstReader->GetOutput()->GetLargestPossibleRegion().GetSize()[1]; } region3D = firstReader->GetOutput()->GetLargestPossibleRegion(); region3D.SetSize( dim, nSlices ); region3D.SetIndex( dim, 0 ); } else { Reader4DType::Pointer reader4D = Reader4DType::New(); reader4D->SetFileName( argv[5] ); imageSeries = reader4D->GetOutput(); imageSeries->Update(); imageSeries->DisconnectPipeline(); nSlices = imageSeries->GetLargestPossibleRegion().GetSize()[3]; ImageSeriesType::IndexType index4D = imageSeries->GetLargestPossibleRegion().GetIndex(); if( dim == 0 ) { size[0] = imageSeries->GetLargestPossibleRegion().GetSize()[1]; size[1] = imageSeries->GetLargestPossibleRegion().GetSize()[2]; region3D.SetSize( 0, nSlices ); region3D.SetSize( 1, size[0] ); region3D.SetSize( 2, size[1] ); region3D.SetIndex( 0, 0 ); region3D.SetIndex( 1, index4D[1] ); region3D.SetIndex( 2, index4D[2] ); } if( dim == 1 ) { size[0] = imageSeries->GetLargestPossibleRegion().GetSize()[0]; size[1] = imageSeries->GetLargestPossibleRegion().GetSize()[2]; region3D.SetSize( 0, size[0] ); region3D.SetSize( 1, nSlices ); region3D.SetSize( 2, size[1] ); region3D.SetIndex( 0, index4D[0] ); region3D.SetIndex( 1, 0 ); region3D.SetIndex( 2, index4D[2] ); } if( dim == 2 ) { size[0] = imageSeries->GetLargestPossibleRegion().GetSize()[0]; size[1] = imageSeries->GetLargestPossibleRegion().GetSize()[1]; region3D.SetSize( 0, size[0] ); region3D.SetSize( 1, size[1] ); region3D.SetSize( 2, nSlices ); region3D.SetIndex( 0, index4D[0] ); region3D.SetIndex( 1, index4D[1] ); region3D.SetIndex( 2, 0 ); } } std::cout << " Output region size = " << region3D.GetSize() << std::endl; ImageType::Pointer stack = AllocImage( region3D ); // Start stacking the slices while normalizing by the mean at each slice. for( unsigned int i = 0; i < nSlices; i++ ) { SliceType::Pointer stackSlice = ITK_NULLPTR; if( !inputIsA4DImage ) { std::cout << " Slice " << i << " :: " << std::string(argv[5 + i]) << std::endl; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[5 + i] ); reader->Update(); ImageType::RegionType extractRegion = reader->GetOutput()->GetLargestPossibleRegion(); extractRegion.SetSize( dim, 0 ); extractRegion.SetIndex( dim, slice ); ExtractFilterType::Pointer extracter = ExtractFilterType::New(); extracter->SetInput( reader->GetOutput() ); extracter->SetDirectionCollapseToIdentity(); extracter->SetExtractionRegion( extractRegion ); stackSlice = extracter->GetOutput(); stackSlice->Update(); stackSlice->DisconnectPipeline(); } else { std::cout << " Slice " << i << " :: " << std::endl; ImageSeriesType::RegionType extractRegion = imageSeries->GetLargestPossibleRegion(); extractRegion.SetSize( dim, 0 ); extractRegion.SetIndex( dim, slice ); extractRegion.SetSize( 3, 0 ); extractRegion.SetIndex( 3, i ); ExtractFilterType2::Pointer extracter2 = ExtractFilterType2::New(); extracter2->SetInput( imageSeries ); extracter2->SetDirectionCollapseToIdentity(); extracter2->SetExtractionRegion( extractRegion ); stackSlice = extracter2->GetOutput(); stackSlice->Update(); stackSlice->DisconnectPipeline(); } typedef itk::BinaryThresholdImageFilter ThresholderType; ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( stackSlice ); thresholder->SetInsideValue( 0 ); thresholder->SetOutsideValue( 1 ); thresholder->SetLowerThreshold( itk::NumericTraits::NonpositiveMin() ); thresholder->SetUpperThreshold( 0 ); typedef itk::LabelStatisticsImageFilter StatsFilterType; StatsFilterType::Pointer stats = StatsFilterType::New(); stats->SetInput( stackSlice ); stats->SetLabelInput( thresholder->GetOutput() ); stats->Update(); PixelType sliceMean = stats->GetMean( 1 ); SliceIt It( stackSlice, stackSlice->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType value = It.Get(); ImageType::IndexType index; index.Fill( 0 ); index[dim] = i; if( dim == 0 ) { index[1] = It.GetIndex()[0]; index[2] = It.GetIndex()[1]; } if( dim == 1 ) { index[0] = It.GetIndex()[0]; index[2] = It.GetIndex()[1]; } if( dim == 2 ) { index[0] = It.GetIndex()[0]; index[1] = It.GetIndex()[1]; } stack->SetPixel( index, value / sliceMean ); } } WriteImage( stack, stackName ); // Input parameters // char * inputName = argv[1]; // unsigned int flip_x = atoi( argv[2] ); // unsigned int flip_y = atoi( argv[3] ); // unsigned int flip_z = atoi( argv[4] ); // char * outputName = argv[5]; // ReaderType::Pointer reader = ReaderType::New(); // reader->SetFileName( inputName ); // reader->Update(); // // flip in desired directions for correct display // FlipFilterType::Pointer flip = FlipFilterType::New(); // FlipFilterType::FlipAxesArrayType flipOver; // flipOver[0] = flip_x; // flipOver[1] = flip_y; // flipOver[2] = flip_z; // flip->SetInput( reader->GetOutput() ); // flip->SetFlipAxes( flipOver ); // flip->Update(); // // write output // WriterType::Pointer writer = WriterType::New(); // writer->SetInput( flip->GetOutput() ); // writer->SetFileName( outputName ); // writer->Update(); return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/StudentsTestOnImages.cxx000066400000000000000000000423121311104306400210510ustar00rootroot00000000000000 #include "antsUtilities.h" #include "antsAllocImage.h" #include #include #include #include #include #include #include #include #include #include #include #include "ReadWriteData.h" #include #include #include #include #include "itkTDistribution.h" #include "vnl/vnl_math.h" #include "vnl/vnl_erf.h" namespace ants { // for computing F distribution extern "C" double dbetai_(double *x, double *pin, double *qin); extern "C" double dgamma_(double *x); typedef struct { double statVal; int index; } StatElement; int smallerStatElem(StatElement * elem1, StatElement * elem2) // comparison function for sorting { if( elem1->statVal > elem2->statVal ) { return 1; } else if( elem1->statVal < elem2->statVal ) { return -1; } else { return 0; } } template void ReadImage(itk::SmartPointer & target, const char *file, bool copy) { // std::cout << " reading b " << std::string(file) << std::endl; typedef itk::ImageFileReader readertype; typename readertype::Pointer reader = readertype::New(); reader->SetFileName(file); reader->Update(); if( !copy ) { target = (reader->GetOutput() ); } else { typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter2( target, target->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { vfIter2.Set( reader->GetOutput()->GetPixel(vfIter2.GetIndex() ) ); } } } #define GROUPALABEL 0 #define GROUPBLABEL 1 typedef struct { int randNum; int index; } PermElement; void generatePerm(int length, int * genPerm); // generate a permutation of numbers from 0 to length-1 void generatePermGroup(int * groupID, int lengthGroupA, int lengthGroupB, int * genGroupID); // generate a permutation of group assignments int smallerPermElem(PermElement * elem1, PermElement * elem2); static int first = 0; void generatePermGroup(int * groupID, int lengthGroupA, int lengthGroupB, int * genGroupID) // generate a permutation of group assignments { int numSubjects = lengthGroupA + lengthGroupB; if( !first ) { first = 1; srand(time(NULL) ); // cout << "generatePermGroup called" << endl; } int * newPerm = new int[numSubjects]; generatePerm(numSubjects, newPerm); for( int i = 0; i < numSubjects; i++ ) { genGroupID[i] = groupID[newPerm[i]]; } delete [] newPerm; } void generatePerm(int length, int * genPerm) { if( !first ) { first = 1; srand(time(NULL) ); // cout << "generatePerm called" << endl; } PermElement * newPerm = new PermElement[length]; int cnt; for( cnt = 0; cnt < length; cnt++ ) { newPerm[cnt].randNum = rand(); newPerm[cnt].index = cnt; } qsort(newPerm, length, sizeof(PermElement), (int (*)(const void *, const void *) )smallerPermElem); for( cnt = 0; cnt < length; cnt++ ) { genPerm[cnt] = newPerm[cnt].index; } delete [] newPerm; } int smallerPermElem(PermElement * elem1, PermElement * elem2) { if( elem1->randNum > elem2->randNum ) { return 1; } else if( elem1->randNum < elem2->randNum ) { return -1; } else { return 0; } } double computeQuantile(int numObs, double * stat, double quantile) // computes the value in the provided statistic for the given quantile value // // numObs = Number of Observations // stat = double array[numObs ] contains the test statisticss { static int first = 0; if( !first ) { first = 1; srand(time(NULL) ); } StatElement * sortStat = new StatElement[numObs]; for( int perm = 0; perm < numObs; perm++ ) { sortStat[perm].statVal = stat[perm]; sortStat[perm].index = perm; } // sort, smallest first qsort(sortStat, numObs, sizeof(StatElement), (int (*)(const void *, const void *) )smallerStatElem); // index at value double quantindex = (double) numObs * quantile; if( quantile == 1.0 ) { quantindex = numObs; } double retval = stat[sortStat[(int) quantindex].index]; delete [] sortStat; return retval; } void computePermStatPval(int numFeatures, int numPerms, double * permStat, double * permStatPval) // computes the Pval for all permutation statistics // the p-val is computed as the percentual ordered rank over all permutations // // numFeatures = Number of scalar features per Subject // numPerms = Number of Permutations // permStat = double array[numPerms * numFeatures] contains the test statistics // permStatPval = double array [numPerms * numFeatures] returns the p-val of the statistics { static int first = 0; if( !first ) { first = 1; srand(time(NULL) ); } int feat; int perm; StatElement * sortPermStat = new StatElement[numPerms]; for( feat = 0; feat < numFeatures; feat++ ) { for( perm = 0; perm < numPerms; perm++ ) { sortPermStat[perm].statVal = permStat[perm * numFeatures + feat]; sortPermStat[perm].index = perm; } // sort, smallest first qsort(sortPermStat, numPerms, sizeof(StatElement), (int (*)(const void *, const void *) )smallerStatElem); double curPval = 0; for( perm = 0; perm < numPerms; perm++ ) { // percentual rank 0..1 -> cumulative probability -> p-val double nextPval = 1.0 - (double) (perm + 1) / (double) numPerms; int curIndex = sortPermStat[perm].index; if( (perm == 0) || (sortPermStat[perm].statVal != sortPermStat[perm - 1].statVal) ) { // current value is different from previous value (or first value), // thus step up p-value curPval = nextPval; } permStatPval[curIndex * numFeatures + feat] = curPval; } } delete [] sortPermStat; } double decode_ieee_single( unsigned char *v, int natural_order) { unsigned char *data = v; int s, e; unsigned long src; long f; double value = 0.0; if( natural_order ) { src = ( (unsigned long)data[0] << 24) | ( (unsigned long)data[1] << 16) | ( (unsigned long)data[2] << 8) | ( (unsigned long)data[3]); } else { src = ( (unsigned long)data[3] << 24) | ( (unsigned long)data[2] << 16) | ( (unsigned long)data[1] << 8) | ( (unsigned long)data[0]); } s = (src & 0x80000000UL) >> 31; e = (src & 0x7F800000UL) >> 23; f = (src & 0x007FFFFFUL); if( e == 255 && f != 0 ) { /* NaN (Not a Number) */ value = DBL_MAX; } else if( e == 255 && f == 0 && s == 1 ) { /* Negative infinity */ value = -DBL_MAX; } else if( e == 255 && f == 0 && s == 0 ) { /* Positive infinity */ value = DBL_MAX; } else if( e > 0 && e < 255 ) { /* Normal number */ f += 0x00800000UL; if( s ) { f = -f; } value = ldexp(f, e - 150); } else if( e == 0 && f != 0 ) { /* Denormal number */ if( s ) { f = -f; } value = ldexp(f, -149); } else if( e == 0 && f == 0 && s == 1 ) { /* Negative zero */ value = 0; } else if( e == 0 && f == 0 && s == 0 ) { /* Positive zero */ value = 0; } else { /* Never happens */ printf("s = %d, e = %d, f = %lu\n", s, e, f); assert(!"Woops, unhandled case in decode_ieee_single()"); } return value; } double factorial( double x) { if( x <= 1 ) { return 1; } double fac = x; double n = fac - 1; while( n >= 1 ) { fac *= n; n = n - 1; } return fac; } double betadist( double a, double b ) { double numer = factorial( a - 1) * factorial(b - 1); double denom = factorial( a + b - 1); return numer / denom; } double TTest(int numSubjects, int* groupLabel, double * featureValue ) { int numSubjA = 0; int numSubjB = 0; double meanA = 0, meanB = 0; // unsigned int GROUPALABEL=0; // unsigned int GROUPBLABEL=1; for( int subj = 0; subj < numSubjects; subj++ ) { if( groupLabel[subj] == GROUPALABEL ) { numSubjA++; meanA += featureValue[subj]; } else if( groupLabel[subj] == GROUPBLABEL ) { numSubjB++; meanB += featureValue[subj]; } else { std::cout << " group label " << groupLabel[subj] << " does not exist" << std::endl; } } meanA /= (float)numSubjA; meanB /= (float)numSubjB; double varA = 0, varB = 0; for( int subj = 0; subj < numSubjects; subj++ ) { if( groupLabel[subj] == GROUPALABEL ) { varA += (featureValue[subj] - meanA) * (featureValue[subj] - meanA); } else if( groupLabel[subj] == GROUPBLABEL ) { varB += (featureValue[subj] - meanB) * (featureValue[subj] - meanB); } } float n1 = (float) numSubjA; float n2 = (float) numSubjB; varA /= (n1); // use n1 -1 for unbiased estimator, here assume normal distribution varB /= (n2); // use n2 - 1 " ... " // float sdA=sqrt(varA); // float sdB=sqrt(varB); // float df = n1 + n2 - 2; // unequal vars float denom = varA / n1 + varB / n2; // for equal vars // float var = ( (n1-1.)*newvar1 + (n2-1.)*newvar2 ) / df; // denom = var*(1.0/n1+1.0/n2); double tt = 0; if( denom > 0 ) { tt = (meanA - meanB) / sqrt(denom); } return tt; } template int StudentsTestOnImages(int argc, char *argv[]) { typedef float PixelType; typedef itk::Image ImageType; typename ImageType::Pointer mask = NULL; // ReadImage(mask, argv[1], false); unsigned int numSubjectsA = atoi(argv[3]); unsigned int numSubjectsB = atoi(argv[4]); unsigned int numSubjects = numSubjectsA + numSubjectsB; std::string outname = std::string(argv[2]); unsigned int numvals = numSubjects; int* groupLabel = new int[numSubjects]; for( unsigned int i = 0; i < numSubjectsA; i++ ) { groupLabel[i] = 0; } for( unsigned int i = numSubjectsA; i < numSubjects; i++ ) { groupLabel[i] = 1; } double* feature = new double[numvals]; for( unsigned int i = 0; i < numvals; i++ ) { feature[i] = 0; } std::cout << " Numvals " << numvals << std::endl; // Get the image dimension std::string fn = std::string(argv[5]); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fn.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(fn.c_str() ); imageIO->ReadImageInformation(); typename ImageType::SizeType size; typename ImageType::SpacingType spacing; typename ImageType::PointType origin; typename ImageType::DirectionType direction; std::vector axis; for( unsigned int i = 0; i < ImageDimension; i++ ) { size[i] = imageIO->GetDimensions(i); // if (size[i] != mask->GetLargestPossibleRegion().GetSize()[i]) // { // std::cout << " mask not same size as data !! " << std::endl; // throw std::exception(); // } spacing[i] = imageIO->GetSpacing(i); origin[i] = imageIO->GetOrigin(i); axis = imageIO->GetDirection(i); for( unsigned j = 0; j < ImageDimension; j++ ) { if( j < imageIO->GetNumberOfDimensions() ) { direction[j][i] = axis[j]; } else { direction[j][i] = 0.0; } } } std::cout << " size " << size << std::endl; typename ImageType::RegionType region; region.SetSize(size ); // ORIENTATION ALERT. the code this replaced originally didn't // bother setting the origins even though the directions were // grabbed from the ImageIO. I'm assuming that was supposed to // happen, and was left out as an oversight. typename ImageType::Pointer StatImage = AllocImage(region, spacing, origin, direction, 0); typename ImageType::Pointer PImage = AllocImage(region, spacing, origin, direction, 0); // unsigned int sizeofpixel=sizeof(PixelType); std::vector imagestack; imagestack.resize(numvals); for( unsigned int j = 0; j < numvals; j++ ) { std::string ifn = std::string(argv[5 + j]); std::cout << "reading " << ifn << std::endl; ReadImage(imagestack[j], ifn.c_str(), false); } typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter(PImage, PImage->GetLargestPossibleRegion() ); unsigned long nvox = 1; for( unsigned int i = 0; i < ImageDimension; i++ ) { nvox *= PImage->GetLargestPossibleRegion().GetSize()[i]; } unsigned long ct = 0; unsigned long prog = nvox / 20; std::cout << " NVals " << numvals << " NSub " << numSubjects << std::endl; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { typename ImageType::IndexType index = vfIter.GetIndex(); for( unsigned int subj = 0; subj < numSubjects; subj++ ) { feature[subj] = imagestack[subj]->GetPixel(index); } if( ct % prog == 0 ) { std::cout << " % " << (float) ct / (float) nvox << std::endl; } double stat = 0; // if (mask->GetPixel(index) >= 0.5) if( true ) { stat = TTest(numSubjects, groupLabel, feature); } ct++; StatImage->SetPixel( index, stat); } typedef itk::Statistics::TDistribution DistributionType; typename DistributionType::Pointer distributionFunction = DistributionType::New(); WriteImage(StatImage, outname.c_str() ); delete [] feature; delete [] groupLabel; return 1; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int StudentsTestOnImages( std::vector args, std::ostream* out_stream = NULL ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "StudentsTestOnImages" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); std::cout << " df P = 0.05 P = 0.01 P = 0.001 " << std::endl; std::cout << " 1 12.71 63.66 636.61 " << std::endl; std::cout << " 2 4.30 9.92 31.60 " << std::endl; std::cout << " 3 3.18 5.84 12.92" << std::endl; std::cout << " 4 2.78 4.60 8.61" << std::endl; std::cout << " 5 2.57 4.03 6.87" << std::endl; std::cout << " 6 2.45 3.71 5.96" << std::endl; std::cout << " 7 2.36 3.50 5.41" << std::endl; std::cout << " 8 2.31 3.36 5.04" << std::endl; std::cout << " 9 2.26 3.25 4.78" << std::endl; std::cout << " 10 2.23 3.17 4.59" << std::endl; std::cout << " 15 2.13 2.95 4.07" << std::endl; std::cout << " 20 2.09 2.85 3.85" << std::endl; std::cout << " 30 2.04 2.75 3.65" << std::endl; std::cout << " 50 2.01 2.68 3.50" << std::endl; std::cout << " 100 1.98 2.63 3.39 " << std::endl; if( argc < 6 ) { std::cout << "Usage: " << argv[0] << " ImageDimension OutName NGroup1 NGroup2 ControlV1* SubjectV1* " << std::endl; std::cout << " Assume all images the same size " << std::endl; std::cout << " Writes out an F-Statistic image " << std::endl; std::cout << " \n example call \n \n "; std::cout << argv[0] << " 2 TEST.nii.gz 4 8 FawtJandADCcon/*SUB.nii FawtJandADCsub/*SUB.nii \n "; return 1; } switch( atoi(argv[1]) ) { case 2: { StudentsTestOnImages<2>(argc, argv); } break; case 3: { StudentsTestOnImages<3>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return 0; } } // namespace ants ants-2.2.0/Examples/SuperResolution.cxx000066400000000000000000000265041311104306400201440ustar00rootroot00000000000000#include "antsUtilities.h" #include #include "itkAddImageFilter.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkContinuousIndex.h" #include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkImportImageFilter.h" #include "itkPointSet.h" #include "itkRescaleIntensityImageFilter.h" #include "itkTimeProbe.h" #include "itkVector.h" #include "itkVectorIndexSelectionCastImageFilter.h" #include "ReadWriteData.h" namespace ants { template int SuperResolution( unsigned int argc, char *argv[] ) { typedef float RealType; typedef itk::Image ImageType; typedef itk::Vector ScalarType; typedef itk::Image ScalarImageType; typedef itk::PointSet PointSetType; typedef itk::BSplineScatteredDataPointSetToImageFilter BSplineFilterType; typename ImageType::Pointer domainImage = ITK_NULLPTR; ReadImage( domainImage, argv[3] ); typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); typename PointSetType::Pointer bsplinePoints = PointSetType::New(); bsplinePoints->Initialize(); typename BSplineFilterType::WeightsContainerType::Pointer weights = BSplineFilterType::WeightsContainerType::New(); weights->Initialize(); unsigned int splineOrder = 3; typename BSplineFilterType::ArrayType numberOfLevels; typename BSplineFilterType::ArrayType ncps; bool useGradientWeighting = true; RealType gradientSigma = atof( argv[4] ); if( gradientSigma < 0.0 ) { useGradientWeighting = false; } std::vector meshSize = ConvertVector( std::string( argv[5] ) ); if ( meshSize.size() == 1 ) { ncps.Fill( meshSize[0] + splineOrder ); } else if ( meshSize.size() == ImageDimension ) { for ( unsigned int d = 0; d < ImageDimension; d++ ) { ncps[d] = meshSize[d] + splineOrder; } } else { std::cerr << "Invalid ncps format." << std::endl; return EXIT_FAILURE; } std::vector nlevels = ConvertVector( std::string( argv[6] ) ); if ( nlevels.size() == 1 ) { numberOfLevels.Fill( nlevels[0] ); } else if ( nlevels.size() == ImageDimension ) { for ( unsigned int d = 0; d < ImageDimension; d++ ) { numberOfLevels[d] = nlevels[d]; } } else { std::cerr << "Invalid nlevels format." << std::endl; return EXIT_FAILURE; } // Find the average for the offset typename ImageType::IndexType domainBeginIndex = domainImage->GetRequestedRegion().GetIndex(); typename ImageType::IndexType domainEndIndex; for( unsigned int d = 0; d < ImageDimension; d++ ) { domainEndIndex[d] = domainBeginIndex[d] + domainImage->GetRequestedRegion().GetSize()[d] - 1; } RealType averageIntensity = 0.0; unsigned int N = 0; for( unsigned int n = 7; n < argc; n++ ) { typename ImageType::Pointer inputImage = ITK_NULLPTR; ReadImage( inputImage, argv[n] ); itk::ImageRegionConstIteratorWithIndex It( inputImage, inputImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { typename ImageType::PointType imagePoint; inputImage->TransformIndexToPhysicalPoint( It.GetIndex(), imagePoint ); itk::ContinuousIndex cidx; bool isInside = domainImage->TransformPhysicalPointToContinuousIndex( imagePoint, cidx ); if( isInside ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { if( cidx[d] <= domainBeginIndex[d] || cidx[d] >= domainEndIndex[d] ) { isInside = false; break; } } } if( !isInside ) { continue; } averageIntensity += It.Get(); N++; } } averageIntensity /= static_cast( N ); typename ScalarImageType::DirectionType identity; identity.SetIdentity(); const typename ScalarImageType::RegionType & bufferedRegion = domainImage->GetBufferedRegion(); const itk::SizeValueType numberOfPixels = bufferedRegion.GetNumberOfPixels(); const bool filterHandlesMemory = false; typedef itk::ImportImageFilter ImporterType; typename ImporterType::Pointer importer = ImporterType::New(); importer->SetImportPointer( domainImage->GetBufferPointer(), numberOfPixels, filterHandlesMemory ); importer->SetRegion( domainImage->GetBufferedRegion() ); importer->SetOrigin( domainImage->GetOrigin() ); importer->SetSpacing( domainImage->GetSpacing() ); importer->SetDirection( identity ); importer->Update(); const typename ImporterType::OutputImageType * parametricDomainImage = importer->GetOutput(); N = 0; for( unsigned int n = 7; n < argc; n++ ) { typename ImageType::Pointer inputImage = ITK_NULLPTR; ReadImage( inputImage, argv[n] ); typename ImageType::Pointer gradientImage = ITK_NULLPTR; if( useGradientWeighting ) { typedef itk::GradientMagnitudeRecursiveGaussianImageFilter GradientFilterType; typename GradientFilterType::Pointer gradientFilter = GradientFilterType::New(); gradientFilter->SetSigma( gradientSigma ); gradientFilter->SetInput( inputImage ); typedef itk::RescaleIntensityImageFilter RescaleFilterType; typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( 0.0 ); rescaler->SetOutputMaximum( 1.0 ); rescaler->SetInput( gradientFilter->GetOutput() ); gradientImage = rescaler->GetOutput(); gradientImage->Update(); gradientImage->DisconnectPipeline(); } itk::ImageRegionConstIteratorWithIndex It( inputImage, inputImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { typename ImageType::PointType imagePoint; inputImage->TransformIndexToPhysicalPoint( It.GetIndex(), imagePoint ); itk::ContinuousIndex cidx; bool isInside = domainImage->TransformPhysicalPointToContinuousIndex( imagePoint, cidx ); if( isInside ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { if( cidx[d] <= domainBeginIndex[d] || cidx[d] >= domainEndIndex[d] ) { isInside = false; break; } } } if( !isInside ) { continue; } RealType weight = 1.0; if( gradientImage.IsNotNull() ) { weight = gradientImage->GetPixel( It.GetIndex() ); } if( weight == 0.0 ) { continue; } // domainImage->SetPixel( index, 1 ); parametricDomainImage->TransformContinuousIndexToPhysicalPoint( cidx, imagePoint ); ScalarType scalar; scalar[0] = It.Get() - averageIntensity; bsplinePoints->SetPointData( N, scalar ); bsplinePoints->SetPoint( N, imagePoint ); weights->InsertElement( N, weight ); N++; } } itk::TimeProbe timer; timer.Start(); typename ScalarImageType::PointType parametricOrigin = domainImage->GetOrigin(); for( unsigned int d = 0; d < ImageDimension; d++ ) { parametricOrigin[d] += ( domainImage->GetSpacing()[d] * domainImage->GetLargestPossibleRegion().GetIndex()[d] ); } bspliner->SetOrigin( parametricOrigin ); bspliner->SetSpacing( domainImage->GetSpacing() ); bspliner->SetSize( domainImage->GetRequestedRegion().GetSize() ); bspliner->SetDirection( domainImage->GetDirection() ); bspliner->SetGenerateOutputImage( true ); bspliner->SetNumberOfLevels( numberOfLevels ); bspliner->SetSplineOrder( splineOrder ); bspliner->SetNumberOfControlPoints( ncps ); bspliner->SetInput( bsplinePoints ); bspliner->SetPointWeights( weights ); bspliner->Update(); timer.Stop(); std::cout << "Elapsed Time: " << timer.GetMean() << std::endl; typedef itk::VectorIndexSelectionCastImageFilter SelectorType; typename SelectorType::Pointer selector = SelectorType::New(); selector->SetInput( bspliner->GetOutput() ); selector->SetIndex( 0 ); selector->Update(); typedef itk::AddImageFilter AdderType; typename AdderType::Pointer adder = AdderType::New(); adder->SetInput( selector->GetOutput() ); adder->SetConstant2( averageIntensity ); adder->Update(); WriteImage( adder->GetOutput(), argv[2] ); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int SuperResolution( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "SuperResolution" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if ( argc < 8 ) { std::cerr << argv[0] << " imageDimension outputImage domainImage gradientSigma meshSize numberOfLevels inputImage1 ... inputImageN" << std::endl; std::cerr << std::endl; std::cerr << " N.B. The \'gradientSigma\' parameter is used in calculating the gradient magnitude of the input images " << std::endl; std::cerr << " for weighting the voxel points during fitting. If a negative \'gradient\' sigma is specified then no " << std::endl; std::cerr << " weighting is used." << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } const int ImageDimension = static_cast( atoi( argv[1] ) ); switch( ImageDimension ) { case 2: return SuperResolution<2>( argc, argv ); break; case 3: return SuperResolution<3>( argc, argv ); break; case 4: return SuperResolution<4>( argc, argv ); break; default: std::cerr << "Unsupported dimension" << std::endl; exit( EXIT_FAILURE ); } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/SurfaceBasedSmoothing.cxx000066400000000000000000000100741311104306400211740ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include "ReadWriteData.h" #include "itkSurfaceImageCurvature.h" namespace ants { // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int SurfaceBasedSmoothing( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "SurfaceBasedSmoothing" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << " usage : " << argv[0] << " ImageToSmooth sigma SurfaceImage outname {numrepeatsofsmoothing}" << std::endl; std::cout << " We assume the SurfaceImage has a label == 1 that defines the surface " << std::endl; std::cout << " sigma defines the geodesic n-hood radius --- numrepeats allows one to use " << std::endl; std::cout << " a small geodesic n-hood repeatedly applied many times -- faster computation, same effect " << std::endl; return 0; } typedef itk::Image ImageType; enum { ImageDimension = ImageType::ImageDimension }; typedef itk::Image floatImageType; typedef itk::SurfaceImageCurvature ParamType; ParamType::Pointer Parameterizer = ParamType::New(); // std::string fn="C://Data//brain15labelimage.img"; float opt = 0; float sig = 1.0; // float thresh=0.0; if( argc > 2 ) { sig = atof( argv[2]); } unsigned int numrepeats = 0; if( argc > 5 ) { numrepeats = atoi(argv[5]); } ImageType::Pointer input; ReadImage(input, argv[1]); ImageType::Pointer surflabel; ReadImage(surflabel, argv[3]); Parameterizer->SetInputImage(surflabel); Parameterizer->SetFunctionImage(input); Parameterizer->SetNeighborhoodRadius( sig ); if( sig <= 0 ) { sig = 1.0; } std::cout << " sigma " << sig << " thresh " << opt << std::endl; Parameterizer->SetSigma(sig); Parameterizer->SetUseGeodesicNeighborhood(true); Parameterizer->SetUseLabel(true); Parameterizer->SetThreshold(0.5); // Parameterizer->ComputeSurfaceArea(); // Parameterizer->IntegrateFunctionOverSurface(); Parameterizer->SetNeighborhoodRadius( sig ); std::cout << " begin integration NOW " << std::endl; Parameterizer->IntegrateFunctionOverSurface(true); for( unsigned int i = 0; i < numrepeats; i++ ) { Parameterizer->IntegrateFunctionOverSurface(true); } std::cout << " end integration " << std::endl; // Parameterizer->PostProcessGeometry(); // double mn=0.0; ImageType::Pointer output = ITK_NULLPTR; floatImageType::Pointer smooth = ITK_NULLPTR; smooth = Parameterizer->GetFunctionImage(); std::string ofn = std::string(argv[4]); std::cout << " writing result " << ofn << std::endl; // writer->SetFileName(ofn.c_str()); // writer->SetInput( smooth ); WriteImage(smooth, ofn.c_str() ); std::cout << " done writing "; return 1; } } // namespace ants ants-2.2.0/Examples/SurfaceCurvature.cxx000066400000000000000000000154511311104306400202520ustar00rootroot00000000000000#include "antsUtilities.h" #include #include "itkSurfaceCurvatureBase.h" #include "itkSurfaceImageCurvature.h" #include "ReadWriteData.h" namespace ants { /* void test1() { typedef itk::SurfaceCurvatureBase ParamType; ParamType::Pointer Parameterizer=ParamType::New(); // Parameterizer->TestEstimateTangentPlane(p); Parameterizer->FindNeighborhood(); // Parameterizer->WeightedEstimateTangentPlane( Parameterizer->GetOrigin() ); Parameterizer->EstimateTangentPlane( Parameterizer->GetAveragePoint()); Parameterizer->PrintFrame(); // Parameterizer->SetOrigin(Parameterizer->GetAveragePoint()); for(int i=0; i<3; i++){ Parameterizer->ComputeWeightsAndDirectionalKappaAndAngles (Parameterizer->GetOrigin()); Parameterizer->ComputeFrame(Parameterizer->GetOrigin()); Parameterizer->EstimateCurvature(); Parameterizer->PrintFrame(); } Parameterizer->ComputeJoshiFrame(Parameterizer->GetOrigin()); Parameterizer->PrintFrame(); std::cout << " err 1 " << Parameterizer->ErrorEstimate(Parameterizer->GetOrigin()) << " err 2 " << Parameterizer->ErrorEstimate(Parameterizer->GetOrigin(),-1) << std::endl; } */ // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int SurfaceCurvature( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "SurfaceCurvature" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << " This implements The Shape Operator for Differential Analysis of Images (google for the pdf)" << std::endl; std::cout << " By B. Avants and J.C. Gee " << std::endl; std::cout << " Documentation is on demand --- if there is enough interest, documentation will improve." << std::endl; std::cout << " There are several modes of operation and you must try parameters and input either binary or gray scale images to see the different effects ---- experimentation or reading the (minimal) documentation / paper / code is needed to understand the program " << std::endl; std::cout << " usage : SurfaceCurvature FileNameIn FileNameOut sigma option " << std::endl; std::cout << " e.g : SurfaceCurvature BrainIn.nii BrainOut.nii 3 0 " << std::endl; std::cout << " option 0 means just compute mean curvature from intensity " << std::endl; std::cout << " option 5 means characterize surface from intensity " << std::endl; std::cout << " option 6 means compute gaussian curvature " << std::endl; std::cout << " option 7 means surface area " << std::endl; std::cout << " ... " << std::endl; std::cout << " for surface characterization " << std::endl; std::cout << " 1 == (+) bowl " << std::endl; std::cout << " 2 == (-) bowl " << std::endl; std::cout << " 3 == (+) saddle " << std::endl; std::cout << " 4 == (-) saddle " << std::endl; std::cout << " 5 == (+) U " << std::endl; std::cout << " 6 == (-) U " << std::endl; std::cout << " 7 == flat " << std::endl; std::cout << " 8 == a perfectly even saddle (rare) " << std::endl; std::cout << " " << std::endl; std::cout << " we add 128 to mean curvature results s.t. they are differentiated from background (zero) " << std::endl; return 0; } typedef itk::Image ImageType; typedef itk::Image floatImageType; enum { ImageDimension = ImageType::ImageDimension }; typedef itk::SurfaceImageCurvature ParamType; ParamType::Pointer Parameterizer = ParamType::New(); int opt = 0; float sig = 1.0; if( argc > 3 ) { sig = atof( argv[3]); } if( argc > 4 ) { opt = (int) atoi(argv[4]); } if( opt < 0 ) { std::cout << " error " << std::endl; return 0; } ImageType::Pointer input; ReadImage(input, argv[1]); Parameterizer->SetInputImage(input); // Parameterizer->ProcessLabelImage(); Parameterizer->SetNeighborhoodRadius( 1. ); if( sig <= 0.5 ) { sig = 1.66; } Parameterizer->SetSigma(sig); if( opt == 1 ) { Parameterizer->SetUseLabel(true); Parameterizer->SetUseGeodesicNeighborhood(false); } else { Parameterizer->SetUseLabel(false); Parameterizer->SetUseGeodesicNeighborhood(false); float sign = 1.0; if( opt == 3 ) { sign = -1.0; } Parameterizer->SetkSign(sign); Parameterizer->SetThreshold(0); } // Parameterizer->ComputeSurfaceArea(); // Parameterizer->IntegrateFunctionOverSurface(); // Parameterizer->IntegrateFunctionOverSurface(true); if( opt != 5 && opt != 6 && opt != 7 ) { Parameterizer->ComputeFrameOverDomain( 3 ); } else { Parameterizer->ComputeFrameOverDomain( opt ); } // Parameterizer->SetNeighborhoodRadius( 2 ); // Parameterizer->LevelSetMeanCurvature(); // Parameterizer->SetNeighborhoodRadius( 2.9 ); // Parameterizer->IntegrateFunctionOverSurface(false); // Parameterizer->SetNeighborhoodRadius( 1.5 ); // Parameterizer->IntegrateFunctionOverSurface(true); // for (int i=0; i<1; i++) Parameterizer->PostProcessGeometry(); ImageType::Pointer output = Parameterizer->GetFunctionImage(); // Parameterizer->GetFunctionImage()->SetSpacing( input->GetSpacing() ); // Parameterizer->GetFunctionImage()->SetDirection( input->GetDirection() ); // Parameterizer->GetFunctionImage()->SetOrigin( input->GetOrigin() ); // smooth->SetSpacing(reader->GetOutput()->GetSpacing()); // SmoothImage(Parameterizer->GetFunctionImage(),smooth,3); // NormalizeImage(smooth,output,mn); // NormalizeImage(Parameterizer->GetFunctionImage(),output,mn); WriteImage( output, argv[2]); return 0; } } // namespace ants ants-2.2.0/Examples/TensorDerivedImage.cxx000066400000000000000000000157041311104306400205020ustar00rootroot00000000000000/*========================================================================= Program: ITK General Author: Jeffrey T. Duda (jtduda@seas.upenn.edu) Institution: PICSL This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =========================================================================*/ #include "antsUtilities.h" #include #include #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include #include "TensorFunctions.h" #include "ReadWriteData.h" #include "itkRGBPixel.h" namespace ants { // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int TensorDerivedImage( std::vector args, std::ostream* out_stream = NULL ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "TensorDerivedImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); // Pixel and Image typedefs typedef float PixelType; typedef itk::Image ScalarImageType; typedef itk::ImageFileWriter WriterType; // typedef itk::Vector TensorType; typedef itk::SymmetricSecondRankTensor TensorType; typedef itk::Image TensorImageType; typedef itk::ImageFileReader ReaderType; typedef itk::RGBPixel ColorPixelType; typedef itk::Image ColorImageType; typedef itk::ImageFileWriter ColorWriterType; // Check for valid input paramters if( argc < 3 ) { std::cout << "Usage: " << argv[0] << " tensorvolume outputvolume outputtype" << std::endl; return 1; } // Input parameters char * inputName = argv[1]; char * outputName = argv[2]; std::string outType = argv[3]; TensorImageType::Pointer dtimg = TensorImageType::New(); ReadTensorImage(dtimg, inputName, false); std::cout << "tensor_image: " << inputName << std::endl; std::cout << "output_image: " << outputName << std::endl; ScalarImageType::Pointer outImage; ColorImageType::Pointer colorImage; if( outType == "DEC" ) { colorImage = AllocImage(dtimage); } else { outImage = AllocImage(dtimg); } itk::ImageRegionIteratorWithIndex inputIt(dtimg, dtimg->GetLargestPossibleRegion() ); if( (outType == "XX") || (outType == "xx") ) { outType = "0"; } if( (outType == "XY") || (outType == "YX") || (outType == "xy") || (outType == "yx") ) { outType = "1"; } if( (outType == "XZ") || (outType == "ZX") || (outType == "xz") || (outType == "zx") ) { outType = "2"; } if( (outType == "YY") || (outType == "yy") ) { outType = "3"; } if( (outType == "YZ") || (outType == "ZY") || (outType == "yz") || (outType == "zy") ) { outType = "4"; } if( (outType == "ZZ") || (outType == "zz") ) { outType = "5"; } std::cout << "Calculating output..." << std::flush; while( !inputIt.IsAtEnd() ) { if( (outType == "0") || (outType == "1") || (outType == "2") || (outType == "3") || (outType == "4") || (outType == "5") ) { int idx = atoi(outType.c_str() ); outImage->SetPixel(inputIt.GetIndex(), inputIt.Value()[idx]); } else if( (outType == "TR") || (outType == "MD") ) { ScalarImageType::PixelType tr; tr = inputIt.Value()[0] + inputIt.Value()[2] + inputIt.Value()[5]; if( tr < 0 ) { tr = 0; } if( outType == "TR" ) { outImage->SetPixel(inputIt.GetIndex(), tr); } else { outImage->SetPixel(inputIt.GetIndex(), tr / 3.0); } } else if( outType == "V" ) { // unsigned int invalids = 0; bool success = true; TensorType t = TensorLogAndExp(inputIt.Value(), true, success); int current = 1; if( !success ) { current = 0; } outImage->SetPixel(inputIt.GetIndex(), current); // std::cout << "Found " << invalids << " invalid tensors" << std::endl; } else if( outType == "FA" ) { float fa = GetTensorFA(inputIt.Value() ); outImage->SetPixel(inputIt.GetIndex(), fa); } else if( outType == "DEC" ) { colorImage->SetPixel(inputIt.GetIndex(), GetTensorRGB(inputIt.Value() ) ); ++inputIt; } } std::cout << "Done. " << std::endl; if( outType == "DEC" ) { std::cout << "output origin: " << colorImage->GetOrigin() << std::endl; std::cout << "output size: " << colorImage->GetLargestPossibleRegion().GetSize() << std::endl; std::cout << "output spacing: " << colorImage->GetSpacing() << std::endl; std::cout << "output direction: " << colorImage->GetDirection() << std::endl; } else { std::cout << "output origin: " << outImage->GetOrigin() << std::endl; std::cout << "output size: " << outImage->GetLargestPossibleRegion().GetSize() << std::endl; std::cout << "output spacing: " << outImage->GetSpacing() << std::endl; std::cout << "output direction: " << outImage->GetDirection() << std::endl; } if( outType == "DEC" ) { ColorWriterType::Pointer writer = ColorWriterType::New(); writer->SetInput( colorImage ); writer->SetFileName( outputName ); writer->Update(); } else { WriterType::Pointer writer = WriterType::New(); writer->SetInput( outImage ); writer->SetFileName( outputName ); writer->Update(); } return 0; } } // namespace ants ants-2.2.0/Examples/TestSuite/000077500000000000000000000000001311104306400161605ustar00rootroot00000000000000ants-2.2.0/Examples/TestSuite/ANTS_CC_1_test.cmake000066400000000000000000000063501311104306400215570ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_CC_1) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m CC[ ${R16_IMAGE}, ${R64_IMAGE}, 1 ,2 ] -r Gauss[ 3, 0 ] -t SyN[ 0.5 ] -i 50x50x30 -o ${OUTPUT_PREFIX}.nii.gz --number-of-affine-iterations 100x100x50 ) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${R64_IMAGE} ${WARP_IMAGE} ${WARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 14.4451 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.996429 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -0.00038593 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 2 ${R16_IMAGE} ${INVERSEWARP_IMAGE} ${INVERSEWARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 2 0 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 50.4483 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 COMMAND $ 2 1 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 1.0 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 COMMAND $ 2 2 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.379897 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_CC_2_test.cmake000066400000000000000000000063201311104306400215550ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_CC_2) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m PR[ ${R16_IMAGE}, ${R64_IMAGE}, 1, 4] -r Gauss[ 3, 0 ] -t SyN[ 0.5 ] -i 50x50x30 -o ${OUTPUT_PREFIX}.nii.gz --go-faster true ) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${R64_IMAGE} ${WARP_IMAGE} ${WARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 14.3283 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.996771 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -0.000461792 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 2 ${R16_IMAGE} ${INVERSEWARP_IMAGE} ${INVERSEWARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 2 0 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 50.4483 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 COMMAND $ 2 1 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 1.0 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 COMMAND $ 2 2 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.379897 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_CC_3_test.cmake000066400000000000000000000063131311104306400215600ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_CC_3) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m CC[ ${R16_IMAGE}, ${R64_IMAGE}, 1, 4] -r Gauss[ 3, 0 ] -t SyN[ 0.5 ] -i 50x50x30 -o ${OUTPUT_PREFIX}.nii.gz --go-faster true ) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${R64_IMAGE} ${WARP_IMAGE} ${WARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 14.3283 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.996796 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -1.2204 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 2 ${R16_IMAGE} ${INVERSEWARP_IMAGE} ${INVERSEWARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 2 0 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 50.4483 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 COMMAND $ 2 1 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 1.0 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 COMMAND $ 2 2 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.379897 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_ELASTIC_test.cmake000066400000000000000000000040011311104306400221250ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_ELASTIC) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m PR[ ${R16_IMAGE}, ${R64_IMAGE}, 1, 2] -r Gauss[ 0, 1 ] -t Elast[1] -i 50x50x50 -o ${OUTPUT_PREFIX}.nii.gz ) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${R64_IMAGE} ${WARP_IMAGE} ${WARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 14.6829 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.989737 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -1.01828 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) ants-2.2.0/Examples/TestSuite/ANTS_EXP_test.cmake000066400000000000000000000062741311104306400215130ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_EXP) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m PR[${R16_IMAGE},${R64_IMAGE},1,4] -r Gauss[0.5,0.25] -t Exp[0.5,2,0.5] -i 50x50x50 -o ${OUTPUT_PREFIX}.nii.gz ) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${R64_IMAGE} ${WARP_IMAGE} ${WARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 14.3283 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.996139 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -1.14716 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 2 ${R16_IMAGE} ${INVERSEWARP_IMAGE} ${INVERSEWARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 2 0 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 50.4483 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 COMMAND $ 2 1 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 1.0 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 COMMAND $ 2 2 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.379897 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_GSYN_test.cmake000066400000000000000000000066271311104306400216410ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_GSYN) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m CC[ ${R16_IMAGE}, ${R64_IMAGE},1 ,3 ] -r Gauss[ 3, 0 ] -t SyN[0.25] -i 50x50x50 -o ${OUTPUT_PREFIX}.nii.gz ) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${R64_IMAGE} ${WARP_IMAGE} ${WARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 14.2847 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 9.996276 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -1.19298 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 2 ${R16_IMAGE} ${INVERSEWARP_IMAGE} ${INVERSEWARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPGINV COMMAND $ ${INVERSEWARP_IMAGE} ${THIS_TEST_NAME}INV.jpg) set_property(TEST ${THIS_TEST_NAME}_JPGINV APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 2 0 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 50.4483 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 COMMAND $ 2 1 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 1.0 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 COMMAND $ 2 2 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.379897 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_MI_1_test.cmake000066400000000000000000000062671311104306400216060ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_MI_1) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m MI[${R16_IMAGE},${R64_IMAGE},1,32] -r Gauss[3,0] -t SyN[0.25] -i 50x50x30 -o ${OUTPUT_PREFIX}.nii.gz ) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${R64_IMAGE} ${WARP_IMAGE} ${WARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 14.5927 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.996429 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -0.00038593 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 2 ${R16_IMAGE} ${INVERSEWARP_IMAGE} ${INVERSEWARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 2 0 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 50.4483 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 COMMAND $ 2 1 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 1.0 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 COMMAND $ 2 2 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.379897 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_MI_2_test.cmake000066400000000000000000000062601311104306400216000ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_MI_2) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m MI[${R16_IMAGE},${R64_IMAGE},1,24] -r Gauss[3,0] -t SyN[0.25] -i 50x50x20 -o ${OUTPUT_PREFIX}.nii.gz ) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${R64_IMAGE} ${WARP_IMAGE} ${WARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 14.545 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.9936 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 1.07288 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 2 ${R16_IMAGE} ${INVERSEWARP_IMAGE} ${INVERSEWARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 2 0 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 50.4483 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 COMMAND $ 2 1 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 1.0 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 COMMAND $ 2 2 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.379897 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_MSQ_test.cmake000066400000000000000000000062621311104306400215140ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_MSQ) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m MSQ[${R16_IMAGE},${R64_IMAGE},1,0] -r Gauss[3,0] -t SyN[0.25] -i 50x50x30 -o ${OUTPUT_PREFIX}.nii.gz ) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${R64_IMAGE} ${WARP_IMAGE} ${WARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 14.3426 0.2) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.997077 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -1.18658 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 2 ${R16_IMAGE} ${INVERSEWARP_IMAGE} ${INVERSEWARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 2 0 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 50.4483 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 COMMAND $ 2 1 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 1.0 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 COMMAND $ 2 2 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.379897 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_PSE_MSQ_IMG_test.cmake000066400000000000000000000071531311104306400227170ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_PSE_MSQ_IMG) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -r Gauss[6,0.25] -t SyN[1,2,0.1] -i 191x170x90x90x10 -m PSE[${DEVIL_IMAGE},${ANGEL_IMAGE},${DEVIL_IMAGE},${ANGEL_IMAGE},0.25,0.1,100,0,10] -m MSQ[${DEVIL_IMAGE},${ANGEL_IMAGE},0.75,1] -o ${OUTPUT_PREFIX}.nii.gz --continue-affine 0 --number-of-affine-iterations 0 --geodesic 2) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${ANGEL_IMAGE} ${WARP_IMAGE} ${WARP} -R ${DEVIL_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${DEVIL_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.0116083 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${DEVIL_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.991658 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${DEVIL_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -0.000710551 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 2 ${DEVIL_IMAGE} ${INVERSEWARP_IMAGE} ${INVERSEWARP} -R ${ANGEL_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPGINV COMMAND $ ${INVERSEWARP_IMAGE} ${THIS_TEST_NAME}INV.jpg) set_property(TEST ${THIS_TEST_NAME}_JPGINV APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 2 0 ${ANGEL_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.0109054 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 COMMAND $ 2 1 ${ANGEL_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.990979 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 COMMAND $ 2 2 ${ANGEL_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -0.000704717 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_PSE_MSQ_TXT_test.cmake000066400000000000000000000016501311104306400227560ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_PSE_MSQ_TXT) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -i 191x170x155x40x30 -r Gauss[3,0] -t SyN[0.2] -m MSQ[${DEVIL_IMAGE},${ANGEL_IMAGE},1,0] -m PSE[${DEVIL_IMAGE},${ANGEL_IMAGE},${DEVIL_IMAGE_TXT},${ANGEL_IMAGE_TXT},1,0.1,11,1,1] --continue-affine 0 --number-of-affine-iterations 0 -o ${OUTPUT_PREFIX}.nii.gz --use-all-metrics-for-convergence 1 ) ants-2.2.0/Examples/TestSuite/ANTS_PSE_MSQ_VTK_test.cmake000066400000000000000000000016051311104306400227430ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTSPSE_MSQ_VTK) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -i 91x70x55x40x30 -r Gauss[3,0.,32] -t SyN[0.25] -m MSQ[${DEVIL_IMAGE},${ANGEL_IMAGE},1,0] -m PSE[${DEVIL_IMAGE},${ANGEL_IMAGE},${DEVIL_IMAGE_VTK},${ANGEL_IMAGE_VTK},1,0.33,11,1,25] --continue-affine 0 --number-of-affine-iterations 0 -o ${OUTPUT_PREFIX}.nii.gz) ants-2.2.0/Examples/TestSuite/ANTS_ROT_EXP_test.cmake000066400000000000000000000057541311104306400222410ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_ROT_EXP) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 3 -m MSQ[${ROT_REF_IMAGE},${ROT_MOV_IMAGE},1,0] -t Exp[3,2] -i 50x5x1 -r Gauss[3,0.0,32] -o ${OUTPUT_PREFIX}.nii.gz) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 3 ${ROT_MOV_IMAGE} ${WARP_IMAGE} -R ${ROT_REF_IMAGE} ${WARP} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 3 0 ${ROT_REF_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 36.3928 0.5) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP2 COMMAND $ 3 ${ROT_MOV_IMAGE} ${WARP_IMAGE} -R ${ROT_REF_IMAGE} --ANTS-prefix ${OUTPUT_PREFIX} ) set_property(TEST ${THIS_TEST_NAME}_WARP2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_WARP2_METRIC_0_2 COMMAND $ 3 0 ${ROT_REF_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 36.3928 0.5) set_property(TEST ${THIS_TEST_NAME}_WARP2_METRIC_0_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP2) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 3 ${ROT_REF_IMAGE} ${INVERSEWARP_IMAGE} -R ${ROT_MOV_IMAGE} ${INVERSEWARP} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 3 0 ${ROT_MOV_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 34.8184 0.5) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP2 COMMAND $ 3 ${ROT_REF_IMAGE} ${INVERSEWARP_IMAGE} -R ${ROT_MOV_IMAGE} --ANTS-prefix-invert ${OUTPUT_PREFIX}) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP2_METRIC_0_2 COMMAND $ 3 0 ${ROT_MOV_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 34.8184 0.5) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP2_METRIC_0_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_ROT_GSYN_test.cmake000066400000000000000000000035071311104306400223570ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_ROT_GSYN) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 3 -m MSQ[${ROT_REF_IMAGE},${ROT_MOV_IMAGE},1,0] -t SyN[0.25] -i 50x5x0 -r Gauss[3,0.0,32] -o ${OUTPUT_PREFIX}.nii.gz) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 3 ${ROT_MOV_IMAGE} ${WARP_IMAGE} -R ${ROT_REF_IMAGE} ${WARP} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 3 0 ${ROT_REF_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 35.4473 0.5) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 3 ${ROT_REF_IMAGE} ${INVERSEWARP_IMAGE} -R ${ROT_MOV_IMAGE} ${INVERSEWARP}) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 3 0 ${ROT_MOV_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 35.5439 0.5) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/ANTS_SYN_WITH_TIME_test.cmake000066400000000000000000000025141311104306400231720ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_SYN_WITH_TIME) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m MSQ[${CHALF_IMAGE},${C_IMAGE},1,0] -r Gauss[0.5,0.1] -t SyN[1,10,0.05] -i 150x100x2x2 -o ${OUTPUT_PREFIX} --geodesic 1 --number-of-affine-iterations 0) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${C_IMAGE} ${OUTPUT_PREFIX}.nii.gz ${OUTPUT_PREFIX}Warp.nii.gz -R ${CHALF_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${CHALF_IMAGE} ${OUTPUT_PREFIX}.nii.gz ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.0943736 0.1) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) ants-2.2.0/Examples/TestSuite/ANTS_SYN_test.cmake000066400000000000000000000066341311104306400215300ustar00rootroot00000000000000##################################################################################### ##################################################################################### set(THIS_TEST_NAME ANTS_SYN) set(OUTPUT_PREFIX ${CMAKE_BINARY_DIR}/TEST_${THIS_TEST_NAME} ) set(WARP ${OUTPUT_PREFIX}Warp.nii.gz ${OUTPUT_PREFIX}Affine.txt ) set(INVERSEWARP -i ${OUTPUT_PREFIX}Affine.txt ${OUTPUT_PREFIX}InverseWarp.nii.gz ) set(WARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_warped.nii.gz) set(INVERSEWARP_IMAGE ${CMAKE_BINARY_DIR}/${THIS_TEST_NAME}_inversewarped.nii.gz) add_test(NAME ${THIS_TEST_NAME} COMMAND $ 2 -m PR[${R16_IMAGE},${R64_IMAGE},1,2] -t SyN[0.5,2,0.05] -i 50x50x50 -r Gauss[3,0.0,32] -o ${OUTPUT_PREFIX}.nii.gz) add_test(NAME ${THIS_TEST_NAME}_WARP COMMAND $ 2 ${R64_IMAGE} ${WARP_IMAGE} ${WARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_WARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPG COMMAND $ ${WARP_IMAGE} ${THIS_TEST_NAME}.jpg) set_property(TEST ${THIS_TEST_NAME}_JPG APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_0 COMMAND $ 2 0 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 14.3283 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_1 COMMAND $ 2 1 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.99559 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_WARP_METRIC_2 COMMAND $ 2 2 ${R16_IMAGE} ${WARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz -1.18658 0.05) set_property(TEST ${THIS_TEST_NAME}_WARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_WARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP COMMAND $ 2 ${R16_IMAGE} ${INVERSEWARP_IMAGE} ${INVERSEWARP} -R ${R16_IMAGE} ) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}) add_test(NAME ${THIS_TEST_NAME}_JPGINV COMMAND $ ${INVERSEWARP_IMAGE} ${THIS_TEST_NAME}_JPGINV.jpg) set_property(TEST ${THIS_TEST_NAME}_JPGINV APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 COMMAND $ 2 0 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 50.4483 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_0 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 COMMAND $ 2 1 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 1.0 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_1 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) add_test(NAME ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 COMMAND $ 2 2 ${R64_IMAGE} ${INVERSEWARP_IMAGE} ${OUTPUT_PREFIX}log.txt ${OUTPUT_PREFIX}metric.nii.gz 0.37897 0.05) set_property(TEST ${THIS_TEST_NAME}_INVERSEWARP_METRIC_2 APPEND PROPERTY DEPENDS ${THIS_TEST_NAME}_INVERSEWARP) ants-2.2.0/Examples/TestSuite/APOC_OTSU_INIT_test.cmake000066400000000000000000000020321311104306400224350ustar00rootroot00000000000000 ## # Apocrita tests ## ExternalData_expand_arguments(${PROJECT_NAME}FetchData R16_MASK DATA{${SMALL_DATA_DIR}/r16mask.nii.gz) ExternalData_expand_arguments(${PROJECT_NAME}FetchData R16_PRIORS DATA{${SMALL_DATA_DIR}/r16priors.nii.gz) #add_test(NAME APOC_OTSU_INIT COMMAND $ 2 -i otsu[${R16_IMAGE},3] -x ${R16_MASK} -n 10 -m [0.3,1x1,0.2,0.1] -o ${OUTPUT_PREFIX}.nii.gz ) #add_test(NAME APOC_OTSU_INIT_RADIUS_2x2 COMMAND $ 2 -i otsu[${R16_IMAGE},3] -x ${R16_MASK} -n 10 -m [0.3,2,0.2,0.1] -o ${OUTPUT_PREFIX}.nii.gz ) #add_test(NAME APOC_KMEANS_INIT COMMAND $ 2 -i kmeans[${R16_IMAGE},3] -x ${R16_MASK} -n 10 -m [0.3,1x1,0.2,0.1] -o [${OUTPUT_PREFIX}.nii.gz,${OUTPUT_PREFIX}_posteriors%d.nii.gz]) #add_test(NAME APOC_PRIORLABELIMAGE_INIT COMMAND $ 2 -i priorlabelimage[${R16_IMAGE},5,${R16_PRIORS},0.5] -x ${R16_MASK} -n 10 -m [0.3,1x1,0.2,0.1] -o [${OUTPUT_PREFIX}.nii.gz,${OUTPUT_PREFIX}_posteriors%d.nii.gz] -l 1[1,0.75] -l 2[1,1.0] -l 3[0.5,0.5] -l 4[1,1]) ants-2.2.0/Examples/TestSuite/CMakeLists.txt000066400000000000000000001414471311104306400207330ustar00rootroot00000000000000############################################################################### ############################################################################### ## MakeTestDriverFromANTSbinary ## For tools made with the slicer execution model, ## This macro will build a test driver that adds the ## --compare ## --compareIntensityTolerance ## --compareRadiusTolerance ## --compareNumberOfPixelsTolerance ## to the SEM tools. macro(MakeTestDriverFromANTSbinary ANTSbinaryName ANTSbinaryTestSourceName ANTS_FUNCTION_NAME) set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN "#include \"itkTestDriverBeforeTest.inc\"") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN "#include \"itkTestDriverAfterTest.inc\"") set(ANTS_FUNCTION_NAME ${ANTS_FUNCTION_NAME}) configure_file( template_for_executableTestWrapper.cxx.in ${ANTSbinaryTestSourceName} ) include_directories(${ITK_INCLUDE_DIRS}) create_test_sourcelist(${ANTSbinaryName} ${ANTSbinaryName}Driver.cxx ${ANTSbinaryTestSourceName} EXTRA_INCLUDE itkTestDriverIncludeRequiredIOFactories.h FUNCTION ProcessArgumentsAndRegisterRequiredFactories ) add_executable(${ANTSbinaryName}Driver ${ANTSbinaryName}Driver.cxx ${ANTSbinaryTestSourceName}) target_link_libraries(${ANTSbinaryName}Driver l_${ANTS_FUNCTION_NAME} ) set_target_properties(${ANTSbinaryName}Driver PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) endmacro(MakeTestDriverFromANTSbinary ANTSbinaryName) #----------------------------------------------------------------------- # Setup locations to find externally maintained test data. #----------------------------------------------------------------------- include(ANTSExternalData) # Tell ExternalData commands to transform raw files to content links. set(ExternalData_LINK_CONTENT MD5) set(ExternalData_SOURCE_ROOT ${${PROJECT_NAME}_SOURCE_DIR}) include(ExternalData) # Set testing environment set(ANTS_TEST_BIN_DIR ${CMAKE_BINARY_DIR}/Examples) set(TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/TestData) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/TEST_LOCATION) ## Test antsRegistration ## file(GLOB_RECURSE INCS ${CMAKE_SOURCE_DIR}/Examples/"*.h") MakeTestDriverFromANTSbinary(antsRegistrationTest antsRegistrationTest.cxx antsRegistration) MakeTestDriverFromANTSbinary(ANTSTest ANTSTest.cxx ANTS) MakeTestDriverFromANTSbinary(WarpImageMultiTransform WarpImageMultiTransformTest.cxx WarpImageMultiTransform) MakeTestDriverFromANTSbinary(simpleSynRegistrationTest simpleSynRegistrationTest.cxx simpleSynRegistration) MakeTestDriverFromANTSbinary(antsApplyTransformsTest antsApplyTransformsTest.cxx antsApplyTransforms) MakeTestDriverFromANTSbinary(CompositeTransformUtilTest CompositeTransformUtilTest.cxx CompositeTransformUtil) MakeTestDriverFromANTSbinary(compareTwoTransformsTest compareTwoTransformsTest.cxx compareTwoTransforms) ### # Perform testing ### set(TestDataMD5_DIR ${CMAKE_SOURCE_DIR}/TestData) set(SMALL_DATA_DIR ${TestDataMD5_DIR}/Data) ExternalData_expand_arguments(${PROJECT_NAME}FetchData R16_IMAGE DATA{${SMALL_DATA_DIR}/r16slice.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData R64_IMAGE DATA{${SMALL_DATA_DIR}/r64slice.nii.gz}) ### # PSE sub-tests: Check to see if .txt files and .vtk files also run correctly ### ExternalData_expand_arguments(${PROJECT_NAME}FetchData DEVIL_IMAGE DATA{${SMALL_DATA_DIR}/Frown.nii}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData ANGEL_IMAGE DATA{${SMALL_DATA_DIR}/Smile.nii}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData DEVIL_IMAGE_TXT DATA{${SMALL_DATA_DIR}/Frown.txt}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData ANGEL_IMAGE_TXT DATA{${SMALL_DATA_DIR}/Smile.txt}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData DEVIL_IMAGE_VTK DATA{${SMALL_DATA_DIR}/Frown.vtk}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData ANGEL_IMAGE_VTK DATA{${SMALL_DATA_DIR}/Smile.vtk}) #ExternalData_expand_arguments(${PROJECT_NAME}FetchData SEG_IMAGE DATA{${SMALL_DATA_DIR}/nslice.nii.gz}) if(RUN_LONG_TESTS) ## A work around for the crazy ANTS command line that is not compatible ## with the testing framework of external tests ExternalData_expand_arguments(${PROJECT_NAME}FetchData test_mask_VAR DATA{${TestDataMD5_DIR}/test_mask.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData scale_test_mask_VAR DATA{${TestDataMD5_DIR}/scale.test_mask.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData rotation_test_mask_VAR DATA{${TestDataMD5_DIR}/rotation.test_mask.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData translation_test_mask_VAR DATA{${TestDataMD5_DIR}/translation.test_mask.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData test_image_VAR DATA{${TestDataMD5_DIR}/test.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData scale_test_image_VAR DATA{${TestDataMD5_DIR}/scale.test.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData rotation_test_image_VAR DATA{${TestDataMD5_DIR}/rotation.test.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData rotation_geom_test_image_VAR DATA{${TestDataMD5_DIR}/rotation.geom.test.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData translation_test_image_VAR DATA{${TestDataMD5_DIR}/translation.test.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData translation_rescale_test_image_VAR DATA{${TestDataMD5_DIR}/translation.rescale.test.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData rotation_rescale_test_image_VAR DATA{${TestDataMD5_DIR}/rotation.rescale.test.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData ANON0006_20_T1_dbg_splayed_image_VAR DATA{${TestDataMD5_DIR}/ANON0006_20_T1_dbg_splayed.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData ANON0006_20_T1_sag_twisted_image_VAR DATA{${TestDataMD5_DIR}/ANON0006_20_T1_sag_twisted.nii.gz}) set(antsRegistrationTestName antsRegistrationTest_AffineScaleMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 #numberOfIterations 2500 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 --metric Mattes[${test_image_VAR},${scale_test_image_VAR},1,200,Regular,0.10] #costMetric & fixedVolume & movingVolume & metricWeight? & numberOfHistogramBins & samplingStrategy? & samplingPercentage(numberOfSamples) --transform "Affine[0.001]" #transformType & minimumStepLength --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_${antsRegistrationTestName}.mat} --masks [${test_mask_VAR},${scale_test_mask_VAR}] #fixedBinaryVolume & movingBinaryVolume --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz,${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}_inverse.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_AffineScaleNoMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 -u --metric Mattes[${test_image_VAR},${scale_test_image_VAR},1,200,Regular,0.10] --transform "Affine[0.001]" #transformType & minimumStepLength --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_AffineScaleMasks.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_AffineRotationMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.10] --transform "Affine[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_${antsRegistrationTestName}.mat} --masks [${test_mask_VAR},${rotation_test_mask_VAR}] --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_AffineRotationNoMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.10] --transform "Affine[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_${antsRegistrationTestName}.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_AffineTranslationMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 --metric MeanSquares[${test_image_VAR},${translation_test_image_VAR},1,radius=NA,Regular,0.10] --transform "Affine[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_${antsRegistrationTestName}.mat} --masks [${test_mask_VAR},${translation_test_mask_VAR}] --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_AffineTranslationNoMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 --metric MeanSquares[${test_image_VAR},${translation_test_image_VAR},1,radius=NA,Regular,0.10] --transform "Affine[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_antsRegistrationTest_AffineTranslationMasks.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_RigidAnisotropicMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 1500 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 --metric Mattes[${ANON0006_20_T1_dbg_splayed_image_VAR},${ANON0006_20_T1_sag_twisted_image_VAR},1,200,Regular,0.20] --transform "Rigid[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_antsRegistrationTest_RigidAnisotropicMasks.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_RigidRotationHeadMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.20] --transform "Rigid[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_${antsRegistrationTestName}.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_RigidRotationMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.20] --transform "Rigid[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_RigidRotationHeadMasks.mat} --masks [${test_mask_VAR},${rotation_test_mask_VAR}] --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_RigidRotationNoMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.20] --transform "Rigid[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_${antsRegistrationTestName}.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_MSEAffineRotationMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 250 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 1x1x1 --smoothing-sigmas 0.1x0.1x0.1 --metric MeanSquares[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.10] --transform "Affine[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_AffineRotationMasks.mat} --masks [${test_mask_VAR},${rotation_test_mask_VAR}] --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_RigidRotGeomNoMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --metric Mattes[${test_image_VAR},${rotation_geom_test_image_VAR},1,200,Regular,0.10] --transform "Rigid[0.05]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${rotation_geom_test_image_VAR},1,200,Regular,0.10] --transform "Rigid[0.005]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_${antsRegistrationTestName}.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_RigidRotaRotaRotNoMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.10] --transform "Rigid[0.05]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.10] --transform "Rigid[0.001]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.10] --transform "Rigid[0.00075]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_RigidRotationNoMasks.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_RigidRotaRotaRotNoMasks_Float) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --float 1 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.10] --transform "Rigid[0.05]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.10] --transform "Rigid[0.001]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.10] --transform "Rigid[0.00075]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_RigidRotationNoMasks_Float.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_SimilarityRotationMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 11 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.20] --transform "Similarity[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_RigidRotationNoMasks.mat} --masks [${test_mask_VAR},${rotation_test_mask_VAR}] --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_SimilarityRotationNoMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 11 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${rotation_test_image_VAR},1,200,Regular,0.20] --transform "Similarity[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_${antsRegistrationTestName}.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_SimilarityScaleMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1200 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${scale_test_image_VAR},1,200,Regular,0.20] --transform "Similarity[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_AffineScaleMasks.mat} --masks [${test_mask_VAR},${scale_test_mask_VAR}] --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_SimilarityScaleNoMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${scale_test_image_VAR},1,200,Regular,0.20] --transform "Similarity[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_${antsRegistrationTestName}.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_SimilarityTranslationRescaleNoMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${translation_rescale_test_image_VAR},1,200,Regular,0.20] --transform "Similarity[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_antsRegistrationTest_TranslationRescaleHeadMasks.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_SimilarityTranslationRescaleNoMasks_Float) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --float 1 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${translation_rescale_test_image_VAR},1,200,Regular,0.20] --transform "Similarity[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_antsRegistrationTest_TranslationRescaleHeadMasks_Float.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_CCSimilarityRotationMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 11 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric CC[${test_image_VAR},${rotation_test_image_VAR},1,5] --transform "Similarity[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_RigidRotationNoMasks.mat} --masks [${test_mask_VAR},${rotation_test_mask_VAR}] --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_MSESimilarityRotationMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 11 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric MeanSquares[${test_image_VAR},${rotation_test_image_VAR},1,NA] --transform "Similarity[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_RigidRotationNoMasks.mat} --masks [${test_mask_VAR},${rotation_test_mask_VAR}] --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_MSESimilarityRotationMasks_Float) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 11 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --float 1 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric MeanSquares[${test_image_VAR},${rotation_test_image_VAR},1,NA] --transform "Similarity[0.001]" --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_RigidRotationNoMasks_Float.mat} --masks [${test_mask_VAR},${rotation_test_mask_VAR}] --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_ScaleTranslationRescaleHeadMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 7 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 777 antsRegistrationTest -v --dimensionality 3 --metric Mattes[${test_image_VAR},${translation_rescale_test_image_VAR},1,200] --transform "Rigid[0.001]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${translation_rescale_test_image_VAR},1,200] --transform "Similarity[0.0001]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_antsRegistrationTest_TranslationRescaleHeadMasks.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_ScaleRotationRescaleHeadMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --metric Mattes[${test_image_VAR},${rotation_rescale_test_image_VAR},1,200] --transform "Rigid[0.01]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${rotation_rescale_test_image_VAR},1,200] --transform "Similarity[0.003]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_${antsRegistrationTestName}.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_ScaleRotationRescaleHeadMasks_Float) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --float 1 --metric Mattes[${test_image_VAR},${rotation_rescale_test_image_VAR},1,200] --transform "Rigid[0.01]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric Mattes[${test_image_VAR},${rotation_rescale_test_image_VAR},1,200] --transform "Similarity[0.003]" --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_${antsRegistrationTestName}.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_SyNScaleNoMasks) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --metric Mattes[${test_image_VAR},${scale_test_image_VAR},1,200] --transform "SyN[0.25,3.0,0.0]" --convergence 100x70x20 --shrink-factors 3x2x1 --smoothing-sigmas 2x1x0 --use-histogram-matching --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_AffineScaleMasks.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_SyNScaleNoMasks_Float) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare DATA{${TestDataMD5_DIR}/${antsRegistrationTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --float 1 --metric Mattes[${test_image_VAR},${scale_test_image_VAR},1,200] --transform "SyN[0.25,3.0,0.0]" --convergence 100x70x20 --shrink-factors 3x2x1 --smoothing-sigmas 2x1x0 --use-histogram-matching --initial-moving-transform DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_AffineScaleMasks_Float.mat} --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) ## Test initializing stages at each iteration, ensure that only 2 stages result. ## This test is not very robust. It ensures that the command line options ## can be accepted without failure, but no real testing of results is done. ## The output transform should have 2 levels (1 affine, 1 SyN) ## The next test uses "compareTwoTransforms" to compare the output transform to the baseline. set(antsRegistrationTestName antsRegistrationTest_initialize_transforms_per_stage ) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ antsRegistrationTest -v --dimensionality 3 --float 0 --initialize-transforms-per-stage --write-composite-transform --collapse-output-transforms 0 --metric Mattes[${test_image_VAR},${scale_test_image_VAR},1,200] --transform "Rigid[0.01]" --convergence 15 --shrink-factors 3 --smoothing-sigmas 2 --metric Mattes[${test_image_VAR},${scale_test_image_VAR},1,200] --transform "Affine[0.001]" --convergence 15 --shrink-factors 3 --smoothing-sigmas 2 --metric Mattes[${test_image_VAR},${scale_test_image_VAR},1,200] --transform "SyN[0.25,3.0,0.0]" --convergence 15 --shrink-factors 3 --smoothing-sigmas 2 --metric Mattes[${test_image_VAR},${scale_test_image_VAR},1,200] --transform "SyN[0.25,3.0,0.0]" --convergence 11 --shrink-factors 3 --smoothing-sigmas 2 --use-histogram-matching --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) # Compare the output transform of the antsRegistrationTest_initialize_transforms_per_stage with the baseline file set(antsRegistrationTestName antsRegistrationTest_initializePerStage_ComparisonTesting) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} compareTwoTransformsTestDriver compareTwoTransformsTest ${CMAKE_CURRENT_BINARY_DIR}/antsRegistrationTest_initialize_transforms_per_stageComposite.h5 DATA{${TestDataMD5_DIR}/antsRegistrationTest_initialize_transforms_per_stageComposite.result.h5} ) set_property(TEST ${antsRegistrationTestName} APPEND PROPERTY DEPENDS antsRegistrationTest_initialize_transforms_per_stage) ############### #TEST Development SHOULD BE CONTINUED FROM HERE ############### ### checks that simpleSynRegistration runs without failure #### set(simpleSynRegistrationTestName simpleSynRegistrationTesting) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${simpleSynRegistrationTestName} COMMAND ${LAUNCH_EXE} simpleSynRegistrationTestDriver simpleSynRegistrationTest ${test_image_VAR} ${scale_test_image_VAR} DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_AffineScaleMasks.mat} ${CMAKE_CURRENT_BINARY_DIR}/${simpleSynRegistrationTestName} ) ### antsApplyTransforms test ##### set(antsApplyTransformsTestName antsApplyTransformsTesting) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsApplyTransformsTestName} COMMAND ${LAUNCH_EXE} antsApplyTransformsTestDriver --compare DATA{${TestDataMD5_DIR}/${antsApplyTransformsTestName}.result.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/${antsApplyTransformsTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 1000 antsApplyTransformsTest -d 3 -i ${scale_test_image_VAR} #{moving image} -r ${test_image_VAR} #{fix image} -n linear #{interpolation type} -t DATA{${TestDataMD5_DIR}/${antsApplyTransformsTestName}_InputWarpTransform.nii.gz} #{Warp transform} -t DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_AffineScaleMasks.mat} #{Affine Transform} -o ${CMAKE_CURRENT_BINARY_DIR}/${antsApplyTransformsTestName}.test.nii.gz #{output file name} ) ### compare the outputs of the antsRegistration and simpleSynRegistration # This needs four steps: # 1- make the output SyN warp transform from antsRegistration # 2- make the output Syn warp transform from simpleSynRegistration # 3- apply the produced transforms to antsApplyTransforms # 4- compare the results of step 3 # we use the results of the previous tests for steps 1 and 2. # step 3 is done as follows: set(antsApplyTransformsTestName antsApplyTransformsTestForAntsRegistration) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsApplyTransformsTestName} COMMAND ${LAUNCH_EXE} antsApplyTransformsTestDriver antsApplyTransformsTest -d 3 -i ${scale_test_image_VAR} #{moving image} -r ${test_image_VAR} #{fix image} -n linear #{interpolation type} -t ${CMAKE_CURRENT_BINARY_DIR}/antsRegistrationTest_SyNScaleNoMasks1Warp.nii.gz #{Warp transform} -t DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_AffineScaleMasks.mat} #{Affine Transform} -o ${CMAKE_CURRENT_BINARY_DIR}/${antsApplyTransformsTestName}.test.nii.gz #{output file name} ) set_property(TEST ${antsApplyTransformsTestName} APPEND PROPERTY DEPENDS antsRegistrationTest_SyNScaleNoMasks) set(antsApplyTransformsTestName antsApplyTransformsTestForSimpleSynRegistration) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsApplyTransformsTestName} COMMAND ${LAUNCH_EXE} antsApplyTransformsTestDriver antsApplyTransformsTest -d 3 -i ${scale_test_image_VAR} #{moving image} -r ${test_image_VAR} #{fix image} -n linear #{interpolation type} -t ${CMAKE_CURRENT_BINARY_DIR}/simpleSynRegistrationTestingWarp.nii.gz #{Warp transform} -t DATA{${TestDataMD5_DIR}/Initializer_0.05_antsRegistrationTest_AffineScaleMasks.mat} #{Affine Transform} -o ${CMAKE_CURRENT_BINARY_DIR}/${antsApplyTransformsTestName}.test.nii.gz #{output file name} ) set_property(TEST ${antsApplyTransformsTestName} APPEND PROPERTY DEPENDS simpleSynRegistrationTesting) # step 4: finally the results are compared together set(antsApplyTransformsTestName simpleSynRegistration_Vs_antsRegistration) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsApplyTransformsTestName} COMMAND ${LAUNCH_EXE} antsApplyTransformsTestDriver --compare ${CMAKE_CURRENT_BINARY_DIR}/antsApplyTransformsTestForAntsRegistration.test.nii.gz ${CMAKE_CURRENT_BINARY_DIR}/antsApplyTransformsTestForSimpleSynRegistration.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 1000 antsApplyTransformsTest -h ) set_property(TEST simpleSynRegistration_Vs_antsRegistration APPEND PROPERTY DEPENDS antsApplyTransformsTestForSimpleSynRegistration antsApplyTransformsTestForAntsRegistration ) ############ End of four step compare tests################# ### checks that CompositeTransformUtil runs without failure using --assemble flag#### set(CompositeTransformUtilTestName CompositeTransformUtilTest) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${CompositeTransformUtilTestName} COMMAND ${LAUNCH_EXE} CompositeTransformUtilTestDriver CompositeTransformUtilTest --assemble ${CMAKE_CURRENT_BINARY_DIR}/${CompositeTransformUtilTestName}.test.h5 DATA{${TestDataMD5_DIR}/${CompositeTransformUtilTestName}_RigidTransform.mat} DATA{${TestDataMD5_DIR}/${CompositeTransformUtilTestName}_AffineTransform.mat} DATA{${TestDataMD5_DIR}/${CompositeTransformUtilTestName}_SyNTransform.nii.gz} ) # Compare the results of the CompositeTransformUtil with the baseline file set(CompositeTransformUtilTestName CompositeTransformUtil_ComparisonTesting) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${CompositeTransformUtilTestName} COMMAND ${LAUNCH_EXE} compareTwoTransformsTestDriver compareTwoTransformsTest ${CMAKE_CURRENT_BINARY_DIR}/CompositeTransformUtilTest.test.h5 DATA{${TestDataMD5_DIR}/CompositeTransformUtilTest.result.h5} ) set_property(TEST ${CompositeTransformUtilTestName} APPEND PROPERTY DEPENDS CompositeTransformUtilTest) # Tests CompositeTransformUtil when it is run by --disassemble flag set(CompositeTransformUtilTestName CompositeTransformUtilTest_disassemble) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${CompositeTransformUtilTestName} COMMAND ${LAUNCH_EXE} CompositeTransformUtilTestDriver --compare DATA{${TestDataMD5_DIR}/CompositeTransformUtilTest_SyNTransform.nii.gz} ${CMAKE_CURRENT_BINARY_DIR}/02_${CompositeTransformUtilTestName}_DisplacementFieldTransform.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 1 --compareNumberOfPixelsTolerance 1000 CompositeTransformUtilTest --disassemble DATA{${TestDataMD5_DIR}/CompositeTransformUtilTest.result.h5} ${CompositeTransformUtilTestName} ) ## Based on conversations with Nick and Brian, it is strange that ## these ToItself functions fail. It is likely in the ITK scales estimators ## that the problem exists. It will be addressed later. if(0) #HACK -- REMOVING THESE TWO FAILING TEST set(antsRegistrationTestName antsRegistrationTest_CCMetricBrainToItself) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare ${test_image_VAR} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric CC[${test_image_VAR},${test_image_VAR},1,5] --transform "Similarity[0.001]" --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) set(antsRegistrationTestName antsRegistrationTest_MSEMetricBrainToItself) ExternalData_add_test( ${PROJECT_NAME}FetchData NAME ${antsRegistrationTestName} COMMAND ${LAUNCH_EXE} $ --compare ${test_image_VAR} ${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz --compareIntensityTolerance 9 --compareRadiusTolerance 0 --compareNumberOfPixelsTolerance 1000 antsRegistrationTest -v --dimensionality 3 --convergence 25x20x5 --shrink-factors 3x2x1 --smoothing-sigmas 0x0x0 --metric MeanSquares[${test_image_VAR},${test_image_VAR},1,NA] --transform "Similarity[0.001]" --output [${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName},${CMAKE_CURRENT_BINARY_DIR}/${antsRegistrationTestName}.test.nii.gz] ) endif() ############ End of CompositeTransformUtil tests################# endif(RUN_LONG_TESTS) ############################################################## ############################################################## ############################################################## ############################################################## if(RUN_SHORT_TESTS) ### # ANTS metric testing ### set(AllANTSPrograms ANTS antsApplyTransforms antsApplyTransformsToPoints ANTSIntegrateVectorField ANTSIntegrateVelocityField ANTSJacobian antsMotionCorr antsRegistration ANTSUseDeformationFieldToGetAffineTransform ANTSUseLandmarkImagesToGetAffineTransform ANTSUseLandmarkImagesToGetBSplineDisplacementField Atropos AverageAffineTransform AverageImages AverageTensorImages ClusterImageStatistics ComposeMultiTransform CompositeTransformUtil ConvertImagePixelType ConvertScalarImageToRGB ConvertToJpg CopyImageHeaderInformation CreateDisplacementField CreateDTICohort CreateImage CreateWarpedGridImage ExtractRegionFromImage ExtractRegionFromImageByMask ExtractSliceFromImage ImageCompare ImageMath ImageSetStatistics KellyKapowski KellySlater LabelClustersUniquely LabelGeometryMeasures LabelOverlapMeasures LaplacianThickness MeasureImageSimilarity MeasureMinMaxMean MemoryTest MultiplyImages N3BiasFieldCorrection N4BiasFieldCorrection PasteImageIntoImage PermuteFlipImageOrientationAxes PrintHeader RebaseTensorImage ReorientTensorImage ResampleImage ResampleImageBySpacing ResetDirection sccan SetDirectionByMatrix SetOrigin SetSpacing SmoothImage StackSlices SurfaceBasedSmoothing SurfaceCurvature ThresholdImage TileImages WarpImageMultiTransform WarpTensorImageMultiTransform WarpTimeSeriesImageMultiTransform ) foreach(CurrProg ${AllANTSPrograms}) set(HELP_FLAG "--help") add_test(NAME ${CurrProg}_HELP_LONG COMMAND $ ${HELP_FLAG} ) ## Just print the help screen set(HELP_FLAG "-h" ) add_test(NAME ${CurrProg}_HELP_SHORT COMMAND $ ${HELP_FLAG} ) ## Just print the help screen set_property(TEST ${CurrProg}_HELP_SHORT APPEND PROPERTY DEPENDS ${CurrProg}_HELP_LONG) endforeach() ### # ANTS transform testing ### include(ANTS_CC_1_test.cmake) include(ANTS_CC_2_test.cmake) include(ANTS_CC_3_test.cmake) include(ANTS_MSQ_test.cmake) include(ANTS_MI_1_test.cmake) include(ANTS_MI_2_test.cmake) include(ANTS_ELASTIC_test.cmake) include(ANTS_GSYN_test.cmake) include(ANTS_EXP_test.cmake) include(ANTS_SYN_test.cmake) ### # ANTS labeled data testing ### if(RUN_LONG_TESTS) include(ANTS_PSE_MSQ_IMG_test.cmake) ### # ANTS images with non-trival rotation header test ### #ExternalData_expand_arguments(${PROJECT_NAME}FetchData ROT_REF_IMAGE DATA{${SMALL_DATA_DIR}/ref2.nii.gz}) #ExternalData_expand_arguments(${PROJECT_NAME}FetchData ROT_MOV_IMAGE DATA{${SMALL_DATA_DIR}/mov2.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData ROT_REF_IMAGE DATA{${SMALL_DATA_DIR}/r16roth.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData ROT_MOV_IMAGE DATA{${SMALL_DATA_DIR}/r64roth.nii.gz}) include(ANTS_ROT_GSYN_test.cmake) include(ANTS_ROT_EXP_test.cmake) #add_test(NAME ANTS_ROT_EXP_CLEAN COMMAND rm ${ROT_WARP_FILES}) ### # Test SyN with time ### ExternalData_expand_arguments(${PROJECT_NAME}FetchData CHALF_IMAGE DATA{${SMALL_DATA_DIR}/chalf.nii.gz}) ExternalData_expand_arguments(${PROJECT_NAME}FetchData C_IMAGE DATA{${SMALL_DATA_DIR}/c.nii.gz}) include(ANTS_SYN_WITH_TIME_test.cmake) include(APOC_OTSU_INIT_test.cmake) endif(RUN_LONG_TESTS) include(ANTS_PSE_MSQ_TXT_test.cmake) include(ANTS_PSE_MSQ_VTK_test.cmake) endif(RUN_SHORT_TESTS) ExternalData_add_target( ${PROJECT_NAME}FetchData ) # Name of data management target ants-2.2.0/Examples/TestSuite/template_for_executableTestWrapper.cxx.in000066400000000000000000000006111311104306400263720ustar00rootroot00000000000000#include #include #include #include "include/ants.h" int @ANTS_FUNCTION_NAME@Test(int argc, char* argv[]) { const char * const * const ptr = &argv[1]; const std::vector command_line_args( ptr, ptr + argc - 1 ); const bool return_value = ants::@ANTS_FUNCTION_NAME@( command_line_args, &std::cout ); return return_value; } ants-2.2.0/Examples/Testing/000077500000000000000000000000001311104306400156445ustar00rootroot00000000000000ants-2.2.0/Examples/Testing/Temporary/000077500000000000000000000000001311104306400176265ustar00rootroot00000000000000ants-2.2.0/Examples/Testing/Temporary/CTestCostData.txt000066400000000000000000000000041311104306400230260ustar00rootroot00000000000000--- ants-2.2.0/Examples/Testing/Temporary/LastTest.log000066400000000000000000000001711311104306400220730ustar00rootroot00000000000000Start testing: Nov 12 21:41 PST ---------------------------------------------------------- End testing: Nov 12 21:41 PST ants-2.2.0/Examples/TextureCooccurrenceFeatures.cxx000066400000000000000000000154671311104306400224620ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include #include "ReadWriteData.h" #include #include "itkImage.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkRescaleIntensityImageFilter.h" #include "itkScalarImageToTextureFeaturesFilter.h" #include "itkDenseFrequencyContainer2.h" namespace ants { template int TextureCooccurrenceFeatures( int argc, char *argv[] ) { typedef float PixelType; typedef float RealType; typedef itk::Image ImageType; typedef itk::Image RealImageType; typename ImageType::Pointer inputImage = ImageType::New(); ReadImage( inputImage, argv[2] ); // we have to rescale the input image since the cooccurrence filter // adds 1 to the upper bound of the joint histogram and small ranges // will be greatly affected by this. typedef itk::RescaleIntensityImageFilter RescalerType; typename RescalerType::Pointer rescaler = RescalerType::New(); rescaler->SetInput( inputImage ); rescaler->SetOutputMinimum( 0 ); rescaler->SetOutputMaximum( 10000 ); rescaler->Update(); typedef itk::Statistics::DenseFrequencyContainer2 HistogramFrequencyContainerType; typedef itk::Statistics::ScalarImageToTextureFeaturesFilter TextureFilterType; typename TextureFilterType::Pointer textureFilter = TextureFilterType::New(); textureFilter->SetInput( rescaler->GetOutput() ); typename ImageType::Pointer mask = NULL; PixelType label = itk::NumericTraits::OneValue(); if ( argc > 4 ) { ReadImage( mask, argv[4] ); textureFilter->SetMaskImage( mask ); if ( argc > 5 ) { label = static_cast( atoi( argv[5] ) ); } textureFilter->SetInsidePixelValue( label ); } unsigned int numberOfBins = 256; if ( argc > 3 ) { numberOfBins = static_cast( atoi( argv[3] ) ); } textureFilter->SetNumberOfBinsPerAxis( numberOfBins ); itk::ImageRegionIteratorWithIndex ItI( rescaler->GetOutput(), rescaler->GetOutput()->GetLargestPossibleRegion() ); PixelType maxValue = itk::NumericTraits::NonpositiveMin(); PixelType minValue = itk::NumericTraits::max(); for( ItI.GoToBegin(); !ItI.IsAtEnd(); ++ItI ) { if ( !mask || ( mask->GetPixel( ItI.GetIndex() ) == label ) ) { if ( ItI.Get() < minValue ) { minValue = ItI.Get(); } else if ( ItI.Get() > maxValue ) { maxValue = ItI.Get(); } } } textureFilter->SetPixelValueMinMax( minValue, maxValue ); textureFilter->SetNumberOfBinsPerAxis( numberOfBins ); textureFilter->FastCalculationsOff(); /** * Second order measurements * These include: * 1. energy (f1) *cth, *amfm * 2. entropy (f2) *cth, *amfm * 3. correlation (f3) *amfm * 4. inverse difference moment (f4) *cth, *amfm * 5. inertia (f5) *cth, *amfm * 6. cluster shade (f6) *cth * 7. cluster prominence (f7) *cth * 8. haralick's correlation (f8) */ textureFilter->Update(); typename TextureFilterType::FeatureValueVectorPointer means = textureFilter->GetFeatureMeans(); const typename TextureFilterType::FeatureNameVector* names = textureFilter->GetRequestedFeatures(); typename TextureFilterType::FeatureValueVector::ConstIterator mIt = means->Begin(); typename TextureFilterType::FeatureNameVector::ConstIterator nIt = names->Begin(); while( mIt != means->End() ) { // std::cout << nIt.Value() << ": " << mIt.Value() << std::endl; std::cout << mIt.Value() << " "; ++mIt; ++nIt; } std::cout << std::endl; // RealType entropy = textureFilter->GetFeatureMeansOutput()[1]; // RealType inverseDifferenceMoment = textureFilter->GetFeatureMeansOutput()[2]; // RealType inertia = textureFilter->GetFeatureMeansOutput()[3]; // RealType clusterShade = textureFilter->GetFeatureMeansOutput()[4]; // RealType clusterProminence = textureFilter->GetFeatureMeansOutput()[5]; // // std::cout << energy << " " // << entropy << " " // << inverseDifferenceMoment << " " // << inertia << " " // << clusterShade << " " // << clusterProminence << std::endl; /* std::cout << "energy : " << energy << std::endl; std::cout << "entropy : " << entropy << std::endl; std::cout << "inverse diff moment: " << inverseDifferenceMoment << std::endl; std::cout << "inertia : " << inertia << std::endl; std::cout << "cluster Shade : " << clusterShade << std::endl; std::cout << "cluster prominence : " << clusterProminence << std::endl; */ return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int TextureCooccurrenceFeatures( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "TextureCooccurrenceFeatures" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); if ( argc < 3 ) { std::cerr << "Usage: " << argv[0] << " imageDimension inputImage " << "[numberOfBinsPerAxis=256] [maskImage] [maskLabel=1]" << std::endl; std::cerr << "Features: Energy,Entropy,InverseDifferenceMoment,Inertia,ClusterShade,ClusterProminence" << std::endl; exit( 1 ); } switch( atoi( argv[1] ) ) { case 2: TextureCooccurrenceFeatures<2>( argc, argv ); break; case 3: TextureCooccurrenceFeatures<3>( argc, argv ); break; default: std::cerr << "Unsupported dimension" << std::endl; exit( EXIT_FAILURE ); } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/TextureRunLengthFeatures.cxx000066400000000000000000000137341311104306400217510ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include #include "ReadWriteData.h" #include #include "itkBoundingBox.h" #include "itkImage.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkPointSet.h" #include "itkScalarImageToRunLengthFeaturesFilter.h" #include "itkDenseFrequencyContainer2.h" namespace ants { template int TextureRunLengthFeatures( int argc, char *argv[] ) { typedef float PixelType; typedef float RealType; typedef itk::Image ImageType; typedef itk::Image RealImageType; typename ImageType::Pointer inputImage = ImageType::New(); ReadImage( inputImage, argv[2] ); typedef itk::Statistics::DenseFrequencyContainer2 HistogramFrequencyContainerType; typedef itk::Statistics::ScalarImageToRunLengthFeaturesFilter RunLengthFilterType; typename RunLengthFilterType::Pointer runLengthFilter = RunLengthFilterType::New(); runLengthFilter->SetInput( inputImage ); typename ImageType::Pointer mask = NULL; PixelType label = itk::NumericTraits::OneValue(); if ( argc > 4 ) { ReadImage( mask, argv[4] ); runLengthFilter->SetMaskImage( mask ); if ( argc > 5 ) { label = static_cast( atoi( argv[5] ) ); } runLengthFilter->SetInsidePixelValue( label ); } unsigned int numberOfBins = 256; if ( argc > 3 ) { numberOfBins = static_cast( atoi( argv[3] ) ); } runLengthFilter->SetNumberOfBinsPerAxis( numberOfBins ); itk::ImageRegionIteratorWithIndex ItI( inputImage, inputImage->GetLargestPossibleRegion() ); PixelType maxValue = itk::NumericTraits::NonpositiveMin(); PixelType minValue = itk::NumericTraits::max(); typedef itk::BoundingBox BoundingBoxType; typename BoundingBoxType::Pointer bbox = BoundingBoxType::New(); typename BoundingBoxType::PointsContainerPointer points = BoundingBoxType::PointsContainer::New(); itk::Point point; unsigned int idx = 0; for( ItI.GoToBegin(); !ItI.IsAtEnd(); ++ItI ) { if ( !mask || ( mask->GetPixel( ItI.GetIndex() ) == label ) ) { if ( ItI.Get() < minValue ) { minValue = ItI.Get(); } else if ( ItI.Get() > maxValue ) { maxValue = ItI.Get(); } inputImage->TransformIndexToPhysicalPoint( ItI.GetIndex(), point ); points->InsertElement( idx++, point ); } } bbox->SetPoints( points ); bbox->ComputeBoundingBox(); typename BoundingBoxType::PointType pointMin = bbox->GetMinimum(); typename BoundingBoxType::PointType pointMax = bbox->GetMaximum(); runLengthFilter->SetPixelValueMinMax( minValue, maxValue ); runLengthFilter->SetDistanceValueMinMax( 0, pointMin.EuclideanDistanceTo( pointMax ) ); runLengthFilter->SetNumberOfBinsPerAxis( numberOfBins ); runLengthFilter->FastCalculationsOff(); runLengthFilter->Update(); typename RunLengthFilterType::FeatureValueVectorPointer means = runLengthFilter->GetFeatureMeans(); const typename RunLengthFilterType::FeatureNameVector* names = runLengthFilter->GetRequestedFeatures(); typename RunLengthFilterType::FeatureValueVector::ConstIterator mIt = means->Begin(); typename RunLengthFilterType::FeatureNameVector::ConstIterator nIt = names->Begin(); while( mIt != means->End() ) { // std::cout << nIt.Value() << ": " << mIt.Value() << std::endl; std::cout << mIt.Value() << " "; ++mIt; ++nIt; } std::cout << std::endl; return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int TextureRunLengthFeatures( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "TextureRunLengthFeatures" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); if ( argc < 3 ) { std::cout << "Usage: " << argv[0] << " imageDimension inputImage " << "[numberOfBinsPerAxis=256] [maskImage] [maskLabel=1]" << std::endl; std::cout << "Features: ShortRunEmphasis,LongRunEmphasis,GreyLevelNonuniformity,"; std::cout << "RunLengthNonuniformity,LowGreyLevelRunEmphasis,HighGreyLevelRunEmphasis,"; std::cout << "ShortRunLowGreyLevelEmphasis,ShortRunHighGreyLevelEmphasis,"; std::cout << "LongRunLowGreyLevelEmphasis,LongRunHighGreyLevelEmphasis" << std::endl; exit( 1 ); } switch( atoi( argv[1] ) ) { case 2: TextureRunLengthFeatures<2>( argc, argv ); break; case 3: TextureRunLengthFeatures<3>( argc, argv ); break; default: std::cerr << "Unsupported dimension" << std::endl; exit( EXIT_FAILURE ); } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/ThresholdImage.cxx000066400000000000000000000450061311104306400176570ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include #include #include #include #include "itkArray.h" #include "itkExtractImageFilter.h" #include "itkImage.h" #include "ReadWriteData.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkKdTreeBasedKmeansEstimator.h" #include "itkLabelStatisticsImageFilter.h" #include "itkLinearInterpolateImageFunction.h" #include "itkListSample.h" #include "itkMinimumDecisionRule.h" #include "itkMultiplyImageFilter.h" #include "itkNeighborhoodIterator.h" #include "itkOtsuMultipleThresholdsImageFilter.h" #include "itkResampleImageFilter.h" #include "itkSampleClassifierFilter.h" #include "itkWeightedCentroidKdTreeGenerator.h" namespace ants { template typename TImage::Pointer MultiplyImage(typename TImage::Pointer image1, typename TImage::Pointer image2) { std::cout << " Multiply " << std::endl; // Begin Multiply Images typedef TImage tImageType; // output will be the speed image for FMM typedef itk::MultiplyImageFilter MultFilterType; typename MultFilterType::Pointer filter = MultFilterType::New(); filter->SetInput1( image1 ); filter->SetInput2( image2 ); filter->Update(); return filter->GetOutput(); // this is the speed image // write a function to threshold the speedimage so // if the dist is g.t. D then speed = 1 } template typename TImage::Pointer BinaryThreshold_AltInsideOutside_threashold( typename TImage::PixelType low, typename TImage::PixelType high, typename TImage::PixelType insideval, typename TImage::PixelType outsideval, typename TImage::Pointer input ) { typedef typename TImage::PixelType PixelType; // Begin Threshold Image typedef itk::BinaryThresholdImageFilter InputThresholderType; typename InputThresholderType::Pointer inputThresholder = InputThresholderType::New(); inputThresholder->SetInput( input ); inputThresholder->SetInsideValue( insideval ); inputThresholder->SetOutsideValue( outsideval ); if( high < low ) { high = 255; } float eps = 1.e-6 * low; inputThresholder->SetLowerThreshold( (PixelType) low - eps ); inputThresholder->SetUpperThreshold( (PixelType) high + eps); inputThresholder->Update(); return inputThresholder->GetOutput(); } template typename TImage::Pointer LabelSurface(typename TImage::PixelType foreground, typename TImage::PixelType newval, typename TImage::Pointer input) { std::cout << " Label Surf " << std::endl; typedef TImage ImageType; enum { ImageDimension = ImageType::ImageDimension }; // ORIENTATION ALERT: Original code set spacing & origin without // setting directions. typename ImageType::Pointer Image = AllocImage(input); typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for( int j = 0; j < ImageDimension; j++ ) { rad[j] = 1; } iteratorType GHood(rad, input, input->GetLargestPossibleRegion() ); GHood.GoToBegin(); // std::cout << " foreg " << (int) foreground; while( !GHood.IsAtEnd() ) { typename TImage::PixelType p = GHood.GetCenterPixel(); typename TImage::IndexType ind = GHood.GetIndex(); typename TImage::IndexType ind2; if( p == foreground ) { bool atedge = false; for( int i = 0; i < GHood.Size(); i++ ) { ind2 = GHood.GetIndex(i); float dist = 0.0; for( int j = 0; j < ImageDimension; j++ ) { dist += (float)(ind[j] - ind2[j]) * (float)(ind[j] - ind2[j]); } dist = sqrt(dist); if( GHood.GetPixel(i) != foreground && dist < 1.1 ) { atedge = true; } } if( atedge && p == foreground ) { Image->SetPixel(ind, newval); } else if( p == foreground ) { Image->SetPixel(ind, 0); } } ++GHood; } return Image; } template typename TImage::Pointer OtsuThreshold( int NumberOfThresholds, typename TImage::Pointer input, typename TMaskImage::Pointer maskImage ) { std::cout << " Otsu Thresh with " << NumberOfThresholds << " thresholds" << std::endl; if( maskImage.IsNull() ) { // Begin Threshold Image typedef itk::OtsuMultipleThresholdsImageFilter InputThresholderType; typename InputThresholderType::Pointer inputThresholder = InputThresholderType::New(); inputThresholder->SetInput( input ); /* inputThresholder->SetInsideValue( replaceval ); int outval=0; if ((float) replaceval == (float) -1) outval=1; inputThresholder->SetOutsideValue( outval ); */ inputThresholder->SetNumberOfThresholds( NumberOfThresholds ); inputThresholder->Update(); return inputThresholder->GetOutput(); } else { typedef TImage ImageType; typedef TMaskImage MaskImageType; typedef float PixelType; unsigned int numberOfBins = 200; int maskLabel = 1; itk::ImageRegionIterator ItI( input, input->GetLargestPossibleRegion() ); itk::ImageRegionIterator ItM( maskImage, maskImage->GetLargestPossibleRegion() ); PixelType maxValue = itk::NumericTraits::min(); PixelType minValue = itk::NumericTraits::max(); for ( ItM.GoToBegin(), ItI.GoToBegin(); !ItI.IsAtEnd(); ++ItM, ++ItI ) { if ( ItM.Get() == maskLabel ) { if ( ItI.Get() < minValue ) { minValue = ItI.Get(); } else if ( ItI.Get() > maxValue ) { maxValue = ItI.Get(); } } } typedef itk::LabelStatisticsImageFilter StatsType; typename StatsType::Pointer stats = StatsType::New(); stats->SetInput( input ); stats->SetLabelInput( maskImage ); stats->UseHistogramsOn(); stats->SetHistogramParameters( numberOfBins, minValue, maxValue ); stats->Update(); typedef itk::OtsuMultipleThresholdsCalculator OtsuType; typename OtsuType::Pointer otsu = OtsuType::New(); otsu->SetInputHistogram( stats->GetHistogram( maskLabel ) ); otsu->SetNumberOfThresholds( NumberOfThresholds ); otsu->Update(); typename OtsuType::OutputType thresholds = otsu->GetOutput(); typename ImageType::Pointer output = ImageType::New(); output->CopyInformation( maskImage ); output->SetRegions( maskImage->GetLargestPossibleRegion() ); output->Allocate(); output->FillBuffer( 0 ); itk::ImageRegionIterator ItO( output, output->GetLargestPossibleRegion() ); for ( unsigned int i = 0; i < thresholds.size(); i++ ) { ItI.GoToBegin(); ItM.GoToBegin(); ItO.GoToBegin(); while ( !ItM.IsAtEnd() ) { if ( ItM.Get() == maskLabel ) { if ( ItO.Get() == 0 && ItI.Get() < thresholds[i] ) { ItO.Set( i+1 ); } } ++ItI; ++ItM; ++ItO; } } ItI.GoToBegin(); ItM.GoToBegin(); ItO.GoToBegin(); while ( !ItM.IsAtEnd() ) { if ( ItM.Get() == maskLabel && ItO.Get() == 0 ) { ItO.Set( thresholds.size()+1 ); } ++ItI; ++ItM; ++ItO; } return output; } } template typename TImage::Pointer KmeansThreshold( int NumberOfThresholds, typename TImage::Pointer input, typename TMaskImage::Pointer maskImage ) { std::cout << " Kmeans with " << NumberOfThresholds << " thresholds" << std::endl; typedef TImage ImageType; typedef TImage LabelImageType; typedef TMaskImage MaskImageType; typedef float RealType; typedef int LabelType; typedef itk::Array MeasurementVectorType; typedef typename itk::Statistics::ListSample SampleType; int maskLabel = 1; if( maskImage.IsNull() ) { maskImage = AllocImage( input, maskLabel ); } unsigned int numberOfTissueClasses = NumberOfThresholds + 1; typename LabelImageType::Pointer output = AllocImage( input, 0 ); typedef itk::LabelStatisticsImageFilter StatsType; typename StatsType::Pointer stats = StatsType::New(); stats->SetInput( input ); stats->SetLabelInput( maskImage ); stats->UseHistogramsOff(); stats->Update(); RealType minValue = stats->GetMinimum( maskLabel ); RealType maxValue = stats->GetMaximum( maskLabel ); // The code below can be replaced by itkListSampleToImageFilter when we // migrate over to the Statistics classes current in the Review/ directory. // typename SampleType::Pointer sample = SampleType::New(); sample->SetMeasurementVectorSize( 1 ); itk::ImageRegionConstIteratorWithIndex ItI( input, input->GetRequestedRegion() ); for( ItI.GoToBegin(); !ItI.IsAtEnd(); ++ItI ) { if( !maskImage || maskImage->GetPixel( ItI.GetIndex() ) == maskLabel ) { typename SampleType::MeasurementVectorType measurement; measurement.SetSize( 1 ); measurement[0] = ItI.Get(); sample->PushBack( measurement ); } } typedef itk::Statistics::WeightedCentroidKdTreeGenerator TreeGeneratorType; typename TreeGeneratorType::Pointer treeGenerator = TreeGeneratorType::New(); treeGenerator->SetSample( sample ); treeGenerator->SetBucketSize( 16 ); treeGenerator->Update(); typedef typename TreeGeneratorType::KdTreeType TreeType; typedef itk::Statistics::KdTreeBasedKmeansEstimator EstimatorType; typename EstimatorType::Pointer estimator = EstimatorType::New(); estimator->SetKdTree( treeGenerator->GetOutput() ); estimator->SetMaximumIteration( 200 ); estimator->SetCentroidPositionChangesThreshold( 0.0 ); typename EstimatorType::ParametersType initialMeans( numberOfTissueClasses ); for( unsigned int n = 0; n < numberOfTissueClasses; n++ ) { initialMeans[n] = minValue + ( maxValue - minValue ) * ( static_cast( n ) + 0.5 ) / static_cast( numberOfTissueClasses ); } estimator->SetParameters( initialMeans ); estimator->StartOptimization(); // // Classify the samples // typedef itk::Statistics::MinimumDecisionRule DecisionRuleType; typename DecisionRuleType::Pointer decisionRule = DecisionRuleType::New(); typedef itk::Statistics::SampleClassifierFilter ClassifierType; typename ClassifierType::Pointer classifier = ClassifierType::New(); classifier->SetDecisionRule( decisionRule ); classifier->SetInput( sample ); classifier->SetNumberOfClasses( numberOfTissueClasses ); typename ClassifierType::ClassLabelVectorObjectType::Pointer classLabels = ClassifierType::ClassLabelVectorObjectType::New(); classifier->SetClassLabels( classLabels ); typename ClassifierType::ClassLabelVectorType & classLabelVector = classLabels->Get(); // // Order the cluster means so that the lowest mean of the input image // corresponds to label '1', the second lowest to label '2', etc. // std::vector estimatorParameters; for( unsigned int n = 0; n < numberOfTissueClasses; n++ ) { estimatorParameters.push_back( estimator->GetParameters()[n] ); } std::sort( estimatorParameters.begin(), estimatorParameters.end() ); typedef itk::Statistics::DistanceToCentroidMembershipFunction MembershipFunctionType; typename ClassifierType::MembershipFunctionVectorObjectType::Pointer membershipFunctions = ClassifierType::MembershipFunctionVectorObjectType::New(); typename ClassifierType::MembershipFunctionVectorType & membershipFunctionsVector = membershipFunctions->Get(); classifier->SetMembershipFunctions( membershipFunctions ); for( unsigned int n = 0; n < numberOfTissueClasses; n++ ) { typename MembershipFunctionType::Pointer membershipFunction = MembershipFunctionType::New(); membershipFunction->SetMeasurementVectorSize( sample->GetMeasurementVectorSize() ); typename MembershipFunctionType::CentroidType centroid; itk::NumericTraits::SetLength( centroid, sample->GetMeasurementVectorSize() ); centroid[0] = estimatorParameters[n]; membershipFunction->SetCentroid( centroid ); membershipFunctionsVector.push_back( membershipFunction.GetPointer() ); classLabelVector.push_back( static_cast( n + 1 ) ); } classifier->Update(); // // Classify the voxels // typedef typename ClassifierType::MembershipSampleType ClassifierOutputType; typedef typename ClassifierOutputType::ConstIterator LabelIterator; itk::ImageRegionIteratorWithIndex ItO( output, output->GetRequestedRegion() ); ItO.GoToBegin(); LabelIterator it = classifier->GetOutput()->Begin(); while( it != classifier->GetOutput()->End() ) { if( !maskImage || maskImage->GetPixel( ItO.GetIndex() ) == maskLabel ) { ItO.Set( it.GetClassLabel() ); ++it; } else { ItO.Set( itk::NumericTraits::ZeroValue() ); } ++ItO; } return output; } template int ThresholdImage( int argc, char * argv[] ) { // const unsigned int InImageDimension = AvantsImageDimension; typedef float PixelType; typedef itk::Image FixedImageType; typedef int LabelType; typedef itk::Image MaskImageType; typename FixedImageType::Pointer fixed; ReadImage( fixed, argv[2] ); typename MaskImageType::Pointer maskImage = ITK_NULLPTR; if( argc > 6 ) { ReadImage( maskImage, argv[6] ); } // Label the surface of the image typename FixedImageType::Pointer thresh; std::string threshtype = std::string(argv[4]); if( strcmp(threshtype.c_str(), "Otsu") == 0 ) { thresh = OtsuThreshold( atoi( argv[5] ), fixed, maskImage ); } else if( strcmp(threshtype.c_str(), "Kmeans") == 0 ) { thresh = KmeansThreshold( atoi( argv[5] ), fixed, maskImage ); } else { PixelType insideValue = 1; PixelType outsideValue = 0; if( argc > 6 ) { insideValue = static_cast( atof( argv[6] ) ); } if( argc > 7 ) { outsideValue = static_cast( atof( argv[7] ) ); } thresh = BinaryThreshold_AltInsideOutside_threashold(atof(argv[4]), atof( argv[5]), insideValue, outsideValue, fixed ); } WriteImage( thresh, argv[3] ); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int ThresholdImage( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "ThresholdImage" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cout << "Usage: " << argv[0]; std::cout << " ImageDimension ImageIn.ext outImage.ext threshlo threshhi " << std::endl; std::cout << " ImageDimension ImageIn.ext outImage.ext Otsu NumberofThresholds " << std::endl; std::cout << " ImageDimension ImageIn.ext outImage.ext Kmeans NumberofThresholds " << std::endl; std::cout << " Inclusive thresholds " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } // Get the image dimension switch( atoi(argv[1]) ) { case 2: { return ThresholdImage<2>(argc, argv); } break; case 3: { return ThresholdImage<3>(argc, argv); } break; case 4: { return ThresholdImage<4>(argc, argv); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/TileImages.cxx000066400000000000000000000214351311104306400170030ustar00rootroot00000000000000#include "antsUtilities.h" #include #include "itkExtractImageFilter.h" #include "itkTileImageFilter.h" #include "ReadWriteData.h" #include #include namespace ants { template int TileImages( unsigned int argc, char *argv[] ) { typedef float PixelType; typedef itk::Image ImageType; typedef itk::TileImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); typename FilterType::LayoutArrayType array; std::vector layout = ConvertVector( std::string( argv[3] ) ); for( unsigned int d = 0; d < ImageDimension; d++ ) { array[d] = layout[d]; } filter->SetLayout( array ); for( unsigned int n = 4; n < argc; n++ ) { typename ImageType::Pointer inputImage; ReadImage( inputImage, argv[n] ); filter->SetInput( n - 4, inputImage ); } filter->Update(); WriteImage( filter->GetOutput(), argv[2] ); return EXIT_SUCCESS; } int CreateMosaic( unsigned int argc, char *argv[] ) { if( argc != 5 ) { std::cerr << "Usage: "<< argv[0] << " imageDimension outputImage layout inputImage1" << std::endl; return EXIT_FAILURE; } const unsigned int ImageDimension = 3; typedef float PixelType; typedef itk::Image ImageType; typedef itk::Image SliceType; ImageType::Pointer inputImage; ReadImage( inputImage, argv[4] ); std::vector layout = ConvertVector( std::string( argv[3] ) ); if( layout.size() != 3 ) { std::cerr << "Layout for CreateMosaic is DxRxC where" << std::endl; std::cerr << " D is direction, i.e. 0, 1, or 2. If not any of those numbers, we pick the coarsest spacing." << std::endl; std::cerr << " R is number of rows." << std::endl; std::cerr << " C is number of cols." << std::endl; std::cerr << " If R < 0 and C > 0 (or vice versa), the negative value is selected based on D" << std::endl; return EXIT_FAILURE; } ImageType::SpacingType spacing = inputImage->GetSpacing(); ImageType::SizeType size = inputImage->GetRequestedRegion().GetSize(); if( layout[0] < 0 || layout[0] > 2 ) { float maxSpacing = spacing[0]; unsigned int maxIndex = 0; for( unsigned int d = 1; d < ImageDimension; d++ ) { if( spacing[d] > maxSpacing ) { maxSpacing = spacing[d]; maxIndex = d; } } layout[0] = maxIndex; } unsigned long numberOfSlices = size[layout[0]]; int numberOfRows = vnl_math_min( static_cast( layout[1] ), static_cast( numberOfSlices ) ); int numberOfColumns = vnl_math_min( static_cast( layout[2] ), static_cast( numberOfSlices ) ); if( numberOfRows <= 0 && numberOfColumns > 0 ) { numberOfRows = std::ceil( static_cast( numberOfSlices ) / static_cast( numberOfColumns ) ); } else if( numberOfColumns <= 0 && numberOfRows > 0 ) { numberOfColumns = std::ceil( static_cast( numberOfSlices ) / static_cast( numberOfRows ) ); } else if( numberOfColumns <= 0 && numberOfRows <= 0 ) { numberOfRows = static_cast( std::sqrt( static_cast( numberOfSlices ) ) ); numberOfColumns = std::ceil( static_cast( numberOfSlices ) / static_cast( numberOfRows ) ); } std::cout << "Slices[" << layout[0] << "]: " << numberOfSlices << std::endl; std::cout << "Rows: " << numberOfRows << std::endl; std::cout << "Columns: " << numberOfColumns << std::endl; typedef itk::TileImageFilter FilterType; FilterType::LayoutArrayType array; array[0] = numberOfColumns; array[1] = numberOfRows; ImageType::RegionType region; size[layout[0]] = 0; FilterType::Pointer filter = FilterType::New(); filter->SetLayout( array ); for( unsigned int n = 0; n < numberOfSlices; n++ ) { ImageType::IndexType index; index.Fill( 0 ); index[layout[0]] = static_cast( n ); region.SetIndex( index ); region.SetSize( size ); typedef itk::ExtractImageFilter ExtracterType; ExtracterType::Pointer extracter = ExtracterType::New(); extracter->SetInput( inputImage ); extracter->SetExtractionRegion( region ); extracter->SetDirectionCollapseToIdentity(); extracter->Update(); filter->SetInput( n, extracter->GetOutput() ); } filter->Update(); WriteImage( filter->GetOutput(), argv[2] ); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int TileImages( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "TileImages" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 4 ) { std::cout << argv[0] << " imageDimension outputImage layout inputImage1 ... inputImageN" << std::endl; std::cout << " The layout has the same dimension as the output image. If all entries of " << std::endl; std::cout << " the layout are positive, the tiled output will contain the exact number " << std::endl; std::cout << " of tiles. If the layout contains a 0 in the last dimension, the filter " << std::endl; std::cout << " will compute a size that will accommodate all of the images. " << std::endl; std::cout << " The input images must have a dimension less than or equal to the output " << std::endl; std::cout << " image. The output image could have a larger dimension than the input. " << std::endl; std::cout << " For example, This filter can be used to create a 3-d volume from a series " << std::endl; std::cout << " of 2-d inputs by specifying a layout of 1x1x0. " << std::endl << std::endl; std::cout << " In addition to the above functionality, there is another usage option" << std::endl; std::cout << " for creating a 2-d tiled mosaic from a 3-D image. The command line options" << std::endl; std::cout << " are the same except only 1 input is expected and the layout for this option" << std::endl; std::cout << " is DxRxC where:" << std::endl; std::cout << " D is direction, i.e. 0, 1, or 2. If not any of those numbers, we pick the coarsest spacing." << std::endl; std::cout << " R is number of rows." << std::endl; std::cout << " C is number of cols." << std::endl; std::cout << " If R < 0 and C > 0 (or vice versa), the negative value is selected based on D" << std::endl; // Should add the following options: // * add rgb overlay (with alpha value?) // * number of slices to skip // * beginning and ending slice // * add or subtract border around each slice/tile // * if adding, set pad constant value if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } const int ImageDimension = static_cast( atoi( argv[1] ) ); if( ImageDimension == 3 && argc == 5 ) { CreateMosaic( argc, argv ); } else { switch( ImageDimension ) { case 2: { return TileImages<2>( argc, argv ); } break; case 3: { return TileImages<3>( argc, argv ); } break; case 4: { return TileImages<4>( argc, argv ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/TimeSCCAN.cxx000066400000000000000000000650121311104306400164250ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include "antsAllocImage.h" #include #include "antsCommandLineOption.h" #include "antsCommandLineParser.h" #include "antsSCCANObject.h" #include "ReadWriteData.h" #include "itkImageRegionIteratorWithIndex.h" #include #include #include "itkVariableLengthVector.h" #include "itkVectorContainer.h" namespace ants { template double vnl_pearson_corr( vnl_vector v1, vnl_vector v2 ) { double xysum = 0; for( unsigned int i = 0; i < v1.size(); i++ ) { xysum += v1(i) * v2(i); } double frac = 1.0 / (double)v1.size(); double xsum = v1.sum(), ysum = v2.sum(); double xsqr = v1.squared_magnitude(); double ysqr = v2.squared_magnitude(); double numer = xysum - frac * xsum * ysum; double denom = sqrt( ( xsqr - frac * xsum * xsum) * ( ysqr - frac * ysum * ysum) ); if( denom <= 0 ) { return 0; } return numer / denom; } template bool RegionSCCA(typename NetworkType::Pointer network, typename NetworkType::Pointer time, typename NetworkType::Pointer labels, unsigned int nLabels, unsigned int minRegionSize, unsigned int n_evec, unsigned int iterct, float sparsity, bool robust, bool useL1, float gradstep, bool keepPositive, unsigned int minClusterSize ) { typedef itk::ants::antsSCCANObject SCCANType; typedef typename SCCANType::MatrixType MatrixType; typedef typename SCCANType::VectorType VectorType; // Determine the number of regions to examine std::set labelset; itk::ImageRegionIteratorWithIndex it( labels, labels->GetLargestPossibleRegion() ); if ( nLabels == 0 ) { while( !it.IsAtEnd() ) { if ( it.Value() > 0 ) { labelset.insert( it.Value() ); } ++it; } } else { for ( unsigned int i=0; iSetRegions( region ); network->Allocate(); network->FillBuffer( 0.0 ); unsigned int nVoxels = labels->GetLargestPossibleRegion().GetSize()[0]; unsigned int nTimes = time->GetLargestPossibleRegion().GetSize()[0]; if ( nVoxels != time->GetLargestPossibleRegion().GetSize()[1] ) { std::cout << "number of labels does not match number of voxels" << std::endl; return EXIT_FAILURE; } std::cout << "Examining " << N << " regions, covering " << nVoxels << " voxels with " << nTimes << " time points each" << std::endl; //unsigned int labelCounts[N]; unsigned int *labelCounts = new unsigned int [N] ; for (unsigned int i=0; iGetPixel(idx) == (i+1) ) { ++labelCounts[i]; } } } // used to rankify matrices if using robust typename SCCANType::Pointer cca_rankify = SCCANType::New(); for (unsigned int i=0; iGetPixel(idx) == (i+1) ) { for ( unsigned int t=0; tGetPixel(timeIdx); } ++iCount; } } if ( robust && ( labelCounts[i] >= minRegionSize) ) { P = cca_rankify->RankifyMatrixColumns(P); } if ( labelCounts[i] >= minRegionSize ) { for ( unsigned int j=i+1; jGetPixel(idx2) == (j+1) ) { for ( unsigned int t2=0; t2GetPixel(timeIdx2); } ++jCount; } } if ( robust ) { Q = cca_rankify->RankifyMatrixColumns(Q); } if ( labelCounts[j] >= minRegionSize) { // Correlation magic goes here typename SCCANType::Pointer cca = SCCANType::New(); cca->SetSilent( true ); cca->SetMaximumNumberOfIterations(iterct); cca->SetUseL1( useL1 ); cca->SetGradStep( gradstep ); cca->SetKeepPositiveP( keepPositive ); cca->SetKeepPositiveQ( keepPositive ); cca->SetFractionNonZeroP( sparsity ); cca->SetFractionNonZeroQ( sparsity ); cca->SetMinClusterSizeP( minClusterSize ); cca->SetMinClusterSizeQ( minClusterSize ); cca->SetMatrixP( P ); cca->SetMatrixQ( Q ); // is truecorr just sccancorrs[0]? cca->SparsePartialArnoldiCCA(n_evec); VectorType sccancorrs = cca->GetCanonicalCorrelations(); VectorType pVec = cca->GetVariateP(); for ( unsigned int ip=0; ipGetVariateQ(); for ( unsigned int iq=0; iqSetPixel( connIdx, final_corr ); connIdx[0] = j; connIdx[1] = i; network->SetPixel( connIdx, final_corr ); } } } } delete []labelCounts; return network; } template bool RegionAveraging(typename NetworkType::Pointer network, typename NetworkType::Pointer time, typename NetworkType::Pointer labels, unsigned int nLabels, unsigned int minSize ) { typedef vnl_vector VectorType; typedef vnl_matrix MatrixType; // Determine the number of regions to examine std::set labelset; itk::ImageRegionIteratorWithIndex it( labels, labels->GetLargestPossibleRegion() ); if ( nLabels == 0 ) { while( !it.IsAtEnd() ) { if ( it.Value() > 0 ) { labelset.insert( it.Value() ); } ++it; } } else { for ( unsigned int i=0; iSetRegions( region ); network->Allocate(); network->FillBuffer( 0.0 ); unsigned int nVoxels = labels->GetLargestPossibleRegion().GetSize()[0]; unsigned int nTimes = time->GetLargestPossibleRegion().GetSize()[0]; if ( nVoxels != time->GetLargestPossibleRegion().GetSize()[1] ) { std::cout << "number of labels does not match number of voxels" << std::endl; return EXIT_FAILURE; } std::cout << "Examining " << N << " regions, covering " << nVoxels << " voxels with " << nTimes << " time points each" << std::endl; VectorType labelCounts( N, 0 ); MatrixType timeSig( N, nTimes, 0.0 ); for ( unsigned int i=0; iGetPixel(idx) == (i+1) ) { labelCounts[i]++; typename NetworkType::IndexType timeIdx; timeIdx[1] = v; for ( unsigned int t=0; tGetPixel(timeIdx); } } } } for ( unsigned int i=0; i 0 ) { timeSig(i,j) /= labelCounts[i]; } } } for (unsigned int i=0; i minSize) && (labelCounts[j] > minSize ) ) { VectorType p = timeSig.get_row(i); VectorType q = timeSig.get_row(j); double corr = vnl_pearson_corr(p,q); if ( ! vnl_math_isfinite( corr ) ) { corr = 0.0; } typename NetworkType::IndexType connIdx; connIdx[0] = i; connIdx[1] = j; network->SetPixel( connIdx, corr ); connIdx[0] = j; connIdx[1] = i; network->SetPixel( connIdx, corr ); } } } return network; } int timesccan( itk::ants::CommandLineParser *parser ) { typedef itk::Image NetworkType; std::string outname = "output.nii.gz"; itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( !outputOption || outputOption->GetNumberOfFunctions() == 0 ) { std::cout << "Warning: no output option set." << std::endl; } else { outname = outputOption->GetFunction()->GetName(); std::cout << "Writing output to: " << outname << std::endl; } unsigned int nLabels = 0; itk::ants::CommandLineParser::OptionType::Pointer labels_option = parser->GetOption( "number-consecutive-labels" ); if( !labels_option || labels_option->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no permutation option set." << std::endl; } else { nLabels = parser->Convert( labels_option->GetFunction()->GetName() ); } unsigned int roiSize = 1; itk::ants::CommandLineParser::OptionType::Pointer size_option = parser->GetOption( "minimum-region-size" ); if( !size_option || size_option->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no permutation option set." << std::endl; } else { roiSize = parser->Convert( size_option->GetFunction()->GetName() ); } unsigned int clusterSize = 1; itk::ants::CommandLineParser::OptionType::Pointer clust_option = parser->GetOption( "minimum-cluster-size" ); if( !clust_option || clust_option->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no permutation option set." << std::endl; } else { clusterSize = parser->Convert( clust_option->GetFunction()->GetName() ); } unsigned int evec_ct = 5; itk::ants::CommandLineParser::OptionType::Pointer evec_option = parser->GetOption( "n_eigenvectors" ); if( !evec_option || evec_option->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no permutation option set." << std::endl; } else { evec_ct = parser->Convert( evec_option->GetFunction()->GetName() ); } unsigned int iterations = 20; itk::ants::CommandLineParser::OptionType::Pointer iter_option = parser->GetOption( "iterations" ); if( !iter_option || iter_option->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no permutation option set." << std::endl; } else { iterations = parser->Convert( iter_option->GetFunction()->GetName() ); } float sparsity = 0.1; itk::ants::CommandLineParser::OptionType::Pointer sparse_option = parser->GetOption( "sparsity" ); if( !sparse_option || sparse_option->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no permutation option set." << std::endl; } else { sparsity = parser->Convert( sparse_option->GetFunction()->GetName() ); } unsigned int keepPositive = 1; itk::ants::CommandLineParser::OptionType::Pointer pos_option = parser->GetOption( "keep-positive" ); if( !pos_option || pos_option->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no permutation option set." << std::endl; } else { keepPositive = parser->Convert( pos_option->GetFunction()->GetName() ); } unsigned int usel1 = 1; itk::ants::CommandLineParser::OptionType::Pointer l1_option = parser->GetOption( "l1" ); if( !l1_option || l1_option->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no permutation option set." << std::endl; } else { usel1 = parser->Convert( l1_option->GetFunction()->GetName() ); } unsigned int robustify = 0; itk::ants::CommandLineParser::OptionType::Pointer robust_option = parser->GetOption( "robustify" ); if( !robust_option || robust_option->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no permutation option set." << std::endl; } else { robustify = parser->Convert( robust_option->GetFunction()->GetName() ); } itk::ants::CommandLineParser::OptionType::Pointer evecg_option = parser->GetOption( "EvecGradPenalty" ); if( evecg_option && evecg_option->GetNumberOfFunctions() != 0 ) { parser->Convert( evecg_option->GetFunction()->GetName() ); } itk::ants::CommandLineParser::OptionType::Pointer eigen_option = parser->GetOption( "ridge_cca" ); if( eigen_option && eigen_option->GetNumberOfFunctions() != 0 ) { parser->Convert( eigen_option->GetFunction()->GetName() ); } NetworkType::Pointer network = NetworkType::New(); itk::ants::CommandLineParser::OptionType::Pointer netOption = parser->GetOption( "network" ); if( netOption && netOption->GetNumberOfFunctions() > 0 ) { std::cout << "Build network" << std::endl; if( netOption && netOption->GetFunction( 0 )->GetNumberOfParameters() < 2 ) { std::cout << " Incorrect number of parameters." << std::endl; return EXIT_FAILURE; } std::string connectivityStrategy = netOption->GetFunction()->GetName(); std::string timeMatrixName = std::string(netOption->GetFunction( 0 )->GetParameter( 0 ) ); std::string labelMatrixName = std::string(netOption->GetFunction( 0 )->GetParameter( 1 ) ); std::cout << "Method: " << connectivityStrategy << std::endl; if ( connectivityStrategy == "scca" ) { std::cout << "Time Series Data: " << timeMatrixName << std::endl; std::cout << "Time Series Labels: " << labelMatrixName << std::endl; NetworkType::Pointer timeMat = ITK_NULLPTR; ReadImage( timeMat, timeMatrixName.c_str() ); NetworkType::Pointer labelMat = ITK_NULLPTR; ReadImage( labelMat, labelMatrixName.c_str() ); float gradstep = -0.5 + vnl_math_abs( usel1 ); RegionSCCA( network, timeMat, labelMat, nLabels, roiSize, evec_ct, iterations, sparsity, robustify, usel1, gradstep, keepPositive, clusterSize ); } else if ( connectivityStrategy == "region-averaging" ) { std::cout << "Time Series Data: " << timeMatrixName << std::endl; std::cout << "Time Series Labels: " << labelMatrixName << std::endl; NetworkType::Pointer timeMat = ITK_NULLPTR; ReadImage( timeMat, timeMatrixName.c_str() ); NetworkType::Pointer labelMat = ITK_NULLPTR; ReadImage( labelMat, labelMatrixName.c_str() ); RegionAveraging( network, timeMat, labelMat, nLabels, roiSize ); } else { std::cout << "Unknown method:" << connectivityStrategy << std::endl; return EXIT_FAILURE; } } WriteImage( network, outname.c_str() ); return 0; } void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { /** in this function, list all the operations you will perform */ typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (long version)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Output is a 2D correlation matrix." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "outputImage" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Number of consecutive labels in data" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "number-consecutive-labels" ); option->SetShortName( 'l' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Minimum size of a region: regions below this size are given a 0.0 connectivity value" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "minimum-region-size" ); option->SetShortName( 'R' ); option->SetUsageOption( 0, "1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Number of iterations" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "iterations" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "20" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Sparsity - a float from (0,1] indicating what fraction of the data to use" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "sparsity" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "0.10" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Number of permutations to use in scca." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "n_eigenvectors" ); option->SetShortName( 'n' ); option->SetUsageOption( 0, "2" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "rank-based scca" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "robustify" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "use l1 ( > 0 ) or l0 ( < 0 ) penalty, also sets gradient step size e.g. -l 0.5 ( L1 ) , -l -0.5 (L0) will set 0.5 grad descent step for either penalty" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "l1" ); option->SetShortName( 'l' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "cluster threshold on view P" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "ClusterThresh" ); option->SetUsageOption( 0, "1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Number of permutations to use in scca." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "ridge_cca" ); option->SetShortName( 'e' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Choices for pscca: PQ, PminusRQ, PQminusR, PminusRQminusR " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "partial-scca-option" ); option->SetUsageOption( 0, "PminusRQ" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "takes a timeseries (4D) image " ) + std::string( "and converts it to a 2D matrix csv format as output." ) + std::string( "If the mask has multiple labels ( more the one ) then the average time series in each label will be computed and put in the csv." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "timeseriesimage-to-matrix" ); option->SetUsageOption( 0, "[four_d_image.nii.gz,three_d_mask.nii.gz]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "takes a labeled (3D) image " ) + std::string( "and converts it to a 2D matrix csv format as output." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "labelsimage-to-matrix" ); option->SetUsageOption( 0, "[three_d_mask.nii.gz]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Build the network connectivity matrix" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "network" ); option->SetUsageOption( 0, "scca[time-matrix.mhd,label-matrix.mhd]" ); option->SetUsageOption( 1, "region-averaging[time-matrix.mhd,label-matrix.mhd]" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int TimeSCCAN( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "TimeSCCAN" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "A tool for sparse statistical analysis on connectivity within a subject : " ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } // Print the entire help menu itk::ants::CommandLineParser::OptionType::Pointer shortHelpOption = parser->GetOption( 'h' ); itk::ants::CommandLineParser::OptionType::Pointer longHelpOption = parser->GetOption( "help" ); if( argc < 2 || ( shortHelpOption->GetFunction() && parser->Convert( shortHelpOption->GetFunction()->GetName() ) == 1 ) ) { parser->PrintMenu( std::cout, 5, true ); if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } if( longHelpOption->GetFunction() && parser->Convert( longHelpOption->GetFunction()->GetName() ) == 1 ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } // Print the long help menu for specific items if( longHelpOption && longHelpOption->GetNumberOfFunctions() > 0 && parser->Convert( longHelpOption->GetFunction()->GetName() ) != 0 ) { itk::ants::CommandLineParser::OptionListType options = parser->GetOptions(); for( unsigned int n = 0; n < longHelpOption->GetNumberOfFunctions(); n++ ) { std::string value = longHelpOption->GetFunction( n )->GetName(); itk::ants::CommandLineParser::OptionListType::const_iterator it; for( it = options.begin(); it != options.end(); ++it ) { const char *longName = ( ( *it )->GetLongName() ).c_str(); if( strstr( longName, value.c_str() ) == longName ) { parser->PrintMenu( std::cout, 5, false ); } } } return EXIT_FAILURE; } // Call main routine return timesccan( parser ); } } // namespace ants ants-2.2.0/Examples/WarpImageMultiTransform.cxx000066400000000000000000001001351311104306400215360ustar00rootroot00000000000000#include "antsUtilities.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMatrixOffsetTransformBase.h" #include "itkTransformFactory.h" #include "itkWarpImageMultiTransformFilter.h" #include "itkTransformFileReader.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "antsUtilities.h" // Needed for the LabelImageGaussianInterpolateImageFunction to work on // vector images #include "itkLabelImageGaussianInterpolateImageFunction.h" namespace ants { static bool IsInverseDeformation(const char *str) { std::string filename = str; std::string::size_type pos = filename.rfind( "Inverse" ); if( pos == std::string::npos ) { return false; } else { return true; } } static bool WarpImageMultiTransform_ParseInput(int argc, char * *argv, char *& moving_image_filename, char *& output_image_filename, TRAN_OPT_QUEUE & opt_queue, MISC_OPT & misc_opt, int NDimensions) { opt_queue.clear(); opt_queue.reserve(argc - 2); misc_opt.reference_image_filename = ITK_NULLPTR; misc_opt.use_BSpline_interpolator = false; misc_opt.use_TightestBoundingBox = false; misc_opt.use_RotationHeader = false; misc_opt.use_NN_interpolator = false; misc_opt.use_MultiLabel_interpolator = false; misc_opt.use_BSpline_interpolator = false; moving_image_filename = argv[0]; output_image_filename = argv[1]; int ind = 2; bool set_current_affine_inv = false; while( ind < argc ) { if( strcmp(argv[ind], "--use-NN") == 0 ) { misc_opt.use_NN_interpolator = true; } else if( strcmp(argv[ind], "--use-BSpline") == 0 ) { misc_opt.use_BSpline_interpolator = true; } else if( strcmp(argv[ind], "--use-ML") == 0 ) { misc_opt.use_MultiLabel_interpolator = true; ind++; if( ind >= argc ) { return false; } char *s = argv[ind]; if( strlen(s) > 3 && strcmp(s + strlen(s) - 3, "vox") == 0 ) { misc_opt.opt_ML.physical_units = false; s[strlen(s) - 3] = 0; } else if( strlen(s) > 2 && strcmp(s + strlen(s) - 2, "mm") == 0 ) { misc_opt.opt_ML.physical_units = true; s[strlen(s) - 2] = 0; } else { std::cout << "Wrong specification of sigma in --use-ML. Must end with 'mm' or 'vox'" << std::endl; return false; } misc_opt.opt_ML.sigma.resize(NDimensions); if( strchr(s, 'x') ) { char *tok = strtok(s, "x"); int i = 0; while( tok!=ITK_NULLPTR && i= argc ) { return false; } misc_opt.reference_image_filename = argv[ind]; } else if( (strcmp(argv[ind], "--tightest-bounding-box") == 0) && (strcmp(argv[ind], "-R") != 0) ) { misc_opt.use_TightestBoundingBox = true; } else if( strcmp(argv[ind], "--reslice-by-header") == 0 ) { misc_opt.use_RotationHeader = true; TRAN_OPT opt; opt.file_type = IMAGE_AFFINE_HEADER; opt.do_affine_inv = false; opt_queue.push_back(opt); } else if( strcmp(argv[ind], "--Id") == 0 ) { TRAN_OPT opt; opt.filename = "--Id"; opt.do_affine_inv = false; opt.file_type = IDENTITY_TRANSFORM; opt_queue.push_back(opt); } else if( strcmp(argv[ind], "--moving-image-header") == 0 || strcmp(argv[ind], "-mh") == 0 ) { TRAN_OPT opt; opt.file_type = IMAGE_AFFINE_HEADER; opt.filename = moving_image_filename; // opt.do_affine_inv = false; SetAffineInvFlag(opt, set_current_affine_inv); opt_queue.push_back(opt); } else if( strcmp(argv[ind], "--reference-image-header") == 0 || strcmp(argv[ind], "-rh") == 0 ) { if( misc_opt.reference_image_filename == ITK_NULLPTR ) { std::cout << "reference image filename is not given yet. Specify it with -R before --reference-image-header / -rh." << std::endl; return false; } TRAN_OPT opt; opt.file_type = IMAGE_AFFINE_HEADER; opt.filename = misc_opt.reference_image_filename; // opt.do_affine_inv = false; SetAffineInvFlag(opt, set_current_affine_inv); opt_queue.push_back(opt); } else if( strcmp(argv[ind], "-i") == 0 ) { set_current_affine_inv = true; } else if( strcmp(argv[ind], "--ANTS-prefix") == 0 ) { ind++; std::string prefix = argv[ind]; std::string path, name, ext; FilePartsWithgz(prefix, path, name, ext); if( ext == "" ) { ext = ".nii.gz"; } std::string deform_file_name, x_deform_name; deform_file_name = path + name + std::string("Warp") + ext; x_deform_name = path + name + std::string("Warpxvec") + ext; if( CheckFileExistence(x_deform_name.c_str() ) ) { TRAN_OPT opt; opt.filename = deform_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; opt_queue.push_back(opt); std::cout << "found deformation file: " << opt.filename << std::endl; DisplayOpt(opt); } std::string affine_file_name; affine_file_name = path + name + std::string("Affine") + GetPreferredTransformFileType(); if( CheckFileExistence(affine_file_name.c_str() ) ) { TRAN_OPT opt; opt.filename = affine_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; opt_queue.push_back(opt); std::cout << "found affine file: " << opt.filename << std::endl; DisplayOpt(opt); } } else if( strcmp(argv[ind], "--ANTS-prefix-invert") == 0 ) { ind++; std::string prefix = argv[ind]; std::string path, name, ext; FilePartsWithgz(prefix, path, name, ext); if( ext == "" ) { ext = ".nii.gz"; } std::string affine_file_name; affine_file_name = path + name + std::string("Affine") + GetPreferredTransformFileType(); if( CheckFileExistence(affine_file_name.c_str() ) ) { TRAN_OPT opt; opt.filename = affine_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = true; opt_queue.push_back(opt); std::cout << "found affine file: " << opt.filename << std::endl; DisplayOpt(opt); } std::string deform_file_name, x_deform_name; deform_file_name = path + name + std::string("InverseWarp.nii.gz"); x_deform_name = path + name + std::string("InverseWarpxvec.nii.gz"); if( CheckFileExistence(x_deform_name.c_str() ) ) { TRAN_OPT opt; opt.filename = deform_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; opt_queue.push_back(opt); std::cout << "found deformation file: " << opt.filename << std::endl; DisplayOpt(opt); } } else { TRAN_OPT opt; opt.filename = argv[ind]; opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; if( opt.file_type == AFFINE_FILE ) { SetAffineInvFlag(opt, set_current_affine_inv); } else if( opt.file_type == DEFORMATION_FILE && set_current_affine_inv ) { std::cout << "Ignore inversion of non-affine file type! " << std::endl; std::cout << "opt.do_affine_inv:" << opt.do_affine_inv << std::endl; } opt_queue.push_back(opt); DisplayOpt(opt); } ind++; } if( misc_opt.use_RotationHeader ) { // if (misc_opt.reference_image_filename) { // opt_queue[0].filename = misc_opt.reference_image_filename; // } else { opt_queue[0].filename = "--Id"; opt_queue[0].file_type = IDENTITY_TRANSFORM; opt_queue[0].do_affine_inv = false; // } // TRAN_OPT opt; // opt.file_type = IMAGE_AFFINE_HEADER; // opt.filename = moving_image_filename; // opt.do_affine_inv = true; // opt_queue.push_back(opt); // // std::cout << "Use Rotation Header!" << std::endl; } return true; } template void GetIdentityTransform(AffineTransformPointer & aff) { typedef typename AffineTransformPointer::ObjectType AffineTransform; aff = AffineTransform::New(); aff->SetIdentity(); } template void WarpImageMultiTransform(char *moving_image_filename, char *output_image_filename, TRAN_OPT_QUEUE & opt_queue, MISC_OPT & misc_opt) { typedef float RealType; typedef itk::Vector PixelType; typedef itk::Image ImageType; typedef itk::VectorImage RefImageType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::MatrixOffsetTransformBase AffineTransformType; typedef itk::WarpImageMultiTransformFilter WarperType; itk::TransformFactory::RegisterTransform(); typedef itk::ImageFileReader ImageFileReaderType; typedef itk::ImageFileReader VectorImageFileReaderType; typename ImageFileReaderType::Pointer reader_img = ImageFileReaderType::New(); reader_img->SetFileName(moving_image_filename); reader_img->Update(); typename ImageType::Pointer img_mov = reader_img->GetOutput(); typename RefImageType::Pointer img_ref; typename VectorImageFileReaderType::Pointer reader_img_ref = VectorImageFileReaderType::New(); if( misc_opt.reference_image_filename ) { reader_img_ref->SetFileName(misc_opt.reference_image_filename); reader_img_ref->Update(); img_ref = reader_img_ref->GetOutput(); } // else // img_ref = NULL; typename WarperType::Pointer warper = WarperType::New(); warper->SetInput(img_mov); PixelType zero; zero.Fill(0); warper->SetEdgePaddingValue( zero ); if( misc_opt.use_NN_interpolator ) { typedef typename itk::NearestNeighborInterpolateImageFunction NNInterpolateType; typename NNInterpolateType::Pointer interpolator_NN = NNInterpolateType::New(); std::cout << "User nearest neighbor interpolation (was Haha) " << std::endl; warper->SetInterpolator(interpolator_NN); } else if( misc_opt.use_MultiLabel_interpolator ) { std::cout << " Need to fix in main itk repository " << std::endl; // typedef VectorPixelCompare CompareType; // typedef typename itk::LabelImageGaussianInterpolateImageFunction MLInterpolateType; // typename MLInterpolateType::Pointer interpolator_ML = MLInterpolateType::New(); // // // std::cout << "Using multi-label anti-aliasing interpolation " << std::endl; // vnl_vector_fixed sigma; // for(size_t i = 0; i < ImageDimension; i++) // { // if(misc_opt.opt_ML.physical_units) // sigma[i] = misc_opt.opt_ML.sigma[i] / img_mov->GetSpacing()[i]; // else // sigma[i] = misc_opt.opt_ML.sigma[i]; // } // // std::cout << " Sigma = " << sigma << " (voxel units)" << std::endl; // // interpolator_ML->SetParameters(sigma.data_block(), 4.0); // // warper->SetInterpolator(interpolator_ML); } else if( misc_opt.use_BSpline_interpolator ) { std::cout << " Not currently supported because of a lack of vector support " << std::endl; /* typedef typename itk::BSplineInterpolateImageFunction BSInterpolateType; typename BSInterpolateType::Pointer interpolator_BS = BSInterpolateType::New(); interpolator_BS->SetSplineOrder(3); std::cout << "User B-spline interpolation " << std::endl; warper->SetInterpolator(interpolator_BS); */ } else { typedef typename itk::LinearInterpolateImageFunction LinInterpolateType; typename LinInterpolateType::Pointer interpolator_LN = LinInterpolateType::New(); std::cout << "User Linear interpolation " << std::endl; warper->SetInterpolator(interpolator_LN); } typedef itk::TransformFileReader TranReaderType; typedef itk::ImageFileReader FieldReaderType; bool takeaffinv = false; unsigned int transcount = 0; const int kOptQueueSize = opt_queue.size(); for( int i = 0; i < kOptQueueSize; i++ ) { const TRAN_OPT & opt = opt_queue[i]; switch( opt.file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); typename AffineTransformType::Pointer aff = dynamic_cast ( (tran_reader->GetTransformList() )->front().GetPointer() ); if( opt.do_affine_inv ) { typename AffineTransformType::Pointer aff_inv = AffineTransformType::New(); aff->GetInverse(aff_inv); aff = aff_inv; takeaffinv = true; } // std::cout <<" aff " << transcount << std::endl; warper->PushBackAffineTransform(aff); if( transcount == 0 ) { warper->SetOutputParametersFromImage( img_mov ); } transcount++; break; } case IDENTITY_TRANSFORM: { typename AffineTransformType::Pointer aff; GetIdentityTransform(aff); // std::cout << " aff id" << transcount << std::endl; warper->PushBackAffineTransform(aff); transcount++; break; } case IMAGE_AFFINE_HEADER: { typename AffineTransformType::Pointer aff = AffineTransformType::New(); typename ImageFileReaderType::Pointer reader_image_affine = ImageFileReaderType::New(); reader_image_affine->SetFileName(opt.filename); reader_image_affine->Update(); typename ImageType::Pointer img_affine = reader_image_affine->GetOutput(); GetAffineTransformFromImage(img_affine, aff); if( opt.do_affine_inv ) { typename AffineTransformType::Pointer aff_inv = AffineTransformType::New(); aff->GetInverse(aff_inv); aff = aff_inv; takeaffinv = true; } // std::cout <<" aff from image header " << transcount << std::endl; warper->PushBackAffineTransform(aff); // if (transcount==0){ // warper->SetOutputParametersFromImage( img_mov); // } transcount++; break; } case DEFORMATION_FILE: { typename FieldReaderType::Pointer field_reader = FieldReaderType::New(); field_reader->SetFileName( opt.filename ); field_reader->Update(); typename DisplacementFieldType::Pointer field = field_reader->GetOutput(); warper->PushBackDisplacementFieldTransform(field); warper->SetOutputParametersFromImage(field ); transcount++; break; } default: std::cout << "Unknown file type!" << std::endl; } } // std::cout << " transcount " << transcount << std::endl; warper->PrintTransformList(); if( transcount == 2 ) { std::cout << " We check the syntax of your call .... " << std::endl; const TRAN_OPT & opt1 = opt_queue[0]; const TRAN_OPT & opt2 = opt_queue[1]; if( opt1.file_type == AFFINE_FILE && opt2.file_type == DEFORMATION_FILE ) { bool defisinv = IsInverseDeformation(opt2.filename.c_str() ); if( !takeaffinv ) { std::cout << " Your 1st parameter should be an inverse affine map and the 2nd an InverseWarp --- exiting without applying warp. Check that , if using an inverse affine map, you pass the -i option before the Affine.txt." << std::endl; return; } if( !defisinv ) { std::cout << " Your 2nd parameter should be an InverseWarp when your 1st parameter is an inverse affine map --- exiting without applying warp. " << std::endl; return; } } if( opt2.file_type == AFFINE_FILE && opt1.file_type == DEFORMATION_FILE ) { bool defisinv = IsInverseDeformation(opt1.filename.c_str() ); if( defisinv ) { std::cout << " Your 1st parameter should be a Warp (not Inverse) when your 2nd parameter is an affine map --- exiting without applying warp. " << std::endl; return; } if( takeaffinv ) { std::cout << " Your 2nd parameter should be a regular affine map (not inverted) if the 1st is a Warp --- exiting without applying warp. " << std::endl; return; } } std::cout << " syntax probably ok. " << std::endl; } else { std::cout << " You are doing something more complex -- we wont check syntax in this case " << std::endl; } if( img_ref.IsNotNull() ) { warper->SetOutputParametersFromImage( img_ref ); } else { if( misc_opt.use_TightestBoundingBox == true ) { // compute the desired spacking after inputting all the transform files using the typename ImageType::SizeType largest_size; typename ImageType::PointType origin_warped; GetLargestSizeAfterWarp(warper, img_mov, largest_size, origin_warped); // Use img_mov as initial template space, then overwrite individual components as desired warper->SetOutputParametersFromImage( img_mov ); warper->SetOutputSize(largest_size); warper->SetOutputOrigin(origin_warped); { typename ImageType::DirectionType d; d.SetIdentity(); warper->SetOutputDirection(d); } } } std::cout << "output origin: " << warper->GetOutputOrigin() << std::endl; std::cout << "output size: " << warper->GetOutputSize() << std::endl; std::cout << "output spacing: " << warper->GetOutputSpacing() << std::endl; std::cout << "output direction: " << warper->GetOutputDirection() << std::endl; // warper->PrintTransformList(); warper->DetermineFirstDeformNoInterp(); warper->Update(); // { // typename ImageType::IndexType ind_orig, ind_warped; // ind_orig[0] = 128; // ind_orig[1] = 128; // ind_orig[2] = 16; // typename ImageType::PointType pt_orig, pt_warped; // warper->GetOutput()->TransformIndexToPhysicalPoint(ind_orig, pt_orig); // warper->MultiTransformSinglePoint(pt_orig, pt_warped); // img_mov->TransformPhysicalPointToIndex(pt_warped, ind_warped); // std::cout << "Transform output index " << ind_orig << "("<GetOutputSize()[i] * 0.5; // } // warper->MultiTransformSinglePoint(pt_in, pt_out); // std::cout << "pt_in=" << pt_in << " pt_out=" <GetOutput(); typedef itk::ImageFileWriter ImageFileWriterType; typename ImageFileWriterType::Pointer writer_img = ImageFileWriterType::New(); if( img_ref ) { img_output->SetDirection(img_ref->GetDirection() ); } writer_img->SetFileName(output_image_filename); writer_img->SetInput(img_output); writer_img->Update(); } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int WarpImageMultiTransform( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "WarpImageMultiTransform" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc <= 3 ) { std::cout << " \n " << std::endl; std::cout << "Usage: \n " << std::endl; // std::cout << argv[0] << " ImageDimension moving_image output_image [-R reference_image | // --tightest-bounding-box] (--reslice-by-header) [--use-NN]" // << "[--ANTS-prefix prefix-name | --ANTS-prefix-invert prefix-name] {[deformation_field | [-i] // InverseAffineTransform.txt | --Id | [-i] --moving-image-header / -mh | [-i] --reference-image-header / -rh]} \n" // << std::endl; std::cout << argv[0] << " ImageDimension moving_image output_image -R reference_image --use-NN SeriesOfTransformations--(See Below) " << std::endl; std::cout << " SeriesOfTransformations --- " << argv[0] << " can apply, via concatenation, an unlimited number of transformations to your data ." << std::endl; std::cout << " Thus, SeriesOfTransformations may be an Affine transform followed by a warp another affine and then another warp. " << std::endl; std::cout << " Inverse affine transformations are invoked by calling -i MyAffine.txt " << std::endl; std::cout << " InverseWarps are invoked by passing the InverseWarp.nii.gz filename (see below for a note about this). " << std::endl; std::cout << std::endl; std::cout << " Example 1: Mapping a warped image into the reference_image domain by applying abcdWarp.nii.gz and then abcdAffine.txt\n" << std::endl; std::cout << argv[0] << " 3 moving_image output_image -R reference_image abcdWarp.nii.gz abcdAffine.txt\n" << std::endl; std::cout << " Example 2: To map the fixed/reference_image warped into the moving_image domain by applying the inversion of abcdAffine.txt and then abcdInverseWarp.nii.gz .\n" << std::endl; std::cout << argv[0] << " 3 reference_image output_image -R moving_image -i abcdAffine.txt abcdInverseWarp.nii.gz \n \n" << std::endl; std::cout << " Note that the inverse maps (Ex. 2) are passed to this program in the reverse order of the forward maps (Ex. 1). " << std::endl; std::cout << " This makes sense, geometrically ... see ANTS.pdf for visualization of this syntax." << std::endl; std::cout << std::endl; std::cout << " Compulsory arguments:\n " << std::endl; std::cout << " ImageDimension: 2 or 3 (for 2 or 3 Dimensional registration)\n " << std::endl; std::cout << " moving_image: the image to apply the transformation to\n " << std::endl; std::cout << " output_image: the resulting image\n \n " << std::endl; std::cout << " Optional arguments:\n " << std::endl; std::cout << " -R: reference_image space that you wish to warp INTO." << std::endl; std::cout << " --tightest-bounding-box: Computes the tightest bounding box using all the affine transformations. It will be overrided by -R reference_image if given." << std::endl; std::cout << " --reslice-by-header: equivalient to -i -mh, or -fh -i -mh if used together with -R. It uses the orientation matrix and origin encoded in the image file header. " << std::endl; std::cout << " It can be used together with -R. This is typically not used together with any other transforms.\n " << std::endl; std::cout << " --use-NN: Use Nearest Neighbor Interpolation. \n " << std::endl; std::cout << " --use-BSpline: Use 3rd order B-Spline Interpolation. \n " << std::endl; std::cout << " --use-ML sigma: Use anti-aliasing interpolation for multi-label images, with Gaussian smoothing with standard deviation sigma. \n " << std::endl; std::cout << " Sigma can be specified in physical or voxel units, as in Convert3D. It can be a scalar or a vector. \n " << std::endl; std::cout << " Examples: --use-ML 0.4mm -use-ML 0.8x0.8x0.8vox " << std::endl; // std::cout << " --ANTS-prefix prefix-name: followed by a deformation field filename. \n " << std::endl; // std::cout << " --ANTS-prefix-invert: . \n" << std::endl; std::cout << " -i: will use the inversion of the following affine transform. \n " << std::endl; // std::cout << " --Id: use an identity transform. \n " << std::endl; // std::cout << " --moving-image-header or -mh: will use the orientation header of the moving image file. This is // typically not used with --reslice-by-header.\n " << std::endl; // std::cout << " --reference-image-header or -rh: use the orientation matrix and origin encoded in the image // file header. It can be used together with -R.\n " << std::endl; std::cout << " \n " << std::endl; // std::cout << " For ANTS users:" << std::endl; std::cout << " Other Example Usages:" << std::endl; std::cout << " Reslice the image: WarpImageMultiTransform 3 Imov.nii.gz Iout.nii.gz --tightest-bounding-box --reslice-by-header" << std::endl; std::cout << " Reslice the image to a reference image: WarpImageMultiTransform 3 Imov.nii.gz Iout.nii.gz -R Iref.nii.gz --tightest-bounding-box --reslice-by-header\n" << std::endl; std::cout << " Important Notes: " << std::endl; std::cout << " Prefixname \"abcd\" without any extension will use \".nii.gz\" by default" << std::endl; std::cout << " The abcdWarp and abcdInverseWarp do not exist. They are formed on the basis of abcd(Inverse)Warp.nii.gz when calling " << argv[0] << ", yet you have to use them as if they exist." << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } TRAN_OPT_QUEUE opt_queue; char * moving_image_filename = ITK_NULLPTR; char * output_image_filename = ITK_NULLPTR; MISC_OPT misc_opt; const int kImageDim = atoi(argv[1]); const bool is_parsing_ok = WarpImageMultiTransform_ParseInput(argc - 2, argv + 2, moving_image_filename, output_image_filename, opt_queue, misc_opt, kImageDim); if( is_parsing_ok ) { itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(moving_image_filename, itk::ImageIOFactory::ReadMode); imageIO->SetFileName(moving_image_filename); imageIO->ReadImageInformation(); unsigned int ncomponents = imageIO->GetNumberOfComponents(); std::cout << "moving_image_filename: " << moving_image_filename << " components " << ncomponents << std::endl; std::cout << "output_image_filename: " << output_image_filename << std::endl; std::cout << "reference_image_filename: "; if( misc_opt.reference_image_filename ) { std::cout << misc_opt.reference_image_filename << std::endl; } else { std::cout << "NULL" << std::endl; } DisplayOptQueue(opt_queue); try { switch( kImageDim ) { case 2: switch( ncomponents ) { case 2: WarpImageMultiTransform<2, 2>(moving_image_filename, output_image_filename, opt_queue, misc_opt); break; default: WarpImageMultiTransform<2, 1>(moving_image_filename, output_image_filename, opt_queue, misc_opt); break; } break; case 3: switch( ncomponents ) { case 3: WarpImageMultiTransform<3, 3>(moving_image_filename, output_image_filename, opt_queue, misc_opt); break; case 6: WarpImageMultiTransform<3, 6>(moving_image_filename, output_image_filename, opt_queue, misc_opt); break; default: WarpImageMultiTransform<3, 1>(moving_image_filename, output_image_filename, opt_queue, misc_opt); break; } break; case 4: switch( ncomponents ) { case 4: WarpImageMultiTransform<4, 4>(moving_image_filename, output_image_filename, opt_queue, misc_opt); break; default: WarpImageMultiTransform<4, 1>(moving_image_filename, output_image_filename, opt_queue, misc_opt); break; } break; default: std::cout << " not supported " << kImageDim << std::endl; return EXIT_FAILURE; } } catch( itk::ExceptionObject & e ) { std::cout << "Exception caught during WarpImageMultiTransform." << std::endl; std::cout << e << std::endl; return EXIT_FAILURE; } // WarpImageMultiTransform<2,2>(moving_image_filename, output_image_filename, opt_queue, misc_opt); } else { std::cout << "Input error!" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/WarpTensorImageMultiTransform.cxx000066400000000000000000000550551311104306400227430ustar00rootroot00000000000000 #include "antsUtilities.h" #include "antsAllocImage.h" #include "itkImageFileReader.h" #include "itkVariableLengthVector.h" #include "itkImageFileWriter.h" #include "itkMatrixOffsetTransformBase.h" #include "itkTransformFactory.h" #include "itkWarpTensorImageMultiTransformFilter.h" #include "itkTransformFileReader.h" #include "itkVectorNearestNeighborInterpolateImageFunction.h" #include "ReadWriteData.h" #include "itkWarpImageMultiTransformFilter.h" #include "itkExpTensorImageFilter.h" namespace ants { static bool WarpTensorImageMultiTransform_ParseInput(int argc, char * *argv, char *& moving_image_filename, char *& output_image_filename, TRAN_OPT_QUEUE & opt_queue, MISC_OPT & misc_opt) { opt_queue.clear(); opt_queue.reserve(argc - 2); misc_opt.reference_image_filename = ITK_NULLPTR; misc_opt.use_NN_interpolator = false; misc_opt.use_TightestBoundingBox = false; misc_opt.use_RotationHeader = false; moving_image_filename = argv[0]; output_image_filename = argv[1]; int ind = 2; bool set_current_affine_inv = false; while( ind < argc ) { if( strcmp(argv[ind], "--use-NN") == 0 ) { misc_opt.use_NN_interpolator = true; } else if( strcmp(argv[ind], "-R") == 0 ) { ind++; if( ind >= argc ) { return false; } misc_opt.reference_image_filename = argv[ind]; } else if( (strcmp(argv[ind], "--tightest-bounding-box") == 0) && (strcmp(argv[ind], "-R") != 0) ) { misc_opt.use_TightestBoundingBox = true; } else if( strcmp(argv[ind], "--reslice-by-header") == 0 ) { misc_opt.use_RotationHeader = true; TRAN_OPT opt; opt.file_type = IMAGE_AFFINE_HEADER; opt.do_affine_inv = false; opt_queue.push_back(opt); } else if( strcmp(argv[ind], "--Id") == 0 ) { TRAN_OPT opt; opt.filename = "--Id"; opt.do_affine_inv = false; opt.file_type = IDENTITY_TRANSFORM; opt_queue.push_back(opt); } else if( strcmp(argv[ind], "--moving-image-header") == 0 || strcmp(argv[ind], "-mh") == 0 ) { TRAN_OPT opt; opt.file_type = IMAGE_AFFINE_HEADER; opt.filename = moving_image_filename; // opt.do_affine_inv = false; SetAffineInvFlag(opt, set_current_affine_inv); opt_queue.push_back(opt); } else if( strcmp(argv[ind], "--reference-image-header") == 0 || strcmp(argv[ind], "-rh") == 0 ) { if( misc_opt.reference_image_filename == ITK_NULLPTR ) { std::cout << "reference image filename is not given yet. Specify it with -R before --reference-image-header / -rh." << std::endl; return false; } TRAN_OPT opt; opt.file_type = IMAGE_AFFINE_HEADER; opt.filename = misc_opt.reference_image_filename; // opt.do_affine_inv = false; SetAffineInvFlag(opt, set_current_affine_inv); opt_queue.push_back(opt); } else if( strcmp(argv[ind], "-i") == 0 ) { set_current_affine_inv = true; } else if( strcmp(argv[ind], "--ANTS-prefix") == 0 ) { ind++; std::string prefix = argv[ind]; std::string path, name, ext; FilePartsWithgz(prefix, path, name, ext); if( ext == "" ) { ext = ".nii.gz"; } std::string deform_file_name, x_deform_name; deform_file_name = path + name + std::string("Warp") + ext; x_deform_name = path + name + std::string("Warpxvec") + ext; if( CheckFileExistence(x_deform_name.c_str() ) ) { TRAN_OPT opt; opt.filename = deform_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; opt_queue.push_back(opt); std::cout << "found deformation file: " << opt.filename << std::endl; DisplayOpt(opt); } std::string affine_file_name; affine_file_name = path + name + std::string("Affine.txt"); if( CheckFileExistence(affine_file_name.c_str() ) ) { TRAN_OPT opt; opt.filename = affine_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; opt_queue.push_back(opt); std::cout << "found affine file: " << opt.filename << std::endl; DisplayOpt(opt); } } else if( strcmp(argv[ind], "--ANTS-prefix-invert") == 0 ) { ind++; std::string prefix = argv[ind]; std::string path, name, ext; FilePartsWithgz(prefix, path, name, ext); if( ext == "" ) { ext = ".nii.gz"; } std::string affine_file_name; affine_file_name = path + name + std::string("Affine.txt"); if( CheckFileExistence(affine_file_name.c_str() ) ) { TRAN_OPT opt; opt.filename = affine_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = true; opt_queue.push_back(opt); std::cout << "found affine file: " << opt.filename << std::endl; DisplayOpt(opt); } std::string deform_file_name, x_deform_name; deform_file_name = path + name + std::string("InverseWarp.nii.gz"); x_deform_name = path + name + std::string("InverseWarpxvec.nii.gz"); if( CheckFileExistence(x_deform_name.c_str() ) ) { TRAN_OPT opt; opt.filename = deform_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; opt_queue.push_back(opt); std::cout << "found deformation file: " << opt.filename << std::endl; DisplayOpt(opt); } } else { TRAN_OPT opt; opt.filename = argv[ind]; opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; if( opt.file_type == AFFINE_FILE ) { SetAffineInvFlag(opt, set_current_affine_inv); } else if( opt.file_type == DEFORMATION_FILE && set_current_affine_inv ) { std::cout << "Ignore inversion of non-affine file type! " << std::endl; std::cout << "opt.do_affine_inv:" << opt.do_affine_inv << std::endl; } opt_queue.push_back(opt); DisplayOpt(opt); } ind++; } if( misc_opt.use_RotationHeader ) { // if (misc_opt.reference_image_filename) { // opt_queue[0].filename = misc_opt.reference_image_filename; // } else { opt_queue[0].filename = "--Id"; opt_queue[0].file_type = IDENTITY_TRANSFORM; opt_queue[0].do_affine_inv = false; // } // TRAN_OPT opt; // opt.file_type = IMAGE_AFFINE_HEADER; // opt.filename = moving_image_filename; // opt.do_affine_inv = true; // opt_queue.push_back(opt); // // std::cout << "Use Rotation Header!" << std::endl; } return true; } template static void GetIdentityTransform(typename TAffineTransform::Pointer & aff) { aff = TAffineTransform::New(); aff->SetIdentity(); } #if 0 template static void DirectionCorrect( typename TensorImageType::Pointer img_mov, typename ImageType::Pointer img_ref ) { itk::ImageRegionIteratorWithIndex it(img_mov, img_mov->GetLargestPossibleRegion() ); typename TensorImageType::DirectionType::InternalMatrixType direction = img_mov->GetDirection().GetTranspose() * img_ref->GetDirection().GetVnlMatrix(); if( !direction.is_identity( 0.00001 ) ) { while( !it.IsAtEnd() ) { typename TensorImageType::DirectionType::InternalMatrixType dt; dt(0, 0) = it.Value()[0]; dt(0, 1) = dt(1, 0) = it.Value()[1]; dt(0, 2) = dt(2, 0) = it.Value()[2]; dt(1, 1) = it.Value()[3]; dt(1, 2) = dt(2, 1) = it.Value()[4]; dt(2, 2) = it.Value()[5]; dt = direction * dt * direction.transpose(); typename TensorImageType::PixelType outDt; outDt[0] = dt(0, 0); outDt[1] = dt(0, 1); outDt[2] = dt(0, 2); outDt[3] = dt(1, 1); outDt[4] = dt(1, 2); outDt[5] = dt(2, 2); it.Set( outDt ); ++it; } } } #endif template static void WarpImageMultiTransform(char *moving_image_filename, char *output_image_filename, TRAN_OPT_QUEUE & opt_queue, MISC_OPT & misc_opt) { // typedef itk::Vector PixelType; typedef itk::SymmetricSecondRankTensor PixelType; typedef itk::Image TensorImageType; typedef itk::Image ImageType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::MatrixOffsetTransformBase AffineTransformType; typedef itk::WarpImageMultiTransformFilter WarperType; itk::TransformFactory::RegisterTransform(); typedef itk::ImageFileReader ImageFileReaderType; // typename ImageFileReaderType::Pointer reader_img = ImageFileReaderType::New(); // reader_img->SetFileName(moving_image_filename); // reader_img->Update(); // typename ImageType::Pointer img_mov = ImageType::New(); // img_mov = reader_img->GetOutput(); typename TensorImageType::Pointer img_mov; ReadTensorImage(img_mov, moving_image_filename, true); typename ImageType::Pointer img_ref; typename ImageFileReaderType::Pointer reader_img_ref = ImageFileReaderType::New(); if( misc_opt.reference_image_filename ) { reader_img_ref->SetFileName(misc_opt.reference_image_filename); reader_img_ref->Update(); img_ref = reader_img_ref->GetOutput(); } // Convert to reference image tensor basis // DirectionCorrect(img_mov, img_ref); typename TensorImageType::Pointer img_output = AllocImage(img_ref); for( unsigned int tensdim = 0; tensdim < 6; tensdim++ ) { typedef itk::VectorIndexSelectionCastImageFilter IndexSelectCasterType; typename IndexSelectCasterType::Pointer fieldCaster = IndexSelectCasterType::New(); fieldCaster->SetInput( img_mov ); fieldCaster->SetIndex( tensdim ); fieldCaster->Update(); typename ImageType::Pointer tenscomponent = fieldCaster->GetOutput(); tenscomponent->SetSpacing(img_mov->GetSpacing() ); tenscomponent->SetOrigin(img_mov->GetOrigin() ); tenscomponent->SetDirection(img_mov->GetDirection() ); typename WarperType::Pointer warper = WarperType::New(); warper->SetInput(tenscomponent); // PixelType nullPix; // nullPix.Fill(0); warper->SetEdgePaddingValue(0); if( misc_opt.use_NN_interpolator ) { typedef typename itk::NearestNeighborInterpolateImageFunction NNInterpolateType; typename NNInterpolateType::Pointer interpolator_NN = NNInterpolateType::New(); std::cout << "Haha" << std::endl; warper->SetInterpolator(interpolator_NN); } typedef itk::TransformFileReader TranReaderType; typedef itk::ImageFileReader FieldReaderType; unsigned int transcount = 0; const int kOptQueueSize = opt_queue.size(); for( int i = 0; i < kOptQueueSize; i++ ) { const TRAN_OPT & opt = opt_queue[i]; switch( opt.file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); typename AffineTransformType::Pointer aff = dynamic_cast ( (tran_reader->GetTransformList() )->front().GetPointer() ); if( opt.do_affine_inv ) { typename AffineTransformType::Pointer aff_inv = AffineTransformType::New(); aff->GetInverse(aff_inv); aff = aff_inv; } // std::cout <<" aff " << transcount << std::endl; warper->PushBackAffineTransform(aff); if( transcount == 0 ) { warper->SetOutputParametersFromImage( img_mov ); } transcount++; } break; case IDENTITY_TRANSFORM: { typename AffineTransformType::Pointer aff; GetIdentityTransform(aff); // std::cout << " aff id" << transcount << std::endl; warper->PushBackAffineTransform(aff); transcount++; } break; case IMAGE_AFFINE_HEADER: { typename AffineTransformType::Pointer aff = AffineTransformType::New(); typename ImageFileReaderType::Pointer reader_image_affine = ImageFileReaderType::New(); reader_image_affine->SetFileName(opt.filename); reader_image_affine->Update(); typename ImageType::Pointer img_affine = reader_image_affine->GetOutput(); GetAffineTransformFromImage(img_affine, aff); if( opt.do_affine_inv ) { typename AffineTransformType::Pointer aff_inv = AffineTransformType::New(); aff->GetInverse(aff_inv); aff = aff_inv; } // std::cout <<" aff from image header " << transcount << std::endl; warper->PushBackAffineTransform(aff); // if (transcount==0){ // warper->SetOutputParametersFromImage( img_mov ); // } transcount++; } break; case DEFORMATION_FILE: { typename FieldReaderType::Pointer field_reader = FieldReaderType::New(); field_reader->SetFileName( opt.filename ); field_reader->Update(); typename DisplacementFieldType::Pointer field = field_reader->GetOutput(); warper->PushBackDisplacementFieldTransform(field); warper->SetOutputParametersFromImage( field ); transcount++; } break; default: { std::cout << "Unknown file type!" << std::endl; } } } // warper->PrintTransformList(); if( img_ref.IsNotNull() ) { warper->SetOutputParametersFromImage( img_ref ); } else { if( misc_opt.use_TightestBoundingBox == true ) { // compute the desired spacking after inputting all the transform files using the typename ImageType::SizeType largest_size; typename ImageType::PointType origin_warped; GetLargestSizeAfterWarp(warper, img_mov, largest_size, origin_warped); warper->SetOutputParametersFromImage( img_mov ); warper->SetOutputSize(largest_size); warper->SetOutputOrigin(origin_warped); { typename ImageType::DirectionType d; d.SetIdentity(); warper->SetOutputDirection(d); } } } // std::cout << "output origin: " << warper->GetOutputOrigin() << std::endl; // std::cout << "output size: " << warper->GetOutputSize() << std::endl; // std::cout << "output spacing: " << warper->GetOutputSpacing() << std::endl; // std::cout << "output direction: " << warper->GetOutputDirection() << std::endl; // warper->PrintTransformList(); warper->DetermineFirstDeformNoInterp(); warper->Update(); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter2( img_output, img_output->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { PixelType tens = vfIter2.Get(); tens[tensdim] = warper->GetOutput()->GetPixel(vfIter2.GetIndex() ); vfIter2.Set(tens); } } // DirectionCorrect(img_output, img_mov); WriteTensorImage(img_output, output_image_filename, true); } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int WarpTensorImageMultiTransform( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "WarpTensorImageMultiTransform" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc <= 3 ) { std::cout << "WarpImageMultiTransform ImageDimension moving_image output_image [-R reference_image | --tightest-bounding-box] (--reslice-by-header) [--use-NN (use Nearest Neighbor Interpolator)]" << "[--ANTS-prefix prefix-name | --ANTS-prefix-invert prefix-name] {[deformation_field | [-i] affine_transform_txt | --Id | [-i] --moving-image-header / -mh | [-i] --reference-image-header / -rh]}" << std::endl << "Example:" << std::endl << "Reslice the image: WarpImageMultiTransform 3 Imov.nii Iout.nii --tightest-bounding-box --reslice-by-header" << std::endl << "Reslice the image to a reference image: WarpImageMultiTransform 3 Imov.nii Iout.nii -R Iref.nii --tightest-bounding-box --reslice-by-header" << std::endl << "Note:" << std::endl << "-i will use the inversion of the following affine transform." << std::endl << "--tightest-bounding-box will be overrided by -R reference_image if given. It computes the tightest bounding box using all the affine transformations." << std::endl << "--Id uses the identity transform." << std::endl << "--moving-image-header or -mh in short will use the orientation header of the moving image file. This is typically not used with --reslice-by-header." << std::endl << "--reference-image-header or -rh in short will use the orientation header of the fixed image file. This is typically not used with --reslice-by-header." << std::endl << "--reslice-by-header uses the orientation matrix and origin encoded in the image file header. It can be used together with -R. " << "This is typically not used together with any other transforms. " << "--reslice-by-header is equvalient to -i -mh, or -fh -i -mh if used together with -R. " << std::endl; std::cout << std::endl << "For ANTS users:" << std::endl << "To use with the deformation field and the affine transform files generated from ANTS:" << std::endl << "--ANTS-prefix prefix-name" << std::endl << "--ANTS-prefix-invert prefix-name" << std::endl << "Example:" << std::endl << "3 moving_image output_image -R reference_image --ANTS-prefix abcd.nii.gz" << std::endl << "Applies abcdWarpxvec.nii.gz/abcdWarpyvec.nii.gz/abcdWarpzvec.nii.gz and then abcdAffine.txt. Use this with ANTS to get the moving_image warped into the reference_image domain. " << std::endl << "3 reference_image output_image -R moving_image --ANTS-prefix-invert abcd.nii.gz --ANTS-invert" << std::endl << "Applies the inversion of abcdAffine.txt and then abcdInverseWarpxvec.nii.gz/abcdInverseWarpyvec.nii.gz/abcdInverseWarpzvec.nii.gz. Use this with ANTS to get the reference_image warped into the moving_image domain. " << std::endl << "Note: " << std::endl << "prefix name \"abcd\" without any extension will use \".nii.gz\" by default" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } TRAN_OPT_QUEUE opt_queue; char * moving_image_filename = ITK_NULLPTR; char * output_image_filename = ITK_NULLPTR; MISC_OPT misc_opt; const int kImageDim = atoi(argv[1]); const bool is_parsing_ok = WarpTensorImageMultiTransform_ParseInput(argc - 2, argv + 2, moving_image_filename, output_image_filename, opt_queue, misc_opt); if( is_parsing_ok ) { std::cout << "moving_image_filename: " << moving_image_filename << std::endl; std::cout << "output_image_filename: " << output_image_filename << std::endl; std::cout << "reference_image_filename: "; if( misc_opt.reference_image_filename ) { std::cout << misc_opt.reference_image_filename << std::endl; } else { std::cout << "NULL" << std::endl; } DisplayOptQueue(opt_queue); switch( kImageDim ) { case 2: { WarpImageMultiTransform<2>(moving_image_filename, output_image_filename, opt_queue, misc_opt); } break; case 3: { WarpImageMultiTransform<3>(moving_image_filename, output_image_filename, opt_queue, misc_opt); } break; } } else { std::cout << "Input error!" << std::endl; } return EXIT_FAILURE; } } // namespace ants ants-2.2.0/Examples/WarpTimeSeriesImageMultiTransform.cxx000066400000000000000000000722531311104306400235410ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include "itkImageFileReader.h" #include "itkVariableLengthVector.h" #include "itkImageFileWriter.h" #include "itkMatrixOffsetTransformBase.h" #include "itkTransformFactory.h" #include "itkTransformFileReader.h" #include "itkVectorNearestNeighborInterpolateImageFunction.h" #include "ReadWriteData.h" #include "itkWarpImageMultiTransformFilter.h" #include "itkExtractImageFilter.h" namespace ants { static bool WarpTimeSeriesImageMultiTransform_ParseInput(int argc, char * *argv, char *& moving_image_filename, char *& output_image_filename, TRAN_OPT_QUEUE & opt_queue, MISC_OPT & misc_opt) { opt_queue.clear(); opt_queue.reserve(argc - 2); misc_opt.reference_image_filename = ITK_NULLPTR; misc_opt.use_NN_interpolator = false; misc_opt.use_TightestBoundingBox = false; misc_opt.use_RotationHeader = false; moving_image_filename = argv[0]; output_image_filename = argv[1]; int ind = 2; bool set_current_affine_inv = false; while( ind < argc ) { if( strcmp(argv[ind], "--use-NN") == 0 ) { misc_opt.use_NN_interpolator = true; } else if( strcmp(argv[ind], "-R") == 0 ) { ind++; if( ind >= argc ) { return false; } misc_opt.reference_image_filename = argv[ind]; } else if( (strcmp(argv[ind], "--tightest-bounding-box") == 0) && (strcmp(argv[ind], "-R") != 0) ) { misc_opt.use_TightestBoundingBox = true; } else if( strcmp(argv[ind], "--reslice-by-header") == 0 ) { misc_opt.use_RotationHeader = true; TRAN_OPT opt; opt.file_type = IMAGE_AFFINE_HEADER; opt.do_affine_inv = false; opt_queue.push_back(opt); } else if( strcmp(argv[ind], "--Id") == 0 ) { TRAN_OPT opt; opt.filename = "--Id"; opt.do_affine_inv = false; opt.file_type = IDENTITY_TRANSFORM; opt_queue.push_back(opt); } else if( strcmp(argv[ind], "--moving-image-header") == 0 || strcmp(argv[ind], "-mh") == 0 ) { TRAN_OPT opt; opt.file_type = IMAGE_AFFINE_HEADER; opt.filename = moving_image_filename; // opt.do_affine_inv = false; SetAffineInvFlag(opt, set_current_affine_inv); opt_queue.push_back(opt); } else if( strcmp(argv[ind], "--reference-image-header") == 0 || strcmp(argv[ind], "-rh") == 0 ) { if( misc_opt.reference_image_filename == ITK_NULLPTR ) { std::cout << "reference image filename is not given yet. Specify it with -R before --reference-image-header / -rh." << std::endl; return false; } TRAN_OPT opt; opt.file_type = IMAGE_AFFINE_HEADER; opt.filename = misc_opt.reference_image_filename; // opt.do_affine_inv = false; SetAffineInvFlag(opt, set_current_affine_inv); opt_queue.push_back(opt); } else if( strcmp(argv[ind], "-i") == 0 ) { set_current_affine_inv = true; } else if( strcmp(argv[ind], "--ANTS-prefix") == 0 ) { ind++; std::string prefix = argv[ind]; std::string path, name, ext; FilePartsWithgz(prefix, path, name, ext); if( ext == "" ) { ext = ".nii.gz"; } std::string deform_file_name, x_deform_name; deform_file_name = path + name + std::string("Warp") + ext; x_deform_name = path + name + std::string("Warpxvec") + ext; if( CheckFileExistence(x_deform_name.c_str() ) ) { TRAN_OPT opt; opt.filename = deform_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; opt_queue.push_back(opt); std::cout << "found deformation file: " << opt.filename << std::endl; DisplayOpt(opt); } std::string affine_file_name; affine_file_name = path + name + std::string("Affine.txt"); if( CheckFileExistence(affine_file_name.c_str() ) ) { TRAN_OPT opt; opt.filename = affine_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; opt_queue.push_back(opt); std::cout << "found affine file: " << opt.filename << std::endl; DisplayOpt(opt); } } else if( strcmp(argv[ind], "--ANTS-prefix-invert") == 0 ) { ind++; std::string prefix = argv[ind]; std::string path, name, ext; FilePartsWithgz(prefix, path, name, ext); if( ext == "" ) { ext = ".nii.gz"; } std::string affine_file_name; affine_file_name = path + name + std::string("Affine.txt"); if( CheckFileExistence(affine_file_name.c_str() ) ) { TRAN_OPT opt; opt.filename = affine_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = true; opt_queue.push_back(opt); std::cout << "found affine file: " << opt.filename << std::endl; DisplayOpt(opt); } std::string deform_file_name, x_deform_name; deform_file_name = path + name + std::string("InverseWarp.nii.gz"); x_deform_name = path + name + std::string("InverseWarpxvec.nii.gz"); if( CheckFileExistence(x_deform_name.c_str() ) ) { TRAN_OPT opt; opt.filename = deform_file_name.c_str(); opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; opt_queue.push_back(opt); std::cout << "found deformation file: " << opt.filename << std::endl; DisplayOpt(opt); } } else { TRAN_OPT opt; opt.filename = argv[ind]; opt.file_type = CheckFileType(opt.filename.c_str() ); opt.do_affine_inv = false; if( opt.file_type == AFFINE_FILE ) { SetAffineInvFlag(opt, set_current_affine_inv); } else if( opt.file_type == DEFORMATION_FILE && set_current_affine_inv ) { std::cout << "Ignore inversion of non-affine file type! " << std::endl; std::cout << "opt.do_affine_inv:" << opt.do_affine_inv << std::endl; } opt_queue.push_back(opt); DisplayOpt(opt); } ind++; } if( misc_opt.use_RotationHeader ) { // if (misc_opt.reference_image_filename) { // opt_queue[0].filename = misc_opt.reference_image_filename; // } else { opt_queue[0].filename = "--Id"; opt_queue[0].file_type = IDENTITY_TRANSFORM; opt_queue[0].do_affine_inv = false; // } // TRAN_OPT opt; // opt.file_type = IMAGE_AFFINE_HEADER; // opt.filename = moving_image_filename; // opt.do_affine_inv = true; // opt_queue.push_back(opt); // // std::cout << "Use Rotation Header!" << std::endl; } return true; } template void GetIdentityTransform(AffineTransformPointer & aff) { typedef typename AffineTransformPointer::ObjectType AffineTransform; aff = AffineTransform::New(); aff->SetIdentity(); } template void WarpImageMultiTransformFourD(char *moving_image_filename, char *output_image_filename, TRAN_OPT_QUEUE & opt_queue, MISC_OPT & misc_opt) { typedef itk::Image VectorImageType; // 4D contains functional image typedef itk::Image ImageType; // 3D image domain -R option typedef itk::Vector VectorType; // 3D warp typedef itk::Image DisplacementFieldType; // 3D Field typedef itk::MatrixOffsetTransformBase AffineTransformType; typedef itk::WarpImageMultiTransformFilter WarperType; itk::TransformFactory::RegisterTransform(); typename VectorImageType::Pointer img_mov; ReadImage(img_mov, moving_image_filename); std::cout << " Four-D image size: " << img_mov->GetLargestPossibleRegion().GetSize() << std::endl; typename ImageType::Pointer img_ref; if( misc_opt.reference_image_filename ) { ReadImage( img_ref, misc_opt.reference_image_filename ); } typedef itk::ExtractImageFilter ExtractFilterType; // ORIENTATION ALERT -- the way this code sets up // transformedvecimage doesn't really make complete sense to me. In // particular, the 'grab upper dim-1 x dim-1 of directions' method // of setting lower-dimension dir cosines can lead to singular mattrices. // allocate output image typename VectorImageType::RegionType region = img_mov->GetLargestPossibleRegion(); for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { region.SetSize( i, img_ref->GetLargestPossibleRegion().GetSize()[i] ); } typename VectorImageType::Pointer transformedvecimage = AllocImage(region); typename VectorImageType::DirectionType direction = transformedvecimage->GetDirection(); direction.Fill(0); typename VectorImageType::PointType origin; typename VectorImageType::SpacingType spc; for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { for( unsigned int j = 0; j < ImageDimension - 1; j++ ) { direction[i][j] = img_ref->GetDirection()[i][j]; } spc[i] = img_ref->GetSpacing()[i]; origin[i] = img_ref->GetOrigin()[i]; } direction[ImageDimension - 1][ImageDimension - 1] = 1; origin[ImageDimension - 1] = img_mov->GetOrigin()[ImageDimension - 1]; spc[ImageDimension - 1] = img_mov->GetSpacing()[ImageDimension - 1]; transformedvecimage->SetDirection(direction); transformedvecimage->SetSpacing(spc); transformedvecimage->SetOrigin(origin); std::cout << " 4D-In-Spc " << img_mov->GetSpacing() << std::endl; std::cout << " 4D-In-Org " << img_mov->GetOrigin() << std::endl; std::cout << " 4D-In-Size " << img_mov->GetLargestPossibleRegion().GetSize() << std::endl; std::cout << " 4D-In-Dir " << img_mov->GetDirection() << std::endl; std::cout << " ...... " << std::endl; std::cout << " 4D-Out-Spc " << transformedvecimage->GetSpacing() << std::endl; std::cout << " 4D-Out-Org " << transformedvecimage->GetOrigin() << std::endl; std::cout << " 4D-Out-Size " << transformedvecimage->GetLargestPossibleRegion().GetSize() << std::endl; std::cout << " 4D-Out-Dir " << transformedvecimage->GetDirection() << std::endl; unsigned int timedims = img_mov->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; for( unsigned int timedim = 0; timedim < timedims; timedim++ ) { typename WarperType::Pointer warper = WarperType::New(); warper->SetEdgePaddingValue(0); if( misc_opt.use_NN_interpolator ) { typedef typename itk::NearestNeighborInterpolateImageFunction NNInterpolateType; typename NNInterpolateType::Pointer interpolator_NN = NNInterpolateType::New(); std::cout << " Use Nearest Neighbor interpolation " << std::endl; warper->SetInterpolator(interpolator_NN); } typedef itk::TransformFileReader TranReaderType; typedef itk::ImageFileReader FieldReaderType; unsigned int transcount = 0; const int kOptQueueSize = opt_queue.size(); for( int i = 0; i < kOptQueueSize; i++ ) { const TRAN_OPT & opt = opt_queue[i]; switch( opt.file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); typename AffineTransformType::Pointer aff = dynamic_cast ( (tran_reader->GetTransformList() )->front().GetPointer() ); if( opt.do_affine_inv ) { typename AffineTransformType::Pointer aff_inv = AffineTransformType::New(); aff->GetInverse(aff_inv); aff = aff_inv; } // std::cout <<" aff " << transcount << std::endl; warper->PushBackAffineTransform(aff); if( transcount == 0 ) { warper->SetOutputParametersFromImage( img_ref ); } transcount++; } break; case IDENTITY_TRANSFORM: { typename AffineTransformType::Pointer aff; GetIdentityTransform(aff); // std::cout << " aff id" << transcount << std::endl; warper->PushBackAffineTransform(aff); transcount++; } break; case DEFORMATION_FILE: { typename FieldReaderType::Pointer field_reader = FieldReaderType::New(); field_reader->SetFileName( opt.filename ); field_reader->Update(); typename DisplacementFieldType::Pointer field = field_reader->GetOutput(); warper->PushBackDisplacementFieldTransform(field); warper->SetOutputParametersFromImage( field ); transcount++; } break; default: { std::cout << "Unknown file type!" << std::endl; } } } // warper->PrintTransformList(); if( img_ref.IsNotNull() ) { warper->SetOutputParametersFromImage( img_ref ); } else { if( misc_opt.use_TightestBoundingBox == true ) { // compute the desired spacking after inputting all the transform files using the std::cout << " not implemented " << std::endl; /* typename ImageType::SizeType largest_size; typename ImageType::PointType origin_warped; GetLaregstSizeAfterWarp(warper, warpthisimage , largest_size, origin_warped); warper->SetOutputParametersFromImage( warpthisimage ); warper->SetOutputSize(largest_size); warper->SetOutputOrigin(origin_warped); { typename ImageType::DirectionType d; d.SetIdentity(); warper->SetOutputDirection(d); } */ } } if( timedim % vnl_math_max(timedims / 10, static_cast(1) ) == 0 ) { std::cout << (float) timedim / (float)timedims * 100 << " % done ... " << std::flush; } typename VectorImageType::RegionType extractRegion = img_mov->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); extractRegion.SetIndex(ImageDimension - 1, timedim ); typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( img_mov ); extractFilter->SetDirectionCollapseToSubmatrix(); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); typename ImageType::Pointer warpthisimage = extractFilter->GetOutput(); typename ImageType::SpacingType qspc = warpthisimage->GetSpacing(); typename ImageType::PointType qorg = warpthisimage->GetOrigin(); typename ImageType::DirectionType qdir = warpthisimage->GetDirection(); qdir.Fill(0); for( unsigned int qq = 0; qq < ImageDimension - 1; qq++ ) { for( unsigned int pp = 0; pp < ImageDimension - 1; pp++ ) { qdir[qq][pp] = img_mov->GetDirection()[qq][pp]; } qspc[qq] = img_mov->GetSpacing()[qq]; qorg[qq] = img_mov->GetOrigin()[qq]; } warpthisimage->SetSpacing(qspc); warpthisimage->SetOrigin(qorg); warpthisimage->SetDirection(qdir); warper->SetInput( warpthisimage ); warper->DetermineFirstDeformNoInterp(); warper->Update(); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter2( warper->GetOutput(), warper->GetOutput()->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { typename ImageType::PixelType fval = vfIter2.Get(); typename VectorImageType::IndexType ind; for( unsigned int xx = 0; xx < ImageDimension - 1; xx++ ) { ind[xx] = vfIter2.GetIndex()[xx]; } ind[ImageDimension - 1] = timedim; transformedvecimage->SetPixel(ind, fval); // if ( ind[0] == 53 && ind[1] == 19 && ind[2] == 30 ) std::cout << " fval " << fval << " td " << timedim << // std::endl; } if( timedim == 0 ) { std::cout << warper->GetOutput()->GetDirection() << std::endl; } } std::cout << " 100 % complete " << std::endl; WriteImage( transformedvecimage, output_image_filename); } template void WarpImageMultiTransform(char *moving_image_filename, char *output_image_filename, TRAN_OPT_QUEUE & opt_queue, MISC_OPT & misc_opt) { typedef itk::VectorImage VectorImageType; typedef itk::Image ImageType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::MatrixOffsetTransformBase AffineTransformType; typedef itk::WarpImageMultiTransformFilter WarperType; itk::TransformFactory::RegisterTransform(); typename VectorImageType::Pointer img_mov; typename itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(moving_image_filename, itk::ImageIOFactory::ReadMode); imageIO->SetFileName(moving_image_filename); imageIO->ReadImageInformation(); // std::cout << " Dimension " << imageIO->GetNumberOfDimensions() << " Components " // <GetNumberOfComponents() << std::endl; unsigned int veclength = imageIO->GetNumberOfComponents(); std::cout << " read veclength as:: " << veclength << std::endl; ReadImage(img_mov, moving_image_filename); typename ImageType::Pointer img_ref; if( misc_opt.reference_image_filename ) { ReadImage( img_ref, misc_opt.reference_image_filename ); } typename VectorImageType::Pointer img_output = AllocImage(img_ref); img_output->SetNumberOfComponentsPerPixel(veclength); typename ImageType::IndexType index; index.Fill(0); typename VectorImageType::PixelType vec = img_mov->GetPixel(index); vec.Fill(0); img_output->FillBuffer( vec ); for( unsigned int tensdim = 0; tensdim < veclength; tensdim++ ) { typedef itk::VectorIndexSelectionCastImageFilter IndexSelectCasterType; typename IndexSelectCasterType::Pointer fieldCaster = IndexSelectCasterType::New(); fieldCaster->SetInput( img_mov ); fieldCaster->SetIndex( tensdim ); fieldCaster->Update(); typename ImageType::Pointer tenscomponent = fieldCaster->GetOutput(); tenscomponent->SetSpacing(img_mov->GetSpacing() ); tenscomponent->SetOrigin(img_mov->GetOrigin() ); tenscomponent->SetDirection(img_mov->GetDirection() ); typename WarperType::Pointer warper = WarperType::New(); warper->SetInput(tenscomponent); // PixelType nullPix; // nullPix.Fill(0); warper->SetEdgePaddingValue(0); if( misc_opt.use_NN_interpolator ) { typedef typename itk::NearestNeighborInterpolateImageFunction NNInterpolateType; typename NNInterpolateType::Pointer interpolator_NN = NNInterpolateType::New(); std::cout << "Haha" << std::endl; warper->SetInterpolator(interpolator_NN); } typedef itk::TransformFileReader TranReaderType; typedef itk::ImageFileReader FieldReaderType; unsigned int transcount = 0; const int kOptQueueSize = opt_queue.size(); for( int i = 0; i < kOptQueueSize; i++ ) { const TRAN_OPT & opt = opt_queue[i]; switch( opt.file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); typename AffineTransformType::Pointer aff = dynamic_cast ( (tran_reader->GetTransformList() )->front().GetPointer() ); if( opt.do_affine_inv ) { typename AffineTransformType::Pointer aff_inv = AffineTransformType::New(); aff->GetInverse(aff_inv); aff = aff_inv; } // std::cout <<" aff " << transcount << std::endl; warper->PushBackAffineTransform(aff); if( transcount == 0 ) { warper->SetOutputParametersFromImage( img_mov ); } transcount++; } break; case IDENTITY_TRANSFORM: { typename AffineTransformType::Pointer aff; GetIdentityTransform(aff); // std::cout << " aff id" << transcount << std::endl; warper->PushBackAffineTransform(aff); transcount++; } break; case DEFORMATION_FILE: { typename FieldReaderType::Pointer field_reader = FieldReaderType::New(); field_reader->SetFileName( opt.filename ); field_reader->Update(); typename DisplacementFieldType::Pointer field = field_reader->GetOutput(); warper->PushBackDisplacementFieldTransform(field); warper->SetOutputParametersFromImage( field ); transcount++; } break; default: { std::cout << "Unknown file type!" << std::endl; } } } // warper->PrintTransformList(); if( img_ref.IsNotNull() ) { warper->SetOutputParametersFromImage( img_ref ); } else { if( misc_opt.use_TightestBoundingBox == true ) { // compute the desired spacking after inputting all the transform files using the typename ImageType::SizeType largest_size; typename ImageType::PointType origin_warped; GetLargestSizeAfterWarp(warper, img_mov, largest_size, origin_warped); warper->SetOutputParametersFromImage( img_mov ); warper->SetOutputSize(largest_size); warper->SetOutputOrigin(origin_warped); { typename ImageType::DirectionType d; d.SetIdentity(); warper->SetOutputDirection(d); } } } warper->DetermineFirstDeformNoInterp(); warper->Update(); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter2( img_output, img_output->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { typename VectorImageType::PixelType tens = vfIter2.Get(); tens[tensdim] = warper->GetOutput()->GetPixel(vfIter2.GetIndex() ); vfIter2.Set(tens); } } WriteImage(img_output, output_image_filename); } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int WarpTimeSeriesImageMultiTransform( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "WarpTimeSeriesImageMultiTransform" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc <= 3 ) { std::cout << "\nUsage 1 (Forward warp): " << argv[0] << " ImageDimension -R [interpolation]" << std::endl; std::cout << "\nUsage 2 (Inverse warp): " << argv[0] << " ImageDimension -R -i [interpolation]" << std::endl; std::cout << "\nUsage Information " << std::endl; std::cout << " ImageDimension : 3 or 4 (required argument)." << std::endl; std::cout << " : The image to apply the transformation to. The moving_image will be either a 3-D image with vector voxels or a 4D image with scalar voxels." << std::endl; std::cout << " : The resulting image. Output will be of the same type as input, but will be resampled to the domain size defined by the -R image." << std::endl; std::cout << " : Mappings can be stringed together, e.g.: MyAffine.txt MySecondAffine.txt MyWarp.nii.gz MySecondWarp.nii.gz -i MyInverseAffine.txt" << std::endl; std::cout << "\nOptions:" << std::endl; std::cout << " -i : Will use the inversion of the following affine transform." << std::endl; std::cout << " \n -R : Reference image space that you wish to warp into." << std::endl; std::cout << " --reslice-by-header : Equivalient to -i -mh, or -fh -i -mh if used together with -R. It uses the orientation matrix and origin encoded in the image file header. " << std::endl; std::cout << " --tightest-bounding-box : Computes the tightest bounding box using all the affine transformations. It will be overrided by -R if given." << std::endl; std::cout << " These options can be used together with -R and are typically not used together with any other transforms." << std::endl; std::cout << "\nInterpolation:" << std::endl; std::cout << " --use-NN : Use Nearest Neighbor Interpolator" << std::endl; std::cout << " --use-BSpline : Use 3rd order B-Spline Interpolation." << std::endl; std::cout << "\n " << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } TRAN_OPT_QUEUE opt_queue; char * moving_image_filename = ITK_NULLPTR; char * output_image_filename = ITK_NULLPTR; MISC_OPT misc_opt; int kImageDim = atoi(argv[1]); const bool is_parsing_ok = WarpTimeSeriesImageMultiTransform_ParseInput(argc - 2, argv + 2, moving_image_filename, output_image_filename, opt_queue, misc_opt); if( is_parsing_ok ) { std::cout << "moving_image_filename: " << moving_image_filename << std::endl; std::cout << "output_image_filename: " << output_image_filename << std::endl; std::cout << "reference_image_filename: "; if( misc_opt.reference_image_filename ) { std::cout << misc_opt.reference_image_filename << std::endl; } else { std::cout << "NULL" << std::endl; } DisplayOptQueue(opt_queue); switch( kImageDim ) { case 2: { WarpImageMultiTransform<2>(moving_image_filename, output_image_filename, opt_queue, misc_opt); } break; case 3: { WarpImageMultiTransform<3>(moving_image_filename, output_image_filename, opt_queue, misc_opt); } break; case 4: { WarpImageMultiTransformFourD<4>(moving_image_filename, output_image_filename, opt_queue, misc_opt); } break; } } else { std::cout << "Input error!" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/WarpVTKPolyDataMultiTransform.cxx000066400000000000000000000513671311104306400226320ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsUtilities.h" #include #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMatrixOffsetTransformBase.h" #include "itkTransformFactory.h" #include "vtkPolyDataReader.h" #include "itkDisplacementFieldFromMultiTransformFilter.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "itkLabeledPointSetFileReader.h" #include "itkLabeledPointSetFileWriter.h" #include "itkMesh.h" #include #include #include #include #include #include #include namespace ants { vnl_matrix_fixed ConstructNiftiSform( vnl_matrix m_dir, vnl_vector v_origin, vnl_vector v_spacing) { // Set the NIFTI/RAS transform vnl_matrix m_ras_matrix; vnl_diag_matrix m_scale, m_lps_to_ras; vnl_vector v_ras_offset; // Compute the matrix m_scale.set(v_spacing); m_lps_to_ras.set(vnl_vector(3, 1.0) ); m_lps_to_ras[0] = -1; m_lps_to_ras[1] = -1; m_ras_matrix = m_lps_to_ras * m_dir * m_scale; // Compute the vector v_ras_offset = m_lps_to_ras * v_origin; // Create the larger matrix vnl_vector vcol(4, 1.0); vcol.update(v_ras_offset); vnl_matrix_fixed m_sform; m_sform.set_identity(); m_sform.update(m_ras_matrix); m_sform.set_column(3, vcol); return m_sform; } vnl_matrix_fixed ConstructVTKtoNiftiTransform( vnl_matrix m_dir, vnl_vector v_origin, vnl_vector v_spacing) { vnl_matrix_fixed vox2nii = ConstructNiftiSform(m_dir, v_origin, v_spacing); vnl_matrix_fixed vtk2vox; vtk2vox.set_identity(); for( size_t i = 0; i < 3; i++ ) { vtk2vox(i, i) = 1.0 / v_spacing[i]; vtk2vox(i, 3) = -v_origin[i] / v_spacing[i]; } return vox2nii * vtk2vox; } static bool WarpVTKPolyDataMultiTransform_ParseInput(int argc, char * *argv, char *& input_vtk_filename, char *& output_vtk_filename, char *& reference_image_filename, TRAN_OPT_QUEUE & opt_queue) { opt_queue.clear(); opt_queue.reserve(argc - 2); input_vtk_filename = argv[0]; output_vtk_filename = argv[1]; reference_image_filename = NULL; int ind = 2; while( ind < argc ) { if( strcmp(argv[ind], "-R") == 0 ) { ind++; if( ind >= argc ) { return false; } reference_image_filename = argv[ind]; } else if( strcmp(argv[ind], "-i") == 0 ) { ind++; if( ind >= argc ) { return false; } TRAN_OPT opt; opt.filename = argv[ind]; if( CheckFileType(opt.filename) != AFFINE_FILE ) { std::cout << "file: " << opt.filename << " is not an affine .txt file. Invalid to use '-i' " << std::endl; return false; } opt.file_type = AFFINE_FILE; opt.do_affine_inv = true; opt_queue.push_back(opt); } else { TRAN_OPT opt; opt.filename = argv[ind]; opt.file_type = CheckFileType(opt.filename); opt.do_affine_inv = false; opt_queue.push_back(opt); } ind++; } // if (reference_image_filename == NULL) { // std::cout << "the reference image file (-R) must be given!!!" // << std::endl; // return false; // } return true; } template void WarpLabeledPointSetFileMultiTransform(char *input_vtk_filename, char *output_vtk_filename, char *reference_image_filename, TRAN_OPT_QUEUE & opt_queue) { typedef itk::Image ImageType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::MatrixOffsetTransformBase AffineTransformType; // typedef itk::WarpImageMultiTransformFilter // WarperType; typedef itk::DisplacementFieldFromMultiTransformFilter WarperType; typedef itk::LinearInterpolateImageFunction FuncType; itk::TransformFactory::RegisterTransform(); typedef itk::ImageFileReader ImageFileReaderType; typename ImageFileReaderType::Pointer reader_img = ImageFileReaderType::New(); typename ImageType::Pointer img_ref; typename ImageFileReaderType::Pointer reader_img_ref = ImageFileReaderType::New(); if( reference_image_filename ) { reader_img_ref->SetFileName(reference_image_filename); reader_img_ref->Update(); img_ref = reader_img_ref->GetOutput(); } else { std::cout << "the reference image file (-R) must be given!!!" << std::endl; return; } typename WarperType::Pointer warper = WarperType::New(); // warper->SetInput(img_mov); // warper->SetEdgePaddingValue( 0); VectorType pad; pad.Fill(0); // warper->SetEdgePaddingValue(pad); typedef itk::TransformFileReader TranReaderType; typedef itk::ImageFileReader FieldReaderType; const int kOptQueueSize = opt_queue.size(); for( int i = 0; i < kOptQueueSize; i++ ) { const TRAN_OPT & opt = opt_queue[i]; switch( opt_queue[i].file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); typename AffineTransformType::Pointer aff = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); if( opt_queue[i].do_affine_inv ) { aff->GetInverse(aff); } // std::cout << aff << std::endl; warper->PushBackAffineTransform(aff); break; } case DEFORMATION_FILE: { typename FieldReaderType::Pointer field_reader = FieldReaderType::New(); field_reader->SetFileName(opt.filename); field_reader->Update(); typename DisplacementFieldType::Pointer field = field_reader->GetOutput(); // std::cout << field << std::endl; warper->PushBackDisplacementFieldTransform(field); break; } default: std::cout << "Unknown file type!" << std::endl; } } warper->SetOutputParametersFromImage( img_ref ); std::cout << "output size: " << warper->GetOutputSize() << std::endl; std::cout << "output spacing: " << warper->GetOutputSpacing() << std::endl; // warper->PrintTransformList(); warper->DetermineFirstDeformNoInterp(); warper->Update(); typename DisplacementFieldType::Pointer field_output = DisplacementFieldType::New(); field_output = warper->GetOutput(); /** * Code to read the mesh */ typename ImageType::PointType point; typename ImageType::PointType warpedPoint; typedef itk::Mesh MeshType; // typedef itk::LabeledPointSetFileReader VTKReaderType; vtkPolyDataReader *vtkreader = vtkPolyDataReader::New(); vtkreader->SetFileName( input_vtk_filename ); vtkreader->Update(); vtkPolyData *mesh = vtkreader->GetOutput(); /* typename VTKReaderType::Pointer vtkreader = VTKReaderType::New(); vtkreader->SetFileName( input_vtk_filename ); vtkreader->Update(); typename MeshType::PointsContainerIterator It = vtkreader->GetOutput()->GetPoints()->Begin(); while( It != vtkreader->GetOutput()->GetPoints()->End() ) { point.CastFrom( It.Value() ); */ vnl_matrix_fixed ijk2ras, vtk2ras, lps2ras; vtk2ras = ConstructVTKtoNiftiTransform( field_output->GetDirection().GetVnlMatrix(), field_output->GetOrigin().GetVnlVector(), field_output->GetSpacing().GetVnlVector() ); ijk2ras.set_identity(); vtk2ras.set_identity(); lps2ras.set_identity(); lps2ras(0, 0) = -1; lps2ras(1, 1) = -1; // Set up the transforms ijk2ras = ConstructNiftiSform( field_output->GetDirection().GetVnlMatrix(), field_output->GetOrigin().GetVnlVector(), field_output->GetSpacing().GetVnlVector() ); vtk2ras = ConstructVTKtoNiftiTransform( field_output->GetDirection().GetVnlMatrix(), field_output->GetOrigin().GetVnlVector(), field_output->GetSpacing().GetVnlVector() ); vnl_matrix_fixed ras2ijk = vnl_inverse(ijk2ras); vnl_matrix_fixed ras2vtk = vnl_inverse(vtk2ras); vnl_matrix_fixed ras2lps = vnl_inverse(lps2ras); // Update the coordinates for( int k = 0; k < mesh->GetNumberOfPoints(); k++ ) { // Get the point (in whatever format that it's stored) vnl_vector_fixed x_mesh, x_ras, x_ijk, v_warp, v_ras; vnl_vector_fixed y_ras, y_mesh; x_mesh[0] = mesh->GetPoint(k)[0]; x_mesh[1] = mesh->GetPoint(k)[1]; x_mesh[2] = mesh->GetPoint(k)[2]; x_mesh[3] = 1.0; // Map the point into RAS coordinates // if(parm.mesh_coord == RAS) x_ras = x_mesh; // else if(parm.mesh_coord == LPS) x_ras = lps2ras * x_mesh; // else if(parm.mesh_coord == IJKOS) x_ras = vtk2ras * x_mesh; // else x_ras = ijk2ras * x_mesh; // Map the point to IJK coordinates (continuous index) x_ijk = ras2ijk * x_ras; typename FuncType::ContinuousIndexType ind; ind[0] = x_ijk[0]; ind[1] = x_ijk[1]; ind[2] = x_ijk[2]; field_output->TransformContinuousIndexToPhysicalPoint(ind, point); // std::cout << " point " << point << std::endl; // std::cout << " point-t " << point << std::endl; bool isInside = warper->MultiTransformSinglePoint( point, warpedPoint ); // if ( isInside ) std::cout << " point-w " << warpedPoint << std::endl; if( isInside ) { typename MeshType::PointType newPoint; newPoint.CastFrom( warpedPoint ); // vtkreader->GetOutput()->SetPoint( It.Index(), newPoint ); if( ImageDimension == 3 ) { mesh->GetPoints()->SetPoint(k, warpedPoint[0], warpedPoint[1], warpedPoint[2]); } } // ++It; } std::string fn = std::string(output_vtk_filename); if( fn.rfind(".vtk") == fn.length() - 4 ) { vtkPolyDataWriter *writer = vtkPolyDataWriter::New(); writer->SetFileName(output_vtk_filename); writer->SetInputData(mesh); writer->Update(); } /* typedef itk::LabeledPointSetFileWriter VTKWriterType; typename VTKWriterType::Pointer vtkwriter = VTKWriterType::New(); vtkwriter->SetFileName( output_vtk_filename ); vtkwriter->SetInput( vtkreader->GetOutput() ); vtkwriter->SetMultiComponentScalars( vtkreader->GetMultiComponentScalars() ); vtkwriter->SetLines( vtkreader->GetLines() ); vtkwriter->Update(); */ // std::string filePrefix = output_vtk_filename; // std::string::size_type pos = filePrefix.rfind("."); // std::string extension = std::string(filePrefix, pos, filePrefix.length() // - 1); // filePrefix = std::string(filePrefix, 0, pos); // // std::cout << "output extension is: " << extension << std::endl; // // if (extension != std::string(".mha")) { // typedef itk::VectorImageFileWriter // WriterType; // typename WriterType::Pointer writer = WriterType::New(); // writer->SetFileName(output_vtk_filename); // writer->SetUseAvantsNamingConvention(true); // writer->SetInput(field_output); // writer->Update(); // } else { // typedef itk::ImageFileWriter WriterType; // typename WriterType::Pointer writer = WriterType::New(); // writer->SetFileName(output_vtk_filename); // writer->SetInput(field_output); // writer->Update(); // } } template void ComposeMultiAffine(char * /*input_affine_txt*/, char *output_affine_txt, char *reference_affine_txt, TRAN_OPT_QUEUE & opt_queue) { typedef itk::Image ImageType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef itk::MatrixOffsetTransformBase AffineTransformType; typedef itk::WarpImageMultiTransformFilter WarperType; // typedef itk::DisplacementFieldFromMultiTransformFilter WarperType; itk::TransformFactory::RegisterTransform(); // typedef itk::ImageFileReader ImageFileReaderType; // typename ImageFileReaderType::Pointer reader_img = ImageFileReaderType::New(); // typename ImageType::Pointer img_ref = ImageType::New(); // typename ImageFileReaderType::Pointer reader_img_ref = ImageFileReaderType::New(); typename WarperType::Pointer warper = WarperType::New(); // warper->SetInput(img_mov); // warper->SetEdgePaddingValue( 0); VectorType pad; pad.Fill(0); // warper->SetEdgePaddingValue(pad); typedef itk::TransformFileReader TranReaderType; int cnt_affine = 0; const int kOptQueueSize = opt_queue.size(); for( int i = 0; i < kOptQueueSize; i++ ) { const TRAN_OPT & opt = opt_queue[i]; switch( opt_queue[i].file_type ) { case AFFINE_FILE: { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(opt.filename); tran_reader->Update(); typename AffineTransformType::Pointer aff = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); if( opt_queue[i].do_affine_inv ) { aff->GetInverse(aff); } // std::cout << aff << std::endl; warper->PushBackAffineTransform(aff); cnt_affine++; break; } case DEFORMATION_FILE: { std::cout << "Compose affine only files: ignore " << opt.filename << std::endl; break; } default: std::cout << "Unknown file type!" << std::endl; } } typedef typename AffineTransformType::CenterType PointType; PointType aff_center; typename AffineTransformType::Pointer aff_ref_tmp; if( reference_affine_txt ) { typename TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(reference_affine_txt); tran_reader->Update(); aff_ref_tmp = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); } else { if( cnt_affine > 0 ) { std::cout << "the reference affine file for center is selected as the first affine!" << std::endl; aff_ref_tmp = ( (warper->GetTransformList() ).begin() )->second.aex.aff; } else { std::cout << "No affine input is given. nothing to do ......" << std::endl; return; } } aff_center = aff_ref_tmp->GetCenter(); std::cout << "new center is : " << aff_center << std::endl; // warper->PrintTransformList(); // typename AffineTransformType::Pointer aff_output = warper->ComposeAffineOnlySequence(aff_center); typename AffineTransformType::Pointer aff_output = AffineTransformType::New(); warper->ComposeAffineOnlySequence(aff_center, aff_output); typedef itk::TransformFileWriter TranWriterType; typename TranWriterType::Pointer tran_writer = TranWriterType::New(); tran_writer->SetFileName(output_affine_txt); tran_writer->SetInput(aff_output); tran_writer->Update(); std::cout << "wrote file to : " << output_affine_txt << std::endl; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int WarpVTKPolyDataMultiTransform( std::vector args, std::ostream* ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "WarpVTKPolyDataMultiTransform" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc <= 4 ) { std::cout << "WarpLabeledPointSetFileMultiTransform ImageDimension inputVTKFile " << "outputVTKFile [-R reference_image] " << "{[deformation_field | [-i] affine_transform_txt ]}" << std::endl; return EXIT_FAILURE; } TRAN_OPT_QUEUE opt_queue; // char *moving_image_filename = NULL; char *input_vtk_filename = NULL; char *output_vtk_filename = NULL; char *reference_image_filename = NULL; int kImageDim = atoi(argv[1]); const bool is_parsing_ok = WarpVTKPolyDataMultiTransform_ParseInput(argc - 2, argv + 2, input_vtk_filename, output_vtk_filename, reference_image_filename, opt_queue); if( is_parsing_ok ) { switch( CheckFileType(output_vtk_filename) ) { case DEFORMATION_FILE: { if( reference_image_filename == NULL ) { std::cout << "the reference image file (-R) must be given!!!" << std::endl; return false; } std::cout << "output_vtk_filename: " << output_vtk_filename << std::endl; std::cout << "reference_image_filename: "; if( reference_image_filename ) { std::cout << reference_image_filename << std::endl; } else { std::cout << "NULL" << std::endl; } DisplayOptQueue(opt_queue); switch( kImageDim ) { case 2: { WarpLabeledPointSetFileMultiTransform<2>(input_vtk_filename, output_vtk_filename, reference_image_filename, opt_queue); break; } case 3: { WarpLabeledPointSetFileMultiTransform<3>(input_vtk_filename, output_vtk_filename, reference_image_filename, opt_queue); break; } } break; } case AFFINE_FILE: { std::cout << "output_affine_txt: " << output_vtk_filename << std::endl; std::cout << "reference_affine_txt: "; if( reference_image_filename ) { std::cout << reference_image_filename << std::endl; } else { std::cout << "NULL" << std::endl; } DisplayOptQueue(opt_queue); switch( kImageDim ) { case 2: { ComposeMultiAffine<2>(input_vtk_filename, output_vtk_filename, reference_image_filename, opt_queue); break; } case 3: { ComposeMultiAffine<3>(input_vtk_filename, output_vtk_filename, reference_image_filename, opt_queue); break; } } break; } default: std::cout << "Unknow output file format: " << output_vtk_filename << std::endl; break; } } else { std::cout << "Input error!" << std::endl; } return EXIT_FAILURE; } } // namespace ants ants-2.2.0/Examples/antsAI.cxx000066400000000000000000001753331311104306400161460ustar00rootroot00000000000000#include "antsAllocImage.h" #include "antsCommandLineParser.h" #include "antsUtilities.h" #include "ReadWriteData.h" #include "itkAffineTransform.h" #include "itkConjugateGradientLineSearchOptimizerv4.h" #include "itkEuler2DTransform.h" #include "itkEuler3DTransform.h" #include "itkCorrelationImageToImageMetricv4.h" #include "itkImageMaskSpatialObject.h" #include "itkImageMomentsCalculator.h" #include "itkImageToImageMetricv4.h" #include "itkJointHistogramMutualInformationImageToImageMetricv4.h" #include "itkLandmarkBasedTransformInitializer.h" #include "itkLinearInterpolateImageFunction.h" #include "itkMattesMutualInformationImageToImageMetricv4.h" #include "itkMersenneTwisterRandomVariateGenerator.h" #include "itkMultiScaleLaplacianBlobDetectorImageFilter.h" #include "itkMultiStartOptimizerv4.h" #include "itkRegistrationParameterScalesFromPhysicalShift.h" #include "itkRigid2DTransform.h" #include "itkSimilarity2DTransform.h" #include "itkSimilarity3DTransform.h" #include "itkVersorRigid3DTransform.h" #include "itkTransformFileWriter.h" #include "vnl/vnl_cross.h" #include "vnl/vnl_inverse.h" namespace ants { // ########################################################################## // Transform traits to generalize the different linear transforms // ########################################################################## template class RigidTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template class LandmarkRigidTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class RigidTransformTraits { public: typedef itk::Euler2DTransform TransformType; }; template <> class RigidTransformTraits { public: typedef itk::Euler2DTransform TransformType; }; template <> class RigidTransformTraits { public: // typedef itk::VersorRigid3DTransform TransformType; // typedef itk::QuaternionRigidTransform TransformType; typedef itk::Euler3DTransform TransformType; }; template <> class RigidTransformTraits { public: // typedef itk::VersorRigid3DTransform TransformType; // typedef itk::QuaternionRigidTransform TransformType; typedef itk::Euler3DTransform TransformType; }; template <> class LandmarkRigidTransformTraits { public: typedef itk::Rigid2DTransform TransformType; }; template <> class LandmarkRigidTransformTraits { public: typedef itk::Rigid2DTransform TransformType; }; template <> class LandmarkRigidTransformTraits { public: typedef itk::VersorRigid3DTransform TransformType; }; template <> class LandmarkRigidTransformTraits { public: typedef itk::VersorRigid3DTransform TransformType; }; template class SimilarityTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity2DTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity2DTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity3DTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity3DTransform TransformType; }; // ########################################################################## // ########################################################################## template TReal PatchCorrelation( itk::NeighborhoodIterator fixedNeighborhood, itk::NeighborhoodIterator movingNeighborhood, std::vector activeIndex, std::vector weights, typename TGradientImage::Pointer fixedGradientImage, typename TGradientImage::Pointer movingGradientImage, typename TInterpolator::Pointer movingInterpolator ) { typedef TReal RealType; typedef TImage ImageType; const unsigned int ImageDimension = ImageType::ImageDimension; typedef typename ImageType::PointType PointType; typedef itk::CovariantVector GradientPixelType; typedef vnl_vector VectorType; typedef typename ImageType::IndexType IndexType; unsigned int numberOfIndices = activeIndex.size(); std::vector movingImagePatchPoints; VectorType fixedSamples( numberOfIndices, 0 ); VectorType movingSamples( numberOfIndices, 0 ); vnl_matrix fixedGradientMatrix( numberOfIndices, ImageDimension, 0.0 ); vnl_matrix movingGradientMatrix( numberOfIndices, ImageDimension, 0.0 ); PointType movingPointCentroid( 0.0 ); RealType weight = 1.0 / static_cast( numberOfIndices ); for( unsigned int i = 0; i < numberOfIndices; i++ ) { fixedSamples[i] = fixedNeighborhood.GetPixel( activeIndex[i] ); movingSamples[i] = movingNeighborhood.GetPixel( activeIndex[i] ); IndexType fixedIndex = fixedNeighborhood.GetIndex( activeIndex[i] ); IndexType movingIndex = movingNeighborhood.GetIndex( activeIndex[i] ); if( fixedGradientImage->GetRequestedRegion().IsInside( fixedIndex ) && movingGradientImage->GetRequestedRegion().IsInside( movingIndex ) ) { GradientPixelType fixedGradient = fixedGradientImage->GetPixel( fixedIndex ) * weights[i]; GradientPixelType movingGradient = movingGradientImage->GetPixel( movingIndex ) * weights[i]; for( unsigned int j = 0; j < ImageDimension; j++ ) { fixedGradientMatrix(i, j) = fixedGradient[j]; movingGradientMatrix(i, j) = movingGradient[j]; } PointType movingPoint( 0.0 ); movingGradientImage->TransformIndexToPhysicalPoint( movingIndex, movingPoint ); for( unsigned int d = 0; d < ImageDimension; d++ ) { movingPointCentroid[d] += movingPoint[d] * weight; } movingImagePatchPoints.push_back( movingPoint ); } else { return 0.0; } } fixedSamples -= fixedSamples.mean(); movingSamples -= movingSamples.mean(); RealType fixedSd = std::sqrt( fixedSamples.squared_magnitude() ); // compute patch orientation vnl_matrix fixedCovariance = fixedGradientMatrix.transpose() * fixedGradientMatrix; vnl_matrix movingCovariance = movingGradientMatrix.transpose() * movingGradientMatrix; vnl_symmetric_eigensystem fixedImagePrincipalAxes( fixedCovariance ); vnl_symmetric_eigensystem movingImagePrincipalAxes( movingCovariance ); vnl_vector fixedPrimaryEigenVector; vnl_vector fixedSecondaryEigenVector; vnl_vector fixedTertiaryEigenVector; vnl_vector movingPrimaryEigenVector; vnl_vector movingSecondaryEigenVector; vnl_matrix B; if( ImageDimension == 2 ) { fixedPrimaryEigenVector = fixedImagePrincipalAxes.get_eigenvector( 1 ); movingPrimaryEigenVector = movingImagePrincipalAxes.get_eigenvector( 1 ); B = outer_product( movingPrimaryEigenVector, fixedPrimaryEigenVector ); } else if( ImageDimension == 3 ) { fixedPrimaryEigenVector = fixedImagePrincipalAxes.get_eigenvector( 2 ); fixedSecondaryEigenVector = fixedImagePrincipalAxes.get_eigenvector( 1 ); movingPrimaryEigenVector = movingImagePrincipalAxes.get_eigenvector( 2 ); movingSecondaryEigenVector = movingImagePrincipalAxes.get_eigenvector( 1 ); B = outer_product( movingPrimaryEigenVector, fixedPrimaryEigenVector ) + outer_product( movingSecondaryEigenVector, fixedSecondaryEigenVector ); } vnl_svd wahba( B ); vnl_matrix A = wahba.V() * wahba.U().transpose(); bool patchIsInside = true; for( unsigned int i = 0; i < numberOfIndices; i++ ) { PointType movingImagePoint = movingImagePatchPoints[i]; vnl_vector movingImageVector( ImageDimension, 0.0 ); for( unsigned int d = 0; d < ImageDimension; d++ ) { movingImagePoint[d] -= movingPointCentroid[d]; movingImageVector[d] = movingImagePoint[d]; } vnl_vector movingImagePointRotated = A * movingImageVector; for( unsigned int d = 0; d < ImageDimension; d++ ) { movingImagePoint[d] = movingImagePointRotated[d] + movingPointCentroid[d]; } if( movingInterpolator->IsInsideBuffer( movingImagePoint ) ) { movingSamples[i] = movingInterpolator->Evaluate( movingImagePoint ); } else { patchIsInside = false; break; } } RealType correlation = 0.0; if( patchIsInside ) { movingSamples -= movingSamples.mean(); RealType movingSd = std::sqrt( movingSamples.squared_magnitude() ); correlation = inner_product( fixedSamples, movingSamples ) / ( fixedSd * movingSd ); if( vnl_math_isnan( correlation ) || vnl_math_isinf( correlation ) ) { correlation = 0.0; } } return correlation; } template void GetBlobCorrespondenceMatrix( typename TImage::Pointer fixedImage, typename TImage::Pointer movingImage, typename TBlobFilter::BlobsListType fixedBlobs, typename TBlobFilter::BlobsListType movingBlobs, float sigma, unsigned int radiusValue, vnl_matrix & correspondenceMatrix ) { typedef TImage ImageType; typedef float RealType; typedef typename ImageType::IndexType IndexType; typedef itk::NeighborhoodIterator NeighborhoodIteratorType; const unsigned int ImageDimension = ImageType::ImageDimension; typedef itk::CovariantVector GradientVectorType; typedef itk::Image GradientImageType; typedef itk::GradientRecursiveGaussianImageFilter GradientImageFilterType; typedef typename GradientImageFilterType::Pointer GradientImageFilterPointer; typename GradientImageFilterType::Pointer fixedGradientFilter = GradientImageFilterType::New(); fixedGradientFilter->SetInput( fixedImage ); fixedGradientFilter->SetSigma( sigma ); typename GradientImageType::Pointer fixedGradientImage = fixedGradientFilter->GetOutput(); fixedGradientImage->Update(); fixedGradientImage->DisconnectPipeline(); GradientImageFilterPointer movingGradientFilter = GradientImageFilterType::New(); movingGradientFilter->SetInput( movingImage ); movingGradientFilter->SetSigma( sigma ); typename GradientImageType::Pointer movingGradientImage = movingGradientFilter->GetOutput(); movingGradientImage->Update(); movingGradientImage->DisconnectPipeline(); correspondenceMatrix.set_size( fixedBlobs.size(), movingBlobs.size() ); correspondenceMatrix.fill( 0 ); typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( radiusValue ); NeighborhoodIteratorType ItF( radius, fixedImage, fixedImage->GetLargestPossibleRegion() ); NeighborhoodIteratorType ItM( radius, movingImage, movingImage->GetLargestPossibleRegion() ); IndexType zeroIndex; zeroIndex.Fill( radiusValue ); ItF.SetLocation( zeroIndex ); std::vector activeIndex; std::vector weights; RealType weightSum = 0.0; for( unsigned int i = 0; i < ItF.Size(); i++ ) { IndexType index = ItF.GetIndex( i ); RealType distance = 0.0; for( unsigned int j = 0; j < ImageDimension; j++ ) { distance += vnl_math_sqr( index[j] - zeroIndex[j] ); } distance = std::sqrt( distance ); if( distance <= radiusValue ) { activeIndex.push_back( i ); RealType weight = std::exp( -1.0 * distance / vnl_math_sqr( radiusValue ) ); weights.push_back( weight ); weightSum += ( weight ); } } for( unsigned int i = 0; i < weights.size(); i++ ) { weights[i] /= weightSum; } typedef itk::LinearInterpolateImageFunction ScalarInterpolatorType; typename ScalarInterpolatorType::Pointer movingInterpolator = ScalarInterpolatorType::New(); movingInterpolator->SetInputImage( movingImage ); for( unsigned int i = 0; i < fixedBlobs.size(); i++ ) { IndexType fixedIndex = fixedBlobs[i]->GetCenter(); if( fixedImage->GetPixel( fixedIndex ) > 1.0e-4 ) { ItF.SetLocation( fixedIndex ); for( unsigned int j = 0; j < movingBlobs.size(); j++ ) { IndexType movingIndex = movingBlobs[j]->GetCenter(); if( movingImage->GetPixel( movingIndex ) > 1.0e-4 ) { ItM.SetLocation( movingIndex ); RealType correlation = PatchCorrelation ( ItF, ItM, activeIndex, weights, fixedGradientImage, movingGradientImage, movingInterpolator ); if( correlation < 0.0 ) { correlation = 0.0; } correspondenceMatrix(i, j) = correlation; } } } } return; } template typename TTransform::Pointer GetTransformFromFeatureMatching( typename TImage::Pointer fixedImage, typename TImage::Pointer movingImage, unsigned int numberOfBlobsToExtract, unsigned int numberOfBlobsToMatch ) { typedef float RealType; typedef TImage ImageType; typedef TTransform TransformType; typedef typename ImageType::PointType PointType; ///////////////////////////////////////////////////////////////// // // Values taken from Brian in ImageMath.cxx // ///////////////////////////////////////////////////////////////// unsigned int radiusValue = 20; RealType gradientSigma = 1.0; RealType maxRadiusDifferenceAllowed = 0.25; RealType distancePreservationThreshold = 0.1; RealType minimumNumberOfNeighborhoodNodes = 3; RealType minScale = std::pow( 1.0, 1.0 ); RealType maxScale = std::pow( 2.0, 10.0 ); unsigned int stepsPerOctave = 10; /////////////////////////////////////////////////////////////// typedef itk::MultiScaleLaplacianBlobDetectorImageFilter BlobFilterType; typedef typename BlobFilterType::BlobPointer BlobPointer; typedef std::pair BlobPairType; typedef typename BlobFilterType::BlobsListType BlobsListType; typename BlobFilterType::Pointer blobFixedImageFilter = BlobFilterType::New(); blobFixedImageFilter->SetStartT( minScale ); blobFixedImageFilter->SetEndT( maxScale ); blobFixedImageFilter->SetStepsPerOctave( stepsPerOctave ); blobFixedImageFilter->SetNumberOfBlobs( numberOfBlobsToExtract ); blobFixedImageFilter->SetInput( fixedImage ); blobFixedImageFilter->Update(); BlobsListType fixedImageBlobs = blobFixedImageFilter->GetBlobs(); typename BlobFilterType::Pointer blobMovingImageFilter = BlobFilterType::New(); blobMovingImageFilter->SetStartT( minScale ); blobMovingImageFilter->SetEndT( maxScale ); blobMovingImageFilter->SetStepsPerOctave( stepsPerOctave ); blobMovingImageFilter->SetNumberOfBlobs( numberOfBlobsToExtract ); blobMovingImageFilter->SetInput( movingImage ); blobMovingImageFilter->Update(); BlobsListType movingImageBlobs = blobMovingImageFilter->GetBlobs(); if( movingImageBlobs.empty() || fixedImageBlobs.empty() ) { std::cerr << "The moving image or fixed image blobs list is empty." << std::endl; return ITK_NULLPTR; } vnl_matrix correspondenceMatrix; ///////////////////////////////////////////////////////////////// // // Get the correspondence matrix based on local image patches // ///////////////////////////////////////////////////////////////// GetBlobCorrespondenceMatrix ( fixedImage, movingImage, fixedImageBlobs, movingImageBlobs, gradientSigma, radiusValue, correspondenceMatrix ); ///////////////////////////////////////////////////////////////// // // Pick the first numberOfBlobsToMatch pairs based on the correlation matrix // ///////////////////////////////////////////////////////////////// std::vector blobPairs; BlobPointer bestBlob = ITK_NULLPTR; unsigned int matchPoint = 1; unsigned int fixedCount = 0; while( ( matchPoint <= numberOfBlobsToMatch ) && ( fixedCount < fixedImageBlobs.size() ) ) { unsigned int maxPair = correspondenceMatrix.arg_max(); unsigned int maxRow = static_cast( maxPair / correspondenceMatrix.cols() ); unsigned int maxCol = maxPair - maxRow * correspondenceMatrix.cols(); BlobPointer fixedBlob = fixedImageBlobs[maxRow]; bestBlob = movingImageBlobs[maxCol]; if( bestBlob && bestBlob->GetObjectRadius() > 1 ) { if( std::fabs( bestBlob->GetObjectRadius() - fixedBlob->GetObjectRadius() ) < maxRadiusDifferenceAllowed ) { if( ( fixedImage->GetPixel( fixedBlob->GetCenter() ) > 1.0e-4 ) && ( movingImage->GetPixel( bestBlob->GetCenter() ) > 1.0e-4 ) ) { BlobPairType blobPairing = std::make_pair( fixedBlob, bestBlob ); blobPairs.push_back( blobPairing ); matchPoint++; } } } correspondenceMatrix.set_row( maxRow, correspondenceMatrix.get_row( 0 ).fill( 0 ) ); correspondenceMatrix.set_column( maxCol, correspondenceMatrix.get_column( 0 ).fill( 0 ) ); fixedCount++; } ///////////////////////////////////////////////////////////////// // // For every blob pair compute the relative distance from its // neighbors in both the fixed and moving images. // ///////////////////////////////////////////////////////////////// vnl_matrix distanceRatios( blobPairs.size(), blobPairs.size(), 0.0 ); for( unsigned int i = 0; i < blobPairs.size(); i++ ) { PointType fixedPoint( 0.0 ); fixedImage->TransformIndexToPhysicalPoint( blobPairs[i].first->GetCenter(), fixedPoint ); PointType movingPoint( 0.0 ); movingImage->TransformIndexToPhysicalPoint( blobPairs[i].second->GetCenter(), movingPoint ); for( unsigned int j = 0; j < blobPairs.size(); j++ ) { PointType fixedNeighborPoint( 0.0 ); fixedImage->TransformIndexToPhysicalPoint( blobPairs[j].first->GetCenter(), fixedNeighborPoint ); PointType movingNeighborPoint( 0.0 ); movingImage->TransformIndexToPhysicalPoint( blobPairs[j].second->GetCenter(), movingNeighborPoint ); distanceRatios(i, j) = distanceRatios(j, i) = fixedNeighborPoint.SquaredEuclideanDistanceTo( movingNeighborPoint ) / fixedPoint.SquaredEuclideanDistanceTo( movingPoint ); } } ///////////////////////////////////////////////////////////////// // // Keep those blob pairs which have close to the same // distance in both the fixed and moving images. // ///////////////////////////////////////////////////////////////// typename TransformType::Pointer transform = TransformType::New(); typedef itk::LandmarkBasedTransformInitializer TransformInitializerType; typedef typename TransformInitializerType::LandmarkPointContainer PointsContainerType; PointsContainerType fixedLandmarks; PointsContainerType movingLandmarks; for( unsigned int i = 0; i < blobPairs.size(); i++ ) { unsigned int neighborhoodCount = 0; for( unsigned int j = 0; j < blobPairs.size(); j++ ) { if( ( j != i ) && ( std::fabs( distanceRatios( i, j ) - 1.0 ) < distancePreservationThreshold ) ) { neighborhoodCount++; } } if( neighborhoodCount >= minimumNumberOfNeighborhoodNodes ) { PointType fixedPoint( 0.0 ); fixedImage->TransformIndexToPhysicalPoint( blobPairs[i].first->GetCenter(), fixedPoint ); fixedLandmarks.push_back( fixedPoint ); PointType movingPoint( 0.0 ); movingImage->TransformIndexToPhysicalPoint( blobPairs[i].second->GetCenter(), movingPoint ); movingLandmarks.push_back( movingPoint ); } } ///////////////////////////////////////////////////////////////// // // Compute initial transform from the landmarks // ///////////////////////////////////////////////////////////////// typename TransformInitializerType::Pointer transformInitializer = TransformInitializerType::New(); transformInitializer->SetFixedLandmarks( fixedLandmarks ); transformInitializer->SetMovingLandmarks( movingLandmarks ); transformInitializer->SetTransform( transform ); transformInitializer->InitializeTransform(); return transform; } template int antsAI( itk::ants::CommandLineParser *parser ) { typedef double RealType; typedef float PixelType; typedef float FloatType; typedef itk::Vector VectorType; typedef itk::Image ImageType; typedef itk::AffineTransform AffineTransformType; typedef typename RigidTransformTraits::TransformType RigidTransformType; typedef typename SimilarityTransformTraits::TransformType SimilarityTransformType; typedef typename LandmarkRigidTransformTraits::TransformType LandmarkRigidTransformType; enum SamplingStrategyType { NONE, REGULAR, RANDOM }; bool verbose = false; itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } ///////////////////////////////////////////////////////////////// // // Read in the metric type and fixed/moving images // ///////////////////////////////////////////////////////////////// typename ImageType::Pointer fixedImage; typename ImageType::Pointer movingImage; std::string metric; unsigned int numberOfBins = 32; SamplingStrategyType samplingStrategy = NONE; RealType samplingPercentage = 1.0; itk::ants::CommandLineParser::OptionType::Pointer metricOption = parser->GetOption( "metric" ); if( metricOption && metricOption->GetNumberOfFunctions() ) { metric = metricOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( metric ); if( metricOption->GetFunction( 0 )->GetNumberOfParameters() < 2 ) { if( verbose ) { std::cerr << "Fixed and/or moving images not specified." << std::endl; } return EXIT_FAILURE; } ReadImage( fixedImage, ( metricOption->GetFunction( 0 )->GetParameter( 0 ) ).c_str() ); ReadImage( movingImage, ( metricOption->GetFunction( 0 )->GetParameter( 1 ) ).c_str() ); if( metricOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { numberOfBins = parser->Convert( metricOption->GetFunction( 0 )->GetParameter( 2 ) ); } if( metricOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { std::string samplingStrategyString = metricOption->GetFunction( 0 )->GetParameter( 3 ); ConvertToLowerCase( samplingStrategyString ); if( std::strcmp( samplingStrategyString.c_str(), "none" ) == 0 ) { samplingStrategy = NONE; } else if( std::strcmp( samplingStrategyString.c_str(), "regular" ) == 0 ) { samplingStrategy = REGULAR; } else if( std::strcmp( samplingStrategyString.c_str(), "random" ) == 0 ) { samplingStrategy = RANDOM; } else { if( verbose ) { std::cerr << "Unrecognized sampling strategy." << std::endl; } return EXIT_FAILURE; } } if( metricOption->GetFunction( 0 )->GetNumberOfParameters() > 4 ) { samplingPercentage = parser->Convert( metricOption->GetFunction( 0 )->GetParameter( 4 ) ); } } else { std::cerr << "Input metric not specified." << std::endl; return EXIT_FAILURE; } ///////////////////////////////////////////////////////////////// // // Read in convergence options // ///////////////////////////////////////////////////////////////// unsigned int numberOfIterations = 0; unsigned int convergenceWindowSize = 5; RealType convergenceThreshold = 1e-6; itk::ants::CommandLineParser::OptionType::Pointer convergenceOption = parser->GetOption( "convergence" ); if( convergenceOption && convergenceOption->GetNumberOfFunctions() ) { if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { numberOfIterations = parser->Convert( convergenceOption->GetFunction( 0 )->GetName() ); } else { numberOfIterations = parser->Convert( convergenceOption->GetFunction( 0 )->GetParameter( 0 ) ); if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { convergenceThreshold = parser->Convert( convergenceOption->GetFunction( 0 )->GetParameter( 1 ) ); } if( convergenceOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { convergenceWindowSize = parser->Convert( convergenceOption->GetFunction( 0 )->GetParameter( 2 ) ); } } } ///////////////////////////////////////////////////////////////// // // Get the transform // ///////////////////////////////////////////////////////////////// std::string transform = ""; std::string outputTransformTypeName = ""; RealType learningRate = 0.1; RealType searchFactor = 10.0 * vnl_math::pi / 180.0; RealType arcFraction = 1.0; itk::ants::CommandLineParser::OptionType::Pointer searchFactorOption = parser->GetOption( "search-factor" ); if( searchFactorOption && searchFactorOption->GetNumberOfFunctions() ) { if( searchFactorOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { searchFactor = parser->Convert( searchFactorOption->GetFunction( 0 )->GetName() ) * vnl_math::pi / 180.0; } if( searchFactorOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { searchFactor = parser->Convert( searchFactorOption->GetFunction( 0 )->GetParameter( 0 ) ) * vnl_math::pi / 180.0; } if( searchFactorOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { arcFraction = parser->Convert( searchFactorOption->GetFunction( 0 )->GetParameter( 1 ) ); } } itk::ants::CommandLineParser::OptionType::Pointer transformOption = parser->GetOption( "transform" ); if( transformOption && transformOption->GetNumberOfFunctions() ) { transform = transformOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( transform ); if( transformOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { learningRate = parser->Convert( transformOption->GetFunction( 0 )->GetParameter( 0 ) ); } } typename AffineTransformType::Pointer affineSearchTransform = AffineTransformType::New(); typename RigidTransformType::Pointer rigidSearchTransform = RigidTransformType::New(); typename SimilarityTransformType::Pointer similaritySearchTransform = SimilarityTransformType::New(); unsigned int numberOfTransformParameters = 0; if( strcmp( transform.c_str(), "affine" ) == 0 ) { numberOfTransformParameters = AffineTransformType::ParametersDimension; outputTransformTypeName = std::string( "Affine" ); } else if( strcmp( transform.c_str(), "rigid" ) == 0 ) { numberOfTransformParameters = RigidTransformType::ParametersDimension; outputTransformTypeName = std::string( "Rigid" ); } else if( strcmp( transform.c_str(), "similarity" ) == 0 ) { numberOfTransformParameters = SimilarityTransformType::ParametersDimension; outputTransformTypeName = std::string( "Similarity" ); } else { if( verbose ) { std::cerr << "Unrecognized transform option." << std::endl; } return EXIT_FAILURE; } ///////////////////////////////////////////////////////////////// // // Align the images using center of mass and principal axes // or feature blobs. // ///////////////////////////////////////////////////////////////// itk::Vector axis1( 0.0 ); itk::Vector axis2( 0.0 ); axis1[0] = 1.0; axis2[1] = 1.0; RealType bestScale = 1.0; typename AffineTransformType::Pointer initialTransform = AffineTransformType::New(); initialTransform->SetIdentity(); const unsigned int minimumNumberOfBlobs = 3; // should a different min number of blobs be expected? unsigned int numberOfBlobsToExtract = 0; unsigned int numberOfBlobsToMatch = 0; itk::ants::CommandLineParser::OptionType::Pointer blobsOption = parser->GetOption( "align-blobs" ); if( blobsOption && blobsOption->GetNumberOfFunctions() ) { if( blobsOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { numberOfBlobsToExtract = parser->Convert( blobsOption->GetFunction( 0 )->GetName() ); numberOfBlobsToMatch = numberOfBlobsToExtract; } if( blobsOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { numberOfBlobsToExtract = parser->Convert( blobsOption->GetFunction( 0 )->GetParameter( 0 ) ); numberOfBlobsToMatch = numberOfBlobsToExtract; } if( blobsOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { numberOfBlobsToMatch = parser->Convert( blobsOption->GetFunction( 0 )->GetParameter( 1 ) ); } if( numberOfBlobsToExtract < minimumNumberOfBlobs ) { std::cerr << "Please specify a greater number of blobs (>=" << minimumNumberOfBlobs << ")." << std::endl; return EXIT_FAILURE; } } if( numberOfBlobsToExtract >= minimumNumberOfBlobs ) { if( strcmp( transform.c_str(), "affine" ) == 0 ) { typename AffineTransformType::Pointer initialAffineTransform = GetTransformFromFeatureMatching( fixedImage, movingImage, numberOfBlobsToExtract, numberOfBlobsToMatch ); initialTransform->SetOffset( initialAffineTransform->GetOffset() ); initialTransform->SetMatrix( initialAffineTransform->GetMatrix() ); } else // RigidTransform or SimilarityTransform { typename LandmarkRigidTransformType::Pointer initialRigidTransform = GetTransformFromFeatureMatching( fixedImage, movingImage, numberOfBlobsToExtract, numberOfBlobsToMatch ); initialTransform->SetOffset( initialRigidTransform->GetOffset() ); initialTransform->SetMatrix( initialRigidTransform->GetMatrix() ); } } else { bool doAlignPrincipalAxes = false; itk::ants::CommandLineParser::OptionType::Pointer axesOption = parser->GetOption( "align-principal-axes" ); if( axesOption && axesOption->GetNumberOfFunctions() ) { doAlignPrincipalAxes = parser->Convert( axesOption->GetFunction( 0 )->GetName() ); } typedef typename itk::ImageMomentsCalculator ImageMomentsCalculatorType; typedef typename ImageMomentsCalculatorType::MatrixType MatrixType; typename ImageMomentsCalculatorType::Pointer fixedImageMomentsCalculator = ImageMomentsCalculatorType::New(); typename ImageMomentsCalculatorType::Pointer movingImageMomentsCalculator = ImageMomentsCalculatorType::New(); fixedImageMomentsCalculator->SetImage( fixedImage ); fixedImageMomentsCalculator->Compute(); VectorType fixedImageCenterOfGravity = fixedImageMomentsCalculator->GetCenterOfGravity(); MatrixType fixedImagePrincipalAxes = fixedImageMomentsCalculator->GetPrincipalAxes(); movingImageMomentsCalculator->SetImage( movingImage ); movingImageMomentsCalculator->Compute(); VectorType movingImageCenterOfGravity = movingImageMomentsCalculator->GetCenterOfGravity(); MatrixType movingImagePrincipalAxes = movingImageMomentsCalculator->GetPrincipalAxes(); // RealType bestScale = 1.0; // movingImageMomentsCalculator->GetTotalMass() / fixedImageMomentsCalculator->GetTotalMass(); typename AffineTransformType::OutputVectorType translation; itk::Point center; for( unsigned int i = 0; i < ImageDimension; i++ ) { translation[i] = movingImageCenterOfGravity[i] - fixedImageCenterOfGravity[i]; center[i] = fixedImageCenterOfGravity[i]; } initialTransform->SetTranslation( translation ); /** Solve Wahba's problem --- http://en.wikipedia.org/wiki/Wahba%27s_problem */ vnl_vector fixedPrimaryEigenVector; vnl_vector fixedSecondaryEigenVector; vnl_vector fixedTertiaryEigenVector; vnl_vector movingPrimaryEigenVector; vnl_vector movingSecondaryEigenVector; vnl_matrix B; if( ImageDimension == 2 ) { fixedPrimaryEigenVector = fixedImagePrincipalAxes.GetVnlMatrix().get_row( 1 ); movingPrimaryEigenVector = movingImagePrincipalAxes.GetVnlMatrix().get_row( 1 ); B = outer_product( movingPrimaryEigenVector, fixedPrimaryEigenVector ); } else if( ImageDimension == 3 ) { fixedPrimaryEigenVector = fixedImagePrincipalAxes.GetVnlMatrix().get_row( 2 ); fixedSecondaryEigenVector = fixedImagePrincipalAxes.GetVnlMatrix().get_row( 1 ); movingPrimaryEigenVector = movingImagePrincipalAxes.GetVnlMatrix().get_row( 2 ); movingSecondaryEigenVector = movingImagePrincipalAxes.GetVnlMatrix().get_row( 1 ); B = outer_product( movingPrimaryEigenVector, fixedPrimaryEigenVector ) + outer_product( movingSecondaryEigenVector, fixedSecondaryEigenVector ); } if( doAlignPrincipalAxes ) { vnl_svd wahba( B ); vnl_matrix A = wahba.V() * wahba.U().transpose(); A = vnl_inverse( A ); RealType det = vnl_determinant( A ); if( det < 0.0 ) { if( verbose ) { std::cout << "Bad determinant = " << det << std::endl; std::cout << " det( V ) = " << vnl_determinant( wahba.V() ) << std::endl; std::cout << " det( U ) = " << vnl_determinant( wahba.U() ) << std::endl; } vnl_matrix I( A ); I.set_identity(); for( unsigned int i = 0; i < ImageDimension; i++ ) { if( A( i, i ) < 0.0 ) { I( i, i ) = -1.0; } } A = A * I.transpose(); det = vnl_determinant( A ); if( verbose ) { std::cout << "New determinant = " << det << std::endl; } } initialTransform->SetMatrix( A ); } initialTransform->SetCenter( center ); if( ImageDimension == 2 ) { fixedTertiaryEigenVector = fixedSecondaryEigenVector; fixedSecondaryEigenVector = fixedPrimaryEigenVector; } if( ImageDimension == 3 ) { fixedTertiaryEigenVector = vnl_cross_3d( fixedPrimaryEigenVector, fixedSecondaryEigenVector ); } for( unsigned int d = 0; d < ImageDimension; d++ ) { axis1[d] = fixedTertiaryEigenVector[d]; axis2[d] = fixedSecondaryEigenVector[d]; } } ///////////////////////////////////////////////////////////////// // // Write the output if the number of iterations == 0 // ///////////////////////////////////////////////////////////////// if( numberOfIterations == 0 ) { itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { std::string outputName = std::string( "" ); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { outputName = outputOption->GetFunction( 0 )->GetName(); } else { outputName = outputOption->GetFunction( 0 )->GetParameter( 0 ); } typedef itk::TransformFileWriter TransformWriterType; typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); if( strcmp( transform.c_str(), "affine" ) == 0 ) { typename AffineTransformType::Pointer bestAffineTransform = AffineTransformType::New(); bestAffineTransform->SetCenter( initialTransform->GetCenter() ); bestAffineTransform->SetMatrix( initialTransform->GetMatrix() ); bestAffineTransform->SetOffset( initialTransform->GetOffset() ); transformWriter->SetInput( bestAffineTransform ); } else if( strcmp( transform.c_str(), "rigid" ) == 0 ) { typename RigidTransformType::Pointer bestRigidTransform = RigidTransformType::New(); bestRigidTransform->SetCenter( initialTransform->GetCenter() ); bestRigidTransform->SetMatrix( initialTransform->GetMatrix() ); bestRigidTransform->SetOffset( initialTransform->GetOffset() ); transformWriter->SetInput( bestRigidTransform ); } else if( strcmp( transform.c_str(), "similarity" ) == 0 ) { typename SimilarityTransformType::Pointer bestSimilarityTransform = SimilarityTransformType::New(); bestSimilarityTransform->SetCenter( initialTransform->GetCenter() ); bestSimilarityTransform->SetMatrix( initialTransform->GetMatrix() ); bestSimilarityTransform->SetOffset( initialTransform->GetOffset() ); transformWriter->SetInput( bestSimilarityTransform ); } transformWriter->SetFileName( outputName.c_str() ); transformWriter->Update(); } return EXIT_SUCCESS; } ///////////////////////////////////////////////////////////////// // // Read in the masks // ///////////////////////////////////////////////////////////////// typedef itk::ImageMaskSpatialObject ImageMaskSpatialObjectType; typedef typename ImageMaskSpatialObjectType::ImageType MaskImageType; typename MaskImageType::Pointer fixedMask = ITK_NULLPTR; typename MaskImageType::Pointer movingMask = ITK_NULLPTR; itk::ants::CommandLineParser::OptionType::Pointer maskOption = parser->GetOption( "masks" ); if( maskOption && maskOption->GetNumberOfFunctions() ) { if( maskOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { ReadImage( fixedMask, ( maskOption->GetFunction( 0 )->GetName() ).c_str() ); } else if( maskOption->GetFunction( 0 )->GetNumberOfParameters() == 1 ) { ReadImage( fixedMask, ( maskOption->GetFunction( 0 )->GetParameter( 0 ) ).c_str() ); } else if( maskOption->GetFunction( 0 )->GetNumberOfParameters() == 2 ) { ReadImage( fixedMask, ( maskOption->GetFunction( 0 )->GetParameter( 0 ) ).c_str() ); ReadImage( movingMask, ( maskOption->GetFunction( 0 )->GetParameter( 1 ) ).c_str() ); } } typename ImageMaskSpatialObjectType::Pointer fixedMaskSpatialObject = ITK_NULLPTR; if( fixedMask.IsNotNull() ) { fixedMaskSpatialObject = ImageMaskSpatialObjectType::New(); fixedMaskSpatialObject->SetImage( const_cast( fixedMask.GetPointer() ) ); } typename ImageMaskSpatialObjectType::Pointer movingMaskSpatialObject = ITK_NULLPTR; if( movingMask.IsNotNull() ) { movingMaskSpatialObject = ImageMaskSpatialObjectType::New(); movingMaskSpatialObject->SetImage( const_cast( movingMask.GetPointer() ) ); } ///////////////////////////////////////////////////////////////// // // Set up the image metric // ///////////////////////////////////////////////////////////////// typedef itk::ImageToImageMetricv4 ImageMetricType; typename ImageMetricType::Pointer imageMetric = ITK_NULLPTR; if( std::strcmp( metric.c_str(), "mattes" ) == 0 ) { if( verbose ) { std::cout << "Using the Mattes MI metric (number of bins = " << numberOfBins << ")" << std::endl; } typedef itk::MattesMutualInformationImageToImageMetricv4 MutualInformationMetricType; typename MutualInformationMetricType::Pointer mutualInformationMetric = MutualInformationMetricType::New(); mutualInformationMetric = mutualInformationMetric; mutualInformationMetric->SetNumberOfHistogramBins( numberOfBins ); mutualInformationMetric->SetUseMovingImageGradientFilter( true ); mutualInformationMetric->SetUseFixedImageGradientFilter( true ); imageMetric = mutualInformationMetric; } else if( std::strcmp( metric.c_str(), "mi" ) == 0 ) { if( verbose ) { std::cout << "Using the joint histogram MI metric (number of bins = " << numberOfBins << ")" << std::endl; } typedef itk::JointHistogramMutualInformationImageToImageMetricv4 MutualInformationMetricType; typename MutualInformationMetricType::Pointer mutualInformationMetric = MutualInformationMetricType::New(); mutualInformationMetric = mutualInformationMetric; mutualInformationMetric->SetNumberOfHistogramBins( numberOfBins ); mutualInformationMetric->SetUseMovingImageGradientFilter( true ); mutualInformationMetric->SetUseFixedImageGradientFilter( true ); mutualInformationMetric->SetVarianceForJointPDFSmoothing( 1.0 ); imageMetric = mutualInformationMetric; } else if( std::strcmp( metric.c_str(), "gc" ) == 0 ) { if( verbose ) { std::cout << "Using the global correlation metric " << std::endl; } typedef itk::CorrelationImageToImageMetricv4 corrMetricType; typename corrMetricType::Pointer corrMetric = corrMetricType::New(); imageMetric = corrMetric; } else { if( verbose ) { std::cerr << "ERROR: Unrecognized metric. " << std::endl; } return EXIT_FAILURE; } imageMetric->SetFixedImage( fixedImage ); imageMetric->SetVirtualDomainFromImage( fixedImage ); imageMetric->SetMovingImage( movingImage ); imageMetric->SetFixedImageMask( fixedMaskSpatialObject ); imageMetric->SetMovingImageMask( movingMaskSpatialObject ); imageMetric->SetUseFixedSampledPointSet( false ); /** Sample the image domain **/ if( samplingStrategy != NONE ) { const typename ImageType::SpacingType oneThirdVirtualSpacing = fixedImage->GetSpacing() / 3.0; typedef typename ImageMetricType::FixedSampledPointSetType MetricSamplePointSetType; typename MetricSamplePointSetType::Pointer samplePointSet = MetricSamplePointSetType::New(); samplePointSet->Initialize(); typedef typename MetricSamplePointSetType::PointType SamplePointType; typedef typename itk::Statistics::MersenneTwisterRandomVariateGenerator RandomizerType; typename RandomizerType::Pointer randomizer = RandomizerType::New(); randomizer->SetSeed( 1234 ); unsigned long index = 0; switch( samplingStrategy ) { case REGULAR: { const unsigned long sampleCount = static_cast( std::ceil( 1.0 / samplingPercentage ) ); unsigned long count = sampleCount; //Start at sampleCount to keep behavior backwards identical, using first element. itk::ImageRegionConstIteratorWithIndex It( fixedImage, fixedImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { if( count == sampleCount ) { count = 0; //Reset counter SamplePointType point; fixedImage->TransformIndexToPhysicalPoint( It.GetIndex(), point ); // randomly perturb the point within a voxel (approximately) for( unsigned int d = 0; d < ImageDimension; d++ ) { point[d] += randomizer->GetNormalVariate() * oneThirdVirtualSpacing[d]; } if( !fixedMaskSpatialObject || fixedMaskSpatialObject->IsInside( point ) ) { samplePointSet->SetPoint( index, point ); ++index; } } ++count; } break; } case RANDOM: { const unsigned long totalVirtualDomainVoxels = fixedImage->GetRequestedRegion().GetNumberOfPixels(); const unsigned long sampleCount = static_cast( static_cast( totalVirtualDomainVoxels ) * samplingPercentage ); itk::ImageRandomConstIteratorWithIndex ItR( fixedImage, fixedImage->GetRequestedRegion() ); ItR.SetNumberOfSamples( sampleCount ); for( ItR.GoToBegin(); !ItR.IsAtEnd(); ++ItR ) { SamplePointType point; fixedImage->TransformIndexToPhysicalPoint( ItR.GetIndex(), point ); // randomly perturb the point within a voxel (approximately) for ( unsigned int d = 0; d < ImageDimension; d++ ) { point[d] += randomizer->GetNormalVariate() * oneThirdVirtualSpacing[d]; } if( !fixedMaskSpatialObject || fixedMaskSpatialObject->IsInside( point ) ) { samplePointSet->SetPoint( index, point ); ++index; } } break; } case NONE: break; } imageMetric->SetFixedSampledPointSet( samplePointSet ); imageMetric->SetUseFixedSampledPointSet( true ); } imageMetric->Initialize(); if( strcmp( transform.c_str(), "affine" ) == 0 ) { imageMetric->SetMovingTransform( affineSearchTransform ); } else if( strcmp( transform.c_str(), "rigid" ) == 0 ) { imageMetric->SetMovingTransform( rigidSearchTransform ); } else if( strcmp( transform.c_str(), "similarity" ) == 0 ) { imageMetric->SetMovingTransform( similaritySearchTransform ); } ///////////////////////////////////////////////////////////////// // // Set up the optimizer // ///////////////////////////////////////////////////////////////// typedef itk::RegistrationParameterScalesFromPhysicalShift RegistrationParameterScalesFromPhysicalShiftType; typename RegistrationParameterScalesFromPhysicalShiftType::Pointer scalesEstimator = RegistrationParameterScalesFromPhysicalShiftType::New(); scalesEstimator->SetMetric( imageMetric ); scalesEstimator->SetTransformForward( true ); typename RegistrationParameterScalesFromPhysicalShiftType::ScalesType movingScales( numberOfTransformParameters ); scalesEstimator->EstimateScales( movingScales ); typedef itk::ConjugateGradientLineSearchOptimizerv4 LocalOptimizerType; typename LocalOptimizerType::Pointer localOptimizer = LocalOptimizerType::New(); localOptimizer->SetLowerLimit( 0 ); localOptimizer->SetUpperLimit( 2 ); localOptimizer->SetEpsilon( 0.1 ); localOptimizer->SetMaximumLineSearchIterations( 10 ); localOptimizer->SetLearningRate( learningRate ); localOptimizer->SetMaximumStepSizeInPhysicalUnits( learningRate ); localOptimizer->SetNumberOfIterations( numberOfIterations ); localOptimizer->SetMinimumConvergenceValue( convergenceThreshold ); localOptimizer->SetConvergenceWindowSize( convergenceWindowSize ); localOptimizer->SetDoEstimateLearningRateOnce( true ); localOptimizer->SetScales( movingScales ); localOptimizer->SetMetric( imageMetric ); typedef itk::MultiStartOptimizerv4 MultiStartOptimizerType; typename MultiStartOptimizerType::Pointer multiStartOptimizer = MultiStartOptimizerType::New(); multiStartOptimizer->SetScales( movingScales ); multiStartOptimizer->SetMetric( imageMetric ); typename MultiStartOptimizerType::ParametersListType parametersList = multiStartOptimizer->GetParametersList(); for( RealType angle1 = ( vnl_math::pi * -arcFraction ); angle1 <= ( vnl_math::pi * arcFraction + 0.000001 ); angle1 += searchFactor ) { if( ImageDimension == 2 ) { affineSearchTransform->SetIdentity(); affineSearchTransform->SetCenter( initialTransform->GetCenter() ); affineSearchTransform->SetMatrix( initialTransform->GetMatrix() ); affineSearchTransform->SetOffset( initialTransform->GetOffset() ); affineSearchTransform->Rotate2D( angle1, 1 ); if( strcmp( transform.c_str(), "affine" ) == 0 ) { affineSearchTransform->Scale( bestScale ); parametersList.push_back( affineSearchTransform->GetParameters() ); } else if( strcmp( transform.c_str(), "rigid" ) == 0 ) { rigidSearchTransform->SetIdentity(); rigidSearchTransform->SetCenter( initialTransform->GetCenter() ); rigidSearchTransform->SetMatrix( affineSearchTransform->GetMatrix() ); rigidSearchTransform->SetOffset( initialTransform->GetOffset() ); parametersList.push_back( rigidSearchTransform->GetParameters() ); } else if( strcmp( transform.c_str(), "similarity" ) == 0 ) { similaritySearchTransform->SetIdentity(); similaritySearchTransform->SetCenter( initialTransform->GetCenter() ); similaritySearchTransform->SetMatrix( affineSearchTransform->GetMatrix() ); similaritySearchTransform->SetOffset( initialTransform->GetOffset() ); similaritySearchTransform->SetScale( bestScale ); similaritySearchTransform->SetScale( bestScale ); parametersList.push_back( similaritySearchTransform->GetParameters() ); } } if( ImageDimension == 3 ) { for( RealType angle2 = ( vnl_math::pi * -arcFraction ); angle2 <= ( vnl_math::pi * arcFraction + 0.000001 ); angle2 += searchFactor ) { affineSearchTransform->SetIdentity(); affineSearchTransform->SetCenter( initialTransform->GetCenter() ); affineSearchTransform->SetOffset( initialTransform->GetOffset() ); affineSearchTransform->SetMatrix( initialTransform->GetMatrix() ); affineSearchTransform->Rotate3D( axis1, angle1, 1 ); affineSearchTransform->Rotate3D( axis2, angle2, 1 ); if( strcmp( transform.c_str(), "affine" ) == 0 ) { affineSearchTransform->Scale( bestScale ); parametersList.push_back( affineSearchTransform->GetParameters() ); } else if( strcmp( transform.c_str(), "rigid" ) == 0 ) { rigidSearchTransform->SetIdentity(); rigidSearchTransform->SetCenter( initialTransform->GetCenter() ); rigidSearchTransform->SetOffset( initialTransform->GetOffset() ); rigidSearchTransform->SetMatrix( affineSearchTransform->GetMatrix() ); parametersList.push_back( rigidSearchTransform->GetParameters() ); } else if( strcmp( transform.c_str(), "similarity" ) == 0 ) { similaritySearchTransform->SetIdentity(); similaritySearchTransform->SetCenter( initialTransform->GetCenter() ); similaritySearchTransform->SetOffset( initialTransform->GetOffset() ); similaritySearchTransform->SetMatrix( affineSearchTransform->GetMatrix() ); similaritySearchTransform->SetScale( bestScale ); parametersList.push_back( similaritySearchTransform->GetParameters() ); } } } } multiStartOptimizer->SetParametersList( parametersList ); multiStartOptimizer->SetLocalOptimizer( localOptimizer ); multiStartOptimizer->StartOptimization(); ///////////////////////////////////////////////////////////////// // // Write the output after convergence // ///////////////////////////////////////////////////////////////// itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { std::string outputName = std::string( "" ); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { outputName = outputOption->GetFunction( 0 )->GetName(); } else { outputName = outputOption->GetFunction( 0 )->GetParameter( 0 ); } typedef itk::TransformFileWriter TransformWriterType; typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); if( strcmp( transform.c_str(), "affine" ) == 0 ) { typename AffineTransformType::Pointer bestAffineTransform = AffineTransformType::New(); bestAffineTransform->SetCenter( initialTransform->GetCenter() ); bestAffineTransform->SetParameters( multiStartOptimizer->GetBestParameters() ); transformWriter->SetInput( bestAffineTransform ); } else if( strcmp( transform.c_str(), "rigid" ) == 0 ) { typename RigidTransformType::Pointer bestRigidTransform = RigidTransformType::New(); bestRigidTransform->SetCenter( initialTransform->GetCenter() ); bestRigidTransform->SetParameters( multiStartOptimizer->GetBestParameters() ); transformWriter->SetInput( bestRigidTransform ); } else if( strcmp( transform.c_str(), "similarity" ) == 0 ) { typename SimilarityTransformType::Pointer bestSimilarityTransform = SimilarityTransformType::New(); bestSimilarityTransform->SetCenter( initialTransform->GetCenter() ); bestSimilarityTransform->SetParameters( multiStartOptimizer->GetBestParameters() ); transformWriter->SetInput( bestSimilarityTransform ); } transformWriter->SetFileName( outputName.c_str() ); transformWriter->Update(); } return EXIT_SUCCESS; } void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, we try to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "These image metrics are available: " ) + std::string( "MI: joint histogram and Mattes: mutual information and GC: global correlation." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "metric" ); option->SetShortName( 'm' ); option->SetUsageOption( 0, "MI[fixedImage,movingImage,,,]" ); option->SetUsageOption( 1, "Mattes[fixedImage,movingImage,,,]" ); option->SetUsageOption( 2, "GC[fixedImage,movingImage,,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Several transform options are available. The gradientStep or " ) + std::string( "learningRate characterizes the gradient descent optimization and is scaled appropriately " ) + std::string( "for each transform using the shift scales estimator. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "transform" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "Rigid[gradientStep]" ); option->SetUsageOption( 1, "Affine[gradientStep]" ); option->SetUsageOption( 2, "Similarity[gradientStep]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Boolean indicating alignment by principal axes. " ) + std::string( "Alternatively, one can align using blobs (see -b option)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "align-principal-axes" ); option->SetShortName( 'p' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Boolean indicating alignment by a set of blobs. " ) + std::string( "Alternatively, one can align using blobs (see -p option)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "align-blobs" ); option->SetShortName( 'b' ); option->SetUsageOption( 0, "numberOfBlobsToExtract" ); option->SetUsageOption( 1, "[numberOfBlobsToExtract,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Incremental search factor (in degrees) which will " ) + std::string( "sample the arc fraction around the principal axis or default axis." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "search-factor" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "searchFactor" ); option->SetUsageOption( 1, "[searchFactor,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Number of iterations." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "convergence" ); option->SetShortName( 'c' ); option->SetUsageOption( 0, "numberOfIterations" ); option->SetUsageOption( 1, "[numberOfIterations,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Image masks to limit voxels considered by the metric." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "masks" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "fixedImageMask" ); option->SetUsageOption( 1, "[fixedImageMask,movingImageMask]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the output transform (output format an ITK .mat file). " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "outputFileName" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } int antsAI( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsAI" ); int argc = args.size(); char** argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ){} ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char** argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); typedef itk::ants::CommandLineParser ParserType; ParserType::Pointer parser = ParserType::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "Program to calculate the optimal" ) + std::string( "linear transform parameters for aligning two images." ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } bool verbose = false; itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } if( argc == 1 ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } unsigned int dimension = 3; ParserType::OptionType::Pointer dimOption = parser->GetOption( "dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { if( verbose ) { std::cerr << "Image dimensionality not specified. See command line option --dimensionality" << std::endl; } return EXIT_FAILURE; } switch( dimension ) { case 2: { return antsAI<2>( parser ); break; } case 3: { return antsAI<3>( parser ); break; } default: { std::cerr << "Unrecognized dimensionality. Please see help menu." << std::endl; return EXIT_FAILURE; } } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsAffineInitializer.cxx000066400000000000000000000532501311104306400212420ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsUtilities.h" #include #include #include "antsAllocImage.h" #include "itkImageMaskSpatialObject.h" #include "itkANTSNeighborhoodCorrelationImageToImageMetricv4.h" #include "itkArray.h" #include "itkGradientImageFilter.h" #include "itkBSplineControlPointImageFilter.h" #include "itkBayesianClassifierImageFilter.h" #include "itkBayesianClassifierInitializationImageFilter.h" #include "itkBilateralImageFilter.h" #include "itkCSVNumericObjectFileWriter.h" #include "itkCastImageFilter.h" #include "itkCompositeValleyFunction.h" #include "itkConjugateGradientLineSearchOptimizerv4.h" #include "itkConnectedComponentImageFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkCorrelationImageToImageMetricv4.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkDistanceToCentroidMembershipFunction.h" #include "itkDanielssonDistanceMapImageFilter.h" #include "itkDemonsImageToImageMetricv4.h" #include "itkExpImageFilter.h" #include "itkExtractImageFilter.h" #include "itkGaussianImageSource.h" #include "itkGradientAnisotropicDiffusionImageFilter.h" #include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" #include "itkHessianRecursiveGaussianImageFilter.h" #include "itkHistogram.h" #include "itkHistogramMatchingImageFilter.h" #include "itkImage.h" #include "itkImageClassifierBase.h" #include "itkImageDuplicator.h" #include "itkImageFileWriter.h" #include "itkImageGaussianModelEstimator.h" #include "itkImageKmeansModelEstimator.h" #include "itkImageMomentsCalculator.h" #include "itkImageRandomConstIteratorWithIndex.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkMattesMutualInformationImageToImageMetricv4.h" #include "itkKdTree.h" #include "itkKdTreeBasedKmeansEstimator.h" #include "itkLabelContourImageFilter.h" #include "itkLabelStatisticsImageFilter.h" #include "itkLabeledPointSetFileReader.h" #include "itkLabeledPointSetFileWriter.h" #include "itkLaplacianRecursiveGaussianImageFilter.h" #include "itkListSample.h" #include "itkMRFImageFilter.h" #include "itkMRIBiasFieldCorrectionFilter.h" #include "itkMaskImageFilter.h" #include "itkMaximumImageFilter.h" #include "itkMedianImageFilter.h" #include "itkMultiplyImageFilter.h" #include "itkMultivariateLegendrePolynomial.h" #include "itkMultiStartOptimizerv4.h" #include "itkNeighborhood.h" #include "itkNeighborhoodAlgorithm.h" #include "itkNeighborhoodIterator.h" #include "itkNormalVariateGenerator.h" #include "itkOptimizerParameterScalesEstimator.h" #include "itkOtsuThresholdImageFilter.h" #include "itkRGBPixel.h" #include "itkRegistrationParameterScalesFromPhysicalShift.h" #include "itkRelabelComponentImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkSampleToHistogramFilter.h" #include "itkScalarImageKmeansImageFilter.h" #include "itkShrinkImageFilter.h" #include "itkSimilarity3DTransform.h" #include "itkSimilarity2DTransform.h" #include "itkSize.h" #include "itkSphereSpatialFunction.h" #include "itkSTAPLEImageFilter.h" #include "itkSubtractImageFilter.h" #include "itkTDistribution.h" #include "itkTimeProbe.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "itkTranslationTransform.h" #include "itkVariableSizeMatrix.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkWeightedCentroidKdTreeGenerator.h" #include "vnl/vnl_matrix_fixed.h" #include "itkTransformFactory.h" #include "itkSurfaceImageCurvature.h" #include "itkMultiScaleLaplacianBlobDetectorImageFilter.h" #include "itkEuler2DTransform.h" #include "itkEuler3DTransform.h" #include "itkCenteredAffineTransform.h" #include "itkCompositeTransform.h" #include #include #include // Here I'm using a map but you could choose even other containers #include #include #include "ReadWriteData.h" #include "TensorFunctions.h" #include "antsMatrixUtilities.h" namespace ants { template class SimilarityTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity2DTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity2DTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity3DTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity3DTransform TransformType; }; template int antsAffineInitializerImp(int argc, char *argv[]) { typedef double RealType; typedef float PixelType; /** Define All Parameters Here */ double pi = vnl_math::pi; // probably a vnl alternative RealType searchfactor = 10; // in degrees, passed by user unsigned int mibins = 32; // for mattes MI metric RealType degtorad = 0.0174532925; // to convert degrees to radians unsigned int localoptimizeriterations = 20; // for local search via conjgrad // piover4 is (+/-) for cross-section of the sphere to multi-start search in increments // of searchfactor ( converted from degrees to radians ). // the search is centered +/- from the principal axis alignment of the images. RealType piover4 = pi / 4; // works in preliminary practical examples in 3D, in 2D use pi. bool useprincaxis = false; typedef typename itk::ImageMaskSpatialObject::ImageType maskimagetype; std::string whichMetric=std::string("MI"); unsigned int localSearchIterations = 20; typedef itk::TransformFileWriter TransformWriterType; typedef itk::Vector VectorType; typedef itk::Image ImageType; typedef typename itk::ImageMomentsCalculator ImageCalculatorType; typedef itk::AffineTransform AffineType; typedef typename ImageCalculatorType::MatrixType MatrixType; if( argc < 2 ) { return 0; } int argct = 2; std::string fn1 = std::string(argv[argct]); argct++; std::string fn2 = std::string(argv[argct]); argct++; std::string outname = std::string(argv[argct]); argct++; if( argc > argct ) { searchfactor = atof( argv[argct] ); argct++; } if( argc > argct ) { RealType temp = atof( argv[argct] ); argct++; if( temp > 1 ) { temp = 1; } if( temp < 0.01 ) { temp = 0.01; } piover4 = pi * temp; } if( argc > argct ) { useprincaxis = atoi( argv[argct] ); argct++; } if( argc > argct ) { localoptimizeriterations = atoi( argv[argct] ); argct++; } typename ImageType::Pointer image1 = ITK_NULLPTR; typename ImageType::Pointer image2 = ITK_NULLPTR; typename maskimagetype::Pointer mask = ITK_NULLPTR; ReadImage(image1, fn1.c_str() ); ReadImage(image2, fn2.c_str() ); std::string maskfn = ""; if( argc > argct ) { maskfn = std::string( argv[argct] ); argct++; ReadImage(mask, maskfn.c_str() ); } searchfactor *= degtorad; // convert degrees to radians VectorType ccg1; VectorType cpm1; MatrixType cpa1; VectorType ccg2; VectorType cpm2; MatrixType cpa2; typename ImageCalculatorType::Pointer calculator1 = ImageCalculatorType::New(); typename ImageCalculatorType::Pointer calculator2 = ImageCalculatorType::New(); calculator1->SetImage( image1 ); calculator2->SetImage( image2 ); typename ImageCalculatorType::VectorType fixed_center; fixed_center.Fill(0); typename ImageCalculatorType::VectorType moving_center; moving_center.Fill(0); try { calculator1->Compute(); fixed_center = calculator1->GetCenterOfGravity(); ccg1 = calculator1->GetCenterOfGravity(); cpm1 = calculator1->GetPrincipalMoments(); cpa1 = calculator1->GetPrincipalAxes(); try { calculator2->Compute(); moving_center = calculator2->GetCenterOfGravity(); ccg2 = calculator2->GetCenterOfGravity(); cpm2 = calculator2->GetPrincipalMoments(); cpa2 = calculator2->GetPrincipalAxes(); } catch( ... ) { std::cerr << " zero image2 error "; fixed_center.Fill(0); } } catch( ... ) { std::cerr << " zero image1 error "; } RealType bestscale = calculator2->GetTotalMass() / calculator1->GetTotalMass(); RealType powlev = 1.0 / static_cast(ImageDimension); bestscale = std::pow( bestscale , powlev ); bestscale=1; unsigned int eigind1 = 1; unsigned int eigind2 = 1; if( ImageDimension == 3 ) { eigind1 = 2; } vnl_vector evec1_2ndary = cpa1.GetVnlMatrix().get_row( eigind2 ); vnl_vector evec1_primary = cpa1.GetVnlMatrix().get_row( eigind1 ); vnl_vector evec2_2ndary = cpa2.GetVnlMatrix().get_row( eigind2 ); vnl_vector evec2_primary = cpa2.GetVnlMatrix().get_row( eigind1 ); /** Solve Wahba's problem --- http://en.wikipedia.org/wiki/Wahba%27s_problem */ vnl_matrix B = outer_product( evec2_primary, evec1_primary ); if( ImageDimension == 3 ) { B = outer_product( evec2_2ndary, evec1_2ndary ) + outer_product( evec2_primary, evec1_primary ); } vnl_svd wahba( B ); vnl_matrix A_solution = wahba.V() * wahba.U().transpose(); A_solution = vnl_inverse( A_solution ); RealType det = vnl_determinant( A_solution ); if( det < 0 ) { std::cerr << " bad det " << det << " v " << vnl_determinant( wahba.V() ) << " u " << vnl_determinant( wahba.U() ) << std::endl; vnl_matrix id( A_solution ); id.set_identity(); for( unsigned int i = 0; i < ImageDimension; i++ ) { if( A_solution( i, i ) < 0 ) { id( i, i ) = -1.0; } } A_solution = A_solution * id.transpose(); std::cerr << " bad det " << det << " v " << vnl_determinant( wahba.V() ) << " u " << vnl_determinant( wahba.U() ) << " new " << vnl_determinant( A_solution ) << std::endl; } typename AffineType::Pointer affine1 = AffineType::New(); // translation to center typename AffineType::OffsetType trans = affine1->GetOffset(); itk::Point trans2; for( unsigned int i = 0; i < ImageDimension; i++ ) { trans[i] = moving_center[i] - fixed_center[i]; trans2[i] = fixed_center[i] * ( 1 ); } affine1->SetIdentity(); affine1->SetOffset( trans ); if( useprincaxis ) { affine1->SetMatrix( A_solution ); } affine1->SetCenter( trans2 ); { typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetInput( affine1 ); transformWriter->SetFileName( outname.c_str() ); transformWriter->Update(); } if( ImageDimension > 3 ) { return EXIT_SUCCESS; } vnl_vector evec_tert; if( ImageDimension == 3 ) { // try to rotate around tertiary and secondary axis evec_tert = vnl_cross_3d( evec1_primary, evec1_2ndary ); } if( ImageDimension == 2 ) { // try to rotate around tertiary and secondary axis evec_tert = evec1_2ndary; evec1_2ndary = evec1_primary; } itk::Vector axis2; itk::Vector axis1; for( unsigned int d = 0; d < ImageDimension; d++ ) { axis1[d] = evec_tert[d]; axis2[d] = evec1_2ndary[d]; } typename AffineType::Pointer affinesearch = AffineType::New(); typedef itk::MultiStartOptimizerv4 OptimizerType; typename OptimizerType::MetricValuesListType metricvalues; typename OptimizerType::Pointer mstartOptimizer = OptimizerType::New(); typedef itk::CorrelationImageToImageMetricv4 GCMetricType; typedef itk::MattesMutualInformationImageToImageMetricv4 MetricType; typename MetricType::ParametersType newparams( affine1->GetParameters() ); typename GCMetricType::Pointer gcmetric = GCMetricType::New(); gcmetric->SetFixedImage( image1 ); gcmetric->SetVirtualDomainFromImage( image1 ); gcmetric->SetMovingImage( image2 ); gcmetric->SetMovingTransform( affinesearch ); gcmetric->SetParameters( newparams ); typename MetricType::Pointer mimetric = MetricType::New(); mimetric->SetNumberOfHistogramBins( mibins ); mimetric->SetFixedImage( image1 ); mimetric->SetMovingImage( image2 ); mimetric->SetMovingTransform( affinesearch ); mimetric->SetParameters( newparams ); if( mask.IsNotNull() ) { typename itk::ImageMaskSpatialObject::Pointer so = itk::ImageMaskSpatialObject::New(); so->SetImage( const_cast( mask.GetPointer() ) ); mimetric->SetFixedImageMask( so ); gcmetric->SetFixedImageMask( so ); } typedef itk::ConjugateGradientLineSearchOptimizerv4 LocalOptimizerType; typename LocalOptimizerType::Pointer localoptimizer = LocalOptimizerType::New(); RealType localoptimizerlearningrate = 0.1; localoptimizer->SetLearningRate( localoptimizerlearningrate ); localoptimizer->SetMaximumStepSizeInPhysicalUnits( localoptimizerlearningrate ); localoptimizer->SetNumberOfIterations( localSearchIterations ); localoptimizer->SetLowerLimit( 0 ); localoptimizer->SetUpperLimit( 2 ); localoptimizer->SetEpsilon( 0.1 ); localoptimizer->SetMaximumLineSearchIterations( 10 ); localoptimizer->SetDoEstimateLearningRateOnce( true ); localoptimizer->SetMinimumConvergenceValue( 1.e-6 ); localoptimizer->SetConvergenceWindowSize( 5 ); if( true ) { typedef typename MetricType::FixedSampledPointSetType PointSetType; typedef typename PointSetType::PointType PointType; typename PointSetType::Pointer pset(PointSetType::New()); unsigned int ind=0; unsigned int ct=0; itk::ImageRegionIteratorWithIndex It(image1, image1->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { // take every N^th point if ( ct % 10 == 0 ) { PointType pt; image1->TransformIndexToPhysicalPoint( It.GetIndex(), pt); pset->SetPoint(ind, pt); ind++; } ct++; } mimetric->SetFixedSampledPointSet( pset ); mimetric->SetUseFixedSampledPointSet( true ); gcmetric->SetFixedSampledPointSet( pset ); gcmetric->SetUseFixedSampledPointSet( true ); } if ( whichMetric.compare("MI") == 0 ) { mimetric->Initialize(); typedef itk::RegistrationParameterScalesFromPhysicalShift RegistrationParameterScalesFromPhysicalShiftType; typename RegistrationParameterScalesFromPhysicalShiftType::Pointer shiftScaleEstimator = RegistrationParameterScalesFromPhysicalShiftType::New(); shiftScaleEstimator->SetMetric( mimetric ); shiftScaleEstimator->SetTransformForward( true ); typename RegistrationParameterScalesFromPhysicalShiftType::ScalesType movingScales( affinesearch->GetNumberOfParameters() ); shiftScaleEstimator->EstimateScales( movingScales ); mstartOptimizer->SetScales( movingScales ); mstartOptimizer->SetMetric( mimetric ); localoptimizer->SetMetric( mimetric ); localoptimizer->SetScales( movingScales ); } if ( whichMetric.compare("MI") != 0 ) { gcmetric->Initialize(); typedef itk::RegistrationParameterScalesFromPhysicalShift RegistrationParameterScalesFromPhysicalShiftType; typename RegistrationParameterScalesFromPhysicalShiftType::Pointer shiftScaleEstimator = RegistrationParameterScalesFromPhysicalShiftType::New(); shiftScaleEstimator->SetMetric( gcmetric ); shiftScaleEstimator->SetTransformForward( true ); typename RegistrationParameterScalesFromPhysicalShiftType::ScalesType movingScales( affinesearch->GetNumberOfParameters() ); shiftScaleEstimator->EstimateScales( movingScales ); mstartOptimizer->SetScales( movingScales ); mstartOptimizer->SetMetric( gcmetric ); localoptimizer->SetMetric( gcmetric ); localoptimizer->SetScales( movingScales ); } typename OptimizerType::ParametersListType parametersList = mstartOptimizer->GetParametersList(); for( double ang1 = ( piover4 * (-1) ); ang1 <= ( piover4 + searchfactor ); ang1 = ang1 + searchfactor ) { if( useprincaxis ) { affinesearch->SetMatrix( A_solution ); } if( ImageDimension == 3 ) { for( double ang2 = ( piover4 * (-1) ); ang2 <= ( piover4 + searchfactor ); ang2 = ang2 + searchfactor ) { affinesearch->SetIdentity(); affinesearch->SetCenter( trans2 ); affinesearch->SetOffset( trans ); if( useprincaxis ) { affinesearch->SetMatrix( A_solution ); } affinesearch->Rotate3D(axis1, ang1, 1); affinesearch->Rotate3D(axis2, ang2, 1); affinesearch->Scale( bestscale ); parametersList.push_back( affinesearch->GetParameters() ); } } if( ImageDimension == 2 ) { affinesearch->SetIdentity(); affinesearch->SetCenter( trans2 ); affinesearch->SetOffset( trans ); if( useprincaxis ) { affinesearch->SetMatrix( A_solution ); } affinesearch->Rotate2D( ang1, 1); affinesearch->Scale( bestscale ); parametersList.push_back( affinesearch->GetParameters() ); } } mstartOptimizer->SetParametersList( parametersList ); if( localSearchIterations > 0 ) { mstartOptimizer->SetLocalOptimizer( localoptimizer ); } mstartOptimizer->StartOptimization(); typename AffineType::Pointer bestaffine = AffineType::New(); bestaffine->SetCenter( trans2 ); bestaffine->SetParameters( mstartOptimizer->GetBestParameters() ); typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetInput( bestaffine ); transformWriter->SetFileName( outname.c_str() ); transformWriter->Update(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsAffineInitializer( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsAffineInitializer" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cerr << "\nUsage: " << argv[0] << " ImageDimension TransformOutput.mat Optional-SearchFactor Optional-Radian-Fraction Optional-bool-UsePrincipalAxes Optional-uint-UseLocalSearch Optional-Image1Mask " << std::endl; std::cerr << " Optional-SearchFactor is in degrees --- e.g. 10 = search in 10 degree increments ." << std::endl; std::cerr << " Radian-Fraction should be between 0 and 1 --- will search this arc +/- around principal axis." << std::endl; std::cerr << " Optional-bool-UsePrincipalAxes determines whether the rotation is searched around an initial principal axis alignment. Default = false. " << std::endl; std::cerr << " Optional-uint-UseLocalSearch determines if a local optimization is run at each search point for the set number of iterations. Default = 20." << std::endl; return 0; } switch( atoi(argv[1]) ) { case 2: { return antsAffineInitializerImp<2>(argc, argv); } case 3: { return antsAffineInitializerImp<3>(argc, argv); } case 4: return antsAffineInitializerImp<4>(argc, argv); } return antsAffineInitializerImp<2>(argc, argv); } } // namespace ants ants-2.2.0/Examples/antsAlignOrigin.cxx000066400000000000000000000351671311104306400200570ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include "itkantsRegistrationHelper.h" #include "ReadWriteData.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkResampleImageFilter.h" #include "itkVectorIndexSelectionCastImageFilter.h" #include "itkAffineTransform.h" #include "itkCompositeTransform.h" #include "itkDisplacementFieldTransform.h" #include "itkIdentityTransform.h" #include "itkMatrixOffsetTransformBase.h" #include "itkTransformFactory.h" #include "itkTransformFileWriter.h" #include "itkTransformToDisplacementFieldFilter.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkGaussianInterpolateImageFunction.h" #include "itkInterpolateImageFunction.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "itkLabelImageGaussianInterpolateImageFunction.h" namespace ants { template int antsAlignOriginImplementation( itk::ants::CommandLineParser::Pointer & parser, unsigned int inputImageType ) { if( inputImageType != 0 ) { std::cerr << "inputImageType is not used, therefore only mode 0 is supported at the momemnt." << std::endl; return EXIT_FAILURE; } typedef double PixelType; typedef itk::Image ImageType; typename ImageType::Pointer inputImage; typename ImageType::Pointer outputImage; /** * Input object option - for now, we're limiting this to images. */ typename itk::ants::CommandLineParser::OptionType::Pointer inputOption = parser->GetOption( "input" ); typename itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( inputOption && inputOption->GetNumberOfFunctions() > 0 ) { if( inputOption->GetFunction()->GetNumberOfParameters() > 1 && parser->Convert( outputOption->GetFunction( 0 )->GetParameter( 1 ) ) == 0 ) { std::cerr << "An input image is required." << std::endl; return EXIT_FAILURE; } std::cout << "Input image: " << inputOption->GetFunction()->GetName() << std::endl; typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( ( inputOption->GetFunction()->GetName() ).c_str() ); reader->Update(); inputImage = reader->GetOutput(); } std::string outputTransform; std::string outputWarpedImageName; if( outputOption && outputOption->GetNumberOfFunctions() > 0 ) { outputTransform = outputOption->GetFunction( 0 )->GetName(); if( outputOption->GetFunction()->GetNumberOfParameters() > 0 ) { outputTransform = outputOption->GetFunction( 0 )->GetParameter( 0 ); } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { outputWarpedImageName = outputOption->GetFunction( 0 )->GetParameter( 1 ); } std::cout << "Output transform: " << outputTransform << std::endl; std::cout << "Output image: " << outputWarpedImageName << std::endl; } /** * Reference image option */ // read in the image as char since we only need the header information. typedef itk::Image ReferenceImageType; typename ReferenceImageType::Pointer referenceImage; typename itk::ants::CommandLineParser::OptionType::Pointer referenceOption = parser->GetOption( "reference-image" ); if( referenceOption && referenceOption->GetNumberOfFunctions() > 0 ) { std::cout << "Reference image: " << referenceOption->GetFunction()->GetName() << std::endl; // read in the image as char since we only need the header information. typedef itk::ImageFileReader ReferenceReaderType; typename ReferenceReaderType::Pointer referenceReader = ReferenceReaderType::New(); referenceReader->SetFileName( ( referenceOption->GetFunction()->GetName() ).c_str() ); referenceImage = referenceReader->GetOutput(); referenceImage->Update(); referenceImage->DisconnectPipeline(); } else { std::cerr << "Error: No reference image specified." << std::endl; return EXIT_FAILURE; } typename ImageType::PointType::VectorType translation = inputImage->GetOrigin() - referenceImage->GetOrigin(); translation = referenceImage->GetDirection() * inputImage->GetDirection() * translation; std::cout << "offset = " << translation << std::endl; typedef itk::MatrixOffsetTransformBase TransformType; typename TransformType::Pointer transform = TransformType::New(); transform->SetIdentity(); transform->SetTranslation( translation ); typename itk::TransformFileWriter::Pointer transform_writer = itk::TransformFileWriter::New(); transform_writer->SetFileName( outputTransform ); transform_writer->SetInput( transform ); transform_writer->Update(); return EXIT_SUCCESS; } static void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, antsWarp tries to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Currently, the only input objects supported are image " ) + std::string( "objects. However, the current framework allows for " ) + std::string( "warping of other objects such as meshes and point sets. "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "input" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "inputFileName" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "For warping input images, the reference image defines the " ) + std::string( "spacing, origin, size, and direction of the output warped " ) + std::string( "image. "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "reference-image" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "imageFileName" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "One can either output the warped image or, if the boolean " ) + std::string( "is set, one can print out the displacement field based on the" ) + std::string( "composite transform and the reference image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "warpedOutputFileName" ); option->SetUsageOption( 1, "[transform,alignedImage]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsAlignOrigin( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsAlignOrigin" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "antsAlignOrigin, applied to an input image, transforms it " ) + std::string( "according to a reference image and a transform " ) + std::string( "(or a set of transforms)." ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc < 2 || ( parser->GetOption( "help" ) && ( parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) ) ) { parser->PrintMenu( std::cout, 5, false ); if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } else if( parser->GetOption( 'h' ) && ( parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Read in the first intensity image to get the image dimension. std::string filename; itk::ants::CommandLineParser::OptionType::Pointer inputOption = parser->GetOption( "reference-image" ); if( inputOption && inputOption->GetNumberOfFunctions() > 0 ) { if( inputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { filename = inputOption->GetFunction( 0 )->GetParameter( 0 ); } else { filename = inputOption->GetFunction( 0 )->GetName(); } } else { std::cerr << "No reference image was specified." << std::endl; return EXIT_FAILURE; } itk::ants::CommandLineParser::OptionType::Pointer inputImageTypeOption = parser->GetOption( "input-image-type" ); itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); unsigned int dimension = imageIO->GetNumberOfDimensions(); itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() > 0 ) { dimension = parser->Convert( dimOption->GetFunction()->GetName() ); } switch( dimension ) { case 2: { if( inputImageTypeOption ) { std::string inputImageType = inputImageTypeOption->GetFunction()->GetName(); if( !std::strcmp( inputImageType.c_str(), "scalar" ) || !std::strcmp( inputImageType.c_str(), "0" ) ) { return antsAlignOriginImplementation<2>( parser, 0 ); } else if( !std::strcmp( inputImageType.c_str(), "vector" ) || !std::strcmp( inputImageType.c_str(), "1" ) ) { return antsAlignOriginImplementation<2>( parser, 1 ); } else if( !std::strcmp( inputImageType.c_str(), "tensor" ) || !std::strcmp( inputImageType.c_str(), "2" ) ) { std::cerr << "antsApplyTransforms is not implemented for 2-D tensor images." << std::endl; } else { std::cerr << "Unrecognized input image type (cf --input-image-type option)." << std::endl; return EXIT_FAILURE; } } else { return antsAlignOriginImplementation<2>( parser, 0 ); } } break; case 3: { if( inputImageTypeOption ) { std::string inputImageType = inputImageTypeOption->GetFunction()->GetName(); if( !std::strcmp( inputImageType.c_str(), "scalar" ) || !std::strcmp( inputImageType.c_str(), "0" ) ) { return antsAlignOriginImplementation<3>( parser, 0 ); } else if( !std::strcmp( inputImageType.c_str(), "vector" ) || !std::strcmp( inputImageType.c_str(), "1" ) ) { return antsAlignOriginImplementation<3>( parser, 1 ); } else if( !std::strcmp( inputImageType.c_str(), "tensor" ) || !std::strcmp( inputImageType.c_str(), "2" ) ) { return antsAlignOriginImplementation<3>( parser, 2 ); } else { std::cerr << "Unrecognized input image type (cf --input-image-type option)." << std::endl; return EXIT_FAILURE; } } else { return antsAlignOriginImplementation<3>( parser, 0 ); } } break; case 4: { if( inputImageTypeOption ) { std::string inputImageType = inputImageTypeOption->GetFunction()->GetName(); if( !std::strcmp( inputImageType.c_str(), "scalar" ) || !std::strcmp( inputImageType.c_str(), "0" ) ) { return antsAlignOriginImplementation<4>( parser, 0 ); } else if( !std::strcmp( inputImageType.c_str(), "vector" ) || !std::strcmp( inputImageType.c_str(), "1" ) ) { return antsAlignOriginImplementation<4>( parser, 1 ); } else if( !std::strcmp( inputImageType.c_str(), "tensor" ) || !std::strcmp( inputImageType.c_str(), "2" ) ) { std::cerr << "antsApplyTransforms is not implemented for 4-D tensor images." << std::endl; } else { std::cerr << "Unrecognized input image type (cf --input-image-type option)." << std::endl; return EXIT_FAILURE; } } else { return antsAlignOriginImplementation<3>( parser, 0 ); } } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsApplyTransforms.cxx000066400000000000000000001202301311104306400210030ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include "itkantsRegistrationHelper.h" #include "ReadWriteData.h" #include "TensorFunctions.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkExtractImageFilter.h" #include "itkResampleImageFilter.h" #include "itkVectorIndexSelectionCastImageFilter.h" #include "itkAffineTransform.h" #include "itkCompositeTransform.h" #include "itkDisplacementFieldTransform.h" #include "itkIdentityTransform.h" #include "itkMatrixOffsetTransformBase.h" #include "itkTransformFactory.h" #include "itkTransformFileReader.h" #include "itkTransformToDisplacementFieldFilter.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkGaussianInterpolateImageFunction.h" #include "itkInterpolateImageFunction.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "itkLabelImageGaussianInterpolateImageFunction.h" #include "itkLabelImageGenericInterpolateImageFunction.h" namespace ants { template void CorrectImageTensorDirection( TensorImageType * movingTensorImage, ImageType * referenceImage ) { typedef typename TensorImageType::DirectionType DirectionType; typedef typename DirectionType::InternalMatrixType MatrixType; MatrixType direction = movingTensorImage->GetDirection().GetTranspose() * referenceImage->GetDirection().GetVnlMatrix(); if( !direction.is_identity( 0.00001 ) ) { itk::ImageRegionIterator It( movingTensorImage, movingTensorImage->GetBufferedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { typedef typename TensorImageType::PixelType TensorType; typedef typename TensorImageType::DirectionType::InternalMatrixType TensorMatrixType; TensorType tensor = It.Get(); TensorMatrixType dt; Vector2Matrix(tensor, dt); dt = direction * dt * direction.transpose(); tensor = Matrix2Vector(dt); It.Set( tensor ); } } } template void CorrectImageVectorDirection( DisplacementFieldType * movingVectorImage, ImageType * referenceImage ) { typedef typename DisplacementFieldType::DirectionType DirectionType; typename DirectionType::InternalMatrixType direction = movingVectorImage->GetDirection().GetTranspose() * referenceImage->GetDirection().GetVnlMatrix(); typedef typename DisplacementFieldType::PixelType VectorType; const unsigned int dimension = ImageType::ImageDimension; if( !direction.is_identity( 0.00001 ) ) { itk::ImageRegionIterator It( movingVectorImage, movingVectorImage->GetBufferedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { VectorType vector = It.Get(); vnl_vector internalVector( dimension ); for( unsigned int d = 0; d < dimension; d++ ) { internalVector[d] = vector[d]; } internalVector.pre_multiply( direction );; for( unsigned int d = 0; d < dimension; d++ ) { vector[d] = internalVector[d]; } It.Set( vector ); } } } template unsigned int numTensorElements() { return NDim + numTensorElements(); } template <> unsigned int numTensorElements<0>() { return 0; } template std::vector tensorDiagonalArrayIndices() { std::vector diagElements; for( unsigned int d = 0; d < NDim; d++ ) { // I think this is correct for upper-triangular ordering but only tested on 3D tensors diagElements.push_back(d * NDim - d * (d - 1) / 2); } return diagElements; } template bool isDiagonalElement(std::vector diagElements, unsigned int ind) { for( unsigned int i = 0; i < NDim; i++ ) { if ( diagElements[i] == ind ) { return true; } } return false; } template int antsApplyTransforms( itk::ants::CommandLineParser::Pointer & parser, unsigned int inputImageType = 0 ) { typedef T RealType; typedef T PixelType; typedef itk::Vector VectorType; // typedef unsigned int LabelPixelType; // typedef itk::Image LabelImageType; typedef itk::Image ImageType; typedef itk::Image TimeSeriesImageType; typedef itk::Image DisplacementFieldType; typedef ImageType ReferenceImageType; typedef typename ants::RegistrationHelper RegistrationHelperType; typedef typename RegistrationHelperType::AffineTransformType AffineTransformType; typedef typename RegistrationHelperType::CompositeTransformType CompositeTransformType; typedef itk::SymmetricSecondRankTensor TensorPixelType; typedef itk::Image TensorImageType; const unsigned int NumberOfTensorElements = numTensorElements(); std::vector tensorDiagIndices = tensorDiagonalArrayIndices(); typename TimeSeriesImageType::Pointer timeSeriesImage = ITK_NULLPTR; typename TensorImageType::Pointer tensorImage = ITK_NULLPTR; typename DisplacementFieldType::Pointer vectorImage = ITK_NULLPTR; std::vector inputImages; inputImages.clear(); std::vector outputImages; outputImages.clear(); bool verbose = false; typename itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } /** * Input object option - for now, we're limiting this to images. */ typename itk::ants::CommandLineParser::OptionType::Pointer inputOption = parser->GetOption( "input" ); typename itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( inputImageType == 3 && inputOption && inputOption->GetNumberOfFunctions() ) { if( verbose ) { std::cout << "Input time-series image: " << inputOption->GetFunction( 0 )->GetName() << std::endl; } ReadImage( timeSeriesImage, ( inputOption->GetFunction( 0 )->GetName() ).c_str() ); } else if( inputImageType == 2 && inputOption && inputOption->GetNumberOfFunctions() ) { if( verbose ) { std::cout << "Input tensor image: " << inputOption->GetFunction( 0 )->GetName() << std::endl; } ReadTensorImage( tensorImage, ( inputOption->GetFunction( 0 )->GetName() ).c_str(), true ); } else if( inputImageType == 0 && inputOption && inputOption->GetNumberOfFunctions() ) { if( verbose ) { std::cout << "Input scalar image: " << inputOption->GetFunction( 0 )->GetName() << std::endl; } typename ImageType::Pointer image; ReadImage( image, ( inputOption->GetFunction( 0 )->GetName() ).c_str() ); inputImages.push_back( image ); } else if( inputImageType == 1 && inputOption && inputOption->GetNumberOfFunctions() ) { if( verbose ) { std::cout << "Input vector image: " << inputOption->GetFunction( 0 )->GetName() << std::endl; } typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( ( inputOption->GetFunction( 0 )->GetName() ).c_str() ); try { vectorImage = reader->GetOutput(); vectorImage->Update(); vectorImage->DisconnectPipeline(); } catch( ... ) { if( verbose ) { std::cerr << "Unable to read vector image " << reader->GetFileName() << std::endl; } return EXIT_FAILURE; } } else if( outputOption && outputOption->GetNumberOfFunctions() ) { if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 && parser->Convert( outputOption->GetFunction( 0 )->GetParameter( 1 ) ) == 0 ) { if( verbose ) { std::cerr << "An input image is required." << std::endl; } return EXIT_FAILURE; } } /** * Reference image option */ bool needReferenceImage = true; if( outputOption && outputOption->GetNumberOfFunctions() ) { std::string outputOptionName = outputOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( outputOptionName ); if( !std::strcmp( outputOptionName.c_str(), "linear" ) ) { needReferenceImage = false; } } typedef ImageType ReferenceImageType; typename ReferenceImageType::Pointer referenceImage; typename itk::ants::CommandLineParser::OptionType::Pointer referenceOption = parser->GetOption( "reference-image" ); if( referenceOption && referenceOption->GetNumberOfFunctions() ) { if( verbose ) { std::cout << "Reference image: " << referenceOption->GetFunction( 0 )->GetName() << std::endl; } ReadImage( referenceImage, ( referenceOption->GetFunction( 0 )->GetName() ).c_str() ); } else if( needReferenceImage == true ) { if( verbose ) { std::cerr << "A reference image is required." << std::endl; } return EXIT_FAILURE; } if( inputImageType == 1 ) { CorrectImageVectorDirection( vectorImage, referenceImage ); for( unsigned int i = 0; i < Dimension; i++ ) { typedef itk::VectorIndexSelectionCastImageFilter SelectorType; typename SelectorType::Pointer selector = SelectorType::New(); selector->SetInput( vectorImage ); selector->SetIndex( i ); selector->Update(); inputImages.push_back( selector->GetOutput() ); } } else if( inputImageType == 2 ) { CorrectImageTensorDirection( tensorImage, referenceImage ); for( unsigned int i = 0; i < NumberOfTensorElements; i++ ) { typedef itk::VectorIndexSelectionCastImageFilter SelectorType; typename SelectorType::Pointer selector = SelectorType::New(); selector->SetInput( tensorImage ); selector->SetIndex( i ); selector->Update(); inputImages.push_back( selector->GetOutput() ); } } else if( inputImageType == 3 ) { typename TimeSeriesImageType::RegionType extractRegion = timeSeriesImage->GetLargestPossibleRegion(); unsigned int numberOfTimePoints = extractRegion.GetSize()[Dimension]; int startTimeIndex = extractRegion.GetIndex()[Dimension]; extractRegion.SetSize( Dimension, 0 ); for( unsigned int i = 0; i < numberOfTimePoints; i++ ) { extractRegion.SetIndex( Dimension, startTimeIndex + i ); typedef itk::ExtractImageFilter ExtracterType; typename ExtracterType::Pointer extracter = ExtracterType::New(); extracter->SetInput( timeSeriesImage ); extracter->SetExtractionRegion( extractRegion ); extracter->SetDirectionCollapseToSubmatrix(); extracter->Update(); inputImages.push_back( extracter->GetOutput() ); } } /** * Transform option */ // Register the matrix offset transform base class to the // transform factory for compatibility with the current ANTs. typedef itk::MatrixOffsetTransformBase MatrixOffsetTransformType; itk::TransformFactory::RegisterTransform(); typename itk::ants::CommandLineParser::OptionType::Pointer transformOption = parser->GetOption( "transform" ); bool useStaticCastForR = false; typename itk::ants::CommandLineParser::OptionType::Pointer rOption = parser->GetOption( "static-cast-for-R" ); if( rOption && rOption->GetNumberOfFunctions() ) { useStaticCastForR = parser->Convert( rOption->GetFunction( 0 )->GetName() ); } std::vector isDerivedTransform; typename CompositeTransformType::Pointer compositeTransform = GetCompositeTransformFromParserOption( parser, transformOption, isDerivedTransform, useStaticCastForR ); if( compositeTransform.IsNull() ) { return EXIT_FAILURE; } if( !compositeTransform->GetNumberOfParameters() ) { if( verbose ) { std::cout << "WARNING: No transforms found, using identify transform" << std::endl; } typename MatrixOffsetTransformType::Pointer idTransform = MatrixOffsetTransformType::New(); idTransform->SetIdentity(); compositeTransform->AddTransform( idTransform ); } std::string whichInterpolator( "linear" ); typename itk::ants::CommandLineParser::OptionType::Pointer interpolationOption = parser->GetOption( "interpolation" ); if( interpolationOption && interpolationOption->GetNumberOfFunctions() ) { whichInterpolator = interpolationOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( whichInterpolator ); } const size_t VImageDimension = Dimension; typename ImageType::SpacingType cache_spacing_for_smoothing_sigmas(itk::NumericTraits::ZeroValue()); if( !std::strcmp( whichInterpolator.c_str(), "gaussian" ) || !std::strcmp( whichInterpolator.c_str(), "multilabel" ) ) { cache_spacing_for_smoothing_sigmas = inputImages[0]->GetSpacing(); } #include "make_interpolator_snip.tmpl" /** * Default voxel value */ PixelType defaultValue = 0; typename itk::ants::CommandLineParser::OptionType::Pointer defaultOption = parser->GetOption( "default-value" ); if( defaultOption && defaultOption->GetNumberOfFunctions() ) { defaultValue = parser->Convert( defaultOption->GetFunction( 0 )->GetName() ); } if( verbose ) { if( inputImageType == 2 ) std::cout << "Default pixel mean diffusivity: " << defaultValue << std::endl; else std::cout << "Default pixel value: " << defaultValue << std::endl; } for( unsigned int n = 0; n < inputImages.size(); n++ ) { typedef itk::ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampleFilter = ResamplerType::New(); resampleFilter->SetInput( inputImages[n] ); resampleFilter->SetOutputParametersFromImage( referenceImage ); resampleFilter->SetTransform( compositeTransform ); if ( inputImageType == 2 ) { // Set background pixel values in tensor images to produce an isotropic tensor if ( defaultValue > 0 && isDiagonalElement(tensorDiagIndices, n) ) { // defaultValue == MD of isotropic tensor. Resampling is done in log space resampleFilter->SetDefaultPixelValue( log( defaultValue ) ); } else { resampleFilter->SetDefaultPixelValue( 0 ); } } else { // for non-tensor images, set the same background value for each component resampleFilter->SetDefaultPixelValue( defaultValue ); } interpolator->SetInputImage( inputImages[n] ); resampleFilter->SetInterpolator( interpolator ); if( n == 0 ) { if( verbose ) { std::cout << "Interpolation type: " << resampleFilter->GetInterpolator()->GetNameOfClass() << std::endl; } } if( inputImageType == 3 ) { if( verbose ) { std::cout << " Applying transform(s) to time point " << n << " (out of " << inputImages.size() << ")." << std::endl; } } resampleFilter->Update(); outputImages.push_back( resampleFilter->GetOutput() ); } /** * output */ if( outputOption && outputOption->GetNumberOfFunctions() ) { std::string outputOptionName = outputOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( outputOptionName ); if( !std::strcmp( outputOptionName.c_str(), "linear" ) ) { if( !compositeTransform->IsLinear() ) { if( verbose ) { std::cerr << "The transform or set of transforms is not linear." << std::endl; } return EXIT_FAILURE; } else { typename RegistrationHelperType::Pointer helper = RegistrationHelperType::New(); typename AffineTransformType::Pointer transform = helper->CollapseLinearTransforms( compositeTransform ); typedef itk::TransformFileWriterTemplate TransformWriterType; typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetFileName( ( outputOption->GetFunction( 0 )->GetParameter( 0 ) ).c_str() ); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 && parser->Convert( outputOption->GetFunction( 0 )->GetParameter( 1 ) ) != 0 ) { typename AffineTransformType::Pointer inverseTransform = AffineTransformType::New(); inverseTransform->SetMatrix( dynamic_cast( transform->GetInverseTransform().GetPointer() )->GetMatrix() ); inverseTransform->SetOffset( -( inverseTransform->GetMatrix() * transform->GetOffset() ) ); transformWriter->SetInput( inverseTransform ); } else { transformWriter->SetInput( transform ); } transformWriter->Update(); } } else if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 && parser->Convert( outputOption->GetFunction( 0 )->GetParameter( 1 ) ) != 0 ) { if( verbose ) { std::cout << "Output composite transform displacement field: " << outputOption->GetFunction( 0 )->GetParameter( 0 ) << std::endl; } typedef typename itk::TransformToDisplacementFieldFilter ConverterType; typename ConverterType::Pointer converter = ConverterType::New(); converter->SetOutputOrigin( referenceImage->GetOrigin() ); converter->SetOutputStartIndex( referenceImage->GetBufferedRegion().GetIndex() ); converter->SetSize( referenceImage->GetBufferedRegion().GetSize() ); converter->SetOutputSpacing( referenceImage->GetSpacing() ); converter->SetOutputDirection( referenceImage->GetDirection() ); converter->SetTransform( compositeTransform ); converter->Update(); typedef itk::ImageFileWriter DisplacementFieldWriterType; typename DisplacementFieldWriterType::Pointer displacementFieldWriter = DisplacementFieldWriterType::New(); displacementFieldWriter->SetInput( converter->GetOutput() ); displacementFieldWriter->SetFileName( ( outputOption->GetFunction( 0 )->GetParameter( 0 ) ).c_str() ); displacementFieldWriter->Update(); } else { std::string outputFileName = ""; if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 && parser->Convert( outputOption->GetFunction( 0 )->GetParameter( 1 ) ) == 0 ) { outputFileName = outputOption->GetFunction( 0 )->GetParameter( 0 ); } else { outputFileName = outputOption->GetFunction( 0 )->GetName(); } if( verbose ) { std::cout << "Output warped image: " << outputFileName << std::endl; } if( inputImageType == 1 ) { if( outputImages.size() != Dimension ) { if( verbose ) { std::cerr << "The number of output images does not match the number of vector components." << std::endl; } return EXIT_FAILURE; } VectorType zeroVector( 0.0 ); typename DisplacementFieldType::Pointer outputVectorImage = DisplacementFieldType::New(); outputVectorImage->CopyInformation( referenceImage ); outputVectorImage->SetRegions( referenceImage->GetRequestedRegion() ); outputVectorImage->Allocate(); outputVectorImage->FillBuffer( zeroVector ); itk::ImageRegionIteratorWithIndex It( outputVectorImage, outputVectorImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { VectorType vector = It.Get(); typename DisplacementFieldType::IndexType index = It.GetIndex(); for( unsigned int n = 0; n < Dimension; n++ ) { vector.SetNthComponent( n, outputImages[n]->GetPixel( index ) ); } It.Set( vector ); } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( outputVectorImage ); writer->SetFileName( ( outputFileName ).c_str() ); writer->Update(); } else if( inputImageType == 2 ) { if( outputImages.size() != NumberOfTensorElements ) { if( verbose ) { std::cerr << "The number of output images does not match the number of tensor elements." << std::endl; } return EXIT_FAILURE; } TensorPixelType zeroTensor( 0.0 ); typename TensorImageType::Pointer outputTensorImage = TensorImageType::New(); outputTensorImage->CopyInformation( referenceImage ); outputTensorImage->SetRegions( referenceImage->GetRequestedRegion() ); outputTensorImage->Allocate(); outputTensorImage->FillBuffer( zeroTensor ); itk::ImageRegionIteratorWithIndex It( outputTensorImage, outputTensorImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { TensorPixelType tensor = It.Get(); typename TensorImageType::IndexType index = It.GetIndex(); for( unsigned int n = 0; n < NumberOfTensorElements; n++ ) { tensor.SetNthComponent( n, outputImages[n]->GetPixel( index ) ); } It.Set( tensor ); } WriteTensorImage( outputTensorImage, ( outputFileName ).c_str(), true ); } else if( inputImageType == 3 ) { unsigned int numberOfTimePoints = timeSeriesImage->GetLargestPossibleRegion().GetSize()[Dimension]; if( outputImages.size() != numberOfTimePoints ) { if( verbose ) { std::cerr << "The number of output images does not match the number of image time points." << std::endl; } return EXIT_FAILURE; } typename TimeSeriesImageType::Pointer outputTimeSeriesImage = TimeSeriesImageType::New(); typename TimeSeriesImageType::PointType origin = timeSeriesImage->GetOrigin(); typename TimeSeriesImageType::SizeType size = timeSeriesImage->GetLargestPossibleRegion().GetSize(); typename TimeSeriesImageType::DirectionType direction = timeSeriesImage->GetDirection(); typename TimeSeriesImageType::IndexType index = timeSeriesImage->GetLargestPossibleRegion().GetIndex(); typename TimeSeriesImageType::SpacingType spacing = timeSeriesImage->GetSpacing(); for( unsigned int i = 0; i < Dimension; i++ ) { origin[i] = referenceImage->GetOrigin()[i]; size[i] = referenceImage->GetRequestedRegion().GetSize()[i]; index[i] = referenceImage->GetRequestedRegion().GetIndex()[i]; spacing[i] = referenceImage->GetSpacing()[i]; for( unsigned int j = 0; j < Dimension; j++ ) { direction[i][j] = referenceImage->GetDirection()[i][j]; } } typename TimeSeriesImageType::RegionType region; region.SetSize( size ); region.SetIndex( index ); int startTimeIndex = timeSeriesImage->GetLargestPossibleRegion().GetIndex()[Dimension]; outputTimeSeriesImage->CopyInformation( timeSeriesImage ); outputTimeSeriesImage->SetOrigin( origin ); outputTimeSeriesImage->SetDirection( direction ); outputTimeSeriesImage->SetSpacing( spacing ); outputTimeSeriesImage->SetRegions( region ); outputTimeSeriesImage->Allocate(); outputTimeSeriesImage->FillBuffer( 0 ); typename ImageType::IndexType referenceIndex; itk::ImageRegionIteratorWithIndex It( outputTimeSeriesImage, outputTimeSeriesImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { typename TimeSeriesImageType::IndexType timeImageIndex = It.GetIndex(); for( unsigned int i = 0; i < Dimension; i++ ) { referenceIndex[i] = timeImageIndex[i]; } It.Set( outputImages[timeImageIndex[Dimension] - startTimeIndex]->GetPixel( referenceIndex ) ); } WriteImage( outputTimeSeriesImage, ( outputFileName ).c_str() ); } else { try { WriteImage( outputImages[0], ( outputFileName ).c_str() ); } catch( itk::ExceptionObject & err ) { if( verbose ) { std::cerr << "Caught an ITK exception: " << std::endl; std::cerr << err << " " << __FILE__ << " " << __LINE__ << std::endl; } return EXIT_FAILURE; // throw &err; } catch( ... ) { if( verbose ) { std::cerr << "Error while writing in image: " << outputFileName << std::endl; } return EXIT_FAILURE; // throw; } } } } return EXIT_SUCCESS; } static void antsApplyTransformsInitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, antsWarp tries to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3/4" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Option specifying the input image type of scalar (default), " ) + std::string( "vector, tensor, or time series." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "input-image-type" ); option->SetShortName( 'e' ); option->SetUsageOption( 0, "0/1/2/3 " ); option->SetUsageOption( 1, "scalar/vector/tensor/time-series " ); option->AddFunction( std::string( "0" ) ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Currently, the only input objects supported are image " ) + std::string( "objects. However, the current framework allows for " ) + std::string( "warping of other objects such as meshes and point sets. "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "input" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "inputFileName" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "For warping input images, the reference image defines the " ) + std::string( "spacing, origin, size, and direction of the output warped " ) + std::string( "image. "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "reference-image" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "imageFileName" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "One can either output the warped image or, if the boolean " ) + std::string( "is set, one can print out the displacement field based on the " ) + std::string( "composite transform and the reference image. A third option " ) + std::string( "is to compose all affine transforms and (if boolean is set) " ) + std::string( "calculate its inverse which is then written to an ITK file "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "warpedOutputFileName" ); option->SetUsageOption( 1, "[warpedOutputFileName or compositeDisplacementField,]" ); option->SetUsageOption( 2, "Linear[genericAffineTransformFile,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Several interpolation options are available in ITK. " ) + std::string( "These have all been made available." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "interpolation" ); option->SetShortName( 'n' ); option->SetUsageOption( 0, "Linear" ); option->SetUsageOption( 1, "NearestNeighbor" ); option->SetUsageOption( 2, "MultiLabel[,]" ); option->SetUsageOption( 3, "Gaussian[,]" ); option->SetUsageOption( 4, "BSpline[]" ); option->SetUsageOption( 5, "CosineWindowedSinc" ); option->SetUsageOption( 6, "WelchWindowedSinc" ); option->SetUsageOption( 7, "HammingWindowedSinc" ); option->SetUsageOption( 8, "LanczosWindowedSinc" ); option->SetUsageOption( 9, "GenericLabel[]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Several transform options are supported including all " ) + std::string( "those defined in the ITK library in addition to " ) + std::string( "a deformation field transform. The ordering of " ) + std::string( "the transformations follows the ordering specified " ) + std::string( "on the command line. An identity transform is pushed " ) + std::string( "onto the transformation stack. Each new transform " ) + std::string( "encountered on the command line is also pushed onto " ) + std::string( "the transformation stack. Then, to warp the input object, " ) + std::string( "each point comprising the input object is warped first " ) + std::string( "according to the last transform pushed onto the stack " ) + std::string( "followed by the second to last transform, etc. until " ) + std::string( "the last transform encountered which is the identity " ) + std::string( "transform. " ) + std::string( "Also, it should be noted that the inverse transform can " ) + std::string( "be accommodated with the usual caveat that such an inverse " ) + std::string( "must be defined by the specified transform class " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "transform" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "transformFileName" ); option->SetUsageOption( 1, "[transformFileName,useInverse]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Default voxel value to be used with input images only. " ) + std::string( "Specifies the voxel value when the input point maps outside " ) + std::string( "the output domain. With tensor input images, specifies the " ) + std::string( "default voxel eigenvalues. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "default-value" ); option->SetShortName( 'f' ); option->SetUsageOption( 0, "value" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "forces static cast in ReadTransform (for R)" ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'z' ); option->SetDescription( description ); option->SetLongName( "static-cast-for-R" ); option->SetUsageOption( 0, "value" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Use 'float' instead of 'double' for computations." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "float" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsApplyTransforms( std::vector args, std::ostream * /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsApplyTransforms" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "antsApplyTransforms, applied to an input image, transforms it " ) + std::string( "according to a reference image and a transform " ) + std::string( "(or a set of transforms)." ); parser->SetCommandDescription( commandDescription ); antsApplyTransformsInitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } bool verbose = false; itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } if( argc == 1 ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } itk::ants::CommandLineParser::OptionType::Pointer inputImageTypeOption = parser->GetOption( "input-image-type" ); std::string inputImageType; if( inputImageTypeOption ) { inputImageType = inputImageTypeOption->GetFunction( 0 )->GetName(); } unsigned int dimension = 3; // BA - code below creates problems in ANTsR // itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( // filename.c_str(), itk::ImageIOFactory::ReadMode ); // dimension = imageIO->GetNumberOfDimensions(); itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } bool useDoublePrecision = true; std::string precisionType; OptionType::Pointer typeOption = parser->GetOption( "float" ); if( typeOption && parser->Convert( typeOption->GetFunction( 0 )->GetName() ) ) { if( verbose ) { std::cout << "Using single precision for computations." << std::endl; } precisionType = "float"; useDoublePrecision = false; } else { if( verbose ) { std::cout << "Using double precision for computations." << std::endl; } precisionType = "double"; useDoublePrecision = true; } enum InputImageType { SCALAR = 0, VECTOR, TENSOR, TIME_SERIES }; InputImageType imageType = SCALAR; if( inputImageTypeOption ) { if( !std::strcmp( inputImageType.c_str(), "scalar" ) || !std::strcmp( inputImageType.c_str(), "0" ) ) { imageType = SCALAR; } else if( !std::strcmp( inputImageType.c_str(), "vector" ) || !std::strcmp( inputImageType.c_str(), "1" ) ) { imageType = VECTOR; } else if( !std::strcmp( inputImageType.c_str(), "tensor" ) || !std::strcmp( inputImageType.c_str(), "2" ) ) { imageType = TENSOR; } else if( !std::strcmp( inputImageType.c_str(), "time-series" ) || !std::strcmp( inputImageType.c_str(), "3" ) ) { imageType = TIME_SERIES; } else { if( verbose ) { std::cerr << "Unrecognized input image type (cf --input-image-type option)." << std::endl; } return EXIT_FAILURE; } } switch( dimension ) { case 2: { if( imageType == TENSOR ) { if( verbose ) { std::cerr << "antsApplyTransforms is not implemented for 2-D tensor images." << std::endl; } return EXIT_FAILURE; } else { if( useDoublePrecision ) { return antsApplyTransforms( parser, imageType ); } else { return antsApplyTransforms( parser, imageType ); } } } break; case 3: { if( useDoublePrecision ) { return antsApplyTransforms( parser, imageType ); } else { return antsApplyTransforms( parser, imageType ); } } break; case 4: { if( imageType == TENSOR ) { if( verbose ) { std::cerr << "antsApplyTransforms is not implemented for 4-D tensor images." << std::endl; } } else if( imageType == TIME_SERIES ) { if( verbose ) { std::cerr << "antsApplyTransforms is not implemented for 4-D + time images." << std::endl; } } else { if( useDoublePrecision ) { return antsApplyTransforms( parser, imageType ); } else { return antsApplyTransforms( parser, imageType ); } } } break; default: { if( verbose ) { std::cerr << "Unsupported dimension" << std::endl; } return EXIT_FAILURE; } } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsApplyTransformsToPoints.cxx000066400000000000000000000504731311104306400225160ustar00rootroot00000000000000#include "itkCSVNumericObjectFileWriter.h" #include "antsUtilities.h" #include "antsAllocImage.h" #include "itkantsRegistrationHelper.h" #include "itkCSVArray2DFileReader.h" #include "itkAffineTransform.h" #include "itkCompositeTransform.h" #include "itkDisplacementFieldTransform.h" #include "itkIdentityTransform.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMatrixOffsetTransformBase.h" #include "itkResampleImageFilter.h" #include "itkTransformFactory.h" #include "itkTransformFileReader.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkGaussianInterpolateImageFunction.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "itkLabelImageGaussianInterpolateImageFunction.h" namespace ants { template int antsApplyTransformsToPoints( itk::ants::CommandLineParser::Pointer & parser ) { typedef vnl_matrix MatrixType; MatrixType points_out; MatrixType points_in; typedef itk::Image ImageType; typedef itk::CSVArray2DFileReader ReaderType; typedef itk::CSVArray2DDataObject DataFrameObjectType; typedef typename DataFrameObjectType::StringVectorType StringVectorType; StringVectorType colheadernames; typename ImageType::Pointer pointimage = ITK_NULLPTR; itk::ants::CommandLineParser::OptionType::Pointer antsrOption = parser->GetOption( "forantsr" ); unsigned int forANTsR = 0; if( antsrOption && antsrOption->GetNumberOfFunctions() > 0 ) { forANTsR = parser->Convert( antsrOption->GetFunction( 0 )->GetName() ); } /** * Input object option */ typename itk::ants::CommandLineParser::OptionType::Pointer inputOption = parser->GetOption( "input" ); typename itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( inputOption && inputOption->GetNumberOfFunctions() > 0 ) { std::size_t lengthInputFileName = std::strlen( inputOption->GetFunction( 0 )->GetName().c_str() ); std::string ext = ( inputOption->GetFunction( 0 )->GetName() ).substr( lengthInputFileName - 4 ); if( strcmp( ext.c_str(), ".csv") == 0 ) { typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( ( inputOption->GetFunction( 0 )->GetName() ).c_str() ); reader->SetFieldDelimiterCharacter( ',' ); reader->SetStringDelimiterCharacter( '"' ); reader->HasColumnHeadersOn(); reader->HasRowHeadersOff(); // reader->UseStringDelimiterCharacterOff(); try { reader->Update(); } catch( itk::ExceptionObject& exp ) { std::cerr << "Exception caught!" << std::endl; std::cerr << exp << std::endl; } typename DataFrameObjectType::Pointer dfo = reader->GetOutput(); colheadernames = dfo->GetColumnHeaders(); if( colheadernames.size() < Dimension ) { std::cerr << "Input csv file must have column names such as x,y,z,t,label - where there are a minimum of N-Spatial-Dimensions names e.g. x,y in 2D. ***Or pass in a 2D mha (meta format) binary image file." << std::endl; return EXIT_FAILURE; } points_in = dfo->GetMatrix(); points_out.set_size( points_in.rows(), points_in.cols() ); } else if( strcmp(ext.c_str(), ".mha" ) == 0 || forANTsR ) { std::string fn1 = inputOption->GetFunction( 0 )->GetName(); ReadImage( pointimage, fn1.c_str() ); typename ImageType::IndexType ind; ind.Fill(0); typename ImageType::SizeType sz; sz.Fill(0); sz = pointimage->GetLargestPossibleRegion().GetSize(); points_in.set_size( sz[0], sz[1] ); points_out.set_size( points_in.rows(), points_in.cols() ); for ( unsigned int d = 0; d < sz[0]; d++ ) { for ( unsigned int dd = 0; dd < sz[1]; dd++ ) { ind[0] = d; ind[1] = dd; points_in( d, dd ) = pointimage->GetPixel( ind ); } } } else { std::cerr << "An input csv or mha file is required." << std::endl; return EXIT_FAILURE; } if( points_in.cols() < Dimension ) { std::cerr << "The number of columns in the input point set is fewer than " << Dimension << " Exiting." << std::endl; return EXIT_FAILURE; } if( outputOption && outputOption->GetNumberOfFunctions() > 0 ) { if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 && parser->Convert( outputOption->GetFunction( 0 )->GetParameter( 1 ) ) == 0 ) { std::cerr << "An input csv file is required." << std::endl; return EXIT_FAILURE; } } /** * Transform option */ // Register the matrix offset transform base class to the // transform factory for compatibility with the current ANTs. typedef itk::MatrixOffsetTransformBase MatrixOffsetTransformType; itk::TransformFactory::RegisterTransform(); typedef itk::MatrixOffsetTransformBase MatrixOffsetTransformType; itk::TransformFactory::RegisterTransform(); /** * Load an identity transform in case no transforms are loaded. */ // Register the matrix offset transform base class to the // transform factory for compatibility with the current ANTs. typedef itk::AffineTransform AffineTransformType; typename AffineTransformType::Pointer aff = AffineTransformType::New(); aff->SetIdentity(); typedef itk::CompositeTransform CompositeTransformType; typename CompositeTransformType::InputPointType point_in; typename CompositeTransformType::OutputPointType point_out; typename itk::ants::CommandLineParser::OptionType::Pointer transformOption = parser->GetOption( "transform" ); std::vector isDerivedTransform; typename CompositeTransformType::Pointer compositeTransform = GetCompositeTransformFromParserOption( parser, transformOption, isDerivedTransform, forANTsR ); if ( compositeTransform->GetNumberOfTransforms() == 0 ) compositeTransform->AddTransform( aff ); if( compositeTransform.IsNull() ) { return EXIT_FAILURE; } for( unsigned int pointct = 0; pointct < points_in.rows(); pointct++ ) { point_in.Fill( 0 ); point_out.Fill( 0 ); for( unsigned int p = 0; p < Dimension; p++ ) { point_in[p] = points_in( pointct, p ); } point_out = compositeTransform->TransformPoint( point_in ); for( unsigned int p = 0; p < Dimension; p++ ) { points_out( pointct, p ) = point_out[p]; } for( unsigned int p = Dimension; p < points_in.cols(); p++ ) { points_out( pointct, p ) = points_in( pointct, p ); } } /** * output */ if( outputOption && outputOption->GetNumberOfFunctions() > 0 ) { std::string outputFileName = ""; if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 && parser->Convert( outputOption->GetFunction( 0 )->GetParameter( 1 ) ) == 0 ) { outputFileName = outputOption->GetFunction( 0 )->GetParameter( 0 ); } else { outputFileName = outputOption->GetFunction( 0 )->GetName(); } std::size_t lengthOutputFileName = std::strlen( outputFileName.c_str() ); std::string exto = outputFileName.substr( lengthOutputFileName - 4 ); if( strcmp(exto.c_str(), ".csv" ) == 0 ) { StringVectorType ColumnHeaders = colheadernames; typedef itk::CSVNumericObjectFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( outputFileName ); writer->SetInput( &points_out ); writer->SetColumnHeaders( ColumnHeaders ); try { writer->Write(); } catch( itk::ExceptionObject& exp ) { std::cerr << "Exception caught!" << std::endl; std::cerr << exp << std::endl; return EXIT_FAILURE; } } if( ( strcmp(exto.c_str(), ".mha" ) == 0 || forANTsR ) && ( ! pointimage.IsNull() ) ) { typename ImageType::IndexType ind; ind.Fill(0); typename ImageType::SizeType sz; sz.Fill(0); sz = pointimage->GetLargestPossibleRegion().GetSize(); if ( sz[0] != points_out.rows() || sz[1] != points_out.cols() ) { std::cout << " the size of points_out must match the input pointimage" << std::endl; return EXIT_FAILURE; } for ( unsigned int d = 0; d < sz[0]; d++ ) for ( unsigned int dd = 0; dd < sz[1]; dd++ ) { ind[0] = d; ind[1] = dd; pointimage->SetPixel( ind , points_out( d, dd ) ); } WriteImage(pointimage, outputFileName.c_str() ); } } } return EXIT_SUCCESS; } static void antsApplyTransformsToPointsInitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { { std::string description = std::string( "This option forces the points to be treated as a specified-" ) + std::string( "dimensionality." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "use-double-precision" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "precision" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "0/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "set true for ANTsR IO" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "forantsr" ); option->SetShortName( 'f' ); option->SetUsageOption( 0, "0/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Currently, the only input supported is a csv file with " ) + std::string( "columns including x,y,z,t (all 4) column headers. " ) + std::string( "if you dont have 4D data, still supply 4D filling in extra places with zero. " ) + std::string( "The points should be defined in physical space. " ) + std::string( "Points are transformed in the OPPOSITE direction of images, therefore " ) + std::string( "you should pass the inverse of what is needed to warp the images. " ) + std::string( "Eg if the image is warped by Affine.mat, you should pass the inverse of Affine.mat " ) + std::string( "to transform points defined in the same space as the image. " ) + std::string( "If in doubt how to convert coordinates from your files to the space " ) + std::string( "required by antsApplyTransformsToPoints try creating/drawing a simple " ) + std::string( "label volume with only one voxel set to 1 and all others set to 0. " ) + std::string( "Write down the voxel coordinates. Then use ImageMaths LabelStats to find " ) + std::string( "out what coordinates for this voxel antsApplyTransformsToPoints is " ) + std::string( "expecting. ITK uses a LPS coordinate system. See http://sourceforge.net/p/advants/discussion/840261/thread/2a1e9307/" ) + std::string(" ***Or pass in a 2D mha (meta format) binary image file."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "input" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "inputFileName" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "One can output the warped points to a csv file."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "warpedOutputFileName" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Several transform options are supported including all " ) + std::string( "those defined in the ITK library in addition to " ) + std::string( "a deformation field transform. The ordering of " ) + std::string( "the transformations follows the ordering specified " ) + std::string( "on the command line. An identity transform is pushed " ) + std::string( "onto the transformation stack. Each new transform " ) + std::string( "encountered on the command line is also pushed onto " ) + std::string( "the transformation stack. Then, to warp the input object, " ) + std::string( "each point comprising the input object is warped first " ) + std::string( "according to the last transform pushed onto the stack " ) + std::string( "followed by the second to last transform, etc. until " ) + std::string( "the last transform encountered which is the identity " ) + std::string( "transform. " ) + std::string( "Also, it should be noted that the inverse transform can " ) + std::string( "be accommodated with the usual caveat that such an inverse " ) + std::string( "must be defined by the specified transform class " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "transform" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "transformFileName" ); option->SetUsageOption( 1, "[transformFileName,useInverse]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsApplyTransformsToPoints( std::vector args, std::ostream * /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsApplyTransformsToPoints" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string examplestring = std::string( "reads in a csv file with the first D columns defining the spatial location where the spatial location is defined in physical coordinates. the csv file should have a header row. here is an example") + std::string("\n") + std::string("cat chicken-3.csv ") + std::string("x,y,z,t,label,comment") + std::string("\n") + std::string("82.5,116.5,0,0,1,this is the breast") + std::string("\n") + std::string( "137.5,35.5,0,0,2,this is the beak") + std::string("\n") + std::string( "antsApplyTransformsToPoints -d 2 -i chicken-3.csv -o test.csv -t [chicken3to4.mat ,1 ]") + std::string("\n") + std::string("cat test.csv ") + std::string("\n") + std::string("x,y,z,t,label,comment") + std::string("\n") + std::string("10.8945447481644,162.082675013049,0,0,1,nan") + std::string("\n") + std::string( "7.5367085472988,52.099713111629,0,0,2,nan") + std::string("\n") + std::string( "the nan appears in the last column until the ITK CSV I/O can handle mixed numeric / string types. if your input is fully numeric, all is well."); std::string mhastring = std::string("\n\n**** We now can also read / write .mha files.") + std::string("\n") + std::string("This is a simple binary format (Meta format - look it up!) that is much faster to read/write than csv format.\n Note: To write a mha file, you must also pass an mha file as input.\n"); std::string commandDescription = std::string( "antsApplyTransformsToPoints, applied to an input image, transforms it " ) + std::string( "according to a reference image and a transform " ) + std::string( "(or a set of transforms). " ) + examplestring + mhastring; parser->SetCommandDescription( commandDescription ); antsApplyTransformsToPointsInitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc < 2 || ( parser->GetOption( "help" ) && ( parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) ) ) { parser->PrintMenu( std::cout, 5, false ); if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } else if( parser->GetOption( 'h' ) && ( parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } unsigned int dimension = 3; itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() > 0 ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { std::cerr << "No -d ( dimensionality ) option is specified. Exiting." << std::endl; return EXIT_FAILURE; } itk::ants::CommandLineParser::OptionType::Pointer precOption = parser->GetOption( "precision" ); unsigned int myprecision = 0; if( precOption && precOption->GetNumberOfFunctions() > 0 ) { myprecision = parser->Convert( precOption->GetFunction( 0 )->GetName() ); } if ( myprecision == 1 ) { switch( dimension ) { case 2: { return antsApplyTransformsToPoints<2,double>( parser ); } break; case 3: { return antsApplyTransformsToPoints<3,double>( parser ); } break; case 4: { return antsApplyTransformsToPoints<4,double>( parser ); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else { switch( dimension ) { case 2: { return antsApplyTransformsToPoints<2,float>( parser ); } break; case 3: { return antsApplyTransformsToPoints<3,float>( parser ); } break; case 4: { return antsApplyTransformsToPoints<4,float>( parser ); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsDisplacementAndVelocityFieldRegistrationCommandIterationUpdate.h000066400000000000000000000621551311104306400317310ustar00rootroot00000000000000#ifndef antsDisplacementAndVelocityFieldRegistrationCommandIterationUpdate__h_ #define antsDisplacementAndVelocityFieldRegistrationCommandIterationUpdate__h_ namespace ants { /* There are two types of registration that do not use generic "itkImageRegistrationMethodv4" filter and generic optimization structures: - DisplacementFieldRegistrationType including: * SyN registration * BSplineSyN registration - VelocityFieldRegistrationType including: * TimeVaryingVelocityFeild * TimeVaryingBSplineVelocityField As these registration types have their own specific optimization processes, a different observer is needed to watch their internal optimization procedure. */ template class antsDisplacementAndVelocityFieldRegistrationCommandIterationUpdate : public itk::Command { public: typedef antsDisplacementAndVelocityFieldRegistrationCommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); typedef typename TFilter::FixedImageType FixedImageType; typedef typename TFilter::MovingImageType MovingImageType; /** ImageDimension constants */ itkStaticConstMacro( VImageDimension, unsigned int, FixedImageType::ImageDimension ); typedef typename TFilter::OutputTransformType OutputTransformType; typedef typename TFilter::OutputTransformType::ScalarType RealType; typedef itk::ImageToImageMetricv4 MetricType; typedef typename MetricType::MeasureType MeasureType; typedef typename MetricType::VirtualImageType VirtualImageType; typedef itk::CompositeTransform CompositeTransformType; typedef typename CompositeTransformType::TransformType TransformBaseType; typedef itk::DisplacementFieldTransform DisplacementFieldTransformType; typedef typename DisplacementFieldTransformType::DisplacementFieldType DisplacementFieldType; typedef typename DisplacementFieldType::PixelType DisplacementVectorType; typedef itk::ImageDuplicator DisplacementFieldDuplicatorType; protected: antsDisplacementAndVelocityFieldRegistrationCommandIterationUpdate() { m_clock.Start(); m_clock.Stop(); const itk::RealTimeClock::TimeStampType now = m_clock.GetTotal(); this->m_lastTotalTime = now; m_clock.Start(); this->m_LogStream = &std::cout; this->m_ComputeFullScaleCCInterval = 0; this->m_WriteInterationsOutputsInIntervals = 0; this->m_CurrentStageNumber = 0; } public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event ) ITK_OVERRIDE { TFilter const * const filter = dynamic_cast( object ); if( typeid( event ) == typeid( itk::InitializeEvent ) ) { const unsigned int currentLevel = filter->GetCurrentLevel(); typename TFilter::ShrinkFactorsPerDimensionContainerType shrinkFactors = filter->GetShrinkFactorsPerDimension( currentLevel ); typename TFilter::SmoothingSigmasArrayType smoothingSigmas = filter->GetSmoothingSigmasPerLevel(); typename TFilter::TransformParametersAdaptorsContainerType adaptors = filter->GetTransformParametersAdaptorsPerLevel(); bool smoothingSigmasAreInPhysicalUnits = filter->GetSmoothingSigmasAreSpecifiedInPhysicalUnits(); m_clock.Stop(); const itk::RealTimeClock::TimeStampType now = m_clock.GetTotal(); this->Logger() << " Current level = " << currentLevel + 1 << " of " << this->m_NumberOfIterations.size() << std::endl; this->Logger() << " number of iterations = " << this->m_NumberOfIterations[currentLevel] << std::endl; this->Logger() << " shrink factors = " << shrinkFactors << std::endl; this->Logger() << " smoothing sigmas = " << smoothingSigmas[currentLevel]; if( smoothingSigmasAreInPhysicalUnits ) { this->Logger() << " mm" << std::endl; } else { this->Logger() << " vox" << std::endl; } this->Logger() << " required fixed parameters = " << adaptors[currentLevel]->GetRequiredFixedParameters() << std::flush << std::endl; // this->Logger() << "\n LEVEL_TIME_INDEX: " << now << " SINCE_LAST: " << (now-this->m_lastTotalTime) << // std::endl; this->m_lastTotalTime = now; m_clock.Start(); typedef itk::GradientDescentOptimizerv4Template GradientDescentOptimizerType; GradientDescentOptimizerType * optimizer = reinterpret_cast( const_cast( filter )->GetModifiableOptimizer() ); // TODO: This looks very wrong. There is a const_cast above, and then the change // of the number of iterations here on what should be a const object. optimizer->SetNumberOfIterations( this->m_NumberOfIterations[currentLevel] ); } else if( typeid( event ) == typeid( itk::IterationEvent ) ) { const unsigned int currentLevel = filter->GetCurrentLevel(); const unsigned int lCurrentIteration = filter->GetCurrentIteration(); if( lCurrentIteration == 1 ) { if( this->m_ComputeFullScaleCCInterval != 0 ) { // Print header line one time this->Logger() << "XXDIAGNOSTIC,Iteration,metricValue,convergenceValue,ITERATION_TIME_INDEX,SINCE_LAST,FullScaleCCInterval=" << this->m_ComputeFullScaleCCInterval << std::flush << std::endl; } else { this->Logger() << "XXDIAGNOSTIC,Iteration,metricValue,convergenceValue,ITERATION_TIME_INDEX,SINCE_LAST" << std::endl; } } m_clock.Stop(); const itk::RealTimeClock::TimeStampType now = m_clock.GetTotal(); MeasureType metricValue = 0.0; const unsigned int lastIteration = this->m_NumberOfIterations[currentLevel]; if( ( this->m_ComputeFullScaleCCInterval != 0 ) && ( lCurrentIteration == 1 || (lCurrentIteration % this->m_ComputeFullScaleCCInterval == 0 ) || lCurrentIteration == lastIteration) ) { // This function finds the similarity value between the original fixed image and the original moving images // using a CC metric type with radius 4. // The feature can be used to observe the progress of the registration process at each iteration. this->UpdateFullScaleMetricValue(filter, metricValue); } if( ( this->m_WriteInterationsOutputsInIntervals != 0 ) && ( lCurrentIteration == 1 || (lCurrentIteration % this->m_WriteInterationsOutputsInIntervals == 0 ) || lCurrentIteration == lastIteration) ) { // This function writes the output volume of each iteration to the disk. // The feature can be used to observe the progress of the registration process at each iteration, // and make a short movie from the the registration process. this->WriteIntervalVolumes(filter); } else { this->Logger() << " "; // if the output of current iteration is written to disk, and star } // will appear before line, else a free space will be printed to keep visual alignment. std::streamsize ss = std::cout.precision(); this->Logger() << "1DIAGNOSTIC, " << std::setw(5) << lCurrentIteration << ", " << std::scientific << std::setprecision(12) << filter->GetCurrentMetricValue() << ", " << std::scientific << std::setprecision(12) << filter->GetCurrentConvergenceValue() << ", " << std::setprecision(4) << now << ", " << std::setprecision(4) << (now - this->m_lastTotalTime) << ", "; if( ( this->m_ComputeFullScaleCCInterval != 0 ) && fabs(metricValue) > 1e-7 ) { this->Logger() << std::scientific << std::setprecision(12) << metricValue << std::flush << std::endl; } else { this->Logger() << std::endl; } this->Logger() << std::setprecision( ss ); this->Logger().unsetf( std::ios::fixed | std::ios::scientific ); this->m_lastTotalTime = now; m_clock.Start(); } else { // Invalid event type return; } } itkSetMacro( ComputeFullScaleCCInterval, unsigned int ); itkSetMacro( WriteInterationsOutputsInIntervals, unsigned int ); itkSetMacro( CurrentStageNumber, unsigned int ); void SetNumberOfIterations( const std::vector & iterations ) { this->m_NumberOfIterations = iterations; } void SetLogStream(std::ostream & logStream) { this->m_LogStream = &logStream; } void SetOrigFixedImage(typename FixedImageType::Pointer origFixedImage) { this->m_origFixedImage = origFixedImage; } void SetOrigMovingImage(typename MovingImageType::Pointer origMovingImage) { this->m_origMovingImage = origMovingImage; } void UpdateFullScaleMetricValue(const TFilter * const filter, MeasureType & metricValue ) const { // Get the registration metric from the filter, input metric is needed to find the type of input transform. typename MetricType::ConstPointer inputMetric( dynamic_cast( filter->GetMetric() ) ); // //////////////////////////////////Define the CC Metric Type to Compute Similarity // Measure//////////////////////////// // This metric type is used to measure the general similarity metric between the original input fixed and moving // images. typename MetricType::Pointer metric; typedef itk::ANTSNeighborhoodCorrelationImageToImageMetricv4 CorrelationMetricType; typename CorrelationMetricType::Pointer correlationMetric = CorrelationMetricType::New(); { typename CorrelationMetricType::RadiusType radius; radius.Fill( 4 ); // NOTE: This is just a common reference for fine-tuning parameters, so perhaps a smaller window // would be sufficient. correlationMetric->SetRadius( radius ); } correlationMetric->SetUseMovingImageGradientFilter( false ); correlationMetric->SetUseFixedImageGradientFilter( false ); metric = correlationMetric; /* Below, the implementation is just provided for SyN registration filter. TODO: expand the similarity metric implementation for other registration types mentioned above. */ if( strcmp( inputMetric->GetMovingTransform()->GetNameOfClass(), "DisplacementFieldTransform" ) == 0 ) { /* Filter returns the SyN internal trnasforms (MovingToMiddleTransform & FixedToMiddleTransform) at each iteration. These transforms are used to generate input transforms of full scale CC metric. NOTICE: Using const_cast for filter does not make any issue because the requested outputs are copied to another objects, and there will be no change to them at future. */ // Copy the SyN internal transforms at each iteration typename DisplacementFieldTransformType::Pointer myFixedToMiddleTransform = DisplacementFieldTransformType::New(); typename DisplacementFieldTransformType::Pointer myMovingToMiddleTransform = DisplacementFieldTransformType::New(); // copy FixedToMiddleTransform typename DisplacementFieldDuplicatorType::Pointer FixedDisplacementDuplicator = DisplacementFieldDuplicatorType::New(); FixedDisplacementDuplicator->SetInputImage( const_cast( filter-> GetFixedToMiddleTransform( ) )-> GetDisplacementField() ); FixedDisplacementDuplicator->Update(); typename DisplacementFieldDuplicatorType::Pointer FixedInverseDisplacementDuplicator = DisplacementFieldDuplicatorType::New(); FixedInverseDisplacementDuplicator->SetInputImage( const_cast( filter-> GetFixedToMiddleTransform( ) )-> GetInverseDisplacementField() ); FixedInverseDisplacementDuplicator->Update(); myFixedToMiddleTransform->SetDisplacementField( FixedDisplacementDuplicator->GetModifiableOutput() ); myFixedToMiddleTransform->SetInverseDisplacementField( FixedInverseDisplacementDuplicator->GetModifiableOutput() ); // copy MovingToMiddleTransform typename DisplacementFieldDuplicatorType::Pointer MovingDisplacementDuplicator = DisplacementFieldDuplicatorType::New(); MovingDisplacementDuplicator->SetInputImage( const_cast( filter-> GetMovingToMiddleTransform( ) )-> GetDisplacementField() ); MovingDisplacementDuplicator->Update(); typename DisplacementFieldDuplicatorType::Pointer MovingInverseDisplacementDuplicator = DisplacementFieldDuplicatorType::New(); MovingInverseDisplacementDuplicator->SetInputImage( const_cast( filter-> GetMovingToMiddleTransform( ) )-> GetInverseDisplacementField() ); MovingInverseDisplacementDuplicator->Update(); myMovingToMiddleTransform->SetDisplacementField( MovingDisplacementDuplicator->GetModifiableOutput() ); myMovingToMiddleTransform->SetInverseDisplacementField( MovingInverseDisplacementDuplicator->GetModifiableOutput() ); // Based on SyN Registration implementation, fixed composite and moving composite transforms are generated to // compute the metric value at each iteration. typedef typename TFilter::InitialTransformType InitialTransformType; typename CompositeTransformType::Pointer fixedComposite = CompositeTransformType::New(); typename CompositeTransformType::Pointer movingComposite = CompositeTransformType::New(); fixedComposite->AddTransform( const_cast( filter->GetFixedInitialTransform() ) ); fixedComposite->AddTransform( myFixedToMiddleTransform->GetInverseTransform() ); fixedComposite->FlattenTransformQueue(); fixedComposite->SetOnlyMostRecentTransformToOptimizeOn(); movingComposite->AddTransform( const_cast( filter->GetMovingInitialTransform() ) ); movingComposite->AddTransform( myMovingToMiddleTransform->GetInverseTransform() ); movingComposite->FlattenTransformQueue(); movingComposite->SetOnlyMostRecentTransformToOptimizeOn(); // SyN uses the above composite transforms to compute the current metric value in two ways as follows: /* At the first method, the input images are downsampled by the fixed and moving transforms, and then, the output of resamplers are passed to the CC similarity metric with identity transforms. */ if( filter->GetDownsampleImagesForMetricDerivatives() ) { typedef itk::ResampleImageFilter FixedResamplerType; typename FixedResamplerType::Pointer fixedResampler = FixedResamplerType::New(); fixedResampler->SetTransform( fixedComposite ); fixedResampler->SetInput( this->m_origFixedImage ); fixedResampler->SetOutputParametersFromImage( this->m_origFixedImage ); fixedResampler->SetDefaultPixelValue( 0 ); fixedResampler->Update(); typedef itk::ResampleImageFilter MovingResamplerType; typename MovingResamplerType::Pointer movingResampler = MovingResamplerType::New(); movingResampler->SetTransform( movingComposite ); movingResampler->SetInput( this->m_origMovingImage ); movingResampler->SetOutputParametersFromImage( this->m_origFixedImage ); movingResampler->SetDefaultPixelValue( 0 ); movingResampler->Update(); typedef typename itk::IdentityTransform IdentityTransformType; typename IdentityTransformType::Pointer identityTransform = IdentityTransformType::New(); const DisplacementVectorType zeroVector( 0.0 ); typename DisplacementFieldType::Pointer identityField = DisplacementFieldType::New(); identityField->CopyInformation( this->m_origFixedImage ); identityField->SetRegions( this->m_origFixedImage->GetRequestedRegion() ); identityField->Allocate(); identityField->FillBuffer( zeroVector ); typename DisplacementFieldTransformType::Pointer identityDisplacementFieldTransform = DisplacementFieldTransformType::New(); identityDisplacementFieldTransform->SetDisplacementField( identityField ); metric->SetFixedImage( fixedResampler->GetOutput() ); metric->SetFixedTransform( identityTransform ); metric->SetMovingImage( movingResampler->GetOutput() ); metric->SetMovingTransform( identityDisplacementFieldTransform ); } /* At the second method, the computed fixed and moving composite transforms are passed to the CC similarity metric directly with the full scale fixed and moving images. */ else if( !( filter->GetDownsampleImagesForMetricDerivatives() ) ) { metric->SetFixedImage( this->m_origFixedImage ); metric->SetFixedTransform( fixedComposite ); metric->SetMovingImage( this->m_origMovingImage ); metric->SetMovingTransform( movingComposite ); } } metric->SetVirtualDomainFromImage( this->m_origFixedImage ); metric->Initialize(); metricValue = metric->GetValue(); } void WriteIntervalVolumes(TFilter const * const filter) const { // ////////////////////////// // Get output transform from the registration filter at each iteration // It can be useful in some cases e.g. when we want to present the registration progress as a series of consequent // pictures. typename DisplacementFieldTransformType::Pointer OutputTransformAtCurrentIteration = DisplacementFieldTransformType::New(); // Filter return the MovingToMiddleTransform and FixedToMiddleTransform of each iteration, so they should be // composed to generate the final output transform of current iteration // Notice that using const_cast for filter does not make any issue, because the inputMovingTransform will never be // used in any processing. It is only copied to another transform. typedef itk::ComposeDisplacementFieldsImageFilter ComposerType; typename ComposerType::Pointer composer = ComposerType::New(); composer->SetDisplacementField( const_cast( filter->GetMovingToMiddleTransform() )->GetInverseDisplacementField() ); composer->SetWarpingField( const_cast( filter->GetFixedToMiddleTransform() )->GetDisplacementField() ); composer->Update(); typename ComposerType::Pointer inverseComposer = ComposerType::New(); inverseComposer->SetDisplacementField( const_cast( filter-> GetFixedToMiddleTransform() ) ->GetInverseDisplacementField() ); inverseComposer->SetWarpingField( const_cast( filter->GetMovingToMiddleTransform() )->GetDisplacementField() ); inverseComposer->Update(); OutputTransformAtCurrentIteration->SetDisplacementField( composer->GetOutput() ); OutputTransformAtCurrentIteration->SetInverseDisplacementField( inverseComposer->GetOutput() ); // Now this output transform is copied to another instance to prevent undesired changes. typename DisplacementFieldDuplicatorType::Pointer disDuplicator = DisplacementFieldDuplicatorType::New(); disDuplicator->SetInputImage( OutputTransformAtCurrentIteration->GetDisplacementField() ); disDuplicator->Update(); typename DisplacementFieldDuplicatorType::Pointer disInverseDuplicator = DisplacementFieldDuplicatorType::New(); disInverseDuplicator->SetInputImage( OutputTransformAtCurrentIteration->GetInverseDisplacementField() ); disInverseDuplicator->Update(); typename DisplacementFieldTransformType::Pointer outputTransformReadyToUse = DisplacementFieldTransformType::New(); outputTransformReadyToUse->SetDisplacementField( disDuplicator->GetModifiableOutput() ); outputTransformReadyToUse->SetInverseDisplacementField( disInverseDuplicator->GetModifiableOutput() ); // Now add this updated transform to the composite transform including the initial trnasform typedef typename TFilter::InitialTransformType InitialTransformType; typename CompositeTransformType::Pointer outputCompositTransform = CompositeTransformType::New(); if( filter->GetMovingInitialTransform() ) { outputCompositTransform->AddTransform( const_cast( filter->GetMovingInitialTransform() ) ); } outputCompositTransform->AddTransform( outputTransformReadyToUse ); outputCompositTransform->FlattenTransformQueue(); outputCompositTransform->SetOnlyMostRecentTransformToOptimizeOn(); // Now we use the output transform to get warped image using linear interpolation typedef itk::LinearInterpolateImageFunction LinearInterpolatorType; typename LinearInterpolatorType::Pointer linearInterpolator = LinearInterpolatorType::New(); typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetTransform( outputCompositTransform ); resampler->SetInput( this->m_origMovingImage ); resampler->SetOutputParametersFromImage( this->m_origFixedImage ); resampler->SetInterpolator( linearInterpolator ); resampler->SetDefaultPixelValue( 0 ); resampler->Update(); // write the results to the disk const unsigned int curLevel = filter->GetCurrentLevel(); const unsigned int curIter = filter->GetCurrentIteration(); std::stringstream currentFileName; currentFileName << "Stage" << this->m_CurrentStageNumber + 1 << "_level" << curLevel + 1; /* The name arrangement of written files are important to us. To prevent: "Iter1 Iter10 Iter2 Iter20" we use the following style. Then the order is: "Iter1 Iter2 ... Iters10 ... Itert20" */ if( curIter > 9 ) { currentFileName << "_Iters" << curIter << ".nii.gz"; } else if( curIter > 19 ) { currentFileName << "_Itert" << curIter << ".nii.gz"; } else { currentFileName << "_Iter" << curIter << ".nii.gz"; } std::cout << "*"; // The star befor each DIAGNOSTIC shows that its output is writtent out. typedef itk::ImageFileWriter WarpedImageWriterType; typename WarpedImageWriterType::Pointer writer = WarpedImageWriterType::New(); writer->SetFileName( currentFileName.str().c_str() ); writer->SetInput( resampler->GetOutput() ); try { writer->Update(); } catch( itk::ExceptionObject & err ) { std::cout << "Can't write warped image " << currentFileName.str().c_str() << std::endl; std::cout << "Exception Object caught: " << std::endl; std::cout << err << std::endl; } } private: std::ostream & Logger() const { return *m_LogStream; } /** * WeakPointer to the Optimizer */ // itk::WeakPointer m_Optimizer; std::vector m_NumberOfIterations; std::ostream * m_LogStream; itk::TimeProbe m_clock; itk::RealTimeClock::TimeStampType m_lastTotalTime; unsigned int m_ComputeFullScaleCCInterval; unsigned int m_WriteInterationsOutputsInIntervals; unsigned int m_CurrentStageNumber; typename FixedImageType::Pointer m_origFixedImage; typename MovingImageType::Pointer m_origMovingImage; }; }; // end namespace ants #endif // antsDisplacementAndVelocityFieldRegistrationCommandIterationUpdate__h_ ants-2.2.0/Examples/antsJointFusion.cxx000066400000000000000000001037451311104306400201220ustar00rootroot00000000000000#include "antsCommandLineParser.h" #include "antsUtilities.h" #include "antsAllocImage.h" #include "ReadWriteData.h" #include "itkNumericSeriesFileNames.h" #include "itkTimeProbe.h" #include "itkWeightedVotingFusionImageFilter.h" #include "stdio.h" #include #include #include #include #include "ANTsVersion.h" namespace ants { template class CommandProgressUpdate : public itk::Command { public: typedef CommandProgressUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( CommandProgressUpdate ); protected: CommandProgressUpdate() : m_CurrentProgress( 0 ), m_StartNewLine( true ) {}; typedef TFilter FilterType; unsigned int m_CurrentProgress; bool m_StartNewLine; public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { const TFilter * filter = dynamic_cast( caller ); if( this->m_CurrentProgress == 0 && ! filter->GetIsWeightedAveragingComplete() ) { std::cout << "Weighted averaging: " << std::flush; } else if( this->m_StartNewLine && filter->GetIsWeightedAveragingComplete() ) { std::cout << std::endl << "Reconstruction: " << std::flush; this->m_StartNewLine = false; this->m_CurrentProgress = 0; } itk::ProcessObject *po = dynamic_cast( caller ); if (! po) return; // std::cout << po->GetProgress() << std::endl; if( typeid( event ) == typeid ( itk::ProgressEvent ) ) { if( this->m_CurrentProgress < 99 ) { this->m_CurrentProgress++; if( this->m_CurrentProgress % 10 == 0 ) { std::cout << this->m_CurrentProgress << std::flush; } else { std::cout << "*" << std::flush; } } } } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { const TFilter * filter = dynamic_cast( object ); if( this->m_CurrentProgress == 0 && ! filter->GetIsWeightedAveragingComplete() ) { std::cout << "Weighted averaging: " << std::flush; } else if( this->m_StartNewLine && filter->GetIsWeightedAveragingComplete() ) { std::cout << std::endl << "Reconstruction: " << std::flush; this->m_StartNewLine = false; this->m_CurrentProgress = 0; } itk::ProcessObject *po = dynamic_cast( const_cast( object ) ); if (! po) return; if( typeid( event ) == typeid ( itk::ProgressEvent ) ) { if( this->m_CurrentProgress < 99 ) { this->m_CurrentProgress++; if( this->m_CurrentProgress % 10 == 0 ) { std::cout << this->m_CurrentProgress << std::flush; } else { std::cout << "*" << std::flush; } } } } }; template int antsJointFusion( itk::ants::CommandLineParser *parser ) { typedef float RealType; typedef itk::Image ImageType; typedef itk::Image LabelImageType; typedef LabelImageType MaskImageType; typedef typename itk::ants::CommandLineParser::OptionType OptionType; // Determine verbosity of output bool verbose = false; typename OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } if( verbose ) { std::cout << std::endl << "Running antsJointFusion for " << ImageDimension << "-dimensional images." << std::endl << std::endl; } // Instantiate the joint fusion filter typedef itk::WeightedVotingFusionImageFilter FusionFilterType; typename FusionFilterType::Pointer fusionFilter = FusionFilterType::New(); typedef typename LabelImageType::PixelType LabelType; // Get the alpha and beta parameters RealType alpha = 0.1; typename OptionType::Pointer alphaOption = parser->GetOption( "alpha" ); if( alphaOption && alphaOption->GetNumberOfFunctions() ) { alpha = parser->Convert( alphaOption->GetFunction( 0 )->GetName() ); } RealType beta = 2.0; typename OptionType::Pointer betaOption = parser->GetOption( "beta" ); if( betaOption && betaOption->GetNumberOfFunctions() ) { beta = parser->Convert( betaOption->GetFunction( 0 )->GetName() ); } fusionFilter->SetAlpha( alpha ); fusionFilter->SetBeta( beta ); // Get the search and patch radii typename OptionType::Pointer searchRadiusOption = parser->GetOption( "search-radius" ); if( searchRadiusOption && searchRadiusOption->GetNumberOfFunctions() ) { // try reading the search radius as an image first. std::string searchRadiusString = searchRadiusOption->GetFunction( 0 )->GetName(); if( itksys::SystemTools::FileExists( searchRadiusString.c_str() ) ) { typedef typename FusionFilterType::RadiusImageType RadiusImageType; typename RadiusImageType::Pointer searchRadiusImage; bool fileReadSuccessfully = ReadImage( searchRadiusImage, searchRadiusString.c_str() ); if( fileReadSuccessfully ) { fusionFilter->SetNeighborhoodSearchRadiusImage( searchRadiusImage ); } else { if( verbose ) { std::cerr << "Search radius image exists but was not read successfully.." << std::endl; } return EXIT_FAILURE; } } else { std::vector searchRadius; searchRadius.push_back( 3 ); if( searchRadiusOption && searchRadiusOption->GetNumberOfFunctions() ) { searchRadius = parser->ConvertVector( searchRadiusString ); } if( searchRadius.size() == 1 ) { for( unsigned int d = 1; d < ImageDimension; d++ ) { searchRadius.push_back( searchRadius[0] ); } } if( searchRadius.size() != ImageDimension ) { if( verbose ) { std::cerr << "Search radius specified incorrectly. Please see usage options." << std::endl; } return EXIT_FAILURE; } typename FusionFilterType::NeighborhoodRadiusType searchNeighborhoodRadius; for( unsigned int d = 0; d < ImageDimension; d++ ) { searchNeighborhoodRadius[d] = searchRadius[d]; } fusionFilter->SetNeighborhoodSearchRadius( searchNeighborhoodRadius ); } } std::vector patchRadius; patchRadius.push_back( 2 ); typename OptionType::Pointer patchRadiusOption = parser->GetOption( "patch-radius" ); if( patchRadiusOption && patchRadiusOption->GetNumberOfFunctions() ) { patchRadius = parser->ConvertVector( patchRadiusOption->GetFunction( 0 )->GetName() ); } if( patchRadius.size() == 1 ) { for( unsigned int d = 1; d < ImageDimension; d++ ) { patchRadius.push_back( patchRadius[0] ); } } if( patchRadius.size() != ImageDimension ) { if( verbose ) { std::cerr << "Patch radius specified incorrectly. Please see usage options." << std::endl; } return EXIT_FAILURE; } typename FusionFilterType::NeighborhoodRadiusType patchNeighborhoodRadius; for( unsigned int d = 0; d < ImageDimension; d++ ) { patchNeighborhoodRadius[d] = patchRadius[d]; } fusionFilter->SetNeighborhoodPatchRadius( patchNeighborhoodRadius ); // Check if the user wants to retain atlas voting and/or label posterior images bool retainAtlasVotingImages = false; bool retainLabelPosteriorImages = false; typename OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { retainLabelPosteriorImages = true; } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { retainAtlasVotingImages = true; } } // typename OptionType::Pointer retainLabelPosteriorOption = parser->GetOption( "retain-label-posterior-images" ); // if( retainLabelPosteriorOption && retainLabelPosteriorOption->GetNumberOfFunctions() > 0 ) // { // retainLabelPosteriorImages = parser->Convert( retainLabelPosteriorOption->GetFunction()->GetName() ); // } // // typename OptionType::Pointer retainAtlasVotingOption = parser->GetOption( "retain-atlas-voting-images" ); // if( retainAtlasVotingOption && retainAtlasVotingOption->GetNumberOfFunctions() > 0 ) // { // retainAtlasVotingImages = parser->Convert( retainAtlasVotingOption->GetFunction()->GetName() ); // } bool constrainSolutionToNonnegativeWeights = false; typename OptionType::Pointer constrainWeightsOption = parser->GetOption( "constrain-nonnegative" ); if( constrainWeightsOption && constrainWeightsOption->GetNumberOfFunctions() > 0 ) { constrainSolutionToNonnegativeWeights = parser->Convert( constrainWeightsOption->GetFunction()->GetName() ); } typename OptionType::Pointer metricOption = parser->GetOption( "patch-metric" ); if( metricOption && metricOption->GetNumberOfFunctions() > 0 ) { std::string metricString = metricOption->GetFunction()->GetName(); ConvertToLowerCase( metricString ); if( metricString.compare( "pc" ) == 0 ) { fusionFilter->SetSimilarityMetric( FusionFilterType::PEARSON_CORRELATION ); } else if( metricString.compare( "msq" ) == 0 ) { fusionFilter->SetSimilarityMetric( FusionFilterType::MEAN_SQUARES ); } else { std::cerr << "Unrecognized metric option. See help menu." << std::endl; return EXIT_FAILURE; } } fusionFilter->SetRetainAtlasVotingWeightImages( retainAtlasVotingImages ); fusionFilter->SetRetainLabelPosteriorProbabilityImages( retainLabelPosteriorImages ); fusionFilter->SetConstrainSolutionToNonnegativeWeights( constrainSolutionToNonnegativeWeights ); // Get the target image unsigned int numberOfTargetModalities = 0; typename FusionFilterType::InputImageList targetImageList; typename OptionType::Pointer targetImageOption = parser->GetOption( "target-image" ); if( targetImageOption && targetImageOption->GetNumberOfFunctions() ) { if( targetImageOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { typename ImageType::Pointer targetImage = ITK_NULLPTR; std::string targetFile = targetImageOption->GetFunction( 0 )->GetName(); ReadImage( targetImage, targetFile.c_str() ); targetImageList.push_back( targetImage ); numberOfTargetModalities = 1; } else { numberOfTargetModalities = targetImageOption->GetFunction( 0 )->GetNumberOfParameters(); for( unsigned int n = 0; n < numberOfTargetModalities; n++ ) { typename ImageType::Pointer targetImage = ITK_NULLPTR; std::string targetFile = targetImageOption->GetFunction( 0 )->GetParameter( n ); ReadImage( targetImage, targetFile.c_str() ); targetImageList.push_back( targetImage ); } } } else { if( verbose ) { std::cerr << "Target image(s) not specified." << std::endl; } return EXIT_FAILURE; } fusionFilter->SetTargetImage( targetImageList ); // Get the atlas images and segmentations typename OptionType::Pointer atlasImageOption = parser->GetOption( "atlas-image" ); typename OptionType::Pointer atlasSegmentationOption = parser->GetOption( "atlas-segmentation" ); unsigned int numberOfAtlases = 0; unsigned int numberOfAtlasSegmentations = 0; unsigned int numberOfAtlasModalities = 0; if( atlasImageOption && atlasImageOption->GetNumberOfFunctions() ) { numberOfAtlases = atlasImageOption->GetNumberOfFunctions(); } if( atlasSegmentationOption && atlasSegmentationOption->GetNumberOfFunctions() ) { numberOfAtlasSegmentations = atlasSegmentationOption->GetNumberOfFunctions(); } if( numberOfAtlases < 2 ) { if( verbose ) { std::cerr << "At least 2 atlases are required." << std::endl; } return EXIT_FAILURE; } if( numberOfAtlasSegmentations != 0 && numberOfAtlasSegmentations != numberOfAtlases ) { if( verbose ) { std::cout << "Warning: the number of atlases does not match the number of " << "segmentations. Only performing joint intensity fusion." << std::endl; } numberOfAtlasSegmentations = 0; } for( unsigned int m = 0; m < numberOfAtlases; m++ ) { typename FusionFilterType::InputImageList atlasImageList; typename LabelImageType::Pointer atlasSegmentation = ITK_NULLPTR; if( atlasImageOption->GetFunction( m )->GetNumberOfParameters() == 0 ) { numberOfAtlasModalities = 1; if( numberOfTargetModalities != 1 ) { if( verbose ) { std::cerr << "The number of atlas modalities does not match the number of target modalities." << std::endl; } return EXIT_FAILURE; } typename ImageType::Pointer atlasImage = ITK_NULLPTR; std::string atlasFile = atlasImageOption->GetFunction( m )->GetName(); ReadImage( atlasImage, atlasFile.c_str() ); atlasImageList.push_back( atlasImage ); } else { if( m == 0 ) { numberOfAtlasModalities = atlasImageOption->GetFunction( m )->GetNumberOfParameters(); } if( numberOfAtlasModalities != atlasImageOption->GetFunction( m )->GetNumberOfParameters() ) { if( verbose ) { std::cerr << "The number of atlas modalities does not match the number of target modalities." << std::endl; } return EXIT_FAILURE; } for( unsigned int n = 0; n < numberOfAtlasModalities; n++ ) { typename ImageType::Pointer atlasImage = ITK_NULLPTR; std::string atlasFile = atlasImageOption->GetFunction( m )->GetParameter( n ); ReadImage( atlasImage, atlasFile.c_str() ); atlasImageList.push_back( atlasImage ); } } if( numberOfAtlasSegmentations > 0 ) { std::string atlasSegmentationFile = atlasSegmentationOption->GetFunction( m )->GetName(); ReadImage( atlasSegmentation, atlasSegmentationFile.c_str() ); } fusionFilter->AddAtlas( atlasImageList, atlasSegmentation ); } // Get the exclusion images typename OptionType::Pointer exclusionImageOption = parser->GetOption( "exclusion-image" ); if( exclusionImageOption && exclusionImageOption->GetNumberOfFunctions() ) { for( unsigned int n = 0; n < exclusionImageOption->GetNumberOfFunctions(); n++ ) { LabelType label = parser->Convert( exclusionImageOption->GetFunction( n )->GetName() ); typename LabelImageType::Pointer exclusionImage = ITK_NULLPTR; std::string exclusionFile = exclusionImageOption->GetFunction( n )->GetParameter( 0 ); ReadImage( exclusionImage, exclusionFile.c_str() ); fusionFilter->AddLabelExclusionImage( label, exclusionImage ); } } // Get the mask typename itk::ants::CommandLineParser::OptionType::Pointer maskImageOption = parser->GetOption( "mask-image" ); if( maskImageOption && maskImageOption->GetNumberOfFunctions() ) { typename MaskImageType::Pointer maskImage = ITK_NULLPTR; std::string inputFile = maskImageOption->GetFunction( 0 )->GetName(); ReadImage( maskImage, inputFile.c_str() ); fusionFilter->SetMaskImage( maskImage ); } // Run the fusion program itk::TimeProbe timer; timer.Start(); if( verbose ) { typedef CommandProgressUpdate CommandType; typename CommandType::Pointer observer = CommandType::New(); fusionFilter->AddObserver( itk::ProgressEvent(), observer ); } try { fusionFilter->Update(); } catch( itk::ExceptionObject & e ) { if( verbose ) { std::cerr << "Exception caught: " << e << std::endl; } return EXIT_FAILURE; } timer.Stop(); if( verbose ) { std::cout << std::endl << std::endl; fusionFilter->Print( std::cout, 3 ); } // write the output if( verbose ) { std::cout << std::endl << "Writing output:" << std::endl; } if( outputOption && outputOption->GetNumberOfFunctions() ) { std::string labelFusionName; std::string intensityFusionName; std::string labelPosteriorName; std::string atlasVotingName; if( outputOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { if( numberOfAtlasSegmentations != 0 ) { labelFusionName = outputOption->GetFunction( 0 )->GetName(); } else { intensityFusionName = outputOption->GetFunction( 0 )->GetName(); } } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { if( numberOfAtlasSegmentations != 0 ) { labelFusionName = outputOption->GetFunction( 0 )->GetParameter( 0 ); } } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { intensityFusionName = outputOption->GetFunction( 0 )->GetParameter( 1 ); } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { if( numberOfAtlasSegmentations != 0 ) { labelPosteriorName = outputOption->GetFunction( 0 )->GetParameter( 2 ); } } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { atlasVotingName = outputOption->GetFunction( 0 )->GetParameter( 3 ); } if( !labelFusionName.empty() ) { WriteImage( fusionFilter->GetOutput(), labelFusionName.c_str() ); } if( !intensityFusionName.empty() ) { itk::NumericSeriesFileNames::Pointer fileNamesCreator = itk::NumericSeriesFileNames::New(); fileNamesCreator->SetStartIndex( 1 ); fileNamesCreator->SetEndIndex( numberOfAtlasModalities ); fileNamesCreator->SetSeriesFormat( intensityFusionName.c_str() ); const std::vector & imageNames = fileNamesCreator->GetFileNames(); for( unsigned int i = 0; i < imageNames.size(); i++ ) { if( verbose ) { std::cout << " Writing intensity fusion image (modality " << i + 1 << ")" << std::endl; } typename ImageType::Pointer jointIntensityFusionImage = fusionFilter->GetJointIntensityFusionImage( i ); WriteImage( jointIntensityFusionImage, imageNames[i].c_str() ); } } if( !labelPosteriorName.empty() && fusionFilter->GetRetainLabelPosteriorProbabilityImages() ) { typename FusionFilterType::LabelSetType labelSet = fusionFilter->GetLabelSet(); typename FusionFilterType::LabelSetType::const_iterator labelIt; for( labelIt = labelSet.begin(); labelIt != labelSet.end(); ++labelIt ) { if( *labelIt == 0 ) { continue; } if( verbose ) { std::cout << " Writing label probability image (label " << *labelIt << ")" << std::endl; } char buffer[256]; std::snprintf( buffer, sizeof( buffer ), labelPosteriorName.c_str(), *labelIt ); WriteImage( fusionFilter->GetLabelPosteriorProbabilityImage( *labelIt ), buffer ); } } if( !atlasVotingName.empty() && fusionFilter->GetRetainAtlasVotingWeightImages() ) { itk::NumericSeriesFileNames::Pointer fileNamesCreator = itk::NumericSeriesFileNames::New(); fileNamesCreator->SetStartIndex( 1 ); fileNamesCreator->SetEndIndex( numberOfAtlases ); fileNamesCreator->SetSeriesFormat( atlasVotingName.c_str() ); const std::vector & imageNames = fileNamesCreator->GetFileNames(); for( unsigned int i = 0; i < imageNames.size(); i++ ) { if( verbose ) { std::cout << " Writing atlas voting image (atlas " << i+1 << ")" << std::endl; } WriteImage( fusionFilter->GetAtlasVotingWeightImage( i ), imageNames[i].c_str() ); } } } if( verbose ) { std::cout << "Elapsed time: " << timer.GetMean() << std::endl; } return EXIT_SUCCESS; } void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, the program tries to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "image-dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3/4" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The target image (or multimodal target images) assumed to be " ) + std::string( "aligned to a common image domain." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "target-image" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "targetImage" ); option->SetUsageOption( 1, "[targetImageModality0,targetImageModality1,...,targetImageModalityN]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The atlas image (or multimodal atlas images) assumed to be " ) + std::string( "aligned to a common image domain." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "atlas-image" ); option->SetShortName( 'g' ); option->SetUsageOption( 0, "atlasImage" ); option->SetUsageOption( 1, "[atlasImageModality0,atlasImageModality1,...,atlasImageModalityN]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The atlas segmentation images. For performing label fusion the number of " ) + std::string( "specified segmentations should be identical to the number of atlas image sets." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "atlas-segmentation" ); option->SetShortName( 'l' ); option->SetUsageOption( 0, "atlasSegmentation" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Regularization term added to matrix Mx for calculating the inverse. Default = 0.1" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "alpha" ); option->SetShortName( 'a' ); option->SetUsageOption( 0, "0.1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Exponent for mapping intensity difference to the joint error. Default = 2.0" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "beta" ); option->SetShortName( 'b' ); option->SetUsageOption( 0, "2.0" ); option->SetDescription( description ); parser->AddOption( option ); } // { // std::string description = // std::string( "Retain label posterior probability images. Requires atlas segmentations " ) // + std::string( "to be specified. Default = false" ); // // OptionType::Pointer option = OptionType::New(); // option->SetLongName( "retain-label-posterior-images" ); // option->SetShortName( 'r' ); // option->SetUsageOption( 0, "(0)/1" ); // option->SetDescription( description ); // parser->AddOption( option ); // } // // { // std::string description = // std::string( "Retain atlas voting images. Default = false" ); // // OptionType::Pointer option = OptionType::New(); // option->SetLongName( "retain-atlas-voting-images" ); // option->SetShortName( 'f' ); // option->SetUsageOption( 0, "(0)/1" ); // option->SetDescription( description ); // parser->AddOption( option ); // } { std::string description = std::string( "Constrain solution to non-negative weights." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'c' ); option->SetLongName( "constrain-nonnegative" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Patch radius for similarity measures. Default = 2x2x2" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "patch-radius" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "2" ); option->SetUsageOption( 1, "2x2x2" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Metric to be used in determining the most similar neighborhood patch. " ) + std::string( "Options include Pearson's correlation (PC) and mean squares (MSQ). " ) + std::string( "Default = PC (Pearson correlation)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "patch-metric" ); option->SetShortName( 'm' ); option->SetUsageOption( 0, "(PC)/MSQ" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Search radius for similarity measures. Default = 3x3x3. One " ) + std::string( "can also specify an image where the value at the voxel specifies " ) + std::string( "the isotropic search radius at that voxel." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "search-radius" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "3" ); option->SetUsageOption( 1, "3x3x3" ); option->SetUsageOption( 2, "searchRadiusMap.nii.gz" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify an exclusion region for the given label." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "exclusion-image" ); option->SetShortName( 'e' ); option->SetUsageOption( 0, "label[exclusionImage]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "If a mask image is specified, fusion is only performed in the mask region." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask-image" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "maskImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The output is the intensity and/or label fusion image. Additional " ) + std::string( "optional outputs include the label posterior probability images " ) + std::string( "and the atlas voting weight images." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "labelFusionImage" ); option->SetUsageOption( 1, "intensityFusionImageFileNameFormat" ); option->SetUsageOption( 2, "[labelFusionImage,intensityFusionImageFileNameFormat,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Get version information." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "version" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsJointFusion( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsJointFusion" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "antsJointFusion is an image fusion algorithm developed by Hongzhi Wang and " ) + std::string( "Paul Yushkevich which won segmentation challenges at MICCAI 2012 and MICCAI 2013. " ) + std::string( "The original label fusion framework was extended to accommodate intensities by " ) + std::string( "Brian Avants. This implementation is based on Paul's original ITK-style " ) + std::string( "implementation and Brian's ANTsR implementation. References include 1) H. Wang, " ) + std::string( "J. W. Suh, S. Das, J. Pluta, C. Craige, P. Yushkevich, Multi-atlas " ) + std::string( "segmentation with joint label fusion IEEE Trans. on Pattern " ) + std::string( "Analysis and Machine Intelligence, 35(3), 611-623, 2013. and 2) " ) + std::string( "H. Wang and P. A. Yushkevich, Multi-atlas segmentation with joint " ) + std::string( "label fusion and corrective learning--an open source implementation, " ) + std::string( "Front. Neuroinform., 2013. " ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc == 1 ) { parser->PrintMenu( std::cerr, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Show automatic version itk::ants::CommandLineParser::OptionType::Pointer versionOption = parser->GetOption( "version" ); if( versionOption && versionOption->GetNumberOfFunctions() ) { std::string versionFunction = versionOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( versionFunction ); if( versionFunction.compare( "1" ) == 0 || versionFunction.compare( "true" ) == 0 ) { //Print Version Information std::cout << ANTs::Version::ExtendedVersionString() << std::endl; return EXIT_SUCCESS; } } // Get dimensionality unsigned int dimension = 3; itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "image-dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { // Read in the first intensity image to get the image dimension. std::string filename; itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "target-image" ); if( imageOption && imageOption->GetNumberOfFunctions() > 0 ) { if( imageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { filename = imageOption->GetFunction( 0 )->GetParameter( 0 ); } else { filename = imageOption->GetFunction( 0 )->GetName(); } } else { std::cerr << "No input images were specified. Specify an input image" << " with the -t option" << std::endl; return EXIT_FAILURE; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); dimension = imageIO->GetNumberOfDimensions(); } switch( dimension ) { case 2: { return antsJointFusion<2>( parser ); } break; case 3: { return antsJointFusion<3>( parser ); } break; case 4: { return antsJointFusion<4>( parser ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsJointTensorFusion.cxx000066400000000000000000001027761311104306400213200ustar00rootroot00000000000000#include "antsCommandLineParser.h" #include "antsUtilities.h" #include "antsAllocImage.h" #include "ReadWriteData.h" #include "itkNumericSeriesFileNames.h" #include "itkTimeProbe.h" #include "itkWeightedVotingFusionImageFilter.h" #include "itkDiffusionTensor3D.h" #include "itkNthElementImageAdaptor.h" #include "itkCastImageFilter.h" #include "itkImageRegionIteratorWithIndex.h" #include "stdio.h" #include #include #include #include #include "ANTsVersion.h" namespace ants { template class CommandProgressUpdate : public itk::Command { public: typedef CommandProgressUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( CommandProgressUpdate ); protected: CommandProgressUpdate() : m_CurrentProgress( 0 ) {}; typedef TFilter FilterType; unsigned int m_CurrentProgress; public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { itk::ProcessObject *po = dynamic_cast( caller ); if (! po) return; // std::cout << po->GetProgress() << std::endl; if( typeid( event ) == typeid ( itk::ProgressEvent ) ) { if( this->m_CurrentProgress < 99 ) { this->m_CurrentProgress++; if( this->m_CurrentProgress % 10 == 0 ) { std::cout << this->m_CurrentProgress << std::flush; } else { std::cout << "*" << std::flush; } } } } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { itk::ProcessObject *po = dynamic_cast( const_cast( object ) ); if (! po) return; if( typeid( event ) == typeid ( itk::ProgressEvent ) ) { if( this->m_CurrentProgress < 99 ) { this->m_CurrentProgress++; if( this->m_CurrentProgress % 10 == 0 ) { std::cout << this->m_CurrentProgress << std::flush; } else { std::cout << "*" << std::flush; } } } } }; template int antsJointTensorFusion( itk::ants::CommandLineParser *parser ) { typedef float RealType; typedef itk::DiffusionTensor3D TensorType; typedef itk::Image ImageType; typedef itk::Image TensorImageType; typedef itk::NthElementImageAdaptor TensorAdaptorType; typedef itk::CastImageFilter CastFilterType; typedef itk::Image LabelImageType; typedef LabelImageType MaskImageType; typedef typename itk::ants::CommandLineParser::OptionType OptionType; // Determine verbosity of output bool verbose = false; typename OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } if( verbose ) { std::cout << std::endl << "Running antsJointTensorFusion for " << ImageDimension << "-dimensional images." << std::endl << std::endl; } // Instantiate the joint fusion filter typedef itk::WeightedVotingFusionImageFilter FusionFilterType; typename FusionFilterType::Pointer fusionFilter = FusionFilterType::New(); typedef typename LabelImageType::PixelType LabelType; // Get the alpha and beta parameters RealType alpha = 0.1; typename OptionType::Pointer alphaOption = parser->GetOption( "alpha" ); if( alphaOption && alphaOption->GetNumberOfFunctions() ) { alpha = parser->Convert( alphaOption->GetFunction( 0 )->GetName() ); } RealType beta = 2.0; typename OptionType::Pointer betaOption = parser->GetOption( "beta" ); if( betaOption && betaOption->GetNumberOfFunctions() ) { beta = parser->Convert( betaOption->GetFunction( 0 )->GetName() ); } fusionFilter->SetAlpha( alpha ); fusionFilter->SetBeta( beta ); // Get the search and patch radii std::vector searchRadius; searchRadius.push_back( 3 ); typename OptionType::Pointer searchRadiusOption = parser->GetOption( "search-radius" ); if( searchRadiusOption && searchRadiusOption->GetNumberOfFunctions() ) { searchRadius = parser->ConvertVector( searchRadiusOption->GetFunction( 0 )->GetName() ); } if( searchRadius.size() == 1 ) { for( unsigned int d = 1; d < ImageDimension; d++ ) { searchRadius.push_back( searchRadius[0] ); } } if( searchRadius.size() != ImageDimension ) { if( verbose ) { std::cerr << "Search radius specified incorrectly. Please see usage options." << std::endl; } return EXIT_FAILURE; } typename FusionFilterType::NeighborhoodRadiusType searchNeighborhoodRadius; for( unsigned int d = 0; d < ImageDimension; d++ ) { searchNeighborhoodRadius[d] = searchRadius[d]; } std::vector patchRadius; patchRadius.push_back( 2 ); typename OptionType::Pointer patchRadiusOption = parser->GetOption( "patch-radius" ); if( patchRadiusOption && patchRadiusOption->GetNumberOfFunctions() ) { patchRadius = parser->ConvertVector( patchRadiusOption->GetFunction( 0 )->GetName() ); } if( patchRadius.size() == 1 ) { for( unsigned int d = 1; d < ImageDimension; d++ ) { patchRadius.push_back( patchRadius[0] ); } } if( patchRadius.size() != ImageDimension ) { if( verbose ) { std::cerr << "Patch radius specified incorrectly. Please see usage options." << std::endl; } return EXIT_FAILURE; } typename FusionFilterType::NeighborhoodRadiusType patchNeighborhoodRadius; for( unsigned int d = 0; d < ImageDimension; d++ ) { patchNeighborhoodRadius[d] = patchRadius[d]; } fusionFilter->SetNeighborhoodSearchRadius( searchNeighborhoodRadius ); fusionFilter->SetNeighborhoodPatchRadius( patchNeighborhoodRadius ); // Retain atlas voting and label posterior images bool retainAtlasVotingImages = false; bool retainLabelPosteriorImages = false; bool constrainSolutionToNonnegativeWeights = false; bool logEuclidean = false; typename OptionType::Pointer logEuclideanOption = parser->GetOption("log-euclidean"); if ( logEuclideanOption && logEuclideanOption->GetNumberOfFunctions() > 0 ) { logEuclidean = parser->Convert( logEuclideanOption->GetFunction()->GetName() ); } typename OptionType::Pointer retainLabelPosteriorOption = parser->GetOption( "retain-label-posterior-images" ); if( retainLabelPosteriorOption && retainLabelPosteriorOption->GetNumberOfFunctions() > 0 ) { retainLabelPosteriorImages = parser->Convert( retainLabelPosteriorOption->GetFunction()->GetName() ); } typename OptionType::Pointer retainAtlasVotingOption = parser->GetOption( "retain-atlas-voting-images" ); if( retainAtlasVotingOption && retainAtlasVotingOption->GetNumberOfFunctions() > 0 ) { retainAtlasVotingImages = parser->Convert( retainAtlasVotingOption->GetFunction()->GetName() ); } typename OptionType::Pointer constrainWeightsOption = parser->GetOption( "constrain-nonnegative" ); if( constrainWeightsOption && constrainWeightsOption->GetNumberOfFunctions() > 0 ) { constrainSolutionToNonnegativeWeights = parser->Convert( constrainWeightsOption->GetFunction()->GetName() ); } typename OptionType::Pointer metricOption = parser->GetOption( "patch-metric" ); if( metricOption && metricOption->GetNumberOfFunctions() > 0 ) { std::string metricString = metricOption->GetFunction()->GetName(); ConvertToLowerCase( metricString ); if( metricString.compare( "pc" ) == 0 ) { fusionFilter->SetSimilarityMetric( FusionFilterType::PEARSON_CORRELATION ); } else if( metricString.compare( "msq" ) == 0 ) { fusionFilter->SetSimilarityMetric( FusionFilterType::MEAN_SQUARES ); } else { std::cerr << "Unrecognized metric option. See help menu." << std::endl; return EXIT_FAILURE; } } fusionFilter->SetRetainAtlasVotingWeightImages( retainAtlasVotingImages ); fusionFilter->SetRetainLabelPosteriorProbabilityImages( retainLabelPosteriorImages ); fusionFilter->SetConstrainSolutionToNonnegativeWeights( constrainSolutionToNonnegativeWeights ); // Get the target image unsigned int numberOfTargetModalities = 0; typename FusionFilterType::InputImageList targetImageList; typename OptionType::Pointer targetImageOption = parser->GetOption( "target-image" ); if( targetImageOption && targetImageOption->GetNumberOfFunctions() ) { if( targetImageOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { typename TensorImageType::Pointer targetImage = ITK_NULLPTR; std::string targetFile = targetImageOption->GetFunction( 0 )->GetName(); ReadTensorImage( targetImage, targetFile.c_str(), logEuclidean ); numberOfTargetModalities = 6; for( unsigned int n = 0; n < numberOfTargetModalities; n++ ) { typename TensorAdaptorType::Pointer targetAdaptorImage = TensorAdaptorType::New(); targetAdaptorImage->SetImage( targetImage ); targetAdaptorImage->SelectNthElement( n ); // Can't use image adaptor directly, get error due to missing "ImageType::NeighborhoodAccessorFunctorType" in // itk::ConstNeighborhoodIterator, 2>, float> typename CastFilterType::Pointer castTensor = CastFilterType::New(); castTensor->SetInput( targetAdaptorImage ); castTensor->Update(); targetImageList.push_back( castTensor->GetOutput() ); } } else { std::cout << "Only 1 modality (DiffusionTensor) allowed for target image" << std::endl; return EXIT_FAILURE; } } else { if( verbose ) { std::cerr << "Target image(s) not specified." << std::endl; } return EXIT_FAILURE; } fusionFilter->SetTargetImage( targetImageList ); // Get the atlas images and segmentations typename OptionType::Pointer atlasImageOption = parser->GetOption( "atlas-image" ); typename OptionType::Pointer atlasSegmentationOption = parser->GetOption( "atlas-segmentation" ); unsigned int numberOfAtlases = 0; unsigned int numberOfAtlasSegmentations = 0; unsigned int numberOfAtlasModalities = 6; if( atlasImageOption && atlasImageOption->GetNumberOfFunctions() ) { numberOfAtlases = atlasImageOption->GetNumberOfFunctions(); } if( atlasSegmentationOption && atlasSegmentationOption->GetNumberOfFunctions() ) { numberOfAtlasSegmentations = atlasSegmentationOption->GetNumberOfFunctions(); } if( numberOfAtlases < 2 ) { if( verbose ) { std::cerr << "At least 2 atlases are required." << std::endl; } return EXIT_FAILURE; } if( numberOfAtlasSegmentations != 0 && numberOfAtlasSegmentations != numberOfAtlases ) { if( verbose ) { std::cout << "Warning: the number of atlases does not match the number of " << "segmentations. Only performing joint intensity fusion." << std::endl; } numberOfAtlasSegmentations = 0; } for( unsigned int m = 0; m < numberOfAtlases; m++ ) { typename FusionFilterType::InputImageList atlasImageList; typename LabelImageType::Pointer atlasSegmentation = ITK_NULLPTR; if( atlasImageOption->GetFunction( m )->GetNumberOfParameters() == 0 ) { numberOfAtlasModalities = 6; if( numberOfTargetModalities != numberOfAtlasModalities ) { if( verbose ) { std::cerr << "The number of atlas modalities does not match the number of target modalities." << std::endl; } return EXIT_FAILURE; } typename TensorImageType::Pointer atlasImage = ITK_NULLPTR; std::string atlasFile = atlasImageOption->GetFunction( m )->GetName(); ReadTensorImage( atlasImage, atlasFile.c_str(), logEuclidean ); for( unsigned int n = 0; n < numberOfAtlasModalities; n++ ) { typename TensorAdaptorType::Pointer atlasAdaptorImage = TensorAdaptorType::New(); atlasAdaptorImage->SetImage( atlasImage ); atlasAdaptorImage->SelectNthElement( n ); typename CastFilterType::Pointer castTensor = CastFilterType::New(); castTensor->SetInput( atlasAdaptorImage ); castTensor->Update(); atlasImageList.push_back( castTensor->GetOutput() ); } } else { std::cerr << "Only 1 modality (DiffusionTensor) allowed for atlas images" << std::endl; return EXIT_FAILURE; } if( numberOfAtlasSegmentations > 0 ) { std::string atlasSegmentationFile = atlasSegmentationOption->GetFunction( m )->GetName(); ReadImage( atlasSegmentation, atlasSegmentationFile.c_str() ); } fusionFilter->AddAtlas( atlasImageList, atlasSegmentation ); } // Get the exclusion images typename OptionType::Pointer exclusionImageOption = parser->GetOption( "exclusion-image" ); if( exclusionImageOption && exclusionImageOption->GetNumberOfFunctions() ) { for( unsigned int n = 0; n < exclusionImageOption->GetNumberOfFunctions(); n++ ) { LabelType label = parser->Convert( exclusionImageOption->GetFunction( n )->GetName() ); typename LabelImageType::Pointer exclusionImage = ITK_NULLPTR; std::string exclusionFile = exclusionImageOption->GetFunction( n )->GetParameter( 0 ); ReadImage( exclusionImage, exclusionFile.c_str() ); fusionFilter->AddLabelExclusionImage( label, exclusionImage ); } } // Get the mask typename itk::ants::CommandLineParser::OptionType::Pointer maskImageOption = parser->GetOption( "mask-image" ); if( maskImageOption && maskImageOption->GetNumberOfFunctions() ) { typename MaskImageType::Pointer maskImage = ITK_NULLPTR; std::string inputFile = maskImageOption->GetFunction( 0 )->GetName(); ReadImage( maskImage, inputFile.c_str() ); fusionFilter->SetMaskImage( maskImage ); } // Run the fusion program itk::TimeProbe timer; timer.Start(); if( verbose ) { typedef CommandProgressUpdate CommandType; typename CommandType::Pointer observer = CommandType::New(); fusionFilter->AddObserver( itk::ProgressEvent(), observer ); } try { fusionFilter->Update(); } catch( itk::ExceptionObject & e ) { if( verbose ) { std::cerr << "Exception caught: " << e << std::endl; } return EXIT_FAILURE; } timer.Stop(); if( verbose ) { std::cout << std::endl << std::endl; fusionFilter->Print( std::cout, 3 ); } // write the output if( verbose ) { std::cout << std::endl << "Writing output:" << std::endl; } typename OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { std::string labelFusionName; std::string intensityFusionName; std::string labelPosteriorName; std::string atlasVotingName; if( outputOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { if( numberOfAtlasSegmentations != 0 ) { labelFusionName = outputOption->GetFunction( 0 )->GetName(); } else { intensityFusionName = outputOption->GetFunction( 0 )->GetName(); } } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { if( numberOfAtlasSegmentations != 0 ) { labelFusionName = outputOption->GetFunction( 0 )->GetParameter( 0 ); } } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { intensityFusionName = outputOption->GetFunction( 0 )->GetParameter( 1 ); } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { if( numberOfAtlasSegmentations != 0 ) { labelPosteriorName = outputOption->GetFunction( 0 )->GetParameter( 2 ); } } if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { atlasVotingName = outputOption->GetFunction( 0 )->GetParameter( 3 ); } if( !labelFusionName.empty() ) { WriteImage( fusionFilter->GetOutput(), labelFusionName.c_str() ); } if( !intensityFusionName.empty() ) { typename TensorImageType::Pointer jointTensorImage = TensorImageType::New(); jointTensorImage->SetRegions(fusionFilter->GetJointIntensityFusionImage( 0 )->GetRequestedRegion().GetSize() ); jointTensorImage->SetSpacing( fusionFilter->GetJointIntensityFusionImage( 0 )->GetSpacing() ); jointTensorImage->SetOrigin( fusionFilter->GetJointIntensityFusionImage( 0 )->GetOrigin() ); jointTensorImage->SetDirection( fusionFilter->GetJointIntensityFusionImage( 0 )->GetDirection() ); jointTensorImage->Allocate(); TensorType nullDT; nullDT.Fill(0.0); jointTensorImage->FillBuffer(nullDT); for( unsigned int i = 0; i < 6; i++ ) { if( verbose ) { std::cout << " Merging tensor fusion image channels " << std::endl; } typename ImageType::Pointer jointIntensityFusionImage = fusionFilter->GetJointIntensityFusionImage( i ); typename itk::ImageRegionIteratorWithIndex valueIt( jointIntensityFusionImage, jointIntensityFusionImage->GetLargestPossibleRegion() ); while ( !valueIt.IsAtEnd() ) { TensorType dt = jointTensorImage->GetPixel(valueIt.GetIndex()); dt[i] = valueIt.Value(); jointTensorImage->SetPixel(valueIt.GetIndex(), dt); ++valueIt; } } if( verbose ) { std::cout << " Writing tensor fusion image" << std::endl; } WriteTensorImage( jointTensorImage, intensityFusionName.c_str() ); } if( !labelPosteriorName.empty() && fusionFilter->GetRetainLabelPosteriorProbabilityImages() ) { typename FusionFilterType::LabelSetType labelSet = fusionFilter->GetLabelSet(); typename FusionFilterType::LabelSetType::const_iterator labelIt; for( labelIt = labelSet.begin(); labelIt != labelSet.end(); ++labelIt ) { if( *labelIt == 0 ) { continue; } if( verbose ) { std::cout << " Writing label probability image (label " << *labelIt << ")" << std::endl; } char buffer[256]; std::snprintf( buffer, sizeof( buffer ), labelPosteriorName.c_str(), *labelIt ); WriteImage( fusionFilter->GetLabelPosteriorProbabilityImage( *labelIt ), buffer ); } } if( !atlasVotingName.empty() && fusionFilter->GetRetainAtlasVotingWeightImages() ) { itk::NumericSeriesFileNames::Pointer fileNamesCreator = itk::NumericSeriesFileNames::New(); fileNamesCreator->SetStartIndex( 1 ); fileNamesCreator->SetEndIndex( numberOfAtlases ); fileNamesCreator->SetSeriesFormat( atlasVotingName.c_str() ); const std::vector & imageNames = fileNamesCreator->GetFileNames(); for( unsigned int i = 0; i < imageNames.size(); i++ ) { if( verbose ) { std::cout << " Writing atlas voting image (atlas " << i+1 << ")" << std::endl; } WriteImage( fusionFilter->GetAtlasVotingWeightImage( i ), imageNames[i].c_str() ); } } } if( verbose ) { std::cout << "Elapsed time: " << timer.GetMean() << std::endl; } return EXIT_SUCCESS; } void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, the program tries to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "image-dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3/4" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The target image (or multimodal target images) assumed to be " ) + std::string( "aligned to a common image domain." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "target-image" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "targetImage" ); option->SetUsageOption( 1, "[targetImageModality0,targetImageModality1,...,targetImageModalityN]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The atlas image (or multimodal atlas images) assumed to be " ) + std::string( "aligned to a common image domain." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "atlas-image" ); option->SetShortName( 'g' ); option->SetUsageOption( 0, "atlasImage" ); option->SetUsageOption( 1, "[atlasImageModality0,atlasImageModality1,...,atlasImageModalityN]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The atlas segmentation images. For performing label fusion the number of " ) + std::string( "specified segmentations should be identical to the number of atlas image sets." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "atlas-segmentation" ); option->SetShortName( 'l' ); option->SetUsageOption( 0, "atlasSegmentation" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Regularization term added to matrix Mx for calculating the inverse. Default = 0.1" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "alpha" ); option->SetShortName( 'a' ); option->SetUsageOption( 0, "0.1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Exponent for mapping intensity difference to the joint error. Default = 2.0" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "beta" ); option->SetShortName( 'b' ); option->SetUsageOption( 0, "2.0" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Retain label posterior probability images. Requires atlas segmentations " ) + std::string( "to be specified. Default = false" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "retain-label-posterior-images" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Retain atlas voting images. Default = false" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "retain-atlas-voting-images" ); option->SetShortName( 'f' ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Constrain solution to non-negative weights." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'c' ); option->SetLongName( "constrain-nonnegative" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Use log Euclidean space for tensor math" ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'u' ); option->SetLongName( "log-euclidean" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Patch radius for similarity measures. Default = 2x2x2" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "patch-radius" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "2" ); option->SetUsageOption( 1, "2x2x2" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Metric to be used in determining the most similar neighborhood patch. " ) + std::string( "Options include Pearson's correlation (PC) and mean squares (MSQ). " ) + std::string( "Default = PC (Pearson correlation)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "patch-metric" ); option->SetShortName( 'm' ); option->SetUsageOption( 0, "(PC)/MSQ" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Search radius for similarity measures. Default = 3x3x3" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "search-radius" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "3" ); option->SetUsageOption( 1, "3x3x3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify an exclusion region for the given label." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "exclusion-image" ); option->SetShortName( 'e' ); option->SetUsageOption( 0, "label[exclusionImage]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "If a mask image is specified, fusion is only performed in the mask region." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask-image" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "maskImageFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The output is the intensity and/or label fusion image. Additional " ) + std::string( "optional outputs include the label posterior probability images " ) + std::string( "and the atlas voting weight images." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "labelFusionImage" ); option->SetUsageOption( 1, "intensityFusionImageFileNameFormat" ); option->SetUsageOption( 2, "[labelFusionImage,intensityFusionImageFileNameFormat,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Get version information." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "version" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsJointTensorFusion( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsJointTensorFusion" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "antsJointTensorFusion is an image fusion algorithm developed by Hongzhi Wang and " ) + std::string( "Paul Yushkevich which won segmentation challenges at MICCAI 2012 and MICCAI 2013. " ) + std::string( "The original label fusion framework was extended to accommodate intensities by " ) + std::string( "Brian Avants. This implementation is based on Paul's original ITK-style " ) + std::string( "implementation and Brian's ANTsR implementation. References include 1) H. Wang, " ) + std::string( "J. W. Suh, S. Das, J. Pluta, C. Craige, P. Yushkevich, Multi-atlas " ) + std::string( "segmentation with joint label fusion IEEE Trans. on Pattern " ) + std::string( "Analysis and Machine Intelligence, 35(3), 611-623, 2013. and 2) " ) + std::string( "H. Wang and P. A. Yushkevich, Multi-atlas segmentation with joint " ) + std::string( "label fusion and corrective learning--an open source implementation, " ) + std::string( "Front. Neuroinform., 2013. " ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc == 1 ) { parser->PrintMenu( std::cerr, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Show automatic version itk::ants::CommandLineParser::OptionType::Pointer versionOption = parser->GetOption( "version" ); if( versionOption && versionOption->GetNumberOfFunctions() ) { std::string versionFunction = versionOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( versionFunction ); if( versionFunction.compare( "1" ) == 0 || versionFunction.compare( "true" ) == 0 ) { //Print Version Information std::cout << ANTs::Version::ExtendedVersionString() << std::endl; return EXIT_SUCCESS; } } // Get dimensionality unsigned int dimension = 3; itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "image-dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { // Read in the first intensity image to get the image dimension. std::string filename; itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "target-image" ); if( imageOption && imageOption->GetNumberOfFunctions() > 0 ) { if( imageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { filename = imageOption->GetFunction( 0 )->GetParameter( 0 ); } else { filename = imageOption->GetFunction( 0 )->GetName(); } } else { std::cerr << "No input images were specified. Specify an input image" << " with the -t option" << std::endl; return EXIT_FAILURE; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); dimension = imageIO->GetNumberOfDimensions(); } switch( dimension ) { case 2: { return antsJointTensorFusion<2>( parser ); } break; case 3: { return antsJointTensorFusion<3>( parser ); } break; case 4: { return antsJointTensorFusion<4>( parser ); } break; default: std::cout << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsLandmarkBasedTransformInitializer.cxx000066400000000000000000000507731311104306400244450ustar00rootroot00000000000000/** ANTS Landmarks used to initialize an b-spline displacement field ... */ #include "antsUtilities.h" #include "itkAffineTransform.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkContinuousIndex.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkImportImageFilter.h" #include "itkLandmarkBasedTransformInitializer.h" #include "itkRigid2DTransform.h" #include "itkVersorRigid3DTransform.h" #include "itkTransformFileWriter.h" #include #include #include namespace ants { template void ReadLabeledPointSetFromImage( typename ImageType::Pointer image, typename PointSetType::Pointer pointSet, std::vector & labels ) { labels.clear(); typename PointSetType::Pointer allPoints = PointSetType::New(); allPoints->Initialize(); itk::ImageRegionIteratorWithIndex It( image, image->GetLargestPossibleRegion() ); unsigned int count = 0; for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { typename ImageType::PixelType value = It.Get(); if( value != 0 ) { if( std::find( labels.begin(), labels.end(), value ) == labels.end() ) { labels.push_back( value ); } typename PointSetType::PointType point; image->TransformIndexToPhysicalPoint( It.GetIndex(), point ); allPoints->SetPointData( count, value ); allPoints->SetPoint( count++, point ); } } std::sort( labels.begin(), labels.end() ); pointSet->Initialize(); for( unsigned int n = 0; n < labels.size(); n++ ) { typename ImageType::PixelType currentLabel = labels[n]; typename PointSetType::PointType center; center.Fill( 0 ); float N = 0; typename PointSetType::PointsContainerConstIterator ItP = allPoints->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator ItD = allPoints->GetPointData()->Begin(); while( ItP != allPoints->GetPoints()->End() ) { if( ItD.Value() == currentLabel ) { typename PointSetType::PointType point = ItP.Value(); for( unsigned int d = 0; d < ImageType::ImageDimension; d++ ) { center[d] += point[d]; } N += 1.0; } ++ItP; ++ItD; } for( unsigned int d = 0; d < ImageType::ImageDimension; d++ ) { center[d] /= N; } pointSet->SetPoint( n, center ); pointSet->SetPointData( n, currentLabel ); } } template int InitializeLinearTransform( int itkNotUsed( argc ), char *argv[] ) { typedef unsigned int LabelType; typedef itk::Image LabelImageType; typedef itk::LandmarkBasedTransformInitializer TransformInitializerType; typedef typename TransformInitializerType::LandmarkPointContainer LandmarkContainerType; typedef itk::PointSet PointSetType; // // Read in the fixed and moving images and convert to point sets // typedef itk::ImageFileReader ImageReaderType; typename ImageReaderType::Pointer fixedReader = ImageReaderType::New(); fixedReader->SetFileName( argv[2] ); fixedReader->Update(); typename LabelImageType::Pointer fixedImage = fixedReader->GetOutput(); typename PointSetType::Pointer fixedPoints = PointSetType::New(); std::vector fixedLabels; ReadLabeledPointSetFromImage( fixedImage, fixedPoints, fixedLabels ); typename ImageReaderType::Pointer movingReader = ImageReaderType::New(); movingReader->SetFileName( argv[3] ); movingReader->Update(); typename LabelImageType::Pointer movingImage = movingReader->GetOutput(); typename PointSetType::Pointer movingPoints = PointSetType::New(); std::vector movingLabels; ReadLabeledPointSetFromImage( movingImage, movingPoints, movingLabels ); LandmarkContainerType fixedLandmarks; typename PointSetType::PointsContainerConstIterator ItF = fixedPoints->GetPoints()->Begin(); while( ItF != fixedPoints->GetPoints()->End() ) { fixedLandmarks.push_back( ItF.Value() ); ++ItF; } LandmarkContainerType movingLandmarks; typename PointSetType::PointsContainerConstIterator ItM = movingPoints->GetPoints()->Begin(); while( ItM != movingPoints->GetPoints()->End() ) { movingLandmarks.push_back( ItM.Value() ); ++ItM; } if( fixedLandmarks.size() != movingLandmarks.size() ) { std::cerr << "The number of fixed points and moving points must be the same." << std::endl; return EXIT_FAILURE; } typename std::vector::const_iterator itf; for( itf = fixedLabels.begin(); itf != fixedLabels.end(); ++itf ) { if( std::find( movingLabels.begin(), movingLabels.end(), *itf ) == movingLabels.end() ) { std::cerr << "Labels do not match." << std::endl; return EXIT_FAILURE; } } // // Calculate the transform // typename TransformType::Pointer transform = TransformType::New(); transform->SetIdentity(); typename TransformInitializerType::Pointer initializer = TransformInitializerType::New(); initializer->SetFixedLandmarks( fixedLandmarks ); initializer->SetMovingLandmarks( movingLandmarks ); initializer->SetTransform( transform ); initializer->InitializeTransform(); // // Write the transform // typename itk::TransformFileWriter::Pointer transformWriter = itk::TransformFileWriter::New(); transformWriter->SetFileName( argv[5] ); transformWriter->SetInput( transform ); try { transformWriter->Update(); } catch( itk::ExceptionObject & err ) { std::cerr << "Exception in writing tranform file: " << argv[5] << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } template int InitializeBSplineTransform( int argc, char *argv[] ) { typedef float RealType; typedef unsigned int LabelType; typedef itk::Image LabelImageType; typedef itk::PointSet PointSetType; // // Read in the fixed and moving images and convert to point sets // typedef itk::ImageFileReader ImageReaderType; typename ImageReaderType::Pointer fixedReader = ImageReaderType::New(); fixedReader->SetFileName( argv[2] ); fixedReader->Update(); typename LabelImageType::Pointer fixedImage = fixedReader->GetOutput(); itk::ImageRegionIteratorWithIndex ItF( fixedImage, fixedImage->GetLargestPossibleRegion() ); typename PointSetType::Pointer fixedLandmarks = PointSetType::New(); typename std::vector fixedLabels; ReadLabeledPointSetFromImage( fixedImage, fixedLandmarks, fixedLabels ); typename ImageReaderType::Pointer movingReader = ImageReaderType::New(); movingReader->SetFileName( argv[3] ); movingReader->Update(); typename LabelImageType::Pointer movingImage = movingReader->GetOutput(); typename PointSetType::Pointer movingLandmarks = PointSetType::New(); typename std::vector movingLabels; ReadLabeledPointSetFromImage( movingImage, movingLandmarks, movingLabels ); if( fixedLandmarks->GetNumberOfPoints() != movingLandmarks->GetNumberOfPoints() ) { std::cerr << "The number of fixed points and moving points must be the same." << std::endl; return EXIT_FAILURE; } typename std::vector::const_iterator itf; for( itf = fixedLabels.begin(); itf != fixedLabels.end(); ++itf ) { if( std::find( movingLabels.begin(), movingLabels.end(), *itf ) == movingLabels.end() ) { std::cerr << "Labels do not match." << std::endl; return EXIT_FAILURE; } } // // Calculate the transform // typename LabelImageType::DirectionType fixedDirection = fixedImage->GetDirection(); typename LabelImageType::DirectionType fixedDirectionInverse( fixedDirection.GetInverse() ); typename LabelImageType::DirectionType identityDirection; identityDirection.SetIdentity(); const typename LabelImageType::RegionType & bufferedRegion = fixedImage->GetBufferedRegion(); const itk::SizeValueType numberOfPixels = bufferedRegion.GetNumberOfPixels(); const bool filterHandlesMemory = false; typedef itk::ImportImageFilter ImporterType; typename ImporterType::Pointer importer = ImporterType::New(); importer->SetImportPointer( const_cast( fixedImage->GetBufferPointer() ), numberOfPixels, filterHandlesMemory ); importer->SetRegion( fixedImage->GetBufferedRegion() ); importer->SetOrigin( fixedImage->GetOrigin() ); importer->SetSpacing( fixedImage->GetSpacing() ); importer->SetDirection( identityDirection ); importer->Update(); const typename ImporterType::OutputImageType * parametricInputImage = importer->GetOutput(); typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; // Read in the optional label weights std::vector labelWeights; std::vector userLabels; bool useWeights = false; unsigned int labelCount = 0; if( argc > 10 ) { useWeights = true; std::fstream labelStr( argv[10] ); if( labelStr.is_open() ) { while( !labelStr.eof() ) { char line[256]; labelStr.getline( line, 256 ); std::string lineString = std::string( line ); std::size_t pos = lineString.find( ',' ); RealType value; if( pos == std::string::npos ) { std::istringstream iss( lineString ); iss >> value; labelWeights.push_back( value ); userLabels.push_back( movingLabels[labelCount++] ); } else { unsigned int localLabel; std::string element = lineString.substr( 0, pos ); std::istringstream iss( element ); iss >> localLabel; userLabels.push_back( localLabel ); element = lineString.substr( pos + 1, lineString.length() ); std::istringstream iss2( element ); iss2 >> value; labelWeights.push_back( value ); } } labelStr.close(); } else { std::cerr << "File " << argv[10] << " cannot be opened." << std::endl; return EXIT_FAILURE; } } // Now match up the center points typedef itk::PointSet DisplacementFieldPointSetType; typedef itk::BSplineScatteredDataPointSetToImageFilter BSplineFilterType; typedef typename BSplineFilterType::WeightsContainerType WeightsContainerType; typename WeightsContainerType::Pointer weights = WeightsContainerType::New(); weights->Initialize(); const typename WeightsContainerType::Element boundaryWeight = 1.0e10; const typename WeightsContainerType::Element weight = 1.0; typename DisplacementFieldPointSetType::Pointer fieldPoints = DisplacementFieldPointSetType::New(); fieldPoints->Initialize(); unsigned long count = 0; typename PointSetType::PointsContainerConstIterator mIt = movingLandmarks->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator mItD = movingLandmarks->GetPointData()->Begin(); while( mItD != movingLandmarks->GetPointData()->End() ) { typename PointSetType::PointsContainerConstIterator fIt = fixedLandmarks->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator fItD = fixedLandmarks->GetPointData()->Begin(); while( fItD != fixedLandmarks->GetPointData()->End() ) { if( fItD.Value() == mItD.Value() ) { typename PointSetType::PointType fpoint = fIt.Value(); typename PointSetType::PointType mpoint = mIt.Value(); VectorType vector; typename LabelImageType::PointType fixedPhysicalPoint; for( unsigned int i = 0; i < ImageDimension; i++ ) { fixedPhysicalPoint[i] = fpoint[i]; vector[i] = mpoint[i] - fpoint[i]; } itk::ContinuousIndex fixedCidx; fixedImage->TransformPhysicalPointToContinuousIndex( fixedPhysicalPoint, fixedCidx ); typename DisplacementFieldType::PointType fieldPoint; parametricInputImage->TransformContinuousIndexToPhysicalPoint( fixedCidx, fieldPoint ); fieldPoints->SetPoint( count, fieldPoint ); fieldPoints->SetPointData( count, vector ); if( useWeights ) { std::vector::const_iterator it = std::find( userLabels.begin(), userLabels.end(), mItD.Value() ); if( it != userLabels.end() ) { weights->InsertElement( count, labelWeights[it - userLabels.begin()] ); } else { std::cerr << "Unspecified label " << mItD.Value() << " in specified user label weights." << std::endl; return EXIT_FAILURE; } } else { weights->InsertElement( count, weight ); } count++; break; } ++fItD; ++fIt; } ++mItD; ++mIt; } bool enforceStationaryBoundary = true; if( argc > 9 ) { enforceStationaryBoundary = static_cast( atoi( argv[9] ) ); } if( enforceStationaryBoundary ) { typename LabelImageType::IndexType startIndex2 = fixedImage->GetLargestPossibleRegion().GetIndex(); typename LabelImageType::SizeType inputSize2 = fixedImage->GetLargestPossibleRegion().GetSize(); for( ItF.GoToBegin(); !ItF.IsAtEnd(); ++ItF ) { typename LabelImageType::IndexType index = ItF.GetIndex(); bool isOnStationaryBoundary = false; for( unsigned int d = 0; d < ImageDimension; d++ ) { if( index[d] == startIndex2[d] || index[d] == startIndex2[d] + static_cast( inputSize2[d] ) - 1 ) { isOnStationaryBoundary = true; break; } } if( isOnStationaryBoundary ) { VectorType vector; vector.Fill( 0.0 ); typename PointSetType::PointType fixedPoint; parametricInputImage->TransformIndexToPhysicalPoint( index, fixedPoint ); fieldPoints->SetPoint( count, fixedPoint ); fieldPoints->SetPointData( count, vector ); weights->InsertElement( count, boundaryWeight ); count++; } } } typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); unsigned int numberOfLevels = 4; if( argc > 7 ) { numberOfLevels = atoi( argv[7] ); } unsigned int splineOrder = 3; if( argc > 8 ) { splineOrder = atoi( argv[8] ); } typename BSplineFilterType::ArrayType ncps; ncps.Fill( 1 + splineOrder ); if( argc > 6 ) { std::vector meshSize = ConvertVector( std::string( argv[6] ) ); if( meshSize.size() == 1 ) { ncps.Fill( meshSize[0] + splineOrder ); } else if( meshSize.size() == ImageDimension ) { for( unsigned int d = 0; d < ImageDimension; d++ ) { ncps[d] = meshSize[d] + splineOrder; } } else { std::cerr << "Invalid meshSize format." << std::endl; } } bspliner->SetOrigin( fixedImage->GetOrigin() ); bspliner->SetSpacing( fixedImage->GetSpacing() ); bspliner->SetSize( fixedImage->GetLargestPossibleRegion().GetSize() ); bspliner->SetDirection( fixedImage->GetDirection() ); bspliner->SetGenerateOutputImage( true ); bspliner->SetNumberOfLevels( numberOfLevels ); bspliner->SetSplineOrder( splineOrder ); bspliner->SetNumberOfControlPoints( ncps ); bspliner->SetInput( fieldPoints ); bspliner->SetPointWeights( weights ); bspliner->Update(); typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( argv[5] ); writer->SetInput( bspliner->GetOutput() ); writer->Update(); return EXIT_SUCCESS; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsLandmarkBasedTransformInitializer( std::vector args, std::ostream* /*out_stream = NULL */) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsLandmarkBasedTransformInitializer" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 6 ) { std::cerr << "Usage: " << argv[0] << " dimension fixedImage movingImage transformType " << "outputTransform [meshSize[0]xmeshSize[1]x...=1x1x1] [numberOfLevels=4] [order=3] " << "[enforceStationaryBoundaries=1] [landmarkWeights] " << std::endl; std::cerr << std::endl << "Notes:" << std::endl; std::cerr << " 1) transformType = \"rigid\", \"affine\", or \"bspline\"." << std::endl; std::cerr << " 2) The optional arguments only apply to the bspline transform." << std::endl; std::cerr << " 3) The landmark weights are read from a text file where each row is either:" << std::endl; std::cerr << " \"label,labelWeight\" or " << std::endl; std::cerr << " \"labelWeight\" " << std::endl; std::cerr << " If the latter format is used, the label weights are assumed to be arranged in ascending order by label." << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } const unsigned int dimension = static_cast( atoi( argv[1] ) ); typedef itk::Rigid2DTransform Rigid2DTransformType; typedef itk::VersorRigid3DTransform Rigid3DTransformType; typedef itk::AffineTransform AffineTransform2DType; typedef itk::AffineTransform AffineTransform3DType; std::string transformType( argv[4] ); ConvertToLowerCase( transformType ); switch( dimension ) { case 2: { if( std::strcmp( transformType.c_str(), "affine" ) == 0 ) { return InitializeLinearTransform<2, AffineTransform2DType>( argc, argv ); } else if( std::strcmp( transformType.c_str(), "rigid" ) == 0 ) { return InitializeLinearTransform<2, Rigid2DTransformType>( argc, argv ); } else if( std::strcmp( transformType.c_str(), "bspline" ) == 0 ) { return InitializeBSplineTransform<2>( argc, argv ); } else { std::cerr << "Unrecognized transform: " << transformType.c_str() << std::endl; } } break; case 3: { if( std::strcmp( transformType.c_str(), "affine" ) == 0 ) { return InitializeLinearTransform<3, AffineTransform3DType>( argc, argv ); } else if( std::strcmp( transformType.c_str(), "rigid" ) == 0 ) { return InitializeLinearTransform<3, Rigid3DTransformType>( argc, argv ); } else if( std::strcmp( transformType.c_str(), "bspline" ) == 0 ) { return InitializeBSplineTransform<3>( argc, argv ); } else { std::cerr << "Unrecognized transform: " << transformType.c_str() << std::endl; } } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsMotionCorr.cxx000066400000000000000000002410271311104306400177420ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 "antsUtilities.h" #include "antsAllocImage.h" #include "ReadWriteData.h" #include "antsCommandLineParser.h" #include "itkCSVNumericObjectFileWriter.h" #include "itkImageRegistrationMethodv4.h" #include "itkSyNImageRegistrationMethod.h" #include "itkDisplacementFieldTransform.h" #include "itkANTSNeighborhoodCorrelationImageToImageMetricv4.h" #include "itkMeanSquaresImageToImageMetricv4.h" #include "itkCorrelationImageToImageMetricv4.h" #include "itkImageToImageMetricv4.h" #include "itkMattesMutualInformationImageToImageMetricv4.h" #include "itkImageMomentsCalculator.h" #include "itkImageToHistogramFilter.h" #include "itkHistogramMatchingImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include "itkTransformToDisplacementFieldFilter.h" #include "itkIdentityTransform.h" #include "itkAffineTransform.h" #include "itkBSplineTransform.h" #include "itkBSplineSmoothingOnUpdateDisplacementFieldTransform.h" #include "itkCompositeTransform.h" #include "itkGaussianSmoothingOnUpdateDisplacementFieldTransform.h" #include "itkIdentityTransform.h" #include "itkEuler2DTransform.h" #include "itkEuler3DTransform.h" #include "itkTransform.h" #include "itkExtractImageFilter.h" #include "itkBSplineTransformParametersAdaptor.h" #include "itkBSplineSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor.h" #include "itkGaussianSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor.h" #include "itkTimeVaryingVelocityFieldTransformParametersAdaptor.h" #include "itkGradientDescentOptimizerv4.h" #include "itkConjugateGradientLineSearchOptimizerv4.h" #include "itkQuasiNewtonOptimizerv4.h" #include "itkHistogramMatchingImageFilter.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMacro.h" #include "itkRegistrationParameterScalesFromPhysicalShift.h" #include "itkResampleImageFilter.h" #include "itkShrinkImageFilter.h" #include "itkTimeProbe.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "itkSimilarity2DTransform.h" #include "itkSimilarity3DTransform.h" #include namespace ants { /** \class antsRegistrationCommandIterationUpdate * \brief change parameters between iterations of registration */ template class antsRegistrationCommandIterationUpdate : public itk::Command { public: typedef antsRegistrationCommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); protected: antsRegistrationCommandIterationUpdate() { this->m_LogStream = &std::cout; } public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { TFilter * filter = const_cast( dynamic_cast( object ) ); unsigned int currentLevel = 0; if( typeid( event ) == typeid( itk::IterationEvent ) ) { currentLevel = filter->GetCurrentLevel() + 1; } if( currentLevel < this->m_NumberOfIterations.size() ) { typename TFilter::ShrinkFactorsPerDimensionContainerType shrinkFactors = filter->GetShrinkFactorsPerDimension( currentLevel ); typename TFilter::SmoothingSigmasArrayType smoothingSigmas = filter->GetSmoothingSigmasPerLevel(); typename TFilter::TransformParametersAdaptorsContainerType adaptors = filter->GetTransformParametersAdaptorsPerLevel(); this->Logger() << " Current level = " << currentLevel << std::endl; this->Logger() << " number of iterations = " << this->m_NumberOfIterations[currentLevel] << std::endl; this->Logger() << " shrink factors = " << shrinkFactors << std::endl; this->Logger() << " smoothing sigmas = " << smoothingSigmas[currentLevel] << std::endl; this->Logger() << " required fixed parameters = " << adaptors[currentLevel]->GetRequiredFixedParameters() << std::endl; typedef itk::ConjugateGradientLineSearchOptimizerv4 GradientDescentOptimizerType; GradientDescentOptimizerType * optimizer = reinterpret_cast( filter->GetModifiableOptimizer() ); optimizer->SetNumberOfIterations( this->m_NumberOfIterations[currentLevel] ); optimizer->SetMinimumConvergenceValue( 1.e-7 ); optimizer->SetConvergenceWindowSize( 10 ); optimizer->SetLowerLimit( 0 ); optimizer->SetUpperLimit( 2 ); optimizer->SetEpsilon( 0.1 ); } } void SetNumberOfIterations( const std::vector & iterations ) { this->m_NumberOfIterations = iterations; } void SetLogStream(std::ostream & logStream) { this->m_LogStream = &logStream; } private: std::ostream & Logger() const { return *m_LogStream; } std::vector m_NumberOfIterations; std::ostream * m_LogStream; }; template inline std::string ants_moco_to_string(const T& t) { std::stringstream ss; ss << t; return ss.str(); } template typename ImageType::Pointer PreprocessImage( ImageType * inputImage, typename ImageType::PixelType lowerScaleFunction, typename ImageType::PixelType upperScaleFunction, float winsorizeLowerQuantile, float winsorizeUpperQuantile, ImageType *histogramMatchSourceImage = NULL ) { bool verbose = false; typedef itk::Statistics::ImageToHistogramFilter HistogramFilterType; typedef typename HistogramFilterType::InputBooleanObjectType InputBooleanObjectType; typedef typename HistogramFilterType::HistogramSizeType HistogramSizeType; HistogramSizeType histogramSize( 1 ); histogramSize[0] = 256; typename InputBooleanObjectType::Pointer autoMinMaxInputObject = InputBooleanObjectType::New(); autoMinMaxInputObject->Set( true ); typename HistogramFilterType::Pointer histogramFilter = HistogramFilterType::New(); histogramFilter->SetInput( inputImage ); histogramFilter->SetAutoMinimumMaximumInput( autoMinMaxInputObject ); histogramFilter->SetHistogramSize( histogramSize ); histogramFilter->SetMarginalScale( 10.0 ); histogramFilter->Update(); float lowerFunction = histogramFilter->GetOutput()->Quantile( 0, winsorizeLowerQuantile ); float upperFunction = histogramFilter->GetOutput()->Quantile( 0, winsorizeUpperQuantile ); typedef itk::IntensityWindowingImageFilter IntensityWindowingImageFilterType; typename IntensityWindowingImageFilterType::Pointer windowingFilter = IntensityWindowingImageFilterType::New(); windowingFilter->SetInput( inputImage ); windowingFilter->SetWindowMinimum( lowerFunction ); windowingFilter->SetWindowMaximum( upperFunction ); windowingFilter->SetOutputMinimum( lowerScaleFunction ); windowingFilter->SetOutputMaximum( upperScaleFunction ); windowingFilter->Update(); typename ImageType::Pointer outputImage = ITK_NULLPTR; if( histogramMatchSourceImage ) { typedef itk::HistogramMatchingImageFilter HistogramMatchingFilterType; typename HistogramMatchingFilterType::Pointer matchingFilter = HistogramMatchingFilterType::New(); matchingFilter->SetSourceImage( windowingFilter->GetOutput() ); matchingFilter->SetReferenceImage( histogramMatchSourceImage ); matchingFilter->SetNumberOfHistogramLevels( 256 ); matchingFilter->SetNumberOfMatchPoints( 12 ); matchingFilter->ThresholdAtMeanIntensityOn(); matchingFilter->Update(); outputImage = matchingFilter->GetOutput(); outputImage->Update(); outputImage->DisconnectPipeline(); typedef itk::MinimumMaximumImageCalculator CalculatorType; typename CalculatorType::Pointer calc = CalculatorType::New(); calc->SetImage( inputImage ); calc->ComputeMaximum(); calc->ComputeMinimum(); if( vnl_math_abs( calc->GetMaximum() - calc->GetMinimum() ) < 1.e-9 ) { if ( verbose ) std::cout << "Warning: bad time point - too little intensity variation" << std::endl; return histogramMatchSourceImage; } } else { outputImage = windowingFilter->GetOutput(); outputImage->Update(); outputImage->DisconnectPipeline(); } return outputImage; } template struct ants_moco_index_cmp { ants_moco_index_cmp(const T _arr) : arr(_arr) { } bool operator()(const size_t a, const size_t b) const { return arr[a] < arr[b]; } const T arr; }; template class CommandIterationUpdate : public itk::Command { public: typedef CommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); protected: CommandIterationUpdate() { }; public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { bool verbose = false; TFilter * filter = const_cast( dynamic_cast( object ) ); if( typeid( event ) != typeid( itk::IterationEvent ) ) { return; } unsigned int currentLevel = filter->GetCurrentLevel(); typename TFilter::ShrinkFactorsPerDimensionContainerType shrinkFactors = filter->GetShrinkFactorsPerDimension( currentLevel ); typename TFilter::SmoothingSigmasArrayType smoothingSigmas = filter->GetSmoothingSigmasPerLevel(); typename TFilter::TransformParametersAdaptorsContainerType adaptors = filter->GetTransformParametersAdaptorsPerLevel(); if ( verbose ) std::cout << " Current level = " << currentLevel << std::endl; if ( verbose ) std::cout << " number of iterations = " << this->m_NumberOfIterations[currentLevel] << std::endl; if ( verbose ) std::cout << " shrink factor = " << shrinkFactors[currentLevel] << std::endl; if ( verbose ) std::cout << " smoothing sigma = " << smoothingSigmas[currentLevel] << std::endl; if ( verbose ) std::cout << " required fixed parameters = " << adaptors[currentLevel]->GetRequiredFixedParameters() << std::endl; typedef itk::ConjugateGradientLineSearchOptimizerv4 OptimizerType; OptimizerType * optimizer = reinterpret_cast( filter->GetModifiableOptimizer() ); optimizer->SetNumberOfIterations( this->m_NumberOfIterations[currentLevel] ); optimizer->SetMinimumConvergenceValue( 1.e-7 ); optimizer->SetConvergenceWindowSize( 10 ); optimizer->SetLowerLimit( 0 ); optimizer->SetUpperLimit( 2 ); optimizer->SetEpsilon( 0.1 ); } void SetNumberOfIterations( std::vector iterations ) { this->m_NumberOfIterations = iterations; } private: std::vector m_NumberOfIterations; }; // Transform traits to generalize the rigid transform // template class RigidTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class RigidTransformTraits<2> { public: typedef itk::Euler2DTransform TransformType; }; template <> class RigidTransformTraits<3> { public: // typedef itk::VersorRigid3DTransform TransformType; // typedef itk::QuaternionRigidTransform TransformType; typedef itk::Euler3DTransform TransformType; }; template class SimilarityTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class SimilarityTransformTraits<2> { public: typedef itk::Similarity2DTransform TransformType; }; template <> class SimilarityTransformTraits<3> { public: typedef itk::Similarity3DTransform TransformType; }; /* template class CompositeAffineTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class CompositeAffineTransformTraits<2> { public: typedef itk::ANTSCenteredAffine2DTransform TransformType; }; template <> class CompositeAffineTransformTraits<3> { public: typedef itk::ANTSAffine3DTransform TransformType; }; */ template void AverageTimeImages( typename TImageIn::Pointer image_in, typename TImageOut::Pointer image_avg, std::vector timelist ) { bool verbose = false; typedef TImageIn ImageType; typedef TImageOut OutImageType; enum { ImageDimension = ImageType::ImageDimension }; typedef itk::ImageRegionIteratorWithIndex Iterator; image_avg->FillBuffer(0); unsigned int timedims = image_in->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; if( timelist.empty() ) { for( unsigned int timedim = 0; timedim < timedims; timedim++ ) { timelist.push_back(timedim); } } if ( verbose ) std::cout << " averaging with " << timelist.size() << " images of " << timedims << " timedims " << std::endl; Iterator vfIter2( image_avg, image_avg->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { typename OutImageType::PixelType fval = 0; typename ImageType::IndexType ind; typename OutImageType::IndexType spind = vfIter2.GetIndex(); for( unsigned int xx = 0; xx < timelist.size(); xx++ ) { for( unsigned int yy = 0; yy < ImageDimension - 1; yy++ ) { ind[yy] = spind[yy]; } ind[ImageDimension - 1] = timelist[xx]; fval += image_in->GetPixel(ind); } fval /= (double)timelist.size(); image_avg->SetPixel(spind, fval); } if ( verbose ) std::cout << " averaging images done " << std::endl; return; } template int ants_motion( itk::ants::CommandLineParser *parser ) { unsigned int verbose = 0; itk::ants::CommandLineParser::OptionType::Pointer vOption = parser->GetOption( "verbose" ); if( vOption && vOption->GetNumberOfFunctions() ) { verbose = parser->Convert( vOption->GetFunction( 0 )->GetName() ); } if ( verbose ) std::cout << " verbose " << std::endl; // We infer the number of stages by the number of transformations // specified by the user which should match the number of metrics. unsigned numberOfStages = 0; typedef float PixelType; typedef double RealType; typedef itk::Image FixedIOImageType; typedef itk::Image FixedImageType; typedef itk::Image MovingIOImageType; typedef itk::Image MovingImageType; typedef itk::Vector VectorIOType; typedef itk::Image DisplacementIOFieldType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef vnl_matrix vMatrix; vMatrix param_values; typedef itk::CompositeTransform CompositeTransformType; std::vector CompositeTransformVector; typedef typename itk::ants::CommandLineParser ParserType; typedef typename ParserType::OptionType OptionType; typename OptionType::Pointer averageOption = parser->GetOption( "average-image" ); if( averageOption && averageOption->GetNumberOfFunctions() ) { typename OptionType::Pointer outputOption = parser->GetOption( "output" ); if( !outputOption ) { std::cerr << "Output option not specified. Should be the output average image name." << std::endl; return EXIT_FAILURE; } std::string outputPrefix = outputOption->GetFunction( 0 )->GetParameter( 0 ); if( outputPrefix.length() < 3 ) { outputPrefix = outputOption->GetFunction( 0 )->GetName(); } std::string fn = averageOption->GetFunction( 0 )->GetName(); typename MovingIOImageType::Pointer movingImage; ReadImage( movingImage, fn.c_str() ); typename FixedIOImageType::Pointer avgImage; typedef itk::ExtractImageFilter ExtractFilterType; typename MovingIOImageType::RegionType extractRegion = movingImage->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension, 0); typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( movingImage ); extractFilter->SetDirectionCollapseToSubmatrix(); if( ImageDimension == 2 ) { extractFilter->SetDirectionCollapseToIdentity(); } unsigned int td = 0; extractRegion.SetIndex(ImageDimension, td ); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); avgImage = extractFilter->GetOutput(); std::vector timelist; AverageTimeImages( movingImage, avgImage, timelist ); if ( verbose ) std::cout << "average out " << outputPrefix << std::endl; WriteImage( avgImage, outputPrefix.c_str() ); return EXIT_SUCCESS; } typename OptionType::Pointer transformOption = parser->GetOption( "transform" ); if( transformOption && transformOption->GetNumberOfFunctions() ) { numberOfStages = transformOption->GetNumberOfFunctions(); } else { std::cerr << "No transformations are specified." << std::endl; return EXIT_FAILURE; } if ( verbose ) std::cout << "Registration using " << numberOfStages << " total stages." << std::endl; typename OptionType::Pointer metricOption = parser->GetOption( "metric" ); if( !metricOption || metricOption->GetNumberOfFunctions() != numberOfStages ) { std::cerr << "The number of metrics specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } typename OptionType::Pointer iterationsOption = parser->GetOption( "iterations" ); if( !iterationsOption || iterationsOption->GetNumberOfFunctions() != numberOfStages ) { std::cerr << "The number of iteration sets specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } typename OptionType::Pointer shrinkFactorsOption = parser->GetOption( "shrinkFactors" ); if( !shrinkFactorsOption || shrinkFactorsOption->GetNumberOfFunctions() != numberOfStages ) { std::cerr << "The number of shrinkFactor sets specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } typename OptionType::Pointer smoothingSigmasOption = parser->GetOption( "smoothingSigmas" ); if( !smoothingSigmasOption || smoothingSigmasOption->GetNumberOfFunctions() != numberOfStages ) { std::cerr << "The number of smoothing sigma sets specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } typename OptionType::Pointer outputOption = parser->GetOption( "output" ); if( !outputOption ) { std::cerr << "Output option not specified." << std::endl; return EXIT_FAILURE; } std::string outputPrefix = outputOption->GetFunction( 0 )->GetParameter( 0 ); if( outputPrefix.length() < 3 ) { outputPrefix = outputOption->GetFunction( 0 )->GetName(); } unsigned int nimagestoavg = 0; itk::ants::CommandLineParser::OptionType::Pointer navgOption = parser->GetOption( "n-images" ); if( navgOption && navgOption->GetNumberOfFunctions() ) { nimagestoavg = parser->Convert( navgOption->GetFunction( 0 )->GetName() ); if ( verbose ) std::cout << " nimagestoavg " << nimagestoavg << std::endl; } unsigned int writeDisplacementField = 0; itk::ants::CommandLineParser::OptionType::Pointer wdopt = parser->GetOption( "write-displacement" ); if( wdopt && wdopt->GetNumberOfFunctions() ) { writeDisplacementField = parser->Convert( wdopt->GetFunction( 0 )->GetName() ); } bool doEstimateLearningRateOnce(false); OptionType::Pointer rateOption = parser->GetOption( "use-estimate-learning-rate-once" ); if( rateOption && rateOption->GetNumberOfFunctions() ) { std::string rateFunction = rateOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( rateFunction ); if( rateFunction.compare( "1" ) == 0 || rateFunction.compare( "true" ) == 0 ) { doEstimateLearningRateOnce = true; } } unsigned int nparams = 2; itk::TimeProbe totalTimer; totalTimer.Start(); double metricmean = 0; typedef itk::AffineTransform AffineTransformType; typedef itk::ImageRegistrationMethodv4 AffineRegistrationType; // We iterate backwards because the command line options are stored as a stack (first in last out) typename DisplacementIOFieldType::Pointer displacementout = ITK_NULLPTR; typename DisplacementIOFieldType::Pointer displacementinv = ITK_NULLPTR; for( int currentStage = numberOfStages - 1; currentStage >= 0; currentStage-- ) { if ( verbose ) std::cout << std::endl << "Stage " << numberOfStages - currentStage << std::endl; std::stringstream currentStageString; currentStageString << currentStage; // Get the fixed and moving images std::string fixedImageFileName = metricOption->GetFunction( currentStage )->GetParameter( 0 ); std::string movingImageFileName = metricOption->GetFunction( currentStage )->GetParameter( 1 ); if ( verbose ) std::cout << " fixed image: " << fixedImageFileName << std::endl; if ( verbose ) std::cout << " moving image: " << movingImageFileName << std::endl; typename FixedImageType::Pointer fixed_time_slice = ITK_NULLPTR; typename FixedImageType::Pointer moving_time_slice = ITK_NULLPTR; typename FixedIOImageType::Pointer fixedInImage; ReadImage( fixedInImage, fixedImageFileName.c_str() ); fixedInImage->Update(); fixedInImage->DisconnectPipeline(); typename FixedImageType::Pointer fixedImage; fixedImage = arCastImage( fixedInImage ); typename MovingIOImageType::Pointer movingInImage; typename MovingImageType::Pointer movingImage; ReadImage( movingInImage, movingImageFileName.c_str() ); movingInImage->Update(); movingInImage->DisconnectPipeline(); movingImage = arCastImage( movingInImage ); unsigned int timedims = movingImage->GetLargestPossibleRegion().GetSize()[ImageDimension]; typename MovingIOImageType::Pointer outputImage = MovingIOImageType::New(); typename MovingIOImageType::RegionType outRegion; typename MovingIOImageType::SizeType outSize; typename MovingIOImageType::SpacingType outSpacing; typename MovingIOImageType::PointType outOrigin; typename MovingIOImageType::DirectionType outDirection; for( unsigned int d = 0; d < ImageDimension; d++ ) { outSize[d] = fixedImage->GetLargestPossibleRegion().GetSize()[d]; outSpacing[d] = fixedImage->GetSpacing()[d]; outOrigin[d] = fixedImage->GetOrigin()[d]; for( unsigned int e = 0; e < ImageDimension; e++ ) { outDirection(e, d) = fixedImage->GetDirection() (e, d); } } for( unsigned int d = 0; d < ImageDimension; d++ ) { outDirection(d, ImageDimension) = 0; outDirection(ImageDimension, d) = 0; } outDirection(ImageDimension, ImageDimension) = 1.0; outSize[ImageDimension] = timedims; outSpacing[ImageDimension] = movingImage->GetSpacing()[ImageDimension]; outOrigin[ImageDimension] = movingImage->GetOrigin()[ImageDimension]; outRegion.SetSize( outSize ); outputImage->SetRegions( outRegion ); outputImage->SetSpacing( outSpacing ); outputImage->SetOrigin( outOrigin ); outputImage->SetDirection( outDirection ); outputImage->Allocate(); outputImage->FillBuffer( 0 ); if ( writeDisplacementField > 0 ) { /** Handle all output: images and displacement fields */ typedef itk::IdentityTransform IdentityIOTransformType; typename IdentityIOTransformType::Pointer identityIOTransform = IdentityIOTransformType::New(); typedef typename itk::TransformToDisplacementFieldFilter ConverterType; typename ConverterType::Pointer idconverter = ConverterType::New(); idconverter->SetOutputOrigin( outputImage->GetOrigin() ); idconverter->SetOutputStartIndex( outputImage->GetBufferedRegion().GetIndex() ); idconverter->SetSize( outputImage->GetBufferedRegion().GetSize() ); idconverter->SetOutputSpacing( outputImage->GetSpacing() ); idconverter->SetOutputDirection( outputImage->GetDirection() ); idconverter->SetTransform( identityIOTransform ); idconverter->Update(); displacementout = idconverter->GetOutput(); typename ConverterType::Pointer invconverter = ConverterType::New(); invconverter->SetOutputOrigin( movingInImage->GetOrigin() ); invconverter->SetOutputStartIndex( movingInImage->GetBufferedRegion().GetIndex() ); invconverter->SetSize( movingInImage->GetBufferedRegion().GetSize() ); invconverter->SetOutputSpacing( movingInImage->GetSpacing() ); invconverter->SetOutputDirection( movingInImage->GetDirection() ); invconverter->SetTransform( identityIOTransform ); invconverter->Update(); displacementinv = invconverter->GetOutput(); } // Get the number of iterations and use that information to specify the number of levels std::vector iterations = parser->ConvertVector( iterationsOption->GetFunction( currentStage )->GetName() ); unsigned int numberOfLevels = iterations.size(); if ( verbose ) std::cout << " number of levels = " << numberOfLevels << std::endl; // Get shrink factors std::vector factors = parser->ConvertVector( shrinkFactorsOption->GetFunction( currentStage )->GetName() ); typename AffineRegistrationType::ShrinkFactorsArrayType shrinkFactorsPerLevel; shrinkFactorsPerLevel.SetSize( factors.size() ); if( factors.size() != numberOfLevels ) { std::cerr << "ERROR: The number of shrink factors does not match the number of levels." << std::endl; return EXIT_FAILURE; } else { for( unsigned int n = 0; n < shrinkFactorsPerLevel.Size(); n++ ) { shrinkFactorsPerLevel[n] = factors[n]; } if ( verbose ) std::cout << " shrink factors per level: " << shrinkFactorsPerLevel << std::endl; } // Get smoothing sigmas std::vector sigmas = parser->ConvertVector( smoothingSigmasOption->GetFunction( currentStage )->GetName() ); typename AffineRegistrationType::SmoothingSigmasArrayType smoothingSigmasPerLevel; smoothingSigmasPerLevel.SetSize( sigmas.size() ); if( sigmas.size() != numberOfLevels ) { std::cerr << "ERROR: The number of smoothing sigmas does not match the number of levels." << std::endl; return EXIT_FAILURE; } else { for( unsigned int n = 0; n < smoothingSigmasPerLevel.Size(); n++ ) { smoothingSigmasPerLevel[n] = sigmas[n]; } if ( verbose ) std::cout << " smoothing sigmas per level: " << smoothingSigmasPerLevel << std::endl; } // the fixed image is a reference image in 3D while the moving is a 4D image // loop over every time point and register image_i+1 to image_i // // Set up the image metric and scales estimator std::vector timelist; std::vector metriclist; for( unsigned int timedim = 0; timedim < timedims; timedim++ ) { timelist.push_back(timedim); } for( unsigned int timedim = 0; timedim < timedims; timedim++ ) { typename CompositeTransformType::Pointer compositeTransform = ITK_NULLPTR; if( currentStage == static_cast(numberOfStages) - 1 ) { compositeTransform = CompositeTransformType::New(); CompositeTransformVector.push_back(compositeTransform); } else if( CompositeTransformVector.size() == timedims && !CompositeTransformVector[timedim].IsNull() ) { compositeTransform = CompositeTransformVector[timedim]; if( timedim == 0 ) { if ( verbose ) std::cout << " use existing transform " << compositeTransform->GetParameters() << std::endl; } } typedef itk::IdentityTransform IdentityTransformType; typename IdentityTransformType::Pointer identityTransform = IdentityTransformType::New(); // typedef itk::ExtractImageFilter ExtractFilterType; typename MovingImageType::RegionType extractRegion = movingImage->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension, 0); bool maptoneighbor = true; typename OptionType::Pointer fixedOption = parser->GetOption( "useFixedReferenceImage" ); if( fixedOption && fixedOption->GetNumberOfFunctions() ) { std::string fixedFunction = fixedOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( fixedFunction ); if( fixedFunction.compare( "1" ) == 0 || fixedFunction.compare( "true" ) == 0 ) { if( timedim == 0 ) { if ( verbose ) std::cout << "using fixed reference image for all frames " << std::endl; } fixed_time_slice = fixedImage; extractRegion.SetIndex(ImageDimension, timedim ); typename ExtractFilterType::Pointer extractFilter2 = ExtractFilterType::New(); extractFilter2->SetInput( movingImage ); extractFilter2->SetDirectionCollapseToSubmatrix(); if( ImageDimension == 2 ) { extractFilter2->SetDirectionCollapseToIdentity(); } extractFilter2->SetExtractionRegion( extractRegion ); extractFilter2->Update(); moving_time_slice = extractFilter2->GetOutput(); maptoneighbor = false; } } if( maptoneighbor ) { extractRegion.SetIndex(ImageDimension, timedim ); typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( movingImage ); extractFilter->SetDirectionCollapseToSubmatrix(); if( ImageDimension == 2 ) { extractFilter->SetDirectionCollapseToIdentity(); } extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); fixed_time_slice = extractFilter->GetOutput(); unsigned int td = timedim + 1; if( td > timedims - 1 ) { td = timedims - 1; } extractRegion.SetIndex(ImageDimension, td ); typename ExtractFilterType::Pointer extractFilter2 = ExtractFilterType::New(); extractFilter2->SetInput( movingImage ); extractFilter2->SetDirectionCollapseToSubmatrix(); if( ImageDimension == 2 ) { extractFilter->SetDirectionCollapseToIdentity(); } extractFilter2->SetExtractionRegion( extractRegion ); extractFilter2->Update(); moving_time_slice = extractFilter2->GetOutput(); } typename FixedImageType::Pointer preprocessFixedImage = PreprocessImage( fixed_time_slice, 0, 1, 0.001, 0.999, ITK_NULLPTR ); typename FixedImageType::Pointer preprocessMovingImage = PreprocessImage( moving_time_slice, 0, 1, 0.001, 0.999, preprocessFixedImage ); typedef itk::ImageToImageMetricv4 MetricType; typename MetricType::Pointer metric; std::string whichMetric = metricOption->GetFunction( currentStage )->GetName(); ConvertToLowerCase( whichMetric ); float samplingPercentage = 1.0; if( metricOption->GetFunction( 0 )->GetNumberOfParameters() > 5 ) { samplingPercentage = parser->Convert( metricOption->GetFunction( currentStage )->GetParameter( 5 ) ); } std::string samplingStrategy = ""; if( metricOption->GetFunction( 0 )->GetNumberOfParameters() > 4 ) { samplingStrategy = metricOption->GetFunction( currentStage )->GetParameter( 4 ); } ConvertToLowerCase( samplingStrategy ); typename AffineRegistrationType::MetricSamplingStrategyType metricSamplingStrategy = AffineRegistrationType::NONE; if( std::strcmp( samplingStrategy.c_str(), "random" ) == 0 ) { if( timedim == 0 ) { if ( verbose ) std::cout << " random sampling (percentage = " << samplingPercentage << ")" << std::endl; } metricSamplingStrategy = AffineRegistrationType::RANDOM; } if( std::strcmp( samplingStrategy.c_str(), "regular" ) == 0 ) { if( timedim == 0 ) { if ( verbose ) std::cout << " regular sampling (percentage = " << samplingPercentage << ")" << std::endl; } metricSamplingStrategy = AffineRegistrationType::REGULAR; } if( std::strcmp( whichMetric.c_str(), "cc" ) == 0 ) { unsigned int radiusOption = parser->Convert( metricOption->GetFunction( currentStage )->GetParameter( 3 ) ); if( timedim == 0 ) { if ( verbose ) std::cout << " using the CC metric (radius = " << radiusOption << ")." << std::endl; } typedef itk::ANTSNeighborhoodCorrelationImageToImageMetricv4 CorrelationMetricType; typename CorrelationMetricType::Pointer correlationMetric = CorrelationMetricType::New(); typename CorrelationMetricType::RadiusType radius; radius.Fill( radiusOption ); correlationMetric->SetRadius( radius ); correlationMetric->SetUseMovingImageGradientFilter( false ); correlationMetric->SetUseFixedImageGradientFilter( false ); metric = correlationMetric; } else if( std::strcmp( whichMetric.c_str(), "mi" ) == 0 ) { unsigned int binOption = parser->Convert( metricOption->GetFunction( currentStage )->GetParameter( 3 ) ); typedef itk::MattesMutualInformationImageToImageMetricv4 MutualInformationMetricType; typename MutualInformationMetricType::Pointer mutualInformationMetric = MutualInformationMetricType::New(); mutualInformationMetric = mutualInformationMetric; mutualInformationMetric->SetNumberOfHistogramBins( binOption ); mutualInformationMetric->SetUseMovingImageGradientFilter( false ); mutualInformationMetric->SetUseFixedImageGradientFilter( false ); metric = mutualInformationMetric; } else if( std::strcmp( whichMetric.c_str(), "demons" ) == 0 ) { if( timedim == 0 ) { if ( verbose ) std::cout << " using the Demons metric." << std::endl; } typedef itk::MeanSquaresImageToImageMetricv4 DemonsMetricType; typename DemonsMetricType::Pointer demonsMetric = DemonsMetricType::New(); demonsMetric = demonsMetric; metric = demonsMetric; } else if( std::strcmp( whichMetric.c_str(), "gc" ) == 0 ) { if( timedim == 0 ) { if ( verbose ) std::cout << " using the global correlation metric." << std::endl; } typedef itk::CorrelationImageToImageMetricv4 corrMetricType; typename corrMetricType::Pointer corrMetric = corrMetricType::New(); metric = corrMetric; if ( verbose ) std::cout << " global corr metric set " << std::endl; } else { std::cerr << "ERROR: Unrecognized image metric: " << whichMetric << std::endl; return EXIT_FAILURE; } metric->SetVirtualDomainFromImage( fixed_time_slice ); typedef itk::RegistrationParameterScalesFromPhysicalShift ScalesEstimatorType; typename ScalesEstimatorType::Pointer scalesEstimator = ScalesEstimatorType::New(); scalesEstimator->SetMetric( metric ); scalesEstimator->SetTransformForward( true ); float learningRate = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 0 ) ); typedef itk::ConjugateGradientLineSearchOptimizerv4 OptimizerType; OptimizerType::Pointer optimizer = OptimizerType::New(); optimizer->SetNumberOfIterations( iterations[0] ); optimizer->SetMinimumConvergenceValue( 1.e-7 ); optimizer->SetConvergenceWindowSize( 10 ); optimizer->SetLowerLimit( 0 ); optimizer->SetUpperLimit( 2 ); optimizer->SetEpsilon( 0.1 ); typename OptionType::Pointer scalesOption = parser->GetOption( "useScalesEstimator" ); if( scalesOption && scalesOption->GetNumberOfFunctions() ) { std::string scalesFunction = scalesOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( scalesFunction ); if( scalesFunction.compare( "1" ) == 0 || scalesFunction.compare( "true" ) == 0 ) { if( timedim == 0 ) { if ( verbose ) std::cout << " employing scales estimator " << std::endl; } optimizer->SetScalesEstimator( scalesEstimator ); } else { if( timedim == 0 ) { if ( verbose ) std::cout << " not employing scales estimator " << scalesFunction << std::endl; } } } optimizer->SetMaximumStepSizeInPhysicalUnits( learningRate ); optimizer->SetDoEstimateLearningRateOnce( doEstimateLearningRateOnce ); optimizer->SetDoEstimateLearningRateAtEachIteration( !doEstimateLearningRateOnce ); // optimizer->SetMaximumNewtonStepSizeInPhysicalUnits(sqrt(small_step)*learningR); // Set up the image registration methods along with the transforms std::string whichTransform = transformOption->GetFunction( currentStage )->GetName(); ConvertToLowerCase( whichTransform ); // initialize with moments typedef typename itk::ImageMomentsCalculator ImageCalculatorType; typename ImageCalculatorType::Pointer calculator1 = ImageCalculatorType::New(); typename ImageCalculatorType::Pointer calculator2 = ImageCalculatorType::New(); calculator1->SetImage( fixed_time_slice ); calculator2->SetImage( moving_time_slice ); typename ImageCalculatorType::VectorType fixed_center; fixed_center.Fill(0); typename ImageCalculatorType::VectorType moving_center; moving_center.Fill(0); try { calculator1->Compute(); fixed_center = calculator1->GetCenterOfGravity(); try { calculator2->Compute(); moving_center = calculator2->GetCenterOfGravity(); } catch( ... ) { fixed_center.Fill(0); } } catch( ... ) { // Rcpp::Rcerr << " zero image1 error "; } typename AffineTransformType::OffsetType trans; itk::Point trans2; for( unsigned int i = 0; i < ImageDimension; i++ ) { trans[i] = moving_center[i] - fixed_center[i]; trans2[i] = fixed_center[i]; } if( std::strcmp( whichTransform.c_str(), "affine" ) == 0 ) { typename AffineRegistrationType::Pointer affineRegistration = AffineRegistrationType::New(); typename AffineTransformType::Pointer affineTransform = AffineTransformType::New(); affineTransform->SetIdentity(); affineTransform->SetOffset( trans ); affineTransform->SetCenter( trans2 ); nparams = affineTransform->GetNumberOfParameters() + 2; metric->SetFixedImage( preprocessFixedImage ); metric->SetVirtualDomainFromImage( preprocessFixedImage ); metric->SetMovingImage( preprocessMovingImage ); metric->SetMovingTransform( affineTransform ); typename ScalesEstimatorType::ScalesType scales(affineTransform->GetNumberOfParameters() ); typename MetricType::ParametersType newparams( affineTransform->GetParameters() ); metric->SetParameters( newparams ); metric->Initialize(); scalesEstimator->SetMetric(metric); scalesEstimator->EstimateScales(scales); optimizer->SetScales(scales); if( compositeTransform->GetNumberOfTransforms() > 0 ) { affineRegistration->SetMovingInitialTransform( compositeTransform ); } affineRegistration->SetFixedImage( preprocessFixedImage ); affineRegistration->SetMovingImage( preprocessMovingImage ); affineRegistration->SetNumberOfLevels( numberOfLevels ); affineRegistration->SetShrinkFactorsPerLevel( shrinkFactorsPerLevel ); affineRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); affineRegistration->SetMetricSamplingStrategy( metricSamplingStrategy ); affineRegistration->SetMetricSamplingPercentage( samplingPercentage ); affineRegistration->SetMetric( metric ); affineRegistration->SetOptimizer( optimizer ); typedef CommandIterationUpdate AffineCommandType; typename AffineCommandType::Pointer affineObserver = AffineCommandType::New(); affineObserver->SetNumberOfIterations( iterations ); affineRegistration->AddObserver( itk::IterationEvent(), affineObserver ); try { if ( verbose ) std::cout << std::endl << "*** Running affine registration ***" << timedim << std::endl << std::endl; affineRegistration->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } compositeTransform->AddTransform( affineRegistration->GetModifiableTransform() ); // Write out the affine transform std::string filename = outputPrefix + std::string("TimeSlice") + ants_moco_to_string(timedim) + std::string( "Affine.txt" ); typedef itk::TransformFileWriter TransformWriterType; typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetInput( affineRegistration->GetOutput()->Get() ); transformWriter->SetFileName( filename.c_str() ); // transformWriter->Update(); if( timedim == 0 ) { param_values.set_size(timedims, nparams); param_values.fill(0); } for( unsigned int i = 0; i < nparams - 2; i++ ) { param_values(timedim, i + 2) = affineRegistration->GetOutput()->Get()->GetParameters()[i]; } } else if( std::strcmp( whichTransform.c_str(), "rigid" ) == 0 ) { typedef typename RigidTransformTraits::TransformType RigidTransformType; typename RigidTransformType::Pointer rigidTransform = RigidTransformType::New(); rigidTransform->SetOffset( trans ); rigidTransform->SetCenter( trans2 ); nparams = rigidTransform->GetNumberOfParameters() + 2; typedef itk::ImageRegistrationMethodv4 RigidRegistrationType; typename RigidRegistrationType::Pointer rigidRegistration = RigidRegistrationType::New(); metric->SetFixedImage( preprocessFixedImage ); metric->SetVirtualDomainFromImage( preprocessFixedImage ); metric->SetMovingImage( preprocessMovingImage ); metric->SetMovingTransform( rigidTransform ); typename ScalesEstimatorType::ScalesType scales( rigidTransform->GetNumberOfParameters() ); typename MetricType::ParametersType newparams( rigidTransform->GetParameters() ); metric->SetParameters( newparams ); metric->Initialize(); scalesEstimator->SetMetric(metric); scalesEstimator->EstimateScales(scales); optimizer->SetScales(scales); rigidRegistration->SetFixedImage( preprocessFixedImage ); rigidRegistration->SetMovingImage( preprocessMovingImage ); rigidRegistration->SetNumberOfLevels( numberOfLevels ); rigidRegistration->SetShrinkFactorsPerLevel( shrinkFactorsPerLevel ); rigidRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); rigidRegistration->SetMetric( metric ); rigidRegistration->SetMetricSamplingStrategy( static_cast( metricSamplingStrategy ) ); rigidRegistration->SetMetricSamplingPercentage( samplingPercentage ); rigidRegistration->SetOptimizer( optimizer ); if( compositeTransform->GetNumberOfTransforms() > 0 ) { rigidRegistration->SetMovingInitialTransform( compositeTransform ); } typedef CommandIterationUpdate RigidCommandType; typename RigidCommandType::Pointer rigidObserver = RigidCommandType::New(); rigidObserver->SetNumberOfIterations( iterations ); rigidRegistration->AddObserver( itk::IterationEvent(), rigidObserver ); try { if ( verbose ) std::cout << std::endl << "*** Running rigid registration ***" << timedim << std::endl << std::endl; rigidRegistration->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } compositeTransform->AddTransform( rigidRegistration->GetModifiableTransform() ); // Write out the rigid transform std::string filename = outputPrefix + std::string("TimeSlice") + ants_moco_to_string(timedim) + std::string( "Rigid.txt" ); typedef itk::TransformFileWriter TransformWriterType; typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetInput( rigidRegistration->GetOutput()->Get() ); transformWriter->SetFileName( filename.c_str() ); // transformWriter->Update(); if( timedim == 0 ) { param_values.set_size(timedims, nparams); param_values.fill(0); } for( unsigned int i = 0; i < nparams - 2; i++ ) { param_values(timedim, i + 2) = rigidRegistration->GetOutput()->Get()->GetParameters()[i]; } } else if( std::strcmp( whichTransform.c_str(), "gaussiandisplacementfield" ) == 0 || std::strcmp( whichTransform.c_str(), "gdf" ) == 0 ) { RealType sigmaForUpdateField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 1 ) ); RealType sigmaForTotalField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); const unsigned int VImageDimension = ImageDimension; typedef itk::Vector VectorType; VectorType zeroVector( 0.0 ); typedef itk::Image DisplacementFieldType; // ORIENTATION ALERT: Original code set image size to // fixedImage buffered region, & if fixedImage BufferedRegion // != LargestPossibleRegion, this code would be wrong. typename DisplacementFieldType::Pointer displacementField = AllocImage( preprocessFixedImage, zeroVector ); typedef itk::GaussianSmoothingOnUpdateDisplacementFieldTransform GaussianDisplacementFieldTransformType; typedef itk::ImageRegistrationMethodv4 DisplacementFieldRegistrationType; typename DisplacementFieldRegistrationType::Pointer displacementFieldRegistration = DisplacementFieldRegistrationType::New(); typename GaussianDisplacementFieldTransformType::Pointer outputDisplacementFieldTransform = displacementFieldRegistration->GetModifiableTransform(); // Create the transform adaptors typedef itk::GaussianSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor DisplacementFieldTransformAdaptorType; typename DisplacementFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; // Extract parameters outputDisplacementFieldTransform->SetGaussianSmoothingVarianceForTheUpdateField( sigmaForUpdateField ); outputDisplacementFieldTransform->SetGaussianSmoothingVarianceForTheTotalField( sigmaForTotalField ); outputDisplacementFieldTransform->SetDisplacementField( displacementField ); for( unsigned int level = 0; level < numberOfLevels; level++ ) { typedef itk::ShrinkImageFilter ShrinkFilterType; typename ShrinkFilterType::Pointer shrinkFilter = ShrinkFilterType::New(); shrinkFilter->SetShrinkFactors( shrinkFactorsPerLevel[level] ); shrinkFilter->SetInput( displacementField ); shrinkFilter->Update(); typename DisplacementFieldTransformAdaptorType::Pointer fieldTransformAdaptor = DisplacementFieldTransformAdaptorType::New(); fieldTransformAdaptor->SetRequiredSpacing( shrinkFilter->GetOutput()->GetSpacing() ); fieldTransformAdaptor->SetRequiredSize( shrinkFilter->GetOutput()->GetBufferedRegion().GetSize() ); fieldTransformAdaptor->SetRequiredDirection( shrinkFilter->GetOutput()->GetDirection() ); fieldTransformAdaptor->SetRequiredOrigin( shrinkFilter->GetOutput()->GetOrigin() ); fieldTransformAdaptor->SetTransform( outputDisplacementFieldTransform ); adaptors.push_back( fieldTransformAdaptor.GetPointer() ); } displacementFieldRegistration->SetFixedImage( 0, preprocessFixedImage ); displacementFieldRegistration->SetMovingImage( 0, preprocessMovingImage ); displacementFieldRegistration->SetMetric( metric ); displacementFieldRegistration->SetNumberOfLevels( numberOfLevels ); displacementFieldRegistration->SetShrinkFactorsPerLevel( shrinkFactorsPerLevel ); displacementFieldRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); displacementFieldRegistration->SetSmoothingSigmasAreSpecifiedInPhysicalUnits( false ); displacementFieldRegistration->SetMetricSamplingStrategy( static_cast( metricSamplingStrategy ) ); displacementFieldRegistration->SetMetricSamplingPercentage( samplingPercentage ); displacementFieldRegistration->SetOptimizer( optimizer ); displacementFieldRegistration->SetTransformParametersAdaptorsPerLevel( adaptors ); if( compositeTransform->GetNumberOfTransforms() > 0 ) { displacementFieldRegistration->SetMovingInitialTransform( compositeTransform ); } try { displacementFieldRegistration->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } compositeTransform->AddTransform( outputDisplacementFieldTransform ); if( timedim == 0 ) { param_values.set_size(timedims, nparams); param_values.fill(0); } } else if( std::strcmp( whichTransform.c_str(), "SyN" ) == 0 || std::strcmp( whichTransform.c_str(), "syn" ) == 0 ) { RealType sigmaForUpdateField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 1 ) ); RealType sigmaForTotalField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); const unsigned int VImageDimension = ImageDimension; typedef itk::Vector VectorType; VectorType zeroVector( 0.0 ); typedef itk::Image DisplacementFieldType; typename DisplacementFieldType::Pointer displacementField = AllocImage( preprocessFixedImage, zeroVector ); typename DisplacementFieldType::Pointer inverseDisplacementField = AllocImage( preprocessFixedImage, zeroVector ); typedef itk::DisplacementFieldTransform DisplacementFieldTransformType; typedef itk::SyNImageRegistrationMethod DisplacementFieldRegistrationType; typename DisplacementFieldRegistrationType::Pointer displacementFieldRegistration = DisplacementFieldRegistrationType::New(); typename DisplacementFieldTransformType::Pointer outputDisplacementFieldTransform = displacementFieldRegistration->GetModifiableTransform(); // Create the transform adaptors typedef itk::DisplacementFieldTransformParametersAdaptor DisplacementFieldTransformAdaptorType; typename DisplacementFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; // Create the transform adaptors // For the gaussian displacement field, the specified variances are in image spacing terms // and, in normal practice, we typically don't change these values at each level. However, // if the user wishes to add that option, they can use the class // GaussianSmoothingOnUpdateDisplacementFieldTransformAdaptor for( unsigned int level = 0; level < numberOfLevels; level++ ) { // TODO: // We use the shrink image filter to calculate the fixed parameters of the virtual // domain at each level. To speed up calculation and avoid unnecessary memory // usage, we could calculate these fixed parameters directly. typedef itk::ShrinkImageFilter ShrinkFilterType; typename ShrinkFilterType::Pointer shrinkFilter = ShrinkFilterType::New(); shrinkFilter->SetShrinkFactors( shrinkFactorsPerLevel[level] ); shrinkFilter->SetInput( displacementField ); shrinkFilter->Update(); typename DisplacementFieldTransformAdaptorType::Pointer fieldTransformAdaptor = DisplacementFieldTransformAdaptorType::New(); fieldTransformAdaptor->SetRequiredSpacing( shrinkFilter->GetOutput()->GetSpacing() ); fieldTransformAdaptor->SetRequiredSize( shrinkFilter->GetOutput()->GetBufferedRegion().GetSize() ); fieldTransformAdaptor->SetRequiredDirection( shrinkFilter->GetOutput()->GetDirection() ); fieldTransformAdaptor->SetRequiredOrigin( shrinkFilter->GetOutput()->GetOrigin() ); fieldTransformAdaptor->SetTransform( outputDisplacementFieldTransform ); adaptors.push_back( fieldTransformAdaptor.GetPointer() ); } // Extract parameters typename DisplacementFieldRegistrationType::NumberOfIterationsArrayType numberOfIterationsPerLevel; numberOfIterationsPerLevel.SetSize( numberOfLevels ); if( timedim == 0 ) { if ( verbose ) std::cout << "SyN iterations:"; } for( unsigned int d = 0; d < numberOfLevels; d++ ) { numberOfIterationsPerLevel[d] = iterations[d]; // currentStageIterations[d]; if( timedim == 0 ) { if ( verbose ) std::cout << numberOfIterationsPerLevel[d] << " "; } } if( timedim == 0 ) { if ( verbose ) std::cout << std::endl; } const RealType varianceForUpdateField = sigmaForUpdateField; const RealType varianceForTotalField = sigmaForTotalField; displacementFieldRegistration->SetFixedImage( 0, preprocessFixedImage ); displacementFieldRegistration->SetMovingImage( 0, preprocessMovingImage ); displacementFieldRegistration->SetMetric( metric ); if( compositeTransform->GetNumberOfTransforms() > 0 ) { displacementFieldRegistration->SetMovingInitialTransform( compositeTransform ); } displacementFieldRegistration->SetDownsampleImagesForMetricDerivatives( true ); displacementFieldRegistration->SetAverageMidPointGradients( false ); displacementFieldRegistration->SetNumberOfLevels( numberOfLevels ); displacementFieldRegistration->SetShrinkFactorsPerLevel( shrinkFactorsPerLevel ); displacementFieldRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); displacementFieldRegistration->SetSmoothingSigmasAreSpecifiedInPhysicalUnits( false ); displacementFieldRegistration->SetLearningRate( learningRate ); displacementFieldRegistration->SetConvergenceThreshold( 1.e-8 ); displacementFieldRegistration->SetConvergenceWindowSize( 10 ); displacementFieldRegistration->SetNumberOfIterationsPerLevel( numberOfIterationsPerLevel ); displacementFieldRegistration->SetTransformParametersAdaptorsPerLevel( adaptors ); displacementFieldRegistration->SetGaussianSmoothingVarianceForTheUpdateField( varianceForUpdateField ); displacementFieldRegistration->SetGaussianSmoothingVarianceForTheTotalField( varianceForTotalField ); outputDisplacementFieldTransform->SetDisplacementField( displacementField ); outputDisplacementFieldTransform->SetInverseDisplacementField( inverseDisplacementField ); try { displacementFieldRegistration->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform compositeTransform->AddTransform( outputDisplacementFieldTransform ); if( timedim == 0 ) { param_values.set_size(timedims, nparams); param_values.fill(0); } } else { std::cerr << "ERROR: Unrecognized transform option - " << whichTransform << std::endl; return EXIT_FAILURE; } if( currentStage == static_cast(numberOfStages) - 1 ) { param_values(timedim, 1) = metric->GetValue(); } metriclist.push_back( param_values(timedim, 1) ); metricmean += param_values(timedim, 1) / ( double ) timedims; // resample the moving image and then put it in its place typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetTransform( compositeTransform ); resampler->SetInput( moving_time_slice ); resampler->SetOutputParametersFromImage( fixed_time_slice ); resampler->SetDefaultPixelValue( 0 ); resampler->Update(); if ( verbose ) std::cout << " done resampling timepoint : " << timedim << std::endl; /** Here, we put the resampled 3D image into the 4D volume */ typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter2( resampler->GetOutput(), resampler->GetOutput()->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { typename FixedImageType::PixelType fval = vfIter2.Get(); typename MovingImageType::IndexType ind; for( unsigned int xx = 0; xx < ImageDimension; xx++ ) { ind[xx] = vfIter2.GetIndex()[xx]; } unsigned int tdim = timedim; if( tdim > ( timedims - 1 ) ) { tdim = timedims - 1; } ind[ImageDimension] = tdim; outputImage->SetPixel(ind, fval); } if ( writeDisplacementField > 0 ) { typedef typename itk::TransformToDisplacementFieldFilter ConverterType; typename ConverterType::Pointer converter = ConverterType::New(); converter->SetOutputOrigin( fixed_time_slice->GetOrigin() ); converter->SetOutputStartIndex( fixed_time_slice->GetBufferedRegion().GetIndex() ); converter->SetSize( fixed_time_slice->GetBufferedRegion().GetSize() ); converter->SetOutputSpacing( fixed_time_slice->GetSpacing() ); converter->SetOutputDirection( fixed_time_slice->GetDirection() ); converter->SetTransform( compositeTransform ); converter->Update(); /** Here, we put the 3d tx into a 4d displacement field */ for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { VectorType vec = converter->GetOutput()->GetPixel( vfIter2.GetIndex() ); VectorIOType vecout; vecout.Fill( 0 ); typename MovingIOImageType::IndexType ind; for( unsigned int xx = 0; xx < ImageDimension; xx++ ) { ind[xx] = vfIter2.GetIndex()[xx]; vecout[xx] = vec[xx]; } unsigned int tdim = timedim; if( tdim > ( timedims - 1 ) ) { tdim = timedims - 1; } ind[ImageDimension] = tdim; displacementout->SetPixel( ind, vecout ); } # typename ConverterType::Pointer converter2 = ConverterType::New(); converter2->SetOutputOrigin( moving_time_slice->GetOrigin() ); converter2->SetOutputStartIndex( moving_time_slice->GetBufferedRegion().GetIndex() ); converter2->SetSize( moving_time_slice->GetBufferedRegion().GetSize() ); converter2->SetOutputSpacing( moving_time_slice->GetSpacing() ); converter2->SetOutputDirection( moving_time_slice->GetDirection() ); converter2->SetTransform( compositeTransform->GetInverseTransform() ); converter2->Update(); /** Here, we put the 3d tx into a 4d displacement field */ typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIterInv( moving_time_slice, moving_time_slice->GetLargestPossibleRegion() ); for( vfIterInv.GoToBegin(); !vfIterInv.IsAtEnd(); ++vfIterInv ) { VectorType vec = converter2->GetOutput()->GetPixel( vfIterInv.GetIndex() ); VectorIOType vecout; vecout.Fill( 0 ); typename MovingIOImageType::IndexType ind; for( unsigned int xx = 0; xx < ImageDimension; xx++ ) { ind[xx] = vfIterInv.GetIndex()[xx]; vecout[xx] = vec[xx]; } unsigned int tdim = timedim; if( tdim > ( timedims - 1 ) ) { tdim = timedims - 1; } ind[ImageDimension] = tdim; displacementinv->SetPixel( ind, vecout ); } } } if( outputOption && outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 && currentStage == 0 ) { std::string fileName = outputOption->GetFunction( 0 )->GetParameter( 1 ); if( outputPrefix.length() < 3 ) { outputPrefix = outputOption->GetFunction( 0 )->GetName(); } if ( verbose ) std::cout << "motion corrected out " << fileName << std::endl; WriteImage( outputImage, fileName.c_str() ); } if( outputOption && outputOption->GetFunction( 0 )->GetNumberOfParameters() > 2 && outputImage && currentStage == 0 ) { std::string fileName = outputOption->GetFunction( 0 )->GetParameter( 2 ); typename FixedIOImageType::Pointer avgImage; typedef itk::ExtractImageFilter ExtractFilterType; typename MovingImageType::RegionType extractRegion = movingImage->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension, 0); typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( movingImage ); extractFilter->SetDirectionCollapseToSubmatrix(); if( ImageDimension == 2 ) { extractFilter->SetDirectionCollapseToIdentity(); } unsigned int td = 0; extractRegion.SetIndex(ImageDimension, td ); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); avgImage = extractFilter->GetOutput(); std::sort(timelist.begin(), timelist.end(), ants_moco_index_cmp &>(metriclist) ); if( nimagestoavg == 0 ) { nimagestoavg = timelist.size(); } std::vector timelistsort; for( unsigned int i = 0; i < nimagestoavg; i++ ) { if( i < timelist.size() ) { timelistsort.push_back(timelist[i]); } if ( verbose ) std::cout << " i^th value " << i << " is " << metriclist[timelist[i]] << std::endl; } AverageTimeImages( outputImage, fixed_time_slice, timelistsort ); if ( verbose ) std::cout << " write average post " << fileName << std::endl; WriteImage( fixed_time_slice, fileName.c_str() ); } } if ( writeDisplacementField > 0 ) { std::string dfn = outputPrefix + std::string("Warp.nii.gz"); WriteImage( displacementout, dfn.c_str() ); dfn = outputPrefix + std::string("InverseWarp.nii.gz"); WriteImage( displacementinv, dfn.c_str() ); } totalTimer.Stop(); if ( verbose ) std::cout << std::endl << "Total elapsed time: " << totalTimer.GetMean() << " averagemetric " << metricmean << std::endl; { std::vector ColumnHeaders; std::string colname; colname = std::string("MetricPre"); ColumnHeaders.push_back( colname ); colname = std::string("MetricPost"); ColumnHeaders.push_back( colname ); for( unsigned int nv = 2; nv < nparams; nv++ ) { std::string _colname = std::string("MOCOparam") + ants_moco_to_string(nv - 2); ColumnHeaders.push_back( _colname ); } typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); std::string fnmp; if ( verbose ) std::cout << " get motion corr params " << outputPrefix << std::endl; if( outputPrefix[0] == '0' && outputPrefix[1] == 'x' ) { void* ptr; std::sscanf(outputPrefix.c_str(), "%p", (void * *)&ptr); // std::stringstream strstream; // strstream << outputPrefix; // void* ptr; // strstream >> ptr; ( static_cast, vnl_matrix > *>( ptr ) )->first = ColumnHeaders; ( static_cast, vnl_matrix > *>( ptr ) )->second = param_values; if ( verbose ) std::cout << "motion-correction params written" << std::endl; } else { fnmp = outputPrefix + std::string("MOCOparams.csv"); if ( verbose ) std::cout << " write " << fnmp << std::endl; writer->SetFileName( fnmp.c_str() ); writer->SetColumnHeaders(ColumnHeaders); writer->SetInput( ¶m_values ); writer->Write(); } } return EXIT_SUCCESS; } void antsMotionCorrInitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, the program tries to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "turn on the option that lets you estimate the learning rate step size only at the beginning of each level. * useful as a second stage of fine-scale registration." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-estimate-learning-rate-once" ); option->SetShortName( 'l' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "This option sets the number of images to use to construct the template image."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "n-images" ); option->SetShortName( 'n' ); option->SetUsageOption( 0, "10" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Four image metrics are available--- " ) + std::string( "GC : global correlation, CC: ANTS neighborhood cross correlation, MI: Mutual information, and " ) + std::string( "Demons: Thirion's Demons (modified mean-squares). " ) + std::string( "Note that the metricWeight is currently not used. " ) + std::string( "Rather, it is a temporary place holder until multivariate metrics " ) + std::string( "are available for a single stage. " ) + std::string( "The fixed image should be a single time point (eg the average of the time series). " ) + std::string( "By default, this image is not used, the fixed image for correction of each volume is the preceding volume " ) + std::string( "in the time series. See below for the option to use a fixed reference image for all volumes. "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "metric" ); option->SetShortName( 'm' ); option->SetUsageOption( 0, "CC[fixedImage,movingImage,metricWeight,radius,,]" ); option->SetUsageOption( 1, "MI[fixedImage,movingImage,metricWeight,numberOfBins,,]" ); option->SetUsageOption( 2, "Demons[fixedImage,movingImage,metricWeight,radius,,]" ); option->SetUsageOption( 3, "GC[fixedImage,movingImage,metricWeight,radius,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "use a fixed reference image to correct all volumes, instead of correcting each image ") + std::string( "to the prior volume in the time series." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "useFixedReferenceImage" ); option->SetShortName( 'u' ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "use the scale estimator to control optimization." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "useScalesEstimator" ); option->SetShortName( 'e' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Several transform options are available. The gradientStep or" ) + std::string( "learningRate characterizes the gradient descent optimization and is scaled appropriately " ) + std::string( "for each transform using the shift scales estimator. Subsequent parameters are " ) + std::string( "transform-specific and can be determined from the usage. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "transform" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "Affine[gradientStep]" ); option->SetUsageOption( 1, "Rigid[gradientStep]" ); option->SetUsageOption( 2, "GaussianDisplacementField[gradientStep,updateFieldSigmaInPhysicalSpace,totalFieldSigmaInPhysicalSpace]" ); option->SetUsageOption( 3, "SyN[gradientStep,updateFieldSigmaInPhysicalSpace,totalFieldSigmaInPhysicalSpace]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the number of iterations at each level." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "iterations" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "MxNx0..." ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the amount of smoothing at each level." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "smoothingSigmas" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "MxNx0..." ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the shrink factor for the virtual domain (typically the fixed image) at each level." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "shrinkFactors" ); option->SetShortName( 'f' ); option->SetUsageOption( 0, "MxNx0..." ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the output transform prefix (output format is .nii.gz )." ) + std::string( "Optionally, one can choose to warp the moving image to the fixed space and, if the " ) + std::string( "inverse transform exists, one can also output the warped fixed image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "[outputTransformPrefix,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Average the input time series image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "average-image" ); option->SetShortName( 'a' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Write the low-dimensional 3D transforms to a 4D displacement field" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "write-displacement" ); option->SetShortName( 'w' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsMotionCorr( std::vector args, std::ostream * /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsMotionCorr" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "antsMotionCorr = motion correction. This program is a user-level " ) + std::string( "registration application meant to utilize ITKv4-only classes. The user can specify " ) + std::string( "any number of \"stages\" where a stage consists of a transform; an image metric; " ) + std::string( " and iterations, shrink factors, and smoothing sigmas for each level. " ) + std::string( " Specialized for 4D time series data: fixed image is 3D, moving image should be the 4D time series. ") + std::string( " Fixed image is a reference space or time slice."); parser->SetCommandDescription( commandDescription ); antsMotionCorrInitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc < 2 || parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } else if( parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Get dimensionality unsigned int dimension = 3; itk::ants::CommandLineParser::OptionType::Pointer dimOption = parser->GetOption( "dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { std::cerr << "Image dimensionality not specified. See command line option --dimensionality" << std::endl; return EXIT_FAILURE; } switch( dimension ) { case 2: { return ants_motion<2>( parser ); } break; case 3: { return ants_motion<3>( parser ); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsMotionCorrDiffusionDirection.cxx000066400000000000000000000476451311104306400234640ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 "antsUtilities.h" #include "antsAllocImage.h" #include "ReadWriteData.h" #include "antsCommandLineParser.h" #include "itkCSVNumericObjectFileWriter.h" #include "itkCSVArray2DFileReader.h" #include "itkImageRegistrationMethodv4.h" #include "itkSyNImageRegistrationMethod.h" #include "itkDisplacementFieldTransform.h" #include "itkANTSNeighborhoodCorrelationImageToImageMetricv4.h" #include "itkMeanSquaresImageToImageMetricv4.h" #include "itkCorrelationImageToImageMetricv4.h" #include "itkImageToImageMetricv4.h" #include "itkMattesMutualInformationImageToImageMetricv4.h" #include "itkImageToHistogramFilter.h" #include "itkHistogramMatchingImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include "itkAffineTransform.h" #include "itkBSplineTransform.h" #include "itkBSplineSmoothingOnUpdateDisplacementFieldTransform.h" #include "itkCompositeTransform.h" #include "itkGaussianSmoothingOnUpdateDisplacementFieldTransform.h" #include "itkIdentityTransform.h" #include "itkEuler2DTransform.h" #include "itkEuler3DTransform.h" #include "itkTransform.h" #include "itkExtractImageFilter.h" #include "itkBSplineTransformParametersAdaptor.h" #include "itkBSplineSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor.h" #include "itkGaussianSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor.h" #include "itkTimeVaryingVelocityFieldTransformParametersAdaptor.h" #include "itkGradientDescentOptimizerv4.h" #include "itkConjugateGradientLineSearchOptimizerv4.h" #include "itkQuasiNewtonOptimizerv4.h" #include "itkHistogramMatchingImageFilter.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMacro.h" #include "itkRegistrationParameterScalesFromPhysicalShift.h" #include "itkResampleImageFilter.h" #include "itkShrinkImageFilter.h" #include "itkTimeProbe.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "itkSimilarity2DTransform.h" #include "itkSimilarity3DTransform.h" #include namespace ants { template inline std::string ants_moco_to_string(const T& t) { std::stringstream ss; ss << t; return ss.str(); } template struct ants_moco_index_cmp { ants_moco_index_cmp(const T _arr) : arr(_arr) { } bool operator()(const size_t a, const size_t b) const { return arr[a] < arr[b]; } const T arr; }; // Transform traits to generalize the rigid transform // template class RigidTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class RigidTransformTraits<2> { public: typedef itk::Euler2DTransform TransformType; }; template <> class RigidTransformTraits<3> { public: // typedef itk::VersorRigid3DTransform TransformType; // typedef itk::QuaternionRigidTransform TransformType; typedef itk::Euler3DTransform TransformType; }; template class SimilarityTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class SimilarityTransformTraits<2> { public: typedef itk::Similarity2DTransform TransformType; }; template <> class SimilarityTransformTraits<3> { public: typedef itk::Similarity3DTransform TransformType; }; int ants_motion_directions( itk::ants::CommandLineParser *parser ) { const unsigned int ImageDimension = 3; typedef double RealType; typedef itk::Image FixedImageType; typedef itk::ImageFileReader ImageReaderType; typedef vnl_matrix vMatrix; vMatrix param_values; typedef itk::CompositeTransform CompositeTransformType; std::vector CompositeTransformVector; typedef itk::Euler3DTransform RigidTransformType; typedef itk::AffineTransform AffineTransformType; typedef itk::ants::CommandLineParser ParserType; typedef ParserType::OptionType OptionType; typedef double ParameterValueType; typedef itk::CSVArray2DFileReader MocoReaderType; typedef MocoReaderType::Array2DDataObjectType MocoDataArrayType; typedef itk::Array2D DirectionArrayType; std::string outputName = ""; std::string mocoName = ""; std::string physicalName = ""; OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { outputName = outputOption->GetFunction(0)->GetName(); std::cout << "Output: " << outputName << std::endl; } else { std::cerr << "Output option not specified." << std::endl; return EXIT_FAILURE; } OptionType::Pointer mocoOption = parser->GetOption( "moco" ); if( mocoOption && mocoOption->GetNumberOfFunctions() ) { mocoName = mocoOption->GetFunction(0)->GetName(); std::cout << "Moco file: " << mocoName << std::endl; } else { std::cerr << "Motion parameter file not specified" << std::endl; return EXIT_FAILURE; } OptionType::Pointer physicalOption = parser->GetOption( "physical" ); if( physicalOption && physicalOption->GetNumberOfFunctions() ) { physicalName = physicalOption->GetFunction(0)->GetName(); std::cout << "Physical space image: " << physicalName << std::endl; } else { std::cerr << "Physical space image not specified" << std::endl; return EXIT_FAILURE; } OptionType::Pointer schemeOption = parser->GetOption( "scheme" ); OptionType::Pointer bvecOption = parser->GetOption( "bvec" ); if ( bvecOption->GetNumberOfFunctions() && schemeOption->GetNumberOfFunctions() ) { std::cerr << "Must use either scheme or bvec input, not both" << std::endl; return EXIT_FAILURE; } DirectionArrayType directionArray; if( schemeOption && schemeOption->GetNumberOfFunctions() ) { std::string schemeName = schemeOption->GetFunction(0)->GetName(); //std::cout << "Scheme file: " << schemeName << std::endl; std::cout << "Scheme input format still a work in progress" << std::endl; return EXIT_FAILURE; MocoReaderType::Pointer schemeReader = MocoReaderType::New(); schemeReader->SetFileName( schemeName.c_str() ) ; schemeReader->SetFieldDelimiterCharacter( ' ' ); schemeReader->HasColumnHeadersOff(); schemeReader->HasRowHeadersOff(); schemeReader->Parse(); // Manually skip first 2 lines of data array MocoDataArrayType::MatrixType schemeMatrix = schemeReader->GetOutput()->GetMatrix(); //std::cout << "scheme data array size = " << schemeMatrix.rows() << " x " << schemeMatrix.cols() << std::endl; directionArray.SetSize( schemeMatrix.rows()-2, schemeMatrix.cols() ); for ( unsigned int i=2; i < schemeMatrix.rows(); i++ ) { for ( unsigned int j=0; j < 3; j++ ) { directionArray(i-2,j) = schemeMatrix(i-2,j); } } } bool transposeArray = true; if( bvecOption && bvecOption->GetNumberOfFunctions() ) { std::string bvecName = bvecOption->GetFunction(0)->GetName(); //std::cout << "bvec file: " << bvecName << std::endl; MocoReaderType::Pointer bvecReader = MocoReaderType::New(); bvecReader->SetFileName( bvecName.c_str() ) ; bvecReader->SetFieldDelimiterCharacter( ' ' ); bvecReader->HasColumnHeadersOff(); bvecReader->HasRowHeadersOff(); bvecReader->Update(); MocoDataArrayType::MatrixType bvecMatrix = bvecReader->GetOutput()->GetMatrix(); //std::cout << "BVEC data array size = " << bvecMatrix.rows() << " x " << bvecMatrix.cols() << std::endl; if ( bvecMatrix.cols() == 3 ) { transposeArray = false; std::cout << "Column based format" << std::endl; } // Transpose the array if ( transposeArray ) { directionArray.SetSize( bvecMatrix.cols(), bvecMatrix.rows() ); } else { directionArray.SetSize( bvecMatrix.rows(), bvecMatrix.cols() ); } if ( transposeArray ) { for ( unsigned int i=0; i < bvecMatrix.cols(); i++ ) { for ( unsigned int j=0; j < bvecMatrix.rows(); j++ ) { directionArray(i,j) = bvecMatrix(j,i); } } } else { for ( unsigned int i=0; i < bvecMatrix.cols(); i++ ) { for ( unsigned int j=0; j < bvecMatrix.rows(); j++ ) { directionArray(j,i) = bvecMatrix(j,i); } } } } std::cout << "Read direction data of size: " << directionArray.rows() << " x " << directionArray.cols() << std::endl; // itkImageFileReader will set direction to identity if the image being read has more dimensions than the class template // eg if you pass a 4D image file name to a ReaderType whose dimension is 3 // // Therefore check reference image is 3D, and fail if not // itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(physicalName.c_str(), itk::ImageIOFactory::ReadMode); imageIO->SetFileName(physicalName.c_str() ); try { imageIO->ReadImageInformation(); } catch( ... ) { std::cout << "Can't read reference image " << physicalName << std::endl; return EXIT_FAILURE; } if (imageIO->GetNumberOfDimensions() != ImageDimension) { std::cout << "Reference image must be 3D " << std::endl; return EXIT_FAILURE; } ImageReaderType::Pointer imageReader = ImageReaderType::New(); imageReader->SetFileName(physicalName.c_str()); imageReader->Update(); DirectionArrayType outputDirectionArray; outputDirectionArray.SetSize( directionArray.rows(), directionArray.cols() ); MocoReaderType::Pointer mocoReader = MocoReaderType::New(); mocoReader->SetFileName( mocoName.c_str() ); mocoReader->SetFieldDelimiterCharacter( ',' ); mocoReader->HasColumnHeadersOn(); mocoReader->HasRowHeadersOff(); mocoReader->Update(); MocoDataArrayType::Pointer mocoDataArray = mocoReader->GetOutput(); std::cout << "Read motion correction data of size: " << mocoDataArray->GetMatrix().rows() << " x " << mocoDataArray->GetMatrix().cols() << std::endl; // [-0.231374, -0.0543334, 0.677384] unsigned int nTransformParams = mocoDataArray->GetMatrix().cols() - 2; //std::cout << "# Transform parameters = " << nTransformParams << std::endl; AffineTransformType::Pointer toPhysical = AffineTransformType::New(); toPhysical->SetIdentity(); AffineTransformType::Pointer toIndex = AffineTransformType::New(); toIndex->SetIdentity(); AffineTransformType::MatrixType toPhysicalMatrix = toPhysical->GetMatrix(); for ( unsigned int i=0; i < ImageDimension; i++) { for ( unsigned int j=0; j < ImageDimension; j++) { toPhysicalMatrix(i,j) = imageReader->GetOutput()->GetDirection()(i,j); } } toPhysical->SetMatrix(toPhysicalMatrix); toPhysical->GetInverse(toIndex); for ( unsigned int i=0; iGetMatrix()(i,t+2); } rigid->SetParameters( rParams ); affineTransform->SetMatrix( rigid->GetMatrix() ); affineTransform->SetTranslation( rigid->GetTranslation() ); } else if (nTransformParams == 12) { params.SetSize( nTransformParams ); for ( unsigned int t=0; tGetMatrix()(i,t+2); } affineTransform->SetParameters( params ); affineTransform->GetInverse( directionTransform ); } else { // Not rigid (6 params) or affine (12), something is wrong return EXIT_FAILURE; } //std::cout << affineTransform->GetTranslation() << std::endl; AffineTransformType::InputVectorType dir; AffineTransformType::OutputVectorType rotatedDir; for ( unsigned int j=0; j 0 ) { dir.Normalize(); rotatedDir = toPhysical->TransformVector( dir ); rotatedDir = affineTransform->TransformVector( rotatedDir ); rotatedDir.Normalize(); rotatedDir = toIndex->TransformVector( rotatedDir ); //rotatedDir = affineTransform->TransformVector( dir ); //rotatedDir.Normalize(); } else { for ( unsigned int j=0; j " << rotatedDir << std::endl; } // Write new directions to output file if ( outputName.find( ".bvec" ) != std::string::npos ) { //std::cout << "Writing bvec file " << outputName << std::endl; std::ofstream outfile( outputName.c_str() ); if ( transposeArray ) { for ( unsigned int i=0; iSetLongName( "scheme" ); option->SetShortName( 's' ); option->SetDescription( description ); option->SetUsageOption( 0, "dwi.scheme" ); parser->AddOption( option ); } { std::string description = std::string( "bvec image specifying diffusion directions."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "bvec" ); option->SetShortName( 'b' ); option->SetDescription( description ); option->SetUsageOption( 0, "dwi.bvec" ); parser->AddOption( option ); } { std::string description = std::string( "3D image in dwi space"); OptionType::Pointer option = OptionType::New(); option->SetLongName( "physical" ); option->SetShortName( 'p' ); option->SetDescription( description ); option->SetUsageOption( 0, "ref.nii.gz" ); parser->AddOption( option ); } { std::string description = std::string( "motion correction parameters from antsMotionCorr." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "moco" ); option->SetShortName( 'm' ); option->SetDescription( description ); option->SetUsageOption( 0, "MOCOparams.csv" ); parser->AddOption( option ); } { std::string description = std::string( "Specify the output file for corrected directions." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetDescription( description ); option->SetUsageOption( 0, "corrected.scheme" ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsMotionCorrDiffusionDirection( std::vector args, std::ostream * /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsMotionCorrDiffusionDirection" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "antsMotionCorrDiffusionDirection" ); parser->SetCommandDescription( commandDescription ); antsMotionCorrDiffusionDirectionInitializeCommandLineOptions( parser ); parser->Parse( argc, argv ); if( argc < 2 || parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } else if( parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } std::cout << std::endl << "Running " << argv[0] << " for 3-dimensional images." << std::endl << std::endl; return ants_motion_directions( parser ); } } // namespace ants ants-2.2.0/Examples/antsMotionCorrStats.cxx000066400000000000000000000450711311104306400207620ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 "antsUtilities.h" #include "antsAllocImage.h" #include "ReadWriteData.h" #include "antsCommandLineParser.h" #include "itkCSVNumericObjectFileWriter.h" #include "itkCSVArray2DFileReader.h" #include "itkAffineTransform.h" #include "itkEuler3DTransform.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkTransformFileWriter.h" #include namespace ants { int ants_motion_stats( itk::ants::CommandLineParser *parser ) { const unsigned int ImageDimension = 3; typedef double RealType; typedef itk::Image ImageType; typedef vnl_matrix vMatrix; vMatrix param_values; typedef itk::Image TimeSeriesImageType; typedef TimeSeriesImageType::RegionType TimeSeriesRegionType; typedef TimeSeriesImageType::IndexType TimeSeriesIndexType; typedef TimeSeriesImageType::SizeType TimeSeriesSizeType; typedef itk::ImageRegionIterator< TimeSeriesImageType > TimeSeriesIteratorType; typedef itk::ImageRegionIteratorWithIndex IteratorType; typedef itk::AffineTransform AffineTransformType; typedef itk::Euler3DTransform RigidTransformType; typedef itk::TransformFileWriterTemplate TransformWriterType; typedef itk::ants::CommandLineParser ParserType; typedef ParserType::OptionType OptionType; typedef double ParameterValueType; typedef itk::CSVArray2DFileReader MocoReaderType; typedef MocoReaderType::Array2DDataObjectType MocoDataArrayType; typedef itk::CSVNumericObjectFileWriter WriterType; typedef WriterType::vnlMatrixType WriterMatrixType; std::string outputName = ""; std::string mocoName = ""; std::string spatialName = ""; std::string timeseriesDisplacementName = ""; unsigned long transformIndex = 0; bool writeMap = false; bool writeTransform = false; bool timeseriesDisplacement = false; OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { outputName = outputOption->GetFunction(0)->GetName(); std::cout << "Output: " << outputName << std::endl; } else { std::cerr << "Output option not specified." << std::endl; return EXIT_FAILURE; } OptionType::Pointer spatialOption = parser->GetOption( "spatial-map" ); if( spatialOption && spatialOption->GetNumberOfFunctions() ) { spatialName = spatialOption->GetFunction(0)->GetName(); std::cout << "Spatial map output: " << spatialName << std::endl; writeMap = true; } OptionType::Pointer timeseriesDisplacementOption = parser->GetOption("timeseries-displacement"); if(timeseriesDisplacementOption && timeseriesDisplacementOption->GetNumberOfFunctions()) { timeseriesDisplacementName = timeseriesDisplacementOption->GetFunction(0)->GetName(); std::cout << "Time-series displacement map input 4d image: " << timeseriesDisplacementName << std::endl; timeseriesDisplacement = true; } OptionType::Pointer transformOption = parser->GetOption( "transform" ); if( transformOption && transformOption->GetNumberOfFunctions() ) { transformIndex = atoi( transformOption->GetFunction(0)->GetName().c_str() ); std::cout << "Index of transform to output: " << transformIndex << std::endl; writeTransform = true; } OptionType::Pointer mocoOption = parser->GetOption( "moco" ); if( mocoOption && mocoOption->GetNumberOfFunctions() ) { mocoName = mocoOption->GetFunction(0)->GetName(); std::cout << "Moco file: " << mocoName << std::endl; } else { std::cerr << "Motion parameter file not specified" << std::endl; return EXIT_FAILURE; } OptionType::Pointer maskOption = parser->GetOption( "mask" ); ImageType::Pointer mask = ImageType::New(); if ( maskOption && maskOption->GetNumberOfFunctions() ) { ReadImage( mask, maskOption->GetFunction(0)->GetName().c_str() ); } else { if ( !writeTransform ) { std::cerr << "Must use mask image" << std::endl; return EXIT_FAILURE; } } ImageType::Pointer map = ImageType::New(); if ( writeMap ) { map->SetRegions( mask->GetLargestPossibleRegion() ); map->Allocate(); map->FillBuffer(0); map->SetOrigin( mask->GetOrigin() ); map->SetSpacing( mask->GetSpacing() ); map->SetDirection( mask->GetDirection() ); } TimeSeriesImageType::Pointer timeseriesImage = TimeSeriesImageType::New(); TimeSeriesImageType::Pointer timeseriesDisplacementImage = TimeSeriesImageType::New(); if ( timeseriesDisplacement ) { ReadImage(timeseriesImage, timeseriesDisplacementName.c_str()); timeseriesDisplacementImage->SetRegions(timeseriesImage->GetLargestPossibleRegion()); timeseriesDisplacementImage->Allocate(); timeseriesDisplacementImage->FillBuffer(0.0); timeseriesDisplacementImage->SetOrigin(timeseriesImage->GetOrigin()); timeseriesDisplacementImage->SetSpacing(timeseriesImage->GetSpacing()); timeseriesDisplacementImage->SetDirection(timeseriesImage->GetDirection()); } bool doFramewise = 0; doFramewise = parser->Convert( parser->GetOption( "framewise" )->GetFunction()->GetName() ); std::cout << "Framewise = " << doFramewise << std::endl; MocoReaderType::Pointer mocoReader = MocoReaderType::New(); mocoReader->SetFileName( mocoName.c_str() ); mocoReader->SetFieldDelimiterCharacter( ',' ); mocoReader->HasColumnHeadersOn(); mocoReader->HasRowHeadersOff(); mocoReader->Update(); MocoDataArrayType::Pointer mocoDataArray = mocoReader->GetOutput(); std::cout << "Read motion correction data of size: " << mocoDataArray->GetMatrix().rows() << " x " << mocoDataArray->GetMatrix().cols() << std::endl; unsigned int nTransformParams = mocoDataArray->GetMatrix().cols() - 2; //std::cout << "# Transform parameters = " << nTransformParams << std::endl; WriterMatrixType dataMatrix( mocoDataArray->GetMatrix().rows(), 2 ); // Extract a single 3D transform and write to file if ( writeTransform ) { AffineTransformType::Pointer affineTransform1 = AffineTransformType::New(); AffineTransformType::ParametersType params1; params1.SetSize( nTransformParams ); for ( unsigned int t=0; tGetMatrix()(transformIndex,t+2); } // If rigid motion corretion if ( nTransformParams == 6 ) { RigidTransformType::Pointer rigid1 = RigidTransformType::New(); rigid1->SetParameters( params1 ); affineTransform1->SetMatrix( rigid1->GetMatrix() ); affineTransform1->SetTranslation( rigid1->GetTranslation() ); } else if ( nTransformParams == 12 ) { affineTransform1->SetParameters( params1 ); } else { std::cout << "Unknown transform type! - Exiting" << std::endl; return EXIT_FAILURE; } TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetInput( affineTransform1 ); transformWriter->SetFileName( outputName.c_str() ); transformWriter->Update(); return EXIT_SUCCESS; } for ( unsigned int i=0; iGetMatrix().rows(); i++ ) { AffineTransformType::Pointer affineTransform1 = AffineTransformType::New(); AffineTransformType::Pointer affineTransform2 = AffineTransformType::New(); AffineTransformType::ParametersType params1; AffineTransformType::ParametersType params2; params1.SetSize( nTransformParams ); params2.SetSize( nTransformParams ); for ( unsigned int t=0; tGetMatrix()(i,t+2); if ( i < (mocoDataArray->GetMatrix().rows()-1) ) { params2[t] = mocoDataArray->GetMatrix()(i+1,t+2); } } // If rigid motion correction if ( nTransformParams == 6 ) { RigidTransformType::Pointer rigid1 = RigidTransformType::New(); RigidTransformType::Pointer rigid2 = RigidTransformType::New(); rigid1->SetParameters( params1 ); rigid2->SetParameters( params2 ); affineTransform1->SetMatrix( rigid1->GetMatrix() ); affineTransform1->SetTranslation( rigid1->GetTranslation() ); affineTransform2->SetMatrix( rigid2->GetMatrix() ); affineTransform2->SetTranslation( rigid2->GetTranslation() ); } else if ( nTransformParams == 12 ) { affineTransform1->SetParameters( params1 ); affineTransform2->SetParameters( params2 ); } else { std::cout << "Unknown transform type! - Exiting" << std::endl; return EXIT_FAILURE; } double meanDisplacement = 0.0; double maxDisplacement = 0.0; double count = 0; // we iterate over the time-series one time volume at a time TimeSeriesRegionType timeSeriesRegion; TimeSeriesIndexType timeSeriesIndex; TimeSeriesSizeType timeSeriesSize; if(timeseriesDisplacement){ for(int jj=0; jj<3; jj++){ timeSeriesIndex[jj] = mask->GetLargestPossibleRegion().GetIndex()[jj]; timeSeriesSize[jj] = mask->GetLargestPossibleRegion().GetSize()[jj]; } timeSeriesIndex[3] = i; timeSeriesSize[3] = 1; timeSeriesRegion.SetIndex(timeSeriesIndex); timeSeriesRegion.SetSize(timeSeriesSize); } TimeSeriesIteratorType timeseriesIterator(timeseriesDisplacementImage, timeSeriesRegion); IteratorType it( mask, mask->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { if ( it.Value() > 0 ) { ImageType::IndexType idx = it.GetIndex(); ImageType::PointType pt; mask->TransformIndexToPhysicalPoint(idx,pt); ImageType::PointType pt1 = affineTransform1->TransformPoint( pt ); double dist = 0; if ( doFramewise && ( i < (mocoDataArray->GetMatrix().rows()-1) ) ) { ImageType::PointType pt2 = affineTransform2->TransformPoint( pt ); dist = pt1.EuclideanDistanceTo(pt2); if ( writeMap ) { map->SetPixel( idx, map->GetPixel(idx) + dist / (mocoDataArray->GetMatrix().rows()-1) ); } } else { dist = pt.EuclideanDistanceTo(pt1); if ( writeMap ) { map->SetPixel( idx, map->GetPixel(idx) + dist / mocoDataArray->GetMatrix().rows() ); } } if ( doFramewise && ( i == mocoDataArray->GetMatrix().rows()-1) ) { dist = 0.0; } if ( dist > maxDisplacement ) { maxDisplacement = dist; } meanDisplacement += dist; ++count; if(timeseriesDisplacement) { timeseriesIterator.Set(dist); } } ++it; if(timeseriesDisplacement) { ++timeseriesIterator; } } meanDisplacement /= count; dataMatrix(i,0) = meanDisplacement; dataMatrix(i,1) = maxDisplacement; //std::cout << i << "," << maxDisplacement << "," << meanDisplacement << std::endl; } // Write summary stats to output file WriterType::Pointer writer = WriterType::New(); writer->ColumnHeadersPushBack("Mean"); writer->ColumnHeadersPushBack("Max"); writer->SetInput( &dataMatrix ); writer->SetFileName( outputOption->GetFunction(0)->GetName().c_str() ); writer->Write(); if (writeMap) { WriteImage( map, spatialOption->GetFunction(0)->GetName().c_str() ); } if(timeseriesDisplacement){ std::string tsOutImageName = outputName; std::size_t lastdot = outputName.find_last_of("."); if (lastdot != std::string::npos) tsOutImageName = outputName.substr(0, lastdot); tsOutImageName += ".nii.gz"; WriteImage(timeseriesDisplacementImage, tsOutImageName.c_str()); } return EXIT_SUCCESS; } void antsMotionCorrStatsInitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "Mask image - compute displacements within mask." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask" ); option->SetShortName( 'x' ); option->SetDescription( description ); option->SetUsageOption( 0, "mask.nii.gz" ); parser->AddOption( option ); } { std::string description = std::string( "motion correction parameters from antsMotionCorr." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "moco" ); option->SetShortName( 'm' ); option->SetDescription( description ); option->SetUsageOption( 0, "MOCOparams.csv" ); parser->AddOption( option ); } { std::string description = std::string( "Specify the output file" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetDescription( description ); option->SetUsageOption( 0, "corrected.csv" ); parser->AddOption( option ); } { std::string description = std::string( "Specify the index for a 3D transform to output" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "transform" ); option->SetShortName( 't' ); option->SetDescription( description ); //option->SetUsageOption( 0, "0" ); parser->AddOption( option ); } { std::string description = std::string( "do framewise summarywise stats" ); OptionType::Pointer option = OptionType::New(); option->SetLongName("framewise"); option->SetShortName( 'f' ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "output image of displacement magnitude" ); OptionType::Pointer option = OptionType::New(); option->SetLongName("spatial-map"); option->SetShortName( 's' ); option->SetDescription( description ); //option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string("output 4d time-series image of displacement magnitude"); OptionType::Pointer option = OptionType::New(); option->SetLongName("timeseries-displacement"); option->SetShortName('d'); option->SetDescription(description); parser->AddOption(option); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsMotionCorrStats( std::vector args, std::ostream * /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsMotionCorrStats" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "antsMotionCorrStats - create summary measures of the parameters that are output by antsMotionCorr. Currently only works for linear transforms. Outputs the mean and max displacements for the voxels within a provided mask, at each time point. By default the displacements are relative to the reference space, but the framewise option may be used to provide displacements between consecutive time points" ); parser->SetCommandDescription( commandDescription ); antsMotionCorrStatsInitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc < 2 || parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } else if( parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } std::cout << std::endl << "Running " << argv[0] << std::endl << std::endl; return ants_motion_stats( parser ); } } // namespace ants ants-2.2.0/Examples/antsRegistration.cxx000066400000000000000000000773241311104306400203300ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 #include #include #include "antsRegistrationTemplateHeader.h" #include "ANTsVersion.h" namespace ants { static void antsRegistrationInitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { // short names in use- a:b:c:d:f:g:h:i:j:k:l:m:n:o:q:r:s:t:u:v:w:x:z { const std::string description = std::string( "Get Version Information." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "version" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "This option forces the image to be treated as a specified-" ) + std::string( "dimensional image. If not specified, we try to " ) + std::string( "infer the dimensionality from the input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "dimensionality" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "2/3" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the output transform prefix (output format is .nii.gz ). " ) + std::string( "Optionally, one can choose to warp the moving image to the fixed space and, if the " ) + std::string( "inverse transform exists, one can also output the warped fixed image. Note that " ) + std::string( "only the images specified in the first metric call are warped. Use antsApplyTransforms " ) + std::string( "to warp other images using the resultant transform(s)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "outputTransformPrefix" ); option->SetUsageOption( 1, "[outputTransformPrefix,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the output file for the current state of the registration. " ) + std::string( "The state file is written to an hdf5 composite file. It is specially usefull if " ) + std::string( "we want to save the current state of a SyN registration to the disk, so " ) + std::string( "we can load and restore that later to continue the next registration process " ) + std::string( "directly started from the last saved state. " ) + std::string( "The output file of this flag is the same as the write-composite-transform, " ) + std::string( "unless the last transform is a SyN transform. In that case, the inverse " ) + std::string( "displacement field of the SyN transform is also added to the output composite transform. " ) + std::string( "Again notice that this file cannot be treated as a transform, and restore-state option " ) + std::string( "must be used to load the written file by this flag." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "save-state" ); option->SetShortName( 'j' ); option->SetUsageOption( 0, "saveSateAsTransform" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the initial state of the registration which get immediately " ) + std::string( "used to directly initialize the registration process. " ) + std::string( "The flag is mutually exclusive with other intialization flags." ) + std::string( "If this flag is used, none of the initial-moving-transform and initial-fixed-transform " ) + std::string( "cannot be used." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "restore-state" ); option->SetShortName( 'k' ); option->SetUsageOption( 0, "restoreStateAsATransform" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Boolean specifying whether or not the " ) + std::string( "composite transform (and its inverse, if it exists) should " ) + std::string( "be written to an hdf5 composite file. This is false by default " ) + std::string( "so that only the transform for each stage is written to file." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "write-composite-transform" ); option->SetShortName( 'a' ); option->SetUsageOption( 0, "1/(0)" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } // This is currently not functioning properly for all linear transforms. If I // restrict the linear transforms to rigid transforms, then it seems to work. // I think there's something in working with images that doesn't work properly // with a generic affine transform in the header. You can certainly store it // and read it from the header but perhaps this interferes with something fundamental // like transforming indices to physical coordinates. I'll have to investigate // in the future. // { // std::string description = std::string( "Collapse initial linear transforms " ) // + std::string( "to the fixed image header. This should speed up subsequent " ) // + std::string( "nonlinear transform optimizations." ); // OptionType::Pointer option = OptionType::New(); // option->SetLongName( "collapse-linear-transforms-to-fixed-image-header" ); // option->SetShortName( 'b' ); // option->SetUsageOption( 0, "1/(0)" ); // option->SetDescription( description ); // option->AddFunction( std::string( "0" ) ); // parser->AddOption( option ); // } { std::string description = std::string( "Prints out the CC similarity metric measure " ) + std::string( "between the full-size input fixed and the transformed moving images at each iteration " ) + std::string( "a value of 0 (the default) indicates that the full scale computation should not take place") + std::string( "any value greater than 0 represents the interval of full scale metric computation."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "print-similarity-measure-interval" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Writes out the output volume at each iteration. It helps to present the registration process as a short movie " ) + std::string( "a value of 0 (the default) indicates that this option should not take place") + std::string( "any value greater than 0 represents the interval between the iterations which outputs are written to the disk."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "write-interval-volumes" ); // option->SetShortName( 'v' ); // BUG! dont set as v because v is verbose option->SetUsageOption( 0, "" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Collapse output transforms. " ) + std::string( "Specifically, enabling this option combines all adjacent transforms where" ) + std::string( "possible. All adjacent linear transforms are written to disk in the form" ) + std::string( "an itk affine transform (called xxxGenericAffine.mat). Similarly, all " ) + std::string( "adjacent displacement field transforms are combined when written to disk " ) + std::string( "(e.g. xxxWarp.nii.gz and xxxInverseWarp.nii.gz (if available))." ) + std::string( "Also, an output composite transform including the collapsed transforms is " ) + std::string( "written to the disk (called outputCollapsed(Inverse)Composite)."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "collapse-output-transforms" ); option->SetShortName( 'z' ); option->SetUsageOption( 0, "(1)/0" ); option->SetDescription( description ); option->AddFunction( std::string( "1" ) ); parser->AddOption( option ); } { std::string description = std::string( "Initialize linear transforms from the previous stage. " ) + std::string( "By enabling this option, the current linear stage transform is directly intialized " ) + std::string( "from the previous stage's linear transform; this allows multiple linear stages to be run " ) + std::string( "where each stage directly updates the estimated linear transform from the previous stage. ") + std::string( "(e.g. Translation -> Rigid -> Affine)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "initialize-transforms-per-stage" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "(1)/0" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Several interpolation options are available in ITK. " ) + std::string( "These have all been made available. Currently the interpolator " ) + std::string( "choice is only used to warp (and possibly inverse warp) the final " ) + std::string( "output image(s)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "interpolation" ); option->SetShortName( 'n' ); option->SetUsageOption( 0, "Linear" ); option->SetUsageOption( 1, "NearestNeighbor" ); option->SetUsageOption( 2, "MultiLabel[,]" ); option->SetUsageOption( 3, "Gaussian[,]" ); option->SetUsageOption( 4, "BSpline[]" ); option->SetUsageOption( 5, "CosineWindowedSinc" ); option->SetUsageOption( 6, "WelchWindowedSinc" ); option->SetUsageOption( 7, "HammingWindowedSinc" ); option->SetUsageOption( 8, "LanczosWindowedSinc" ); option->SetUsageOption( 9, "GenericLabel[]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "This option allows the user to restrict the " ) + std::string( "optimization of the displacement field, translation, rigid or affine " ) + std::string( "transform on a per-component basis. For example, if one wants to limit " ) + std::string( "the deformation or rotation of 3-D volume to the first two dimensions, ") + std::string( "this is possible by specifying a weight vector of \'1x1x0\' for a " ) + std::string( "deformation field or \'1x1x0x1x1x0\' for a rigid transformation. " ) + std::string( "Low-dimensional restriction only works if there are no preceding transformations." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "restrict-deformation" ); option->SetShortName( 'g' ); option->SetUsageOption( 0, "PxQxR" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the initial fixed transform(s) which get immediately " ) + std::string( "incorporated into the composite transform. The order of the " ) + std::string( "transforms is stack-esque in that the last transform specified on " ) + std::string( "the command line is the first to be applied. In addition to initialization " ) + std::string( "with ITK transforms, the user can perform an initial translation alignment " ) + std::string( "by specifying the fixed and moving images and selecting an initialization " ) + std::string( "feature. These features include using the geometric center of the images (=0), " ) + std::string( "the image intensities (=1), or the origin of the images (=2)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "initial-fixed-transform" ); option->SetShortName( 'q' ); option->SetUsageOption( 0, "initialTransform" ); option->SetUsageOption( 1, "[initialTransform,]" ); option->SetUsageOption( 2, "[fixedImage,movingImage,initializationFeature]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the initial moving transform(s) which get immediately " ) + std::string( "incorporated into the composite transform. The order of the " ) + std::string( "transforms is stack-esque in that the last transform specified on " ) + std::string( "the command line is the first to be applied. In addition to initialization " ) + std::string( "with ITK transforms, the user can perform an initial translation alignment " ) + std::string( "by specifying the fixed and moving images and selecting an initialization " ) + std::string( "feature. These features include using the geometric center of the images (=0), " ) + std::string( "the image intensities (=1), or the origin of the images (=2)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "initial-moving-transform" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "initialTransform" ); option->SetUsageOption( 1, "[initialTransform,]" ); option->SetUsageOption( 2, "[fixedImage,movingImage,initializationFeature]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "These image metrics are available--- " ) + std::string( "CC: ANTS neighborhood cross correlation, MI: Mutual information, " ) + std::string( "Demons: (Thirion), MeanSquares, and GC: Global Correlation. " ) + std::string( "The \"metricWeight\" variable is used to modulate the per stage weighting of the metrics. " ) + std::string( "The metrics can also employ a sampling strategy defined by a " ) + std::string( "sampling percentage. The sampling strategy defaults to \'None\' (aka a dense sampling of ") + std::string( "one sample per voxel), otherwise it defines a point set over which to optimize the metric. " ) + std::string( "The point set can be on a regular lattice or a random lattice of points slightly " ) + std::string( "perturbed to minimize aliasing artifacts. samplingPercentage defines the " ) + std::string( "fraction of points to select from the domain. " ) + std::string( "In addition, three point set metrics are available: Euclidean " ) + std::string( "(ICP), Point-set expectation (PSE), and Jensen-Havrda-Charvet-Tsallis (JHCT)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "metric" ); option->SetShortName( 'm' ); option->SetUsageOption( 0, "CC[fixedImage,movingImage,metricWeight,radius,,]" ); option->SetUsageOption( 1, "MI[fixedImage,movingImage,metricWeight,numberOfBins,,]" ); option->SetUsageOption( 2, "Mattes[fixedImage,movingImage,metricWeight,numberOfBins,,]" ); option->SetUsageOption( 3, "MeanSquares[fixedImage,movingImage,metricWeight,radius=NA,,]" ); option->SetUsageOption( 4, "Demons[fixedImage,movingImage,metricWeight,radius=NA,,]" ); option->SetUsageOption( 5, "GC[fixedImage,movingImage,metricWeight,radius=NA,,]" ); option->SetUsageOption( 6, "ICP[fixedPointSet,movingPointSet,metricWeight,,]" ); option->SetUsageOption( 7, "PSE[fixedPointSet,movingPointSet,metricWeight,,,,]" ); option->SetUsageOption( 8, "JHCT[fixedPointSet,movingPointSet,metricWeight,,,,,,]" ); option->SetUsageOption( 9, "IGDM[fixedImage,movingImage,metricWeight,fixedMask,movingMask,,,,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Several transform options are available. The gradientStep or " ) + std::string( "learningRate characterizes the gradient descent optimization and is scaled appropriately " ) + std::string( "for each transform using the shift scales estimator. Subsequent parameters are " ) + std::string( "transform-specific and can be determined from the usage. For the B-spline transforms " ) + std::string( "one can also specify the smoothing in terms of spline distance (i.e. knot spacing). " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "transform" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "Rigid[gradientStep]" ); option->SetUsageOption( 1, "Affine[gradientStep]" ); option->SetUsageOption( 2, "CompositeAffine[gradientStep]" ); option->SetUsageOption( 3, "Similarity[gradientStep]" ); option->SetUsageOption( 4, "Translation[gradientStep]" ); option->SetUsageOption( 5, "BSpline[gradientStep,meshSizeAtBaseLevel]" ); option->SetUsageOption( 6, "GaussianDisplacementField[gradientStep,updateFieldVarianceInVoxelSpace,totalFieldVarianceInVoxelSpace]" ); option->SetUsageOption( 7, "BSplineDisplacementField[gradientStep,updateFieldMeshSizeAtBaseLevel,totalFieldMeshSizeAtBaseLevel,]" ); option->SetUsageOption( 8, "TimeVaryingVelocityField[gradientStep,numberOfTimeIndices,updateFieldVarianceInVoxelSpace,updateFieldTimeVariance,totalFieldVarianceInVoxelSpace,totalFieldTimeVariance]" ); option->SetUsageOption( 9, "TimeVaryingBSplineVelocityField[gradientStep,velocityFieldMeshSize,,]" ); option->SetUsageOption( 10, "SyN[gradientStep,updateFieldVarianceInVoxelSpace,totalFieldVarianceInVoxelSpace]" ); option->SetUsageOption( 11, "BSplineSyN[gradientStep,updateFieldMeshSizeAtBaseLevel,totalFieldMeshSizeAtBaseLevel,]" ); option->SetUsageOption( 12, "Exponential[gradientStep,updateFieldVarianceInVoxelSpace,velocityFieldVarianceInVoxelSpace,]" ); option->SetUsageOption( 13, "BSplineExponential[gradientStep,updateFieldMeshSizeAtBaseLevel,velocityFieldMeshSizeAtBaseLevel,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Convergence is determined from the number of iterations per level " ) + std::string( "and is determined by fitting a line to the normalized energy " ) + std::string( "profile of the last N iterations (where N is specified by " ) + std::string( "the window size) and determining the slope which is then " ) + std::string( "compared with the convergence threshold." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "convergence" ); option->SetShortName( 'c' ); option->SetUsageOption( 0, "MxNxO" ); option->SetUsageOption( 1, "[MxNxO,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the sigma of gaussian smoothing at each level. " ) + std::string( "Units are given in terms of voxels (\'vox\') or physical spacing (\'mm\'). " ) + std::string( "Example usage is \'4x2x1mm\' and \'4x2x1vox\' where no units implies voxel spacing." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "smoothing-sigmas" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "MxNxO..." ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the shrink factor for the virtual domain (typically the fixed image) at each level." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "shrink-factors" ); option->SetShortName( 'f' ); option->SetUsageOption( 0, "MxNxO..." ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Histogram match the images before registration." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-histogram-matching" ); option->SetShortName( 'u' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "turn on the option that lets you estimate the learning rate step size only at the beginning of each level. * useful as a second stage of fine-scale registration." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-estimate-learning-rate-once" ); option->SetShortName( 'l' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Winsorize data based on specified quantiles." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "winsorize-image-intensities" ); option->SetShortName( 'w' ); option->SetUsageOption( 0, "[lowerQuantile,upperQuantile]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Image masks to limit voxels considered by the metric. " ) + std::string( "Two options are allowed for mask specification: 1) Either " ) + std::string( "the user specifies a single mask to be used for all stages or " ) + std::string( "2) the user specifies a mask for each stage. With the latter " ) + std::string( "one can select to which stages masks are applied by supplying " ) + std::string( "valid file names. If the file does not exist, a mask will not " ) + std::string( "be used for that stage. Note that we handle the fixed and moving " ) + std::string( "masks separately to enforce this constraint." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "masks" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "[fixedImageMask,movingImageMask]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Use 'float' instead of 'double' for computations." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "float" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Use MINC file formats for transformations." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "minc" ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "Verbose output." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'v' ); option->SetLongName( "verbose" ); option->SetUsageOption( 0, "(0)/1" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu. Will also print values " ) + std::string( "used on the current command line call." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsRegistration( std::vector args, std::ostream * /*out_stream = NULL */ ) { try { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsRegistration" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // // antscout->set_stream( out_stream ); ParserType::Pointer parser = ParserType::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "This program is a user-level " ) + std::string( "registration application meant to utilize ITKv4-only classes. The user can specify " ) + std::string( "any number of \"stages\" where a stage consists of a transform; an image metric; " ) + std::string( "and iterations, shrink factors, and smoothing sigmas for each level. " ) + std::string( "Note that explicitly setting the dimensionality, metric, transform, output, " ) + std::string( "convergence, shrink-factors, and smoothing-sigmas parameters is mandatory." ); parser->SetCommandDescription( commandDescription ); antsRegistrationInitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } bool verbose = false; itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } OptionType::Pointer collapseOutputTransformsOption = parser->GetOption( "collapse-output-transforms" ); OptionType::Pointer compositeOutputOption = parser->GetOption( "write-composite-transform" ); OptionType::Pointer initializePerStageOption = parser->GetOption( "initialize-transforms-per-stage" ); OptionType::Pointer saveStateOption = parser->GetOption( "save-state" ); const bool writeCompositeTransform = parser->Convert( compositeOutputOption->GetFunction( 0 )->GetName() ); const bool shouldInitializePerStage = parser->Convert( initializePerStageOption->GetFunction( 0 )->GetName() ); if ( shouldInitializePerStage && ( ! writeCompositeTransform ) ) { if( verbose ) { std::cerr << "ERROR: --initialize-transforms-per-stage requires --write-composite-transform" << std::endl; std::cerr << " because the initializizing transform is collapsed into each stage for optimization" << std::endl; } return EXIT_FAILURE; } if ( ( saveStateOption && saveStateOption->GetNumberOfFunctions() ) && ( ! writeCompositeTransform ) ) { if( verbose ) { std::cerr << "ERROR: --save-state requires --write-composite-transform" << std::endl; std::cerr << " because the the output transform will contain the this processes initializer" << std::endl; } return EXIT_FAILURE; } if( verbose ) { std::cout << "All_Command_lines_OK" << std::endl; } if( argc == 1 ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } ParserType::OptionType::Pointer versionOption = parser->GetOption( "version" ); if( versionOption && versionOption->GetNumberOfFunctions() ) { std::string versionFunction = versionOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( versionFunction ); if( versionFunction.compare( "1" ) == 0 || versionFunction.compare( "true" ) == 0 ) { //Print Version Information std::cout << ANTs::Version::ExtendedVersionString() << std::endl; return EXIT_SUCCESS; } } unsigned int dimension = 3; ParserType::OptionType::Pointer dimOption = parser->GetOption( "dimensionality" ); if( dimOption && dimOption->GetNumberOfFunctions() ) { dimension = parser->Convert( dimOption->GetFunction( 0 )->GetName() ); } else { if( verbose ) { std::cerr << "Image dimensionality not specified. See command line option --dimensionality" << std::endl; } return EXIT_FAILURE; } std::string precisionType; OptionType::Pointer typeOption = parser->GetOption( "float" ); if( typeOption && parser->Convert( typeOption->GetFunction( 0 )->GetName() ) ) { if( verbose ) { std::cout << "Using single precision for computations." << std::endl; } precisionType = "float"; } else { if( verbose ) { std::cout << "Using double precision for computations." << std::endl; } precisionType = "double"; } switch( dimension ) { case 2: { if( strcmp( precisionType.c_str(), "float" ) == 0 ) { return antsRegistration2DFloat( parser ); } else { return antsRegistration2DDouble( parser ); } } case 3: { if( strcmp( precisionType.c_str(), "float" ) == 0 ) { return antsRegistration3DFloat( parser ); } else { return antsRegistration3DDouble( parser ); } } case 4: { if( strcmp( precisionType.c_str(), "float" ) == 0 ) { return antsRegistration4DFloat( parser ); } else { return antsRegistration4DDouble( parser ); } } default: { if( verbose ) { std::cerr << "bad image dimension " << dimension << std::endl; } return EXIT_FAILURE; } } } catch( itk::ExceptionObject & err ) { std::cerr << "Exception Object caught: " << std::endl; std::cerr << err << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsRegistration2DDouble.cxx000066400000000000000000000003521311104306400216340ustar00rootroot00000000000000#include "antsRegistrationTemplateHeader.h" namespace ants { //Instantiate the 2DDouble version int antsRegistration2DDouble(ParserType::Pointer & parser) { return DoRegistration( parser ); } } //end namespace ants ants-2.2.0/Examples/antsRegistration2DFloat.cxx000066400000000000000000000003471311104306400214730ustar00rootroot00000000000000#include "antsRegistrationTemplateHeader.h" namespace ants { //Instantiate the 2DFloat version int antsRegistration2DFloat(ParserType::Pointer & parser) { return DoRegistration( parser ); } } //end namespace ants ants-2.2.0/Examples/antsRegistration3DDouble.cxx000066400000000000000000000003521311104306400216350ustar00rootroot00000000000000#include "antsRegistrationTemplateHeader.h" namespace ants { //Instantiate the 3DDouble version int antsRegistration3DDouble(ParserType::Pointer & parser) { return DoRegistration( parser ); } } //end namespace ants ants-2.2.0/Examples/antsRegistration3DFloat.cxx000066400000000000000000000003471311104306400214740ustar00rootroot00000000000000#include "antsRegistrationTemplateHeader.h" namespace ants { //Instantiate the 3DFloat version int antsRegistration3DFloat(ParserType::Pointer & parser) { return DoRegistration( parser ); } } //end namespace ants ants-2.2.0/Examples/antsRegistration4DDouble.cxx000066400000000000000000000003521311104306400216360ustar00rootroot00000000000000#include "antsRegistrationTemplateHeader.h" namespace ants { //Instantiate the 4DDouble version int antsRegistration4DDouble(ParserType::Pointer & parser) { return DoRegistration( parser ); } } //end namespace ants ants-2.2.0/Examples/antsRegistration4DFloat.cxx000066400000000000000000000003471311104306400214750ustar00rootroot00000000000000#include "antsRegistrationTemplateHeader.h" namespace ants { //Instantiate the 4DFloat version int antsRegistration4DFloat(ParserType::Pointer & parser) { return DoRegistration( parser ); } } //end namespace ants ants-2.2.0/Examples/antsRegistrationCommandIterationUpdate.h000066400000000000000000000117621311104306400242700ustar00rootroot00000000000000#ifndef antsRegistrationCommandIterationUpdate__h_ #define antsRegistrationCommandIterationUpdate__h_ namespace ants { /** \class antsRegistrationCommandIterationUpdate * \brief change parameters between iterations of registration */ template class antsRegistrationCommandIterationUpdate : public itk::Command { public: typedef antsRegistrationCommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); protected: antsRegistrationCommandIterationUpdate() { m_clock.Start(); m_clock.Stop(); const itk::RealTimeClock::TimeStampType now = m_clock.GetTotal(); this->m_lastTotalTime = now; m_clock.Start(); this->m_LogStream = &std::cout; } public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event ) ITK_OVERRIDE { TFilter const * const filter = dynamic_cast( object ); if( typeid( event ) == typeid( itk::InitializeEvent ) ) { const unsigned int currentLevel = filter->GetCurrentLevel(); typename TFilter::ShrinkFactorsPerDimensionContainerType shrinkFactors = filter->GetShrinkFactorsPerDimension( currentLevel ); typename TFilter::SmoothingSigmasArrayType smoothingSigmas = filter->GetSmoothingSigmasPerLevel(); typename TFilter::TransformParametersAdaptorsContainerType adaptors = filter->GetTransformParametersAdaptorsPerLevel(); bool smoothingSigmasAreInPhysicalUnits = filter->GetSmoothingSigmasAreSpecifiedInPhysicalUnits(); m_clock.Stop(); const itk::RealTimeClock::TimeStampType now = m_clock.GetTotal(); this->Logger() << " Current level = " << currentLevel + 1 << " of " << this->m_NumberOfIterations.size() << std::endl; this->Logger() << " number of iterations = " << this->m_NumberOfIterations[currentLevel] << std::endl; this->Logger() << " shrink factors = " << shrinkFactors << std::endl; this->Logger() << " smoothing sigmas = " << smoothingSigmas[currentLevel]; if( smoothingSigmasAreInPhysicalUnits ) { this->Logger() << " mm" << std::endl; } else { this->Logger() << " vox" << std::endl; } this->Logger() << " required fixed parameters = " << adaptors[currentLevel]->GetRequiredFixedParameters() << std::flush << std::endl; // this->Logger() << "\n LEVEL_TIME_INDEX: " << now << " SINCE_LAST: " << (now-this->m_lastTotalTime) << // std::endl; this->m_lastTotalTime = now; m_clock.Start(); typedef itk::GradientDescentOptimizerv4Template GradientDescentOptimizerType; GradientDescentOptimizerType * optimizer = reinterpret_cast( const_cast( filter )->GetModifiableOptimizer() ); // TODO: This looks very wrong. There is a const_cast above, and then the change // of the number of iterations here on what should be a const object. optimizer->SetNumberOfIterations( this->m_NumberOfIterations[currentLevel] ); } else if( typeid( event ) == typeid( itk::IterationEvent ) ) { const unsigned int lCurrentIteration = filter->GetCurrentIteration(); if( lCurrentIteration == 1 ) { // Print header line one time this->Logger() << "XDIAGNOSTIC,Iteration,metricValue,convergenceValue,ITERATION_TIME_INDEX,SINCE_LAST" << std::flush << std::endl; } m_clock.Stop(); const itk::RealTimeClock::TimeStampType now = m_clock.GetTotal(); this->Logger() << "WDIAGNOSTIC, " << std::setw(5) << lCurrentIteration << ", " << std::scientific << std::setprecision(12) << filter->GetCurrentMetricValue() << ", " << std::scientific << std::setprecision(12) << filter->GetCurrentConvergenceValue() << ", " << std::setprecision(4) << now << ", " << std::setprecision(4) << (now - this->m_lastTotalTime) << ", " << std::flush << std::endl; this->m_lastTotalTime = now; m_clock.Start(); } } void SetNumberOfIterations( const std::vector & iterations ) { this->m_NumberOfIterations = iterations; } void SetLogStream(std::ostream & logStream) { this->m_LogStream = &logStream; } private: std::ostream & Logger() const { return *m_LogStream; } std::vector m_NumberOfIterations; std::ostream * m_LogStream; itk::TimeProbe m_clock; itk::RealTimeClock::TimeStampType m_lastTotalTime; // typename ImageType::Pointer m_origFixedImage; // typename ImageType::Pointer m_origMovingImage; }; }; // end namespace ants #endif // antsRegistrationCommandIterationUpdate__h_ ants-2.2.0/Examples/antsRegistrationOptimizerCommandIterationUpdate.h000066400000000000000000000436451311104306400262000ustar00rootroot00000000000000#ifndef antsRegistrationOptimizerCommandIterationUpdate__h_ #define antsRegistrationOptimizerCommandIterationUpdate__h_ namespace ants { /** \class antsRegistrationOptimizerCommandIterationUpdate * \brief observe the optimizer for traditional registration methods */ template class antsRegistrationOptimizerCommandIterationUpdate : public itk::Command { public: typedef antsRegistrationOptimizerCommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); typedef ParametersValueType RealType; typedef ParametersValueType PixelType; typedef typename itk::Image ImageType; typedef itk::ImageToImageMetricv4 ImageMetricType; typedef typename ImageMetricType::MeasureType MeasureType; typedef itk::CompositeTransform CompositeTransformType; typedef typename CompositeTransformType::TransformType TransformBaseType; protected: antsRegistrationOptimizerCommandIterationUpdate() { m_clock.Start(); m_clock.Stop(); const itk::RealTimeClock::TimeStampType now = m_clock.GetTotal(); this->m_lastTotalTime = now; m_clock.Start(); this->m_LogStream = &std::cout; this->m_origFixedImage = ImageType::New(); this->m_origMovingImage = ImageType::New(); this->m_ComputeFullScaleCCInterval = 0; this->m_WriteInterationsOutputsInIntervals = 0; this->m_CurrentStageNumber = 0; this->m_CurLevel = -1; } public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object *, const itk::EventObject & event) ITK_OVERRIDE { #if 0 if( typeid( event ) == typeid( itk::InitializeEvent ) ) { const unsigned int currentLevel = this->m_Optimizer->GetCurrentLevel(); typename TOptimizer::ShrinkFactorsPerDimensionContainerType shrinkFactors = this->m_Optimizer->GetShrinkFactorsPerDimension( currentLevel ); typename TOptimizer::SmoothingSigmasArrayType smoothingSigmas = this->m_Optimizer->GetSmoothingSigmasPerLevel(); typename TOptimizer::TransformParametersAdaptorsContainerType adaptors = this->m_Optimizer->GetTransformParametersAdaptorsPerLevel(); bool smoothingSigmasAreInPhysicalUnits = this->m_Optimizer->GetSmoothingSigmasAreSpecifiedInPhysicalUnits(); m_clock.Stop(); const itk::RealTimeClock::TimeStampType now = m_clock.GetTotal(); this->Logger() << " Current level = " << currentLevel + 1 << " of " << this->m_NumberOfIterations.size() << std::endl; this->Logger() << " number of iterations = " << this->m_NumberOfIterations[currentLevel] << std::endl; this->Logger() << " shrink factors = " << shrinkFactors << std::endl; this->Logger() << " smoothing sigmas = " << smoothingSigmas[currentLevel]; if( smoothingSigmasAreInPhysicalUnits ) { this->Logger() << " mm" << std::endl; } else { this->Logger() << " vox" << std::endl; } this->Logger() << " required fixed parameters = " << adaptors[currentLevel]->GetRequiredFixedParameters() << std::flush << std::endl; // this->Logger() << "\n LEVEL_TIME_INDEX: " << now << " SINCE_LAST: " << (now-this->m_lastTotalTime) << // std::endl; this->m_lastTotalTime = now; m_clock.Start(); typedef itk::GradientDescentOptimizerv4 GradientDescentOptimizerType; GradientDescentOptimizerType * optimizer = reinterpret_cast( this->m_Optimizer->GetModifiableOptimizer() ); optimizer->SetNumberOfIterations( this->m_NumberOfIterations[currentLevel] ); } else #endif if( typeid( event ) == typeid( itk::IterationEvent ) ) { // const unsigned int curLevel = this->m_Optimizer->GetCurrentLevel(); const unsigned int curIter = this->m_Optimizer->GetCurrentIteration() + 1; if( curIter == 1 ) { ++this->m_CurLevel; } const unsigned int lCurrentIteration = this->m_Optimizer->GetCurrentIteration() + 1; if( lCurrentIteration == 1 ) { if( this->m_ComputeFullScaleCCInterval != 0 ) { // Print header line one time this->Logger() << "DIAGNOSTIC,Iteration,metricValue,convergenceValue,ITERATION_TIME_INDEX,SINCE_LAST,FullScaleCCInterval=" << this->m_ComputeFullScaleCCInterval << std::flush << std::endl; } else { this->Logger() << "DIAGNOSTIC,Iteration,metricValue,convergenceValue,ITERATION_TIME_INDEX,SINCE_LAST" << std::flush << std::endl; } } m_clock.Stop(); const itk::RealTimeClock::TimeStampType now = m_clock.GetTotal(); MeasureType metricValue = 0.0; const unsigned int lastIteration = this->m_Optimizer->GetNumberOfIterations(); if( ( this->m_ComputeFullScaleCCInterval != 0 ) && ( lCurrentIteration == 1 || ( lCurrentIteration % this->m_ComputeFullScaleCCInterval == 0 ) || lCurrentIteration == lastIteration) ) { // This function finds the similarity value between the original fixed image and the original moving images // using a CC metric type with radius 4. // The feature can be used to observe the progress of the registration process at each iteration. this->UpdateFullScaleMetricValue(this->m_Optimizer, metricValue); } if( ( this->m_WriteInterationsOutputsInIntervals != 0 ) && ( lCurrentIteration == 1 || (lCurrentIteration % this->m_WriteInterationsOutputsInIntervals == 0 ) || lCurrentIteration == lastIteration) ) { // This function writes the output volume of each iteration to the disk. // The feature can be used to observe the progress of the registration process at each iteration, // and make a short movie from the the registration process. this->WriteIntervalVolumes(this->m_Optimizer); } else { this->Logger() << " "; // if the output of current iteration is written to disk, and star } // will appear before line, else a free space will be printed to keep visual alignment. this->Logger() << "2DIAGNOSTIC, " << std::setw(5) << lCurrentIteration << ", " << std::scientific << std::setprecision(12) << this->m_Optimizer->GetValue() << ", " << std::scientific << std::setprecision(12) << this->m_Optimizer->GetConvergenceValue() << ", " << std::setprecision(4) << now << ", " << std::setprecision(4) << (now - this->m_lastTotalTime) << ", "; if( ( this->m_ComputeFullScaleCCInterval != 0 ) && std::fabs(metricValue) > 1e-7 ) { this->Logger() << std::scientific << std::setprecision(12) << metricValue << std::flush << std::endl; } else { this->Logger() << std::flush << std::endl; } this->m_Optimizer->SetNumberOfIterations( this->m_NumberOfIterations[this->m_CurLevel] ); this->m_lastTotalTime = now; m_clock.Start(); } else { // Unknown event type return; } } itkSetMacro( ComputeFullScaleCCInterval, unsigned int ); itkSetMacro( WriteInterationsOutputsInIntervals, unsigned int ); itkSetMacro( CurrentStageNumber, unsigned int ); void SetNumberOfIterations( const std::vector & iterations ) { this->m_NumberOfIterations = iterations; } void SetLogStream(std::ostream & logStream) { this->m_LogStream = &logStream; } /** * Type defining the optimizer */ typedef TOptimizer OptimizerType; /** * Set Optimizer */ void SetOptimizer( OptimizerType * optimizer ) { this->m_Optimizer = optimizer; this->m_Optimizer->AddObserver( itk::IterationEvent(), this ); } void SetOrigFixedImage(typename ImageType::Pointer origFixedImage) { this->m_origFixedImage = origFixedImage; } void SetOrigMovingImage(typename ImageType::Pointer origMovingImage) { this->m_origMovingImage = origMovingImage; } void UpdateFullScaleMetricValue(itk::WeakPointer myOptimizer, MeasureType & metricValue ) const { // Get the registration metric from the optimizer typename ImageMetricType::Pointer inputMetric( dynamic_cast( myOptimizer->GetModifiableMetric() ) ); // Define the CC metric type // This metric type is used to measure the general similarity metric between the original input fixed and moving // images. typedef itk::ANTSNeighborhoodCorrelationImageToImageMetricv4 CorrelationImageMetricType; typename CorrelationImageMetricType::Pointer correlationMetric = CorrelationImageMetricType::New(); { typename CorrelationImageMetricType::RadiusType radius; radius.Fill( 4 ); // NOTE: This is just a common reference for fine-tuning parameters, so perhaps a smaller // window would be sufficient. correlationMetric->SetRadius( radius ); } correlationMetric->SetUseMovingImageGradientFilter( false ); correlationMetric->SetUseFixedImageGradientFilter( false ); typename ImageMetricType::Pointer metric = correlationMetric.GetPointer(); // We need to create an exact copy from the composite fixed and moving transforms returned from the metric // We should roll off the composite transform and create a new instance from each of its sub transforms // For the fixed transform, first we should check that wether it is an identity transform or composite transform. typename TransformBaseType::Pointer fixedTransform; if( strcmp( inputMetric->GetFixedTransform()->GetNameOfClass(), "CompositeTransform" ) == 0 ) { typename CompositeTransformType::Pointer myFixedTransform = CompositeTransformType::New(); // We cast the metric's transform to a composite transform, so we can copy each // of its sub transforms to a new instance. // Notice that the metric transform will not be changed inside this fuction. typename CompositeTransformType::ConstPointer inputFixedTransform = dynamic_cast( inputMetric->GetModifiableFixedTransform() ); const unsigned int N = inputFixedTransform->GetNumberOfTransforms(); for( unsigned int i = 0; i < N; i++ ) { // Create a new instance from each sub transform. typename TransformBaseType::Pointer subTransform( dynamic_cast( inputFixedTransform->GetNthTransform(i)->CreateAnother().GetPointer() ) ); // Copy the information to each sub transform and add this transform to the final composite transform. const typename TransformBaseType::ParametersType & fixedImage_paras = inputFixedTransform->GetNthTransform(i)->GetParameters(); const typename TransformBaseType::FixedParametersType & fixedImage_fixed_paras = inputFixedTransform->GetNthTransform(i)->GetFixedParameters(); subTransform->SetParameters( fixedImage_paras ); subTransform->SetFixedParameters( fixedImage_fixed_paras ); myFixedTransform->AddTransform( subTransform ); } myFixedTransform->SetOnlyMostRecentTransformToOptimizeOn(); fixedTransform = myFixedTransform; } else if( strcmp( inputMetric->GetFixedTransform()->GetNameOfClass(), "IdentityTransform" ) == 0 ) { typedef typename itk::IdentityTransform IdentityTransformType; typename IdentityTransformType::Pointer myFixedTransform = IdentityTransformType::New(); fixedTransform = myFixedTransform; } else { itkExceptionMacro( "Fixed Transform should be either \"Composite\" or \"Identity\" transform." ); } // Same procedure for the moving transform. Moving transform is always a Composite transform. typename CompositeTransformType::Pointer movingTransform = CompositeTransformType::New(); typename CompositeTransformType::ConstPointer inputMovingTransform = dynamic_cast( inputMetric->GetModifiableMovingTransform() ); const unsigned int N = inputMovingTransform->GetNumberOfTransforms(); for( unsigned int i = 0; i < N; i++ ) { typename TransformBaseType::Pointer subTransform( dynamic_cast( inputMovingTransform->GetNthTransform(i)->CreateAnother().GetPointer() ) ); const typename TransformBaseType::ParametersType & moving_paras = inputMovingTransform->GetNthTransform(i)->GetParameters(); const typename TransformBaseType::FixedParametersType & moving_fixed_paras = inputMovingTransform->GetNthTransform(i)->GetFixedParameters(); subTransform->SetParameters( moving_paras ); subTransform->SetFixedParameters( moving_fixed_paras ); movingTransform->AddTransform( subTransform ); } movingTransform->SetOnlyMostRecentTransformToOptimizeOn(); metric->SetVirtualDomainFromImage( this->m_origFixedImage ); metric->SetFixedImage( this->m_origFixedImage ); metric->SetFixedTransform( fixedTransform ); metric->SetMovingImage( this->m_origMovingImage ); metric->SetMovingTransform( movingTransform ); metric->Initialize(); metricValue = metric->GetValue(); } void WriteIntervalVolumes(itk::WeakPointer myOptimizer) { // Get the registration metric from the optimizer typename ImageMetricType::Pointer inputMetric( dynamic_cast( myOptimizer->GetModifiableMetric() ) ); // First, compute the moving transform typename CompositeTransformType::Pointer movingTransform = CompositeTransformType::New(); typename CompositeTransformType::ConstPointer inputMovingTransform = dynamic_cast( inputMetric->GetModifiableMovingTransform() ); const unsigned int N = inputMovingTransform->GetNumberOfTransforms(); for( unsigned int i = 0; i < N; i++ ) { typename TransformBaseType::Pointer subTransform( dynamic_cast( inputMovingTransform->GetNthTransform(i)->CreateAnother().GetPointer() ) ); const typename TransformBaseType::ParametersType & moving_paras = inputMovingTransform->GetNthTransform(i)->GetParameters(); const typename TransformBaseType::FixedParametersType & moving_fixed_paras = inputMovingTransform->GetNthTransform(i)->GetFixedParameters(); subTransform->SetParameters( moving_paras ); subTransform->SetFixedParameters( moving_fixed_paras ); movingTransform->AddTransform( subTransform ); } movingTransform->SetOnlyMostRecentTransformToOptimizeOn(); // Now we apply this output transform to get warped image typedef itk::LinearInterpolateImageFunction LinearInterpolatorType; typename LinearInterpolatorType::Pointer linearInterpolator = LinearInterpolatorType::New(); typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetTransform( movingTransform ); resampler->SetInput( this->m_origMovingImage ); resampler->SetOutputParametersFromImage( this->m_origFixedImage ); resampler->SetInterpolator( linearInterpolator ); resampler->SetDefaultPixelValue( 0 ); resampler->Update(); // write the results to the disk std::stringstream currentFileName; currentFileName << "Stage" << this->m_CurrentStageNumber + 1 << "_level" << this->m_CurLevel; /* The name arrangement of written files are important to us. To prevent: "Iter1 Iter10 Iter2 Iter20" we use the following style. Then the order is: "Iter1 Iter2 ... Iters10 ... Itert20" */ const unsigned int curIter = this->m_Optimizer->GetCurrentIteration() + 1; if( curIter > 9 ) { currentFileName << "_Iters" << curIter << ".nii.gz"; } else if( curIter > 19 ) { currentFileName << "_Itert" << curIter << ".nii.gz"; } else { currentFileName << "_Iter" << curIter << ".nii.gz"; } std::cout << "*"; // The star befor each DIAGNOSTIC shows that its output is writtent out. typedef itk::ImageFileWriter WarpedImageWriterType; typename WarpedImageWriterType::Pointer writer = WarpedImageWriterType::New(); writer->SetFileName( currentFileName.str().c_str() ); writer->SetInput( resampler->GetOutput() ); try { writer->Update(); } catch( itk::ExceptionObject & err ) { std::cout << "Can't write warped image " << currentFileName.str().c_str() << std::endl; std::cout << "Exception Object caught: " << std::endl; std::cout << err << std::endl; } } private: std::ostream & Logger() const { return *m_LogStream; } /** * WeakPointer to the Optimizer */ itk::WeakPointer m_Optimizer; std::vector m_NumberOfIterations; std::ostream * m_LogStream; itk::TimeProbe m_clock; itk::RealTimeClock::TimeStampType m_lastTotalTime; unsigned int m_ComputeFullScaleCCInterval; unsigned int m_WriteInterationsOutputsInIntervals; unsigned int m_CurrentStageNumber; unsigned int m_CurLevel; typename ImageType::Pointer m_origFixedImage; typename ImageType::Pointer m_origMovingImage; }; }; // end namespace ants #endif // antsRegistrationOptimizerCommandIterationUpdate__h_ ants-2.2.0/Examples/antsRegistrationTemplateHeader.cxx000066400000000000000000000046321311104306400231250ustar00rootroot00000000000000#include "antsRegistrationTemplateHeader.h" namespace ants{ const char * RegTypeToFileName(const std::string & type, bool & writeInverse, bool & writeVelocityField, bool minc) { std::string str(type); ConvertToLowerCase(str); if( str == "syn" || str == "symmetricnormalization" || str == "bsplinesyn" || str == "timevaryingbsplinevelocityfield" || str == "tvdmffd" || str == "timevaryingvelocityfield" || str == "tvf" || str == "exponential" || str == "bsplineexponential" ) { writeInverse = true; } else { writeInverse = false; } if( str == "timevaryingvelocityfield" || str == "tvf" || str == "exp" || str == "exponential" || str == "bsplineexponential" ) { writeVelocityField = true; } else { writeVelocityField = false; } if( str == "rigid" ) { if(minc) return "_Rigid.xfm"; else return "Rigid.mat"; } else if( str == "affine" || str == "compositeaffine" || str == "compaff" ) { if(minc) return "_Affine.xfm"; else return "Affine.mat"; } else if( str == "similarity" ) { if(minc) return "_Similarity.xfm"; else return "Similarity.mat"; } else if( str == "translation" ) { if(minc) return "_Translation.xfm"; else return "Translation.mat"; } else if( str == "bspline" || str == "ffd" ) { if(minc) return "_BSpline.txt"; else return "BSpline.txt"; } else if( str == "genericaffine" ) { if(minc) return "_GenericAffine.xfm"; else return "GenericAffine.mat"; } else if( str == "gaussiandisplacementfield" || str == "gdf" || str == "bsplinedisplacementfield" || str == "dmffd" || str == "syn" || str == "symmetricnormalization" || str == "bsplinesyn" || str == "exp" || str == "exponential" || str == "bsplineexponential" ) { if(minc) return "_NL.xfm"; else return "Warp.nii.gz"; } else if( str == "timevaryingvelocityfield" || str == "tvf" || str == "timevaryingbsplinevelocityfield" || str == "tvdmffd" ) { if(minc) return "_Warp.mnc"; else return "Warp.nii.gz"; } return "BOGUS.XXXX"; } }// end namespace ants ants-2.2.0/Examples/antsRegistrationTemplateHeader.h000066400000000000000000001750041311104306400225540ustar00rootroot00000000000000/* Instantiating all 4 combinations of 2D,3D and float and double in one file was causing object files that were too big to be linked with gcc44, and is anticipated to cause problems on Windows machines that require relatively small object files as well. This file will use explicit template instantiation to make the overall size of each object smaller. */ #ifndef __ANTSREGISTRATIONTEMPLATEHEADER_H__ #define __ANTSREGISTRATIONTEMPLATEHEADER_H__ #include "antsUtilities.h" #include "itkantsRegistrationHelper.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkGaussianInterpolateImageFunction.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "itkLabelImageGaussianInterpolateImageFunction.h" #include "itkLabelImageGenericInterpolateImageFunction.h" #include "include/antsRegistration.h" #include "ReadWriteData.h" namespace ants { extern const char * RegTypeToFileName(const std::string & type, bool & writeInverse, bool & writeVelocityField,bool minc); template int DoRegistration(typename ParserType::Pointer & parser) { typedef TComputeType RealType; typedef typename ants::RegistrationHelper RegistrationHelperType; typedef typename RegistrationHelperType::ImageType ImageType; typedef typename RegistrationHelperType::MaskImageType MaskImageType; typedef typename RegistrationHelperType::LabeledPointSetType LabeledPointSetType; typedef typename RegistrationHelperType::IntensityPointSetType IntensityPointSetType; typedef typename RegistrationHelperType::CompositeTransformType CompositeTransformType; typename RegistrationHelperType::Pointer regHelper = RegistrationHelperType::New(); OptionType::Pointer useMincFormatOption = parser->GetOption( "minc" ); const bool use_minc_format = parser->Convert( useMincFormatOption->GetFunction( 0 )->GetName() ); bool verbose = false; typename itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } nullStream cnul; if( ! verbose ) { regHelper->SetLogStream( cnul ); } OptionType::Pointer transformOption = parser->GetOption( "transform" ); if( !transformOption || transformOption->GetNumberOfFunctions() == 0 ) { if( verbose ) { std::cerr << "ERROR: the transform option ('-t') must be specified. See help menu." << std::endl; } return EXIT_FAILURE; } OptionType::Pointer metricOption = parser->GetOption( "metric" ); if( !metricOption || metricOption->GetNumberOfFunctions() == 0 ) { if( verbose ) { std::cerr << "ERROR: the metric option ('-m') must be specified. See help menu." << std::endl; } return EXIT_FAILURE; } OptionType::Pointer convergenceOption = parser->GetOption( "convergence" ); if( !convergenceOption || convergenceOption->GetNumberOfFunctions() == 0 ) { if( verbose ) { std::cerr << "ERROR: the convergence option ('-c') must be specified. See help menu." << std::endl; } return EXIT_FAILURE; } OptionType::Pointer shrinkFactorsOption = parser->GetOption( "shrink-factors" ); if( !shrinkFactorsOption || shrinkFactorsOption->GetNumberOfFunctions() == 0 ) { if( verbose ) { std::cerr << "ERROR: the shrink factors option ('-f') must be specified. See help menu." << std::endl; } return EXIT_FAILURE; } OptionType::Pointer smoothingSigmasOption = parser->GetOption( "smoothing-sigmas" ); if( !smoothingSigmasOption || smoothingSigmasOption->GetNumberOfFunctions() == 0 ) { if( verbose ) { std::cerr << "ERROR: the smoothing sigmas option ('-s') must be specified. See help menu." << std::endl; } return EXIT_FAILURE; } OptionType::Pointer restrictDeformationOption = parser->GetOption( "restrict-deformation" ); OptionType::Pointer outputOption = parser->GetOption( "output" ); if( !outputOption || outputOption->GetNumberOfFunctions() == 0 ) { if( verbose ) { std::cerr << "ERROR: the output option ('-o') must be specified. See help menu." << std::endl; } return EXIT_FAILURE; } OptionType::Pointer maskOption = parser->GetOption( "masks" ); OptionType::Pointer compositeOutputOption = parser->GetOption( "write-composite-transform" ); const bool writeCompositeTransform = parser->Convert( compositeOutputOption->GetFunction( 0 )->GetName() ); OptionType::Pointer saveStateOption = parser->GetOption( "save-state" ); OptionType::Pointer collapseOutputTransformsOption = parser->GetOption( "collapse-output-transforms" ); const bool shouldCollapseBeDone = parser->Convert( collapseOutputTransformsOption->GetFunction( 0 )->GetName() ); OptionType::Pointer initializeTransformsPerStageOption = parser->GetOption( "initialize-transforms-per-stage" ); if( initializeTransformsPerStageOption && parser->Convert( initializeTransformsPerStageOption->GetFunction( 0 )->GetName() ) ) { if( shouldCollapseBeDone ) { if( verbose ) { std::cerr << "ERROR: initialize-transforms-per-stage & collapse-output-transforms options are mutually exclusive." << std::endl; } return EXIT_FAILURE; } regHelper->SetInitializeTransformsPerStage( true ); } else { regHelper->SetInitializeTransformsPerStage( false ); } /* * This option is currently disabled, so commenting it's usage out here * * OptionType::Pointer collapseLinearTransforms = * parser->GetOption( "collapse-linear-transforms-to-fixed-image-header" ); * if( collapseLinearTransforms && parser->Convert( collapseLinearTransforms->GetFunction( 0 )->GetName() ) ) * { * regHelper->SetApplyLinearTransformsToFixedImageHeader( true ); * } * else */ { regHelper->SetApplyLinearTransformsToFixedImageHeader( false ); } OptionType::Pointer printSimilarityMeasureInterval = parser->GetOption( "print-similarity-measure-interval" ); if( printSimilarityMeasureInterval && printSimilarityMeasureInterval->GetNumberOfFunctions() ) { unsigned int intervalLength = parser->Convert( printSimilarityMeasureInterval->GetFunction( 0 )->GetName() ); regHelper->SetPrintSimilarityMeasureInterval(intervalLength); } else { regHelper->SetPrintSimilarityMeasureInterval( 0 ); } OptionType::Pointer writeIntervalVolumes = parser->GetOption( "write-interval-volumes" ); if( writeIntervalVolumes && writeIntervalVolumes->GetNumberOfFunctions() ) { unsigned int LengthOfIntervals = parser->Convert( writeIntervalVolumes->GetFunction( 0 )->GetName() ); regHelper->SetWriteIntervalVolumes(LengthOfIntervals); } else { regHelper->SetPrintSimilarityMeasureInterval( 0 ); } std::string outputPrefix = outputOption->GetFunction( 0 )->GetName(); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { outputPrefix = outputOption->GetFunction( 0 )->GetParameter( 0 ); } std::string outputWarpedImageName; if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { outputWarpedImageName = outputOption->GetFunction( 0 )->GetParameter( 1 ); } std::string outputInverseWarpedImageName; if( outputOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { outputInverseWarpedImageName = outputOption->GetFunction( 0 )->GetParameter( 2 ); } ParserType::OptionType::Pointer initialMovingTransformOption = parser->GetOption( "initial-moving-transform" ); if( initialMovingTransformOption && initialMovingTransformOption->GetNumberOfFunctions() ) { std::vector isDerivedInitialMovingTransform; typename CompositeTransformType::Pointer compositeTransform = GetCompositeTransformFromParserOption( parser, initialMovingTransformOption, isDerivedInitialMovingTransform ); if( compositeTransform.IsNull() ) { return EXIT_FAILURE; } regHelper->SetMovingInitialTransform( compositeTransform ); // Write out initial derived transforms only if we're not collapsing them in the output if( !shouldCollapseBeDone ) { for( unsigned int n = 0; n < isDerivedInitialMovingTransform.size(); n++ ) { std::stringstream curFileName; curFileName << outputPrefix << n << (use_minc_format?"DerivedInitialMovingTranslation.xfm":"DerivedInitialMovingTranslation.mat"); typename RegistrationHelperType::CompositeTransformType::TransformTypePointer curTransform = compositeTransform->GetNthTransform( n ); if( curTransform->IsLinear() && isDerivedInitialMovingTransform[n] ) { itk::ants::WriteTransform( curTransform, curFileName.str() ); } } } } ParserType::OptionType::Pointer initialFixedTransformOption = parser->GetOption( "initial-fixed-transform" ); if( initialFixedTransformOption && initialFixedTransformOption->GetNumberOfFunctions() ) { std::vector isDerivedInitialFixedTransform; typename CompositeTransformType::Pointer compositeTransform = GetCompositeTransformFromParserOption( parser, initialFixedTransformOption, isDerivedInitialFixedTransform ); if( compositeTransform.IsNull() ) { return EXIT_FAILURE; } regHelper->SetFixedInitialTransform( compositeTransform ); // Write out initial derived transforms only if we're not collapsing them in the output if( !shouldCollapseBeDone ) { for( unsigned int n = 0; n < isDerivedInitialFixedTransform.size(); n++ ) { std::stringstream curFileName; curFileName << outputPrefix << n << (use_minc_format?"DerivedInitialFixedTranslation.xfm":"DerivedInitialFixedTranslation.mat"); typename RegistrationHelperType::CompositeTransformType::TransformTypePointer curTransform = compositeTransform->GetNthTransform( n ); if( curTransform->IsLinear() && isDerivedInitialFixedTransform[n] ) { itk::ants::WriteTransform( curTransform, curFileName.str() ); } } } } ParserType::OptionType::Pointer restoreStateOption = parser->GetOption( "restore-state" ); if( restoreStateOption && restoreStateOption->GetNumberOfFunctions() ) { if( initialMovingTransformOption->GetNumberOfFunctions() || initialFixedTransformOption->GetNumberOfFunctions() ) { if( verbose ) { std::cerr << "restore-state option is mutually exclusive with " << "initial-moving-transform & initial-fixed-transform options." << std::endl; } return EXIT_FAILURE; } if( verbose ) { std::cout << "Restoring previous registration state" << std::endl; } std::vector isDerivedInitialMovingTransform; typename CompositeTransformType::Pointer compositeTransform = GetCompositeTransformFromParserOption( parser, restoreStateOption, isDerivedInitialMovingTransform ); if( verbose ) { std::cout << "+" << std::endl; } if( compositeTransform.IsNull() ) { return EXIT_FAILURE; } regHelper->SetRestoreStateTransform( compositeTransform ); if( verbose ) { std::cout << "+" << std::endl; } } if( maskOption && maskOption->GetNumberOfFunctions() ) { if( verbose ) { std::cout << " Reading mask(s)." << std::endl; } for( int l = maskOption->GetNumberOfFunctions() - 1; l >= 0; l-- ) { if( verbose ) { std::cout << " Registration stage " << ( maskOption->GetNumberOfFunctions() - l - 1 ) << std::endl; } if( maskOption->GetFunction( l )->GetNumberOfParameters() > 0 ) { for( unsigned m = 0; m < maskOption->GetFunction( l )->GetNumberOfParameters(); m++ ) { std::string fname = maskOption->GetFunction( l )->GetParameter( m ); typename MaskImageType::Pointer maskImage; ReadImage( maskImage, fname.c_str() ); if( m == 0 ) { regHelper->AddFixedImageMask( maskImage ); if( verbose ) { if( maskImage.IsNotNull() ) { std::cout << " Fixed mask = " << fname.c_str() << std::endl; } else { std::cout << " No fixed mask" << std::endl; } } } else if( m == 1 ) { regHelper->AddMovingImageMask( maskImage ); if( verbose ) { if( maskImage.IsNotNull() ) { std::cout << " Moving mask = " << fname << std::endl; } else { std::cout << " No moving mask" << std::endl; } } } } } else { std::string fname = maskOption->GetFunction( l )->GetName(); typename MaskImageType::Pointer maskImage; ReadImage( maskImage, fname.c_str() ); regHelper->AddFixedImageMask( maskImage ); if( verbose ) { if( maskImage.IsNotNull() ) { std::cout << " Fixed mask = " << fname << std::endl; } else { std::cout << " No fixed mask" << std::endl; } } } } } // The misc. options // * winsorize image intensities // * use histogram matching // * estimate learning rate // are currently specified once on the command line and then apply to all // stages. Advanced parameter specification might require us to rewrite // this in the future. float lowerQuantile = 0.0; float upperQuantile = 1.0; bool doWinsorize = false; OptionType::Pointer winsorizeOption = parser->GetOption( "winsorize-image-intensities" ); if( winsorizeOption && winsorizeOption->GetNumberOfFunctions() ) { doWinsorize = true; if( winsorizeOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { lowerQuantile = parser->Convert( winsorizeOption->GetFunction( 0 )->GetParameter( 0 ) ); } if( winsorizeOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { upperQuantile = parser->Convert( winsorizeOption->GetFunction( 0 )->GetParameter( 1 ) ); } } regHelper->SetWinsorizeImageIntensities( doWinsorize, lowerQuantile, upperQuantile ); bool doHistogramMatch = false; OptionType::Pointer histOption = parser->GetOption( "use-histogram-matching" ); if( histOption && histOption->GetNumberOfFunctions() ) { std::string histFunction = histOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( histFunction ); if( histFunction.compare( "1" ) == 0 || histFunction.compare( "true" ) == 0 ) { doHistogramMatch = true; } } regHelper->SetUseHistogramMatching( doHistogramMatch ); bool doEstimateLearningRateAtEachIteration = true; OptionType::Pointer rateOption = parser->GetOption( "use-estimate-learning-rate-once" ); if( rateOption && rateOption->GetNumberOfFunctions() ) { std::string rateFunction = rateOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( rateFunction ); if( rateFunction.compare( "1" ) == 0 || rateFunction.compare( "true" ) == 0 ) { doEstimateLearningRateAtEachIteration = false; } } regHelper->SetDoEstimateLearningRateAtEachIteration( doEstimateLearningRateAtEachIteration ); // We find both the number of transforms and the number of metrics unsigned int numberOfTransforms = transformOption->GetNumberOfFunctions(); if( transformOption.IsNull() || numberOfTransforms == 0 ) { if( verbose ) { std::cerr << "No transformations are specified." << std::endl; } return EXIT_FAILURE; } std::vector > iterationList; std::vector > restrictDeformationWeightsList; std::vector convergenceThresholdList; std::vector convergenceWindowSizeList; std::vector > shrinkFactorsList; std::vector > smoothingSigmasList; std::vector smoothingSigmasAreInPhysicalUnitsList; std::deque TransformTypeNames; // Each image registration "stage" is characterized by // * a transform // * a set of convergence criteria (number of iterations, convergence threshold, // and/or convergence window) // * a set of shrink factors // * a set of smoothing factors (specified in physical space or voxel space) // Note that the set of the number of iterations, the set of shrink factors, and the // set of smoothing factors imply the number of levels that will be used for that stage // and thus they all need to be the same vector length, e.g. "-c 100x50x10 -f 4x2x1 -s 2x1x0". // We use the number of transforms to implicitly guess how many stages are being used in // current registration call. We don't add the metrics in this loop as there could be // more than one metric per stage. // // Also, we iterate backwards because the command line options are stored as a stack (first // in last out). for( int currentStage = numberOfTransforms - 1; currentStage >= 0; currentStage-- ) { // Get the number of iterations and use that information to specify the number of levels std::vector iterations; RealType convergenceThreshold = 1e-6; unsigned int convergenceWindowSize = 10; if( convergenceOption.IsNotNull() && convergenceOption->GetNumberOfFunctions() ) { if( convergenceOption->GetFunction( currentStage )->GetNumberOfParameters() == 0 ) { iterations = parser->ConvertVector( convergenceOption->GetFunction( currentStage )->GetName() ); } else if( convergenceOption->GetFunction( currentStage )->GetNumberOfParameters() > 0 ) { iterations = parser->ConvertVector( convergenceOption->GetFunction( currentStage )->GetParameter( 0 ) ); } if( convergenceOption->GetFunction( currentStage )->GetNumberOfParameters() > 1 ) { convergenceThreshold = parser->Convert( convergenceOption->GetFunction( currentStage )->GetParameter( 1 ) ); } if( convergenceOption->GetFunction( currentStage )->GetNumberOfParameters() > 2 ) { convergenceWindowSize = parser->Convert( convergenceOption->GetFunction( currentStage )->GetParameter( 2 ) ); const unsigned int minAllowedconvergenceWindowSize = 2; // The BSplineScatteredDataPoints requires at least 2 // points for interpolation. if( convergenceWindowSize < minAllowedconvergenceWindowSize ) { if( verbose ) { std::cerr << "Convergence Window Size must be greater than or equal to " << minAllowedconvergenceWindowSize << std::endl; } return EXIT_FAILURE; } } } else { if( verbose ) { std::cerr << "No convergence criteria are specified." << std::endl; } return EXIT_FAILURE; } iterationList.push_back( iterations ); convergenceThresholdList.push_back( convergenceThreshold ); convergenceWindowSizeList.push_back( convergenceWindowSize ); if( restrictDeformationOption.IsNotNull() && restrictDeformationOption->GetNumberOfFunctions() > static_cast( currentStage ) ) { std::vector restrictDeformationWeights = parser->ConvertVector( restrictDeformationOption->GetFunction( currentStage )->GetName() ); restrictDeformationWeightsList.push_back( restrictDeformationWeights ); } unsigned int numberOfLevels = iterations.size(); if( verbose ) { std::cout << " number of levels = " << numberOfLevels << std::endl; } // Get the first metricOption for the currentStage (for use with the B-spline transforms) unsigned int numberOfMetrics = metricOption->GetNumberOfFunctions(); std::string fixedImageFileName; for( int currentMetricNumber = numberOfMetrics - 1; currentMetricNumber >= 0; currentMetricNumber-- ) { // Get the fixed filename to read later in the case of a B-spline transform unsigned int stageID = metricOption->GetFunction( currentMetricNumber )->GetStageID(); if( stageID == static_cast( currentStage ) ) { fixedImageFileName = metricOption->GetFunction( currentMetricNumber )->GetParameter( 0 ); break; } } // Get shrink factors std::vector factors = parser->ConvertVector( shrinkFactorsOption->GetFunction( currentStage )->GetName() ); shrinkFactorsList.push_back( factors ); // Get smoothing sigmas std::string smoothingSigmasString = smoothingSigmasOption->GetFunction( currentStage )->GetName(); const size_t mmPosition = smoothingSigmasString.find( "mm" ); const size_t voxPosition = smoothingSigmasString.find( "vox" ); if( mmPosition != std::string::npos ) { smoothingSigmasString.replace( mmPosition, 2, "" ); smoothingSigmasAreInPhysicalUnitsList.push_back( true ); } else if( voxPosition != std::string::npos ) { smoothingSigmasString.replace( voxPosition, 3, "" ); smoothingSigmasAreInPhysicalUnitsList.push_back( false ); } else { smoothingSigmasAreInPhysicalUnitsList.push_back( false ); } std::vector sigmas = parser->ConvertVector( smoothingSigmasString ); if( sigmas.size() == 1 ) { sigmas.resize( numberOfLevels, sigmas[0] ); } smoothingSigmasList.push_back( sigmas ); // Set up the optimizer. To change the iteration number for each level we rely // on the command observer. float learningRate = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 0 ) ); std::string whichTransform = transformOption->GetFunction( currentStage )->GetName(); ConvertToLowerCase( whichTransform ); TransformTypeNames.push_back( whichTransform ); typename RegistrationHelperType::XfrmMethod xfrmMethod = regHelper->StringToXfrmMethod( whichTransform ); switch( xfrmMethod ) { case RegistrationHelperType::Affine: { regHelper->AddAffineTransform( learningRate ); } break; case RegistrationHelperType::Rigid: { regHelper->AddRigidTransform( learningRate ); } break; case RegistrationHelperType::CompositeAffine: { regHelper->AddCompositeAffineTransform( learningRate ); } break; case RegistrationHelperType::Similarity: { regHelper->AddSimilarityTransform( learningRate ); } break; case RegistrationHelperType::Translation: { regHelper->AddTranslationTransform( learningRate ); } break; case RegistrationHelperType::GaussianDisplacementField: { const float varianceForUpdateField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 1 ) ); const float varianceForTotalField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); regHelper->AddGaussianDisplacementFieldTransform(learningRate, varianceForUpdateField, varianceForTotalField); } break; case RegistrationHelperType::BSplineDisplacementField: { unsigned int splineOrder = 3; if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 3 ) { splineOrder = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 3 ) ); } std::vector meshSizeForTheUpdateField = parser->ConvertVector( transformOption->GetFunction( currentStage )->GetParameter( 1 ) ); if( meshSizeForTheUpdateField.size() == 1 ) { typename ImageType::Pointer fixedImage; ReadImage( fixedImage, fixedImageFileName.c_str() ); fixedImage->DisconnectPipeline(); meshSizeForTheUpdateField = regHelper->CalculateMeshSizeForSpecifiedKnotSpacing( fixedImage, meshSizeForTheUpdateField[0], splineOrder ); } std::vector meshSizeForTheTotalField; if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 2 ) { meshSizeForTheTotalField = parser->ConvertVector( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); if( meshSizeForTheTotalField.size() == 1 ) { typename ImageType::Pointer fixedImage; ReadImage( fixedImage, fixedImageFileName.c_str() ); fixedImage->DisconnectPipeline(); meshSizeForTheTotalField = regHelper->CalculateMeshSizeForSpecifiedKnotSpacing( fixedImage, meshSizeForTheTotalField[0], splineOrder ); } } else { for( unsigned int d = 0; d < VImageDimension; d++ ) { meshSizeForTheTotalField.push_back( 0 ); } } regHelper->AddBSplineDisplacementFieldTransform( learningRate, meshSizeForTheUpdateField, meshSizeForTheTotalField, splineOrder ); } break; case RegistrationHelperType::BSpline: { std::vector meshSizeAtBaseLevel = parser->ConvertVector( transformOption->GetFunction( currentStage )->GetParameter( 1 ) ); if( meshSizeAtBaseLevel.size() == 1 ) { typename ImageType::Pointer fixedImage; ReadImage( fixedImage, fixedImageFileName.c_str() ); fixedImage->DisconnectPipeline(); meshSizeAtBaseLevel = regHelper->CalculateMeshSizeForSpecifiedKnotSpacing( fixedImage, meshSizeAtBaseLevel[0], 3 ); } regHelper->AddBSplineTransform( learningRate, meshSizeAtBaseLevel ); } break; case RegistrationHelperType::TimeVaryingVelocityField: { unsigned int numberOfTimeIndices = parser->Convert( transformOption->GetFunction( 0 )->GetParameter( 1 ) ); const float varianceForUpdateField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); const float varianceForUpdateFieldTime = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 3 ) ); const float varianceForTotalField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 4 ) ); const float varianceForTotalFieldTime = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 5 ) ); regHelper->AddTimeVaryingVelocityFieldTransform( learningRate, numberOfTimeIndices, varianceForUpdateField, varianceForUpdateFieldTime, varianceForTotalField, varianceForTotalFieldTime ); } break; case RegistrationHelperType::TimeVaryingBSplineVelocityField: { std::vector meshSize = parser->ConvertVector( transformOption->GetFunction( 0 )->GetParameter( 1 ) ); unsigned int numberOfTimePointSamples = 4; if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 2 ) { numberOfTimePointSamples = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); } unsigned int splineOrder = 3; if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 3 ) { splineOrder = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 3 ) ); } regHelper->AddTimeVaryingBSplineVelocityFieldTransform( learningRate, meshSize, numberOfTimePointSamples, splineOrder ); } break; case RegistrationHelperType::SyN: { float varianceForUpdateField = 3.0; if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 1 ) { varianceForUpdateField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 1 ) ); } float varianceForTotalField = 0.0; if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 2 ) { varianceForTotalField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); } regHelper->AddSyNTransform( learningRate, varianceForUpdateField, varianceForTotalField ); } break; case RegistrationHelperType::BSplineSyN: { unsigned int splineOrder = 3; if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 3 ) { splineOrder = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 3 ) ); } std::vector meshSizeForTheUpdateField; std::vector meshSizeForTheUpdateFieldFloat = parser->ConvertVector( transformOption->GetFunction( currentStage )->GetParameter( 1 ) ); if( meshSizeForTheUpdateFieldFloat.size() == 1 ) { typename ImageType::Pointer fixedImage; ReadImage( fixedImage, fixedImageFileName.c_str() ); fixedImage->DisconnectPipeline(); meshSizeForTheUpdateField = regHelper->CalculateMeshSizeForSpecifiedKnotSpacing( fixedImage, meshSizeForTheUpdateFieldFloat[0], splineOrder ); } else { meshSizeForTheUpdateField = parser->ConvertVector( transformOption->GetFunction( currentStage )->GetParameter( 1 ) ); } std::vector meshSizeForTheTotalField; if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 2 ) { std::vector meshSizeForTheTotalFieldFloat = parser->ConvertVector( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); if( meshSizeForTheTotalFieldFloat.size() == 1 ) { typename ImageType::Pointer fixedImage; ReadImage( fixedImage, fixedImageFileName.c_str() ); fixedImage->DisconnectPipeline(); meshSizeForTheTotalField = regHelper->CalculateMeshSizeForSpecifiedKnotSpacing( fixedImage, meshSizeForTheTotalFieldFloat[0], splineOrder ); } else { meshSizeForTheTotalField = parser->ConvertVector( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); } } else { for( unsigned int d = 0; d < VImageDimension; d++ ) { meshSizeForTheTotalField.push_back( 0 ); } } regHelper->AddBSplineSyNTransform( learningRate, meshSizeForTheUpdateField, meshSizeForTheTotalField, splineOrder ); } break; case RegistrationHelperType::Exponential: { const float varianceForUpdateField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 1 ) ); const float varianceForVelocityField = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); unsigned int numberOfIntegrationSteps = 0; // If the number of integration steps = 0, compute steps // automatically if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 3 ) { numberOfIntegrationSteps = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 3 ) ); } regHelper->AddExponentialTransform( learningRate, varianceForUpdateField, varianceForVelocityField, numberOfIntegrationSteps ); } break; case RegistrationHelperType::BSplineExponential: { unsigned int splineOrder = 3; if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 4 ) { splineOrder = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 4 ) ); } std::vector meshSizeForTheUpdateField = parser->ConvertVector( transformOption->GetFunction( currentStage )->GetParameter( 1 ) ); if( meshSizeForTheUpdateField.size() == 1 ) { typename ImageType::Pointer fixedImage; ReadImage( fixedImage, fixedImageFileName.c_str() ); fixedImage->DisconnectPipeline(); meshSizeForTheUpdateField = regHelper->CalculateMeshSizeForSpecifiedKnotSpacing( fixedImage, meshSizeForTheUpdateField[0], splineOrder ); } std::vector meshSizeForTheVelocityField; if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 2 ) { meshSizeForTheVelocityField = parser->ConvertVector( transformOption->GetFunction( currentStage )->GetParameter( 2 ) ); if( meshSizeForTheVelocityField.size() == 1 ) { typename ImageType::Pointer fixedImage; ReadImage( fixedImage, fixedImageFileName.c_str() ); fixedImage->DisconnectPipeline(); meshSizeForTheVelocityField = regHelper->CalculateMeshSizeForSpecifiedKnotSpacing( fixedImage, meshSizeForTheVelocityField[0], splineOrder ); } } else { for( unsigned int d = 0; d < VImageDimension; d++ ) { meshSizeForTheVelocityField.push_back( 0 ); } } unsigned int numberOfIntegrationSteps = 0; // If the number of integration steps = 0, compute steps // automatically if( transformOption->GetFunction( currentStage )->GetNumberOfParameters() > 3 ) { numberOfIntegrationSteps = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 3 ) ); } regHelper->AddBSplineExponentialTransform( learningRate, meshSizeForTheUpdateField, meshSizeForTheVelocityField, numberOfIntegrationSteps, splineOrder ); } break; default: { if( verbose ) { std::cerr << "Unknown registration method " << "\"" << whichTransform << "\"" << std::endl; } return EXIT_FAILURE; } break; } } // set the vector-vector parameters accumulated regHelper->SetIterations( iterationList ); regHelper->SetRestrictDeformationOptimizerWeights( restrictDeformationWeightsList ); regHelper->SetConvergenceWindowSizes( convergenceWindowSizeList ); regHelper->SetConvergenceThresholds( convergenceThresholdList ); regHelper->SetSmoothingSigmas( smoothingSigmasList ); regHelper->SetSmoothingSigmasAreInPhysicalUnits( smoothingSigmasAreInPhysicalUnitsList ); regHelper->SetShrinkFactors( shrinkFactorsList ); // We iterate through each of the metric "functions" specified on the command // line and add it the registration helper. We also need to assign the stage // ID to the added metric. Multiple metrics for a single stage are specified // on the command line by being specified adjacently. unsigned int numberOfMetrics = metricOption->GetNumberOfFunctions(); for( int currentMetricNumber = numberOfMetrics - 1; currentMetricNumber >= 0; currentMetricNumber-- ) { // Get the stage ID unsigned int stageID = metricOption->GetFunction( currentMetricNumber )->GetStageID(); // We check the last stage ID (first iteration) to ensure that the number of stages // (as determined by the number of transforms) is equal to the number of stages (as // determined by the metrics command line specification). if( currentMetricNumber == static_cast( numberOfMetrics - 1 ) ) { if( stageID != numberOfTransforms - 1 ) { if( verbose ) { std::cerr << "\n\n\n" << "Error: The number of stages does not match up with the metrics." << std::endl << "The number of transforms is " << numberOfTransforms << " and the last stage ID " << " as determined by the metrics is " << stageID << "." << std::endl; } return EXIT_FAILURE; } } std::string whichMetric = metricOption->GetFunction( currentMetricNumber )->GetName(); ConvertToLowerCase( whichMetric ); typename RegistrationHelperType::MetricEnumeration currentMetric = regHelper->StringToMetricType( whichMetric ); // Get the fixed and moving images or point sets typename ImageType::Pointer fixedImage = ITK_NULLPTR; typename ImageType::Pointer movingImage = ITK_NULLPTR; typename LabeledPointSetType::Pointer fixedLabeledPointSet = ITK_NULLPTR; typename LabeledPointSetType::Pointer movingLabeledPointSet = ITK_NULLPTR; typename IntensityPointSetType::Pointer fixedIntensityPointSet = ITK_NULLPTR; typename IntensityPointSetType::Pointer movingIntensityPointSet = ITK_NULLPTR; float metricWeighting = 1.0; if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 2 ) { metricWeighting = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 2 ) ); } // assign default image metric variables typename RegistrationHelperType::SamplingStrategy samplingStrategy = RegistrationHelperType::none; unsigned int numberOfBins = 32; unsigned int radius = 4; // assign default point-set variables // labeled point sets bool useBoundaryPointsOnly = false; float pointSetSigma = 1.0; unsigned int evaluationKNeighborhood = 50; float alpha = 1.1; float useAnisotropicCovariances = false; float samplingPercentage = 1.0; // intensity point sets float intensityDistanceSigma = 0.0; float euclideanDistanceSigma = 0.0; if( !regHelper->IsPointSetMetric( currentMetric ) ) { if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 5 ) { samplingPercentage = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 5 ) ); } std::string fixedFileName = metricOption->GetFunction( currentMetricNumber )->GetParameter( 0 ); std::string movingFileName = metricOption->GetFunction( currentMetricNumber )->GetParameter( 1 ); if( verbose ) { std::cout << " fixed image: " << fixedFileName << std::endl; std::cout << " moving image: " << movingFileName << std::endl; } ReadImage( fixedImage, fixedFileName.c_str() ); ReadImage( movingImage, movingFileName.c_str() ); fixedImage->DisconnectPipeline(); movingImage->DisconnectPipeline(); std::string strategy = "none"; if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 4 ) { strategy = metricOption->GetFunction( currentMetricNumber )->GetParameter( 4 ); } ConvertToLowerCase( strategy ); if( strategy == "random" ) { samplingStrategy = RegistrationHelperType::random; } else if( strategy == "regular" ) { samplingStrategy = RegistrationHelperType::regular; } else if( ( strategy == "none" ) || ( strategy == "" ) ) { samplingStrategy = RegistrationHelperType::none; } else { samplingStrategy = RegistrationHelperType::invalid; if( verbose ) { std::cerr << "ERROR: invalid sampling strategy specified: " << strategy << std::endl; } return EXIT_FAILURE; } if( currentMetric == RegistrationHelperType::CC ) { if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 3 ) { radius = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 3 ) ); } } else if( currentMetric == RegistrationHelperType::Mattes || currentMetric == RegistrationHelperType::MI ) { if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 3 ) { numberOfBins = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 3 ) ); } } } else { if( whichMetric == "igdm" ) { if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() < 5 ) { if( verbose ) { std::cerr << "The expected number of parameters aren't specified. Please see help menu." << std::endl; } return EXIT_FAILURE; } std::string fixedFileName = metricOption->GetFunction( currentMetricNumber )->GetParameter( 0 ); std::string movingFileName = metricOption->GetFunction( currentMetricNumber )->GetParameter( 1 ); if( verbose ) { std::cout << " fixed intensity point set: " << fixedFileName << std::endl; std::cout << " moving intensity point set: " << movingFileName << std::endl; } std::string fixedPointSetMaskFile = metricOption->GetFunction( currentMetricNumber )->GetParameter( 3 ); std::string movingPointSetMaskFile = metricOption->GetFunction( currentMetricNumber )->GetParameter( 4 ); if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 6 ) { intensityDistanceSigma = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 6 ) ); } if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 7 ) { euclideanDistanceSigma = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 7 ) ); } evaluationKNeighborhood = 1; if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 8 ) { evaluationKNeighborhood = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 8 ) ); } double gradientPointSetSigma = 1.0; if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 9 ) { gradientPointSetSigma = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 9 ) ); } std::vector neighborhoodRadius; if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 5 ) { neighborhoodRadius = parser->ConvertVector( metricOption->GetFunction( currentMetricNumber )->GetParameter( 5 ) ); } else { for( unsigned d = 0; d < VImageDimension; d++ ) { neighborhoodRadius.push_back( 0 ); } } if( neighborhoodRadius.size() != VImageDimension ) { if( verbose ) { std::cerr << "The neighborhood size must equal the dimension." << std::endl; } return EXIT_FAILURE; } ReadImageIntensityPointSet( fixedIntensityPointSet, fixedFileName.c_str(), fixedPointSetMaskFile.c_str(), neighborhoodRadius, gradientPointSetSigma ); ReadImageIntensityPointSet( movingIntensityPointSet, movingFileName.c_str(), movingPointSetMaskFile.c_str(), neighborhoodRadius, gradientPointSetSigma ); fixedIntensityPointSet->DisconnectPipeline(); movingIntensityPointSet->DisconnectPipeline(); } else { if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 3 ) { samplingPercentage = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 3 ) ); } if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 4 ) { useBoundaryPointsOnly = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 4 ) ); } if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 5 ) { pointSetSigma = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 5 ) ); } if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 6 ) { evaluationKNeighborhood = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 6 ) ); } if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 7 ) { alpha = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 7 ) ); } if( metricOption->GetFunction( currentMetricNumber )->GetNumberOfParameters() > 8 ) { useAnisotropicCovariances = parser->Convert( metricOption->GetFunction( currentMetricNumber )->GetParameter( 8 ) ); } std::string fixedFileName = metricOption->GetFunction( currentMetricNumber )->GetParameter( 0 ); std::string movingFileName = metricOption->GetFunction( currentMetricNumber )->GetParameter( 1 ); if( verbose ) { std::cout << " fixed labeled point set: " << fixedFileName << std::endl; std::cout << " moving labeled point set: " << movingFileName << std::endl; } ReadLabeledPointSet( fixedLabeledPointSet, fixedFileName.c_str(), useBoundaryPointsOnly, samplingPercentage ); ReadLabeledPointSet( movingLabeledPointSet, movingFileName.c_str(), useBoundaryPointsOnly, samplingPercentage ); fixedLabeledPointSet->DisconnectPipeline(); movingLabeledPointSet->DisconnectPipeline(); } } regHelper->AddMetric( currentMetric, fixedImage, movingImage, fixedLabeledPointSet, movingLabeledPointSet, fixedIntensityPointSet, movingIntensityPointSet, stageID, metricWeighting, samplingStrategy, numberOfBins, radius, useBoundaryPointsOnly, pointSetSigma, evaluationKNeighborhood, alpha, useAnisotropicCovariances, samplingPercentage, intensityDistanceSigma, euclideanDistanceSigma ); } // Perform the registration if( regHelper->DoRegistration() == EXIT_FAILURE ) { return EXIT_FAILURE; } // write out transforms stored in the composite typename CompositeTransformType::Pointer resultTransform = regHelper->GetModifiableCompositeTransform(); unsigned int numTransforms = resultTransform->GetNumberOfTransforms(); //// typedef typename RegistrationHelperType::CompositeTransformType CompositeTransformType; typedef typename CompositeTransformType::Pointer CompositeTransformPointer; typedef typename RegistrationHelperType::DisplacementFieldTransformType DisplacementFieldTransformType; typedef typename RegistrationHelperType::TransformType TransformType; if( saveStateOption && saveStateOption->GetNumberOfFunctions() ) { CompositeTransformPointer savedStateTx = dynamic_cast( regHelper->GetModifiableRegistrationState() ); if( savedStateTx.IsNull() ) { return EXIT_FAILURE; } unsigned int numStateComponents = savedStateTx->GetNumberOfTransforms(); // If the last two transforms are displacement field transforms, we add their inverse displacement field to the saved state composite. if( savedStateTx->GetNthTransform( numStateComponents-1 )->GetTransformCategory() == TransformType::DisplacementField && savedStateTx->GetNthTransform( numStateComponents-2 )->GetTransformCategory() == TransformType::DisplacementField ) { typename DisplacementFieldTransformType::Pointer oneToEndTransform = dynamic_cast( savedStateTx->GetNthTransform( numStateComponents-2 ).GetPointer() ); typename DisplacementFieldTransformType::Pointer endTransform = dynamic_cast( savedStateTx->GetNthTransform( numStateComponents-1 ).GetPointer() ); if( oneToEndTransform && oneToEndTransform->GetInverseDisplacementField() && endTransform && endTransform->GetInverseDisplacementField() ) { savedStateTx->RemoveTransform(); savedStateTx->AddTransform( oneToEndTransform->GetInverseTransform() ); savedStateTx->AddTransform( endTransform ); savedStateTx->AddTransform( endTransform->GetInverseTransform() ); } } typename RegistrationHelperType::CompositeTransformType::TransformTypePointer savedStateCompositeTransform = savedStateTx.GetPointer(); const std::string saveStateFileName = saveStateOption->GetFunction( 0 )->GetName(); // The savedState includes: // output linear transforms // + SyN FixedToMiddle displacement field + SyN FixedToMiddle inverse displacement field // + SyN MovingToMiddle displacement field + SyN MovingToMiddle inverse displacement field // itk::ants::WriteTransform( savedStateCompositeTransform, saveStateFileName.c_str() ); } // write out transforms actually computed, so skip any initial transforms unless // we're collapsing the output transforms. CompositeTransformPointer transformToWrite; if( shouldCollapseBeDone ) { transformToWrite = regHelper->CollapseCompositeTransform( resultTransform ); numTransforms = transformToWrite->GetNumberOfTransforms(); TransformTypeNames.clear(); for( unsigned int i = 0; i < numTransforms; i++ ) { if( transformToWrite->GetNthTransform( i )->GetTransformCategory() == TransformType::Linear ) { // The output type must be Affine, not matrixoffset! TransformTypeNames.push_back( "matrixoffset" ); TransformTypeNames.push_back( "genericaffine" ); } else if( transformToWrite->GetNthTransform( i )->GetTransformCategory() == TransformType::DisplacementField ) { typename DisplacementFieldTransformType::Pointer nthTransform = dynamic_cast( transformToWrite->GetNthTransform( i ).GetPointer() ); // We don't know what set of displacement field transforms were optimized. // All we know is whether or not an inverse displacement field exists. If so, // we simply pass a transform name which either does have an inverse or does // not. if( nthTransform && nthTransform->GetInverseDisplacementField() ) { TransformTypeNames.push_back( "syn" ); } else { TransformTypeNames.push_back( "gdf" ); } } else if( transformToWrite->GetNthTransform( i )->GetTransformCategory() == TransformType::BSpline ) { TransformTypeNames.push_back( "bspline" ); } } } else { transformToWrite = resultTransform.GetPointer(); } if( writeCompositeTransform ) { std::string compositeTransformFileName = outputPrefix + (use_minc_format?std::string( ".xfm" ):std::string( "Composite.h5" )); std::string inverseCompositeTransformFileName = outputPrefix + (use_minc_format?std::string( "_inverse.xfm" ):std::string( "InverseComposite.h5" )); typename RegistrationHelperType::CompositeTransformType::TransformTypePointer compositeTransform = transformToWrite.GetPointer(); itk::ants::WriteTransform( compositeTransform, compositeTransformFileName.c_str() ); typename RegistrationHelperType::CompositeTransformType::TransformTypePointer inverseCompositeTransform = compositeTransform->GetInverseTransform(); if( inverseCompositeTransform.IsNotNull() ) { itk::ants::WriteTransform( inverseCompositeTransform, inverseCompositeTransformFileName.c_str() ); } } else //Write out each individual transform { const unsigned int startIndex = ( shouldCollapseBeDone ) ? 0 : initialMovingTransformOption->GetNumberOfFunctions(); for( unsigned int i = startIndex; i < numTransforms; ++i ) { typename CompositeTransformType::TransformTypePointer curTransform = transformToWrite->GetNthTransform( i ); // only registrations not part of the initial transforms in the // TransformTypeNames list. const std::string curTransformType = TransformTypeNames.front(); TransformTypeNames.pop_front(); bool writeInverse; bool writeVelocityField; std::string transformTemplateName = RegTypeToFileName( curTransformType, writeInverse, writeVelocityField, use_minc_format ); std::stringstream curFileName; curFileName << outputPrefix << i << transformTemplateName; // WriteTransform will spit all sorts of error messages if it // fails, and we want to keep going even if it does so ignore its // return value. itk::ants::WriteTransform( curTransform, curFileName.str() ); typename DisplacementFieldTransformType::Pointer dispTransform = dynamic_cast(curTransform.GetPointer() ); if( writeInverse && dispTransform.IsNotNull() ) { std::stringstream curInverseFileName; curInverseFileName << outputPrefix << i << (use_minc_format?"_inverse":"Inverse") << transformTemplateName; // write inverse transform file itk::ants::WriteInverseTransform( dispTransform, curInverseFileName.str() ); } if( writeVelocityField ) { // write velocity field (if applicable) typedef typename RegistrationHelperType::TimeVaryingVelocityFieldTransformType TimeVaryingVelocityFieldTransformType; typedef itk::GaussianExponentialDiffeomorphicTransform GaussianDisplacementFieldTransformType; typename TimeVaryingVelocityFieldTransformType::Pointer tvVelocityFieldTransform = dynamic_cast(curTransform.GetPointer() ); typename GaussianDisplacementFieldTransformType::Pointer constVelocityFieldTransform = dynamic_cast(curTransform.GetPointer() ); std::stringstream curVelocityFieldFileName; curVelocityFieldFileName << outputPrefix << i << (use_minc_format?"_VelocityField.mnc":"VelocityField.nii.gz"); try { if( !tvVelocityFieldTransform.IsNull() ) { typedef itk::Image, VImageDimension + 1> VelocityFieldType; typedef itk::ImageFileWriter VelocityFieldWriterType; typename VelocityFieldWriterType::Pointer velocityFieldWriter = VelocityFieldWriterType::New(); velocityFieldWriter->SetInput( tvVelocityFieldTransform->GetTimeVaryingVelocityField() ); velocityFieldWriter->SetFileName( curVelocityFieldFileName.str().c_str() ); velocityFieldWriter->Update(); } else if( !constVelocityFieldTransform.IsNull() ) { typedef itk::Image, VImageDimension> VelocityFieldType; typedef itk::ImageFileWriter VelocityFieldWriterType; typename VelocityFieldWriterType::Pointer velocityFieldWriter = VelocityFieldWriterType::New(); velocityFieldWriter->SetInput( constVelocityFieldTransform->GetModifiableConstantVelocityField() ); velocityFieldWriter->SetFileName( curVelocityFieldFileName.str().c_str() ); velocityFieldWriter->Update(); } } catch( itk::ExceptionObject & err ) { if( verbose ) { std::cerr << "Can't write velocity field transform file " << curVelocityFieldFileName.str().c_str() << std::endl; std::cerr << "Exception Object caught: " << std::endl; std::cerr << err << std::endl; } } } } } std::string whichInterpolator( "linear" ); typename itk::ants::CommandLineParser::OptionType::Pointer interpolationOption = parser->GetOption( "interpolation" ); if( interpolationOption && interpolationOption->GetNumberOfFunctions() ) { whichInterpolator = interpolationOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( whichInterpolator ); } typename ImageType::SpacingType cache_spacing_for_smoothing_sigmas (itk::NumericTraits::ZeroValue()); if( !std::strcmp( whichInterpolator.c_str(), "gaussian" ) || !std::strcmp( whichInterpolator.c_str(), "multilabel" ) ) { #if 1 // HACK:: This can just be cached when reading the fixedImage from above!! // std::string fixedImageFileName = metricOption->GetFunction( numberOfTransforms - 1 )->GetParameter( 0 ); typedef itk::ImageFileReader ImageReaderType; typename ImageReaderType::Pointer fixedImageReader = ImageReaderType::New(); fixedImageReader->SetFileName( fixedImageFileName.c_str() ); fixedImageReader->Update(); typename ImageType::Pointer fixedImage = fixedImageReader->GetOutput(); #endif cache_spacing_for_smoothing_sigmas = fixedImage->GetSpacing(); } #include "make_interpolator_snip.tmpl" regHelper->SetInterpolator( interpolator ); if( !outputWarpedImageName.empty() ) { typename ImageType::Pointer warpedImage = regHelper->GetWarpedImage(); if( warpedImage.IsNotNull() ) { WriteImage( warpedImage, outputWarpedImageName.c_str() ); } } if( !outputInverseWarpedImageName.empty() ) { typename ImageType::Pointer inverseWarpedImage = regHelper->GetInverseWarpedImage(); if( inverseWarpedImage.IsNotNull() ) { WriteImage( inverseWarpedImage, outputInverseWarpedImageName.c_str() ); } } return EXIT_SUCCESS; } extern int antsRegistration2DDouble(ParserType::Pointer & parser); extern int antsRegistration3DDouble(ParserType::Pointer & parser); extern int antsRegistration4DDouble(ParserType::Pointer & parser); extern int antsRegistration2DFloat(ParserType::Pointer & parser); extern int antsRegistration3DFloat(ParserType::Pointer & parser); extern int antsRegistration4DFloat(ParserType::Pointer & parser); } // End namespace #endif // __ANTSREGISTRATIONTEMPLATEHEADER_H__ ants-2.2.0/Examples/antsSliceRegularizedRegistration.cxx000066400000000000000000001621671311104306400235060ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 "antsUtilities.h" #include "antsAllocImage.h" #include "ReadWriteData.h" #include "antsCommandLineParser.h" #include "itkCSVNumericObjectFileWriter.h" #include "itkImageRegistrationMethodv4.h" #include "itkSyNImageRegistrationMethod.h" #include "itkDisplacementFieldTransform.h" #include "itkANTSNeighborhoodCorrelationImageToImageMetricv4.h" #include "itkMeanSquaresImageToImageMetricv4.h" #include "itkCorrelationImageToImageMetricv4.h" #include "itkImageToImageMetricv4.h" #include "itkMattesMutualInformationImageToImageMetricv4.h" #include "itkImageToHistogramFilter.h" #include "itkHistogramMatchingImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include "itkTransformToDisplacementFieldFilter.h" #include "itkAffineTransform.h" #include "itkBSplineTransform.h" #include "itkBSplineSmoothingOnUpdateDisplacementFieldTransform.h" #include "itkCompositeTransform.h" #include "itkGaussianSmoothingOnUpdateDisplacementFieldTransform.h" #include "itkIdentityTransform.h" #include "itkEuler2DTransform.h" #include "itkEuler3DTransform.h" #include "itkSimilarity2DTransform.h" #include "itkTransform.h" #include "itkExtractImageFilter.h" #include "itkBSplineTransformParametersAdaptor.h" #include "itkBSplineSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor.h" #include "itkGaussianSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor.h" #include "itkTimeVaryingVelocityFieldTransformParametersAdaptor.h" #include "itkGradientDescentOptimizerv4.h" #include "itkConjugateGradientLineSearchOptimizerv4.h" #include "itkQuasiNewtonOptimizerv4.h" #include "itkHistogramMatchingImageFilter.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMacro.h" #include "itkRegistrationParameterScalesFromPhysicalShift.h" #include "itkResampleImageFilter.h" #include "itkShrinkImageFilter.h" #include "itkTimeProbe.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "itkSimilarity2DTransform.h" #include "itkSimilarity3DTransform.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkGaussianInterpolateImageFunction.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "itkLabelImageGaussianInterpolateImageFunction.h" #include "itkLabelImageGenericInterpolateImageFunction.h" #include namespace ants { /** \class antsRegistrationCommandIterationUpdate * \brief change parameters between iterations of registration */ template class antsSliceRegularizedRegistrationCommandIterationUpdate : public itk::Command { public: typedef antsSliceRegularizedRegistrationCommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); protected: antsSliceRegularizedRegistrationCommandIterationUpdate() { this->m_LogStream = &std::cout; } public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { TFilter * filter = const_cast( dynamic_cast( object ) ); unsigned int currentLevel = 0; if( typeid( event ) == typeid( itk::IterationEvent ) ) { currentLevel = filter->GetCurrentLevel() + 1; } if( currentLevel < this->m_NumberOfIterations.size() ) { typename TFilter::ShrinkFactorsPerDimensionContainerType shrinkFactors = filter->GetShrinkFactorsPerDimension( currentLevel ); typename TFilter::SmoothingSigmasArrayType smoothingSigmas = filter->GetSmoothingSigmasPerLevel(); typename TFilter::TransformParametersAdaptorsContainerType adaptors = filter->GetTransformParametersAdaptorsPerLevel(); this->Logger() << " Current level = " << currentLevel << std::endl; this->Logger() << " number of iterations = " << this->m_NumberOfIterations[currentLevel] << std::endl; this->Logger() << " shrink factors = " << shrinkFactors << std::endl; this->Logger() << " smoothing sigmas = " << smoothingSigmas[currentLevel] << std::endl; this->Logger() << " required fixed parameters = " << adaptors[currentLevel]->GetRequiredFixedParameters() << std::endl; typedef itk::ConjugateGradientLineSearchOptimizerv4 GradientDescentOptimizerType; GradientDescentOptimizerType * optimizer = reinterpret_cast( filter->GetModifiableOptimizer() ); optimizer->SetNumberOfIterations( this->m_NumberOfIterations[currentLevel] ); optimizer->SetMinimumConvergenceValue( 1.e-7 ); optimizer->SetConvergenceWindowSize( 10 ); optimizer->SetLowerLimit( 0 ); optimizer->SetUpperLimit( 2 ); optimizer->SetEpsilon( 0.1 ); } } void SetNumberOfIterations( const std::vector & iterations ) { this->m_NumberOfIterations = iterations; } void SetLogStream(std::ostream & logStream) { this->m_LogStream = &logStream; } private: std::ostream & Logger() const { return *m_LogStream; } std::vector m_NumberOfIterations; std::ostream * m_LogStream; }; template inline std::string ants_slice_regularized_to_string(const T& t) { std::stringstream ss; ss << t; return ss.str(); } template typename ImageType::Pointer sliceRegularizedPreprocessImage( ImageType * inputImage, typename ImageType::PixelType lowerScaleFunction, typename ImageType::PixelType upperScaleFunction, float winsorizeLowerQuantile, float winsorizeUpperQuantile, ImageType *histogramMatchSourceImage = NULL ) { typedef itk::Statistics::ImageToHistogramFilter HistogramFilterType; typedef typename HistogramFilterType::InputBooleanObjectType InputBooleanObjectType; typedef typename HistogramFilterType::HistogramSizeType HistogramSizeType; HistogramSizeType histogramSize( 1 ); histogramSize[0] = 256; typename InputBooleanObjectType::Pointer autoMinMaxInputObject = InputBooleanObjectType::New(); autoMinMaxInputObject->Set( true ); typename HistogramFilterType::Pointer histogramFilter = HistogramFilterType::New(); histogramFilter->SetInput( inputImage ); histogramFilter->SetAutoMinimumMaximumInput( autoMinMaxInputObject ); histogramFilter->SetHistogramSize( histogramSize ); histogramFilter->SetMarginalScale( 10.0 ); histogramFilter->Update(); float lowerFunction = histogramFilter->GetOutput()->Quantile( 0, winsorizeLowerQuantile ); float upperFunction = histogramFilter->GetOutput()->Quantile( 0, winsorizeUpperQuantile ); typedef itk::IntensityWindowingImageFilter IntensityWindowingImageFilterType; typename IntensityWindowingImageFilterType::Pointer windowingFilter = IntensityWindowingImageFilterType::New(); windowingFilter->SetInput( inputImage ); windowingFilter->SetWindowMinimum( lowerFunction ); windowingFilter->SetWindowMaximum( upperFunction ); windowingFilter->SetOutputMinimum( lowerScaleFunction ); windowingFilter->SetOutputMaximum( upperScaleFunction ); windowingFilter->Update(); typename ImageType::Pointer outputImage = ITK_NULLPTR; if( histogramMatchSourceImage ) { typedef itk::HistogramMatchingImageFilter HistogramMatchingFilterType; typename HistogramMatchingFilterType::Pointer matchingFilter = HistogramMatchingFilterType::New(); matchingFilter->SetSourceImage( windowingFilter->GetOutput() ); matchingFilter->SetReferenceImage( histogramMatchSourceImage ); matchingFilter->SetNumberOfHistogramLevels( 64 ); matchingFilter->SetNumberOfMatchPoints( 12 ); matchingFilter->ThresholdAtMeanIntensityOn(); matchingFilter->Update(); outputImage = matchingFilter->GetOutput(); outputImage->Update(); outputImage->DisconnectPipeline(); typedef itk::MinimumMaximumImageCalculator CalculatorType; typename CalculatorType::Pointer calc = CalculatorType::New(); calc->SetImage( inputImage ); calc->ComputeMaximum(); calc->ComputeMinimum(); if( vnl_math_abs( calc->GetMaximum() - calc->GetMinimum() ) < 1.e-9 ) { std::cout << "Warning: bad time point - too little intensity variation" << calc->GetMinimum() << " " << calc->GetMaximum() << std::endl; return ITK_NULLPTR; } } else { outputImage = windowingFilter->GetOutput(); outputImage->Update(); outputImage->DisconnectPipeline(); } return outputImage; } template struct ants_slice_regularized_index_cmp { ants_slice_regularized_index_cmp(const T _arr) : arr(_arr) { } bool operator()(const size_t a, const size_t b) const { return arr[a] < arr[b]; } const T arr; }; template class CommandIterationUpdate : public itk::Command { public: typedef CommandIterationUpdate Self; typedef itk::Command Superclass; typedef itk::SmartPointer Pointer; itkNewMacro( Self ); protected: CommandIterationUpdate() { }; public: void Execute(itk::Object *caller, const itk::EventObject & event) ITK_OVERRIDE { Execute( (const itk::Object *) caller, event); } void Execute(const itk::Object * object, const itk::EventObject & event) ITK_OVERRIDE { TFilter * filter = const_cast( dynamic_cast( object ) ); if( typeid( event ) != typeid( itk::IterationEvent ) ) { return; } unsigned int currentLevel = filter->GetCurrentLevel(); typename TFilter::TransformParametersAdaptorsContainerType adaptors = filter->GetTransformParametersAdaptorsPerLevel(); typedef itk::ConjugateGradientLineSearchOptimizerv4 OptimizerType; OptimizerType * optimizer = reinterpret_cast( filter->GetModifiableOptimizer() ); optimizer->SetNumberOfIterations( this->m_NumberOfIterations[currentLevel] ); optimizer->SetMinimumConvergenceValue( 1.e-7 ); optimizer->SetConvergenceWindowSize( 10 ); optimizer->SetLowerLimit( 0 ); optimizer->SetUpperLimit( 2 ); optimizer->SetEpsilon( 0.1 ); } void SetNumberOfIterations( std::vector iterations ) { this->m_NumberOfIterations = iterations; } private: std::vector m_NumberOfIterations; }; void ants_slice_poly_regularize( vnl_matrix A, unsigned int timedims, vnl_vector& solnx, double& interceptx, vnl_matrix param_values, unsigned int whichCol ) { typedef double RealType; typedef vnl_vector vVector; for ( unsigned int z = 0; z < timedims; z++ ) { RealType zz = static_cast( z + 1 ); A(z,0) = zz; for ( unsigned int lcol = 1; lcol < A.cols(); lcol++ ) { A( z, lcol ) = std::pow( zz, static_cast(lcol+1) ); } } for ( unsigned int lcol = 0; lcol < A.cols(); lcol++ ) { vVector acol = A.get_column( lcol ); RealType acolsd = ( acol - acol.mean() ).rms(); A.set_column( lcol, ( acol - acol.mean() ) / acolsd ); } vnl_svd svd( A ); vVector ob = param_values.get_column( whichCol ); vVector polyx = svd.solve( ob ); interceptx = param_values.get_column( whichCol ).mean(); for( unsigned int Acol = 0; Acol < A.cols(); Acol++ ) { interceptx -= A.get_column( Acol ).mean() * polyx( Acol ); } solnx = A * polyx + interceptx; } template int ants_slice_regularized_registration( itk::ants::CommandLineParser *parser ) { // We infer the number of stages by the number of transformations // specified by the user which should match the number of metrics. unsigned numberOfStages = 0; typedef float PixelType; typedef double RealType; typedef itk::Image FixedIOImageType; typedef itk::Image FixedImageType; typedef itk::Image MovingIOImageType; typedef itk::Image MovingImageType; typedef itk::Vector VectorIOType; typedef itk::Image DisplacementIOFieldType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef vnl_matrix vMatrix; typedef vnl_vector vVector; vMatrix param_values; typedef typename itk::ants::CommandLineParser ParserType; typedef typename ParserType::OptionType OptionType; typename OptionType::Pointer transformOption = parser->GetOption( "transform" ); if( transformOption && transformOption->GetNumberOfFunctions() ) { numberOfStages = transformOption->GetNumberOfFunctions(); } else { std::cerr << "No transformations are specified." << std::endl; return EXIT_FAILURE; } if ( numberOfStages != 1 ) { std::cerr << "Must specify one and only one stage." << std::endl; return EXIT_FAILURE; } typename OptionType::Pointer metricOption = parser->GetOption( "metric" ); if( !metricOption || metricOption->GetNumberOfFunctions() != numberOfStages ) { std::cerr << "The number of metrics specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } std::string whichInterpolator( "linear" ); typename itk::ants::CommandLineParser::OptionType::Pointer interpolationOption = parser->GetOption( "interpolation" ); if( interpolationOption && interpolationOption->GetNumberOfFunctions() ) { whichInterpolator = interpolationOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( whichInterpolator ); } typedef MovingImageType ImageType; typename ImageType::SpacingType cache_spacing_for_smoothing_sigmas(itk::NumericTraits::ZeroValue()); unsigned int VImageDimension = ImageDimension - 1; #include "make_interpolator_snip.tmpl" typename OptionType::Pointer iterationsOption = parser->GetOption( "iterations" ); if( !iterationsOption || iterationsOption->GetNumberOfFunctions() != numberOfStages ) { std::cerr << "The number of iteration sets specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } typename OptionType::Pointer shrinkFactorsOption = parser->GetOption( "shrinkFactors" ); if( !shrinkFactorsOption || shrinkFactorsOption->GetNumberOfFunctions() != numberOfStages ) { std::cerr << "The number of shrinkFactor sets specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } typename OptionType::Pointer smoothingSigmasOption = parser->GetOption( "smoothingSigmas" ); if( !smoothingSigmasOption || smoothingSigmasOption->GetNumberOfFunctions() != numberOfStages ) { std::cerr << "The number of smoothing sigma sets specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } typename OptionType::Pointer outputOption = parser->GetOption( "output" ); if( !outputOption ) { std::cerr << "Output option not specified." << std::endl; return EXIT_FAILURE; } std::string outputPrefix = outputOption->GetFunction( 0 )->GetParameter( 0 ); if( outputPrefix.length() < 3 ) { outputPrefix = outputOption->GetFunction( 0 )->GetName(); } std::string maskfn = std::string(""); typename OptionType::Pointer maskOption = parser->GetOption( "mask" ); if( maskOption->GetNumberOfFunctions() > 0 ) { maskfn = maskOption->GetFunction( 0 )->GetName(); } bool doEstimateLearningRateOnce(true); itk::TimeProbe totalTimer; totalTimer.Start(); typedef itk::ImageRegistrationMethodv4 TranslationRegistrationType; // We iterate backwards because the command line options are stored as a stack (first in last out) typedef typename TXType::Pointer SingleTransformItemType; std::vector transformList; std::vector transformUList; std::vector fixedSliceList; std::vector movingSliceList; typename FixedIOImageType::Pointer maskImage; typedef itk::Image< unsigned char, ImageDimension-1 > ImageMaskType; typename ImageMaskType::Pointer mask_time_slice = ITK_NULLPTR; if ( maskfn.length() > 3 ) ReadImage( maskImage, maskfn.c_str() ); for( int currentStage = numberOfStages - 1; currentStage >= 0; currentStage-- ) { std::stringstream currentStageString; currentStageString << currentStage; // Get the fixed and moving images std::string fixedImageFileName = metricOption->GetFunction( currentStage )->GetParameter( 0 ); std::string movingImageFileName = metricOption->GetFunction( currentStage )->GetParameter( 1 ); typename FixedImageType::Pointer fixed_time_slice = ITK_NULLPTR; typename FixedImageType::Pointer moving_time_slice = ITK_NULLPTR; typename FixedIOImageType::Pointer fixedImage; ReadImage( fixedImage, fixedImageFileName.c_str() ); unsigned int timedims = fixedImage->GetLargestPossibleRegion().GetSize()[ImageDimension-1]; unsigned int nparams = TXType::New()->GetNumberOfParameters(); param_values.set_size(timedims, nparams ); param_values.fill(0); typename MovingIOImageType::Pointer movingImage; ReadImage( movingImage, movingImageFileName.c_str() ); unsigned int moving_timedims = movingImage->GetLargestPossibleRegion().GetSize()[ImageDimension-1]; if ( timedims != moving_timedims ) { std::cerr << "We require that the n^th dimensions of the fixed and moving image are equal" << std::endl; return EXIT_FAILURE; } typename FixedIOImageType::Pointer outputImage; ReadImage( outputImage, fixedImageFileName.c_str() ); outputImage->FillBuffer( 0 ); // Get the number of iterations and use that information to specify the number of levels std::vector iterations = parser->ConvertVector( iterationsOption->GetFunction( currentStage )->GetName() ); unsigned int numberOfLevels = iterations.size(); // Get shrink factors std::vector factors = parser->ConvertVector( shrinkFactorsOption->GetFunction( currentStage )->GetName() ); typename TranslationRegistrationType::ShrinkFactorsArrayType shrinkFactorsPerLevel; shrinkFactorsPerLevel.SetSize( factors.size() ); if( factors.size() != numberOfLevels ) { std::cerr << "ERROR: The number of shrink factors does not match the number of levels." << std::endl; return EXIT_FAILURE; } else { for( unsigned int n = 0; n < shrinkFactorsPerLevel.Size(); n++ ) { shrinkFactorsPerLevel[n] = factors[n]; } } // Get smoothing sigmas std::vector sigmas = parser->ConvertVector( smoothingSigmasOption->GetFunction( currentStage )->GetName() ); typename TranslationRegistrationType::SmoothingSigmasArrayType smoothingSigmasPerLevel; smoothingSigmasPerLevel.SetSize( sigmas.size() ); if( sigmas.size() != numberOfLevels ) { std::cerr << "ERROR: The number of smoothing sigmas does not match the number of levels." << std::endl; return EXIT_FAILURE; } else { for( unsigned int n = 0; n < smoothingSigmasPerLevel.Size(); n++ ) { smoothingSigmasPerLevel[n] = sigmas[n]; } } bool verbose = false; if( parser->Convert( parser->GetOption( 'v' )->GetFunction()->GetName() ) ) { verbose = true; } std::vector polydegree; itk::ants::CommandLineParser::OptionType::Pointer polyOption = parser->GetOption( "polydegree" ); if( polyOption && polyOption->GetNumberOfFunctions() ) { polydegree = parser->ConvertVector( polyOption->GetFunction( 0 )->GetName() ); } if ( ( polydegree.size() != nparams ) ) { polydegree.resize( nparams, polydegree[ 0 ] ); } for ( unsigned int pind = 0; pind < polydegree.size(); pind++ ) { if ( polydegree[pind] > (timedims-2) ) polydegree[pind] = timedims-2; } // the fixed image slice is a reference image in 2D while the moving is a 2D slice image // loop over every time point and register image_i_moving to image_i_fixed // // Set up the image metric and scales estimator for( unsigned int timedim = 0; timedim < timedims; timedim++ ) { typedef itk::IdentityTransform IdentityTransformType; typename IdentityTransformType::Pointer identityTransform = IdentityTransformType::New(); typedef itk::ExtractImageFilter ExtractFilterType; typename FixedIOImageType::RegionType extractRegion = movingImage->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension-1, 0); extractRegion.SetIndex(ImageDimension-1, timedim ); typename ExtractFilterType::Pointer extractFilterF = ExtractFilterType::New(); typename ExtractFilterType::Pointer extractFilterM = ExtractFilterType::New(); extractFilterF->SetInput( fixedImage ); extractFilterM->SetInput( movingImage ); extractFilterF->SetDirectionCollapseToSubmatrix(); extractFilterM->SetDirectionCollapseToSubmatrix(); bool toidentity = false; if ( toidentity ) extractFilterF->SetDirectionCollapseToIdentity(); if ( toidentity ) extractFilterM->SetDirectionCollapseToIdentity(); extractFilterF->SetExtractionRegion( extractRegion ); extractFilterM->SetExtractionRegion( extractRegion ); extractFilterF->Update(); extractFilterM->Update(); fixed_time_slice = extractFilterF->GetOutput(); moving_time_slice = extractFilterM->GetOutput(); fixedSliceList.push_back( fixed_time_slice ); movingSliceList.push_back( moving_time_slice ); if ( maskfn.length() > 3 ) { typedef itk::ExtractImageFilter ExtractFilterTypeX; typename ExtractFilterTypeX::Pointer extractFilterX = ExtractFilterTypeX::New(); extractFilterX->SetInput( maskImage ); extractFilterX->SetDirectionCollapseToSubmatrix(); if ( toidentity ) extractFilterX->SetDirectionCollapseToIdentity(); extractFilterX->SetExtractionRegion( extractRegion ); extractFilterX->Update(); mask_time_slice = extractFilterX->GetOutput(); } // set up initial transform parameters typename TXType::Pointer translationTransform = TXType::New(); translationTransform->SetIdentity(); transformList.push_back( translationTransform ); // set up update transform parameters typename TXType::Pointer translationTransformU = TXType::New(); translationTransformU->SetIdentity(); transformUList.push_back( translationTransformU ); } // implement a gradient descent on the polynomial parameters by looping over registration results typedef itk::ImageToImageMetricv4 MetricType; typename MetricType::Pointer metric; unsigned int maxloop = 2; for ( unsigned int loop = 0; loop < maxloop; loop++ ) { RealType metricval = 0; bool skipThisTimePoint = false; for( unsigned int timedim = 0; timedim < timedims; timedim++ ) { typename FixedImageType::Pointer preprocessFixedImage = sliceRegularizedPreprocessImage( fixedSliceList[timedim], 0, 1, 0.005, 0.995, ITK_NULLPTR ); typename FixedImageType::Pointer preprocessMovingImage = sliceRegularizedPreprocessImage( movingSliceList[timedim], 0, 1, 0.005, 0.995, preprocessFixedImage ); if ( preprocessFixedImage.IsNull() || preprocessMovingImage.IsNull() ) { preprocessFixedImage = fixedSliceList[timedim]; preprocessMovingImage = movingSliceList[timedim]; skipThisTimePoint = true; } std::string whichMetric = metricOption->GetFunction( currentStage )->GetName(); ConvertToLowerCase( whichMetric ); float samplingPercentage = 1.0; if( metricOption->GetFunction( 0 )->GetNumberOfParameters() > 5 ) { samplingPercentage = parser->Convert( metricOption->GetFunction( currentStage )->GetParameter( 5 ) ); } std::string samplingStrategy = ""; if( metricOption->GetFunction( 0 )->GetNumberOfParameters() > 4 ) { samplingStrategy = metricOption->GetFunction( currentStage )->GetParameter( 4 ); } ConvertToLowerCase( samplingStrategy ); typename TranslationRegistrationType::MetricSamplingStrategyType metricSamplingStrategy = TranslationRegistrationType::NONE; if( std::strcmp( samplingStrategy.c_str(), "random" ) == 0 ) { metricSamplingStrategy = TranslationRegistrationType::RANDOM; } if( std::strcmp( samplingStrategy.c_str(), "regular" ) == 0 ) { metricSamplingStrategy = TranslationRegistrationType::REGULAR; } if( std::strcmp( whichMetric.c_str(), "cc" ) == 0 ) { unsigned int radiusOption = parser->Convert( metricOption->GetFunction( currentStage )->GetParameter( 3 ) ); typedef itk::ANTSNeighborhoodCorrelationImageToImageMetricv4 CorrelationMetricType; typename CorrelationMetricType::Pointer correlationMetric = CorrelationMetricType::New(); typename CorrelationMetricType::RadiusType radius; radius.Fill( radiusOption ); correlationMetric->SetRadius( radius ); correlationMetric->SetUseMovingImageGradientFilter( false ); correlationMetric->SetUseFixedImageGradientFilter( false ); metric = correlationMetric; } else if( std::strcmp( whichMetric.c_str(), "mi" ) == 0 ) { unsigned int binOption = parser->Convert( metricOption->GetFunction( currentStage )->GetParameter( 3 ) ); typedef itk::MattesMutualInformationImageToImageMetricv4 MutualInformationMetricType; typename MutualInformationMetricType::Pointer mutualInformationMetric = MutualInformationMetricType::New(); mutualInformationMetric = mutualInformationMetric; mutualInformationMetric->SetNumberOfHistogramBins( binOption ); mutualInformationMetric->SetUseMovingImageGradientFilter( false ); mutualInformationMetric->SetUseFixedImageGradientFilter( false ); metric = mutualInformationMetric; } else if( std::strcmp( whichMetric.c_str(), "meansquares" ) == 0 ) { typedef itk::MeanSquaresImageToImageMetricv4 MSQMetricType; typename MSQMetricType::Pointer demonsMetric = MSQMetricType::New(); demonsMetric = demonsMetric; metric = demonsMetric; } else if( std::strcmp( whichMetric.c_str(), "gc" ) == 0 ) { typedef itk::CorrelationImageToImageMetricv4 corrMetricType; typename corrMetricType::Pointer corrMetric = corrMetricType::New(); metric = corrMetric; } else { std::cerr << "ERROR: Unrecognized image metric: " << whichMetric << std::endl; return EXIT_FAILURE; } metric->SetVirtualDomainFromImage( fixedSliceList[timedim] ); if ( maskfn.length() > 3 ) { typedef itk::ImageMaskSpatialObject spMaskType; typename spMaskType::Pointer spatialObjectMask = spMaskType::New(); spatialObjectMask->SetImage( mask_time_slice ); metric->SetFixedImageMask( spatialObjectMask ); if ( ( verbose ) && ( loop == 0 ) && ( timedim == 0 ) ) std::cout << " setting mask " << maskfn << std::endl; } typedef itk::RegistrationParameterScalesFromPhysicalShift ScalesEstimatorType; typename ScalesEstimatorType::Pointer scalesEstimator = ScalesEstimatorType::New(); scalesEstimator->SetMetric( metric ); scalesEstimator->SetTransformForward( true ); float learningRate = parser->Convert( transformOption->GetFunction( currentStage )->GetParameter( 0 ) ); typedef itk::ConjugateGradientLineSearchOptimizerv4 OptimizerType; typename OptimizerType::Pointer optimizer = OptimizerType::New(); optimizer->SetNumberOfIterations( iterations[0] ); optimizer->SetMinimumConvergenceValue( 1.e-7 ); optimizer->SetConvergenceWindowSize( 8 ); optimizer->SetLowerLimit( 0 ); optimizer->SetUpperLimit( 2 ); optimizer->SetEpsilon( 0.1 ); optimizer->SetScalesEstimator( scalesEstimator ); optimizer->SetMaximumStepSizeInPhysicalUnits( learningRate ); optimizer->SetDoEstimateLearningRateOnce( doEstimateLearningRateOnce ); optimizer->SetDoEstimateLearningRateAtEachIteration( !doEstimateLearningRateOnce ); // Set up the image registration methods along with the transforms std::string whichTransform = transformOption->GetFunction( currentStage )->GetName(); ConvertToLowerCase( whichTransform ); typename TranslationRegistrationType::Pointer translationRegistration = TranslationRegistrationType::New(); if ( ( std::strcmp( whichTransform.c_str(), "translation" ) == 0 ) | ( std::strcmp( whichTransform.c_str(), "rigid" ) == 0 ) | ( std::strcmp( whichTransform.c_str(), "similarity" ) == 0 ) ) { transformList[timedim]->GetNumberOfParameters(); metric->SetFixedImage( preprocessFixedImage ); metric->SetVirtualDomainFromImage( preprocessFixedImage ); metric->SetMovingImage( preprocessMovingImage ); metric->SetMovingTransform( transformList[timedim] ); typename ScalesEstimatorType::ScalesType scales( transformList[timedim]->GetNumberOfParameters() ); typename MetricType::ParametersType newparams( transformList[timedim]->GetParameters() ); metric->SetParameters( newparams ); metric->Initialize(); scalesEstimator->SetMetric(metric); scalesEstimator->EstimateScales(scales); optimizer->SetScales(scales); translationRegistration->SetFixedImage( preprocessFixedImage ); translationRegistration->SetMovingImage( preprocessMovingImage ); translationRegistration->SetNumberOfLevels( numberOfLevels ); translationRegistration->SetShrinkFactorsPerLevel( shrinkFactorsPerLevel ); translationRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); translationRegistration->SetMetricSamplingStrategy( metricSamplingStrategy ); translationRegistration->SetMetricSamplingPercentage( samplingPercentage ); translationRegistration->SetMetric( metric ); translationRegistration->SetOptimizer( optimizer ); typedef CommandIterationUpdate TranslationCommandType; typename TranslationCommandType::Pointer translationObserver = TranslationCommandType::New(); translationObserver->SetNumberOfIterations( iterations ); translationRegistration->AddObserver( itk::IterationEvent(), translationObserver ); if ( ! skipThisTimePoint ) try { translationRegistration->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } } else { std::cerr << "ERROR: Unrecognized transform option - " << whichTransform << std::endl; return EXIT_FAILURE; } transformUList[timedim] = translationRegistration->GetModifiableTransform(); metricval += metric->GetValue(); } for ( unsigned int i = 0; i < transformList.size(); i++) { typename TXType::ParametersType pu = transformUList[i]->GetParameters(); for ( unsigned int kk = 0; kk < pu.Size(); kk++ ) param_values( i, kk ) = pu[ kk ]; } // project updated solution back to polynomial space vVector solnx; RealType interceptx = 0; if ( polydegree[ 0 ] > 0 ) { // set up polynomial system of equations vMatrix A( timedims, polydegree[ 0 ], 0.0 ); ants_slice_poly_regularize( A, timedims, solnx, interceptx, param_values, 0 ); } // now y-dimension vVector solny; RealType intercepty = 0; if ( polydegree[ 1 ] > 0 ) { vMatrix A( timedims, polydegree[ 1 ], 0.0 ); ants_slice_poly_regularize( A, timedims, solny, intercepty, param_values, 1 ); } // now rotation vVector solnr; RealType interceptr = 0; if ( nparams >= 3 ) if ( polydegree[ 2 ] > 0 ) { vMatrix A( timedims, polydegree[ 2 ], 0.0 ); ants_slice_poly_regularize( A, timedims, solnr, interceptr, param_values, 2 ); } // now scaling-dimension vVector solns; RealType intercepts = 0; if ( nparams >= 4 ) if ( polydegree[ 3 ] > 0 ) { vMatrix A( timedims, polydegree[ 3 ], 0.0 ); ants_slice_poly_regularize( A, timedims, solns, intercepts, param_values, 3 ); } // now look at delta and do projection if ( ( solnx.size() != transformList.size() ) && ( polydegree[ 0 ] > 0 ) ) std::cerr << "solnx.size() != transformList.size()" << std::endl; if ( ( solny.size() != transformList.size() ) && ( polydegree[ 1 ] > 0 ) ) std::cerr << "solny.size() != transformList.size()" << std::endl; RealType err = 0; for ( unsigned int i = 0; i < transformList.size(); i++) { /** FIXME - this should be vectorized: DRY */ typename TXType::ParametersType pOld = transformList[i]->GetParameters(); typename TXType::ParametersType p = transformList[i]->GetParameters(); if ( polydegree[ 0 ] > 0 ) p[ 0 ] = solnx[ i ]; if ( polydegree[ 1 ] > 0 ) p[ 1 ] = solny[ i ]; param_values( i, 0 ) = p[ 0 ]; param_values( i, 1 ) = p[ 1 ]; if ( nparams >= 3 ) { if ( polydegree[ 2 ] > 0 ) p[ 2 ] = solnr[i]; param_values( i, 2 ) = p[ 2 ]; } if ( nparams >= 4 ) { if ( polydegree[ 3 ] > 0 ) p[ 3 ] = solns[i]; param_values(i,3) = p[3]; } transformList[i]->SetParameters( p ); transformUList[i]->SetParameters( p ); err += ( p - pOld ).rms(); } err = err / static_cast< double >( transformList.size() ); if ( verbose ) { std::cout << "Loop" << loop << " polyerr: " << err << " image-metric " << metricval << std::endl; } // transformList = transformUList; for ( unsigned int i = 0; i < transformList.size(); i++) { typename TXType::ParametersType p = transformList[i]->GetParameters(); if ( polydegree[0] == 0 ) param_values(i,0) = p[0]; if ( polydegree[1] == 0 ) param_values(i,1) = p[1]; if ( nparams >= 3 ) if ( polydegree[2] == 0 ) param_values(i,2) = p[2]; if ( nparams >= 4 ) if ( polydegree[3] == 0 ) param_values(i,3) = p[3]; } }// done with optimization, now move on to writing data ... closes for ( unsigned int loop = 0; loop < maxloop; loop++ ) // write polynomial predicted data { std::vector ColumnHeaders; std::string colname; colname = std::string("Tx"); ColumnHeaders.push_back( colname ); colname = std::string("Ty"); ColumnHeaders.push_back( colname ); if ( nparams >= 3 ) { colname = std::string("Tr"); ColumnHeaders.push_back( colname ); } if ( nparams >= 4 ) { colname = std::string("Ts"); ColumnHeaders.push_back( colname ); } typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); std::string fnmp; fnmp = outputPrefix + std::string("TxTy_poly.csv"); writer->SetFileName( fnmp.c_str() ); writer->SetColumnHeaders(ColumnHeaders); writer->SetInput( ¶m_values ); writer->Write(); } /** Handle all output: images and displacement fields */ typedef itk::IdentityTransform IdentityIOTransformType; typename IdentityIOTransformType::Pointer identityIOTransform = IdentityIOTransformType::New(); typedef typename itk::TransformToDisplacementFieldFilter ConverterType; typename ConverterType::Pointer idconverter = ConverterType::New(); idconverter->SetOutputOrigin( outputImage->GetOrigin() ); idconverter->SetOutputStartIndex( outputImage->GetBufferedRegion().GetIndex() ); idconverter->SetSize( outputImage->GetBufferedRegion().GetSize() ); idconverter->SetOutputSpacing( outputImage->GetSpacing() ); idconverter->SetOutputDirection( outputImage->GetDirection() ); idconverter->SetTransform( identityIOTransform ); idconverter->Update(); typename DisplacementIOFieldType::Pointer displacementout = idconverter->GetOutput(); typename DisplacementIOFieldType::Pointer displacementinv = DisplacementIOFieldType::New(); displacementinv->CopyInformation( displacementout ); displacementinv->SetRegions( displacementout->GetRequestedRegion() ); displacementinv->Allocate(); typename DisplacementIOFieldType::IndexType dind; dind.Fill( 0 ); displacementinv->FillBuffer( displacementout->GetPixel( dind ) ); for( unsigned int timedim = 0; timedim < timedims; timedim++ ) { typedef typename itk::TransformToDisplacementFieldFilter _ConverterType; typename _ConverterType::Pointer converter = _ConverterType::New(); converter->SetOutputOrigin( fixedSliceList[timedim]->GetOrigin() ); converter->SetOutputStartIndex( fixedSliceList[timedim]->GetBufferedRegion().GetIndex() ); converter->SetSize( fixedSliceList[timedim]->GetBufferedRegion().GetSize() ); converter->SetOutputSpacing( fixedSliceList[timedim]->GetSpacing() ); converter->SetOutputDirection( fixedSliceList[timedim]->GetDirection() ); converter->SetTransform( transformList[timedim] ); converter->Update(); // resample the moving image and then put it in its place interpolator->SetInputImage( movingSliceList[timedim] ); typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetTransform( transformList[timedim] ); resampler->SetInterpolator( interpolator ); resampler->SetInput( movingSliceList[timedim] ); resampler->SetOutputParametersFromImage( fixedSliceList[timedim] ); resampler->SetDefaultPixelValue( 0 ); resampler->Update(); /** Here, we put the resampled 2D image into the 3D volume */ typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter2( resampler->GetOutput(), resampler->GetOutput()->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { typename FixedImageType::PixelType fval = vfIter2.Get(); VectorType vec = converter->GetOutput()->GetPixel( vfIter2.GetIndex() ); VectorIOType vecout; vecout.Fill( 0 ); typename MovingIOImageType::IndexType ind; for( unsigned int xx = 0; xx < ImageDimension; xx++ ) { ind[xx] = vfIter2.GetIndex()[xx]; vecout[xx] = vec[xx]; } unsigned int tdim = timedim; if( tdim > ( timedims - 1 ) ) { tdim = timedims - 1; } ind[ImageDimension-1] = tdim; outputImage->SetPixel(ind, fval); displacementout->SetPixel( ind, vecout ); } } // now apply to the inverse map unsigned int timedimX = 0; for( timedimX = 0; timedimX < timedims; timedimX++ ) { typedef typename itk::TransformToDisplacementFieldFilter _ConverterType; typename _ConverterType::Pointer converter = _ConverterType::New(); converter->SetOutputOrigin( movingSliceList[ timedimX ]->GetOrigin() ); converter->SetOutputStartIndex( movingSliceList[ timedimX ]->GetBufferedRegion().GetIndex() ); converter->SetSize( movingSliceList[ timedimX ]->GetBufferedRegion().GetSize() ); converter->SetOutputSpacing( movingSliceList[ timedimX ]->GetSpacing() ); converter->SetOutputDirection( movingSliceList[ timedimX ]->GetDirection() ); typename TXType::Pointer invtx = TXType::New(); invtx->SetIdentity(); transformList[ timedimX ]->GetInverse( invtx ); converter->SetTransform( invtx ); converter->Update(); // resample the moving image and then put it in its place typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetTransform( invtx ); interpolator->SetInputImage( fixedSliceList[ timedimX ] ); resampler->SetInterpolator( interpolator ); resampler->SetInput( fixedSliceList[ timedimX ] ); resampler->SetOutputParametersFromImage( movingSliceList[ timedimX ] ); resampler->SetDefaultPixelValue( 0 ); resampler->Update(); /** Here, we put the resampled 2D image into the 3D volume */ typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter2( resampler->GetOutput(), resampler->GetOutput()->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { VectorType vec = converter->GetOutput()->GetPixel( vfIter2.GetIndex() ); VectorIOType vecout; vecout.Fill( 0 ); typename MovingIOImageType::IndexType ind; for( unsigned int xx = 0; xx < ImageDimension; xx++ ) { ind[xx] = vfIter2.GetIndex()[xx]; vecout[xx] = vec[xx]; } unsigned int tdim = timedimX; if( tdim > ( timedims - 1 ) ) { tdim = timedims - 1; } ind[ImageDimension-1] = tdim; displacementinv->SetPixel( ind, vecout ); } } if ( outputOption && outputOption->GetFunction( 0 )->GetNumberOfParameters() > 1 && currentStage == 0 ) { std::string fileName = outputOption->GetFunction( 0 )->GetParameter( 1 ); if( outputPrefix.length() < 3 ) { outputPrefix = outputOption->GetFunction( 0 )->GetName(); } WriteImage( outputImage, fileName.c_str() ); } if( outputOption && outputOption->GetFunction( 0 )->GetNumberOfParameters() > 2 && outputImage && currentStage == 0 ) { std::string fileName = outputOption->GetFunction( 0 )->GetParameter( 2 ); } { std::string dispfn = outputPrefix + std::string("Warp.nii.gz"); typedef itk::ImageFileWriter DisplacementFieldWriterType; typename DisplacementFieldWriterType::Pointer displacementFieldWriter = DisplacementFieldWriterType::New(); displacementFieldWriter->SetInput( displacementout ); displacementFieldWriter->SetFileName( dispfn.c_str() ); displacementFieldWriter->Update(); } { std::string dispfn = outputPrefix + std::string("InverseWarp.nii.gz"); typedef itk::ImageFileWriter DisplacementFieldWriterType; typename DisplacementFieldWriterType::Pointer displacementFieldWriter = DisplacementFieldWriterType::New(); displacementFieldWriter->SetInput( displacementinv ); displacementFieldWriter->SetFileName( dispfn.c_str() ); displacementFieldWriter->Update(); } } totalTimer.Stop(); // std::cout << std::endl << "Total elapsed time: " << totalTimer.GetMean() << std::endl; return EXIT_SUCCESS; } void antsSliceRegularizedRegistrationInitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "Four image metrics are available--- " ) + std::string( "GC : global correlation, CC: ANTS neighborhood cross correlation, MI: Mutual information, and " ) + std::string( "MeanSquares: mean-squares intensity difference. " ) + std::string( "Note that the metricWeight is currently not used. " ) + std::string( "Rather, it is a temporary place holder until multivariate metrics " ) + std::string( "are available for a single stage." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "metric" ); option->SetShortName( 'm' ); option->SetUsageOption( 0, "CC[fixedImage,movingImage,metricWeight,radius,,]" ); option->SetUsageOption( 1, "MI[fixedImage,movingImage,metricWeight,numberOfBins,,]" ); option->SetUsageOption( 2, "MeanSquares[fixedImage,movingImage,metricWeight,radius,,]" ); option->SetUsageOption( 3, "GC[fixedImage,movingImage,metricWeight,radius,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = "Fixed image mask to limit voxels considered by the metric."; OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "mask-in-fixed-image-space.nii.gz" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Several interpolation options are available in ITK. " ) + std::string( "These have all been made available." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "interpolation" ); option->SetShortName( 'n' ); option->SetUsageOption( 0, "Linear" ); option->SetUsageOption( 1, "NearestNeighbor" ); option->SetUsageOption( 2, "MultiLabel[,]" ); option->SetUsageOption( 3, "Gaussian[,]" ); option->SetUsageOption( 4, "BSpline[]" ); option->SetUsageOption( 5, "CosineWindowedSinc" ); option->SetUsageOption( 6, "WelchWindowedSinc" ); option->SetUsageOption( 7, "HammingWindowedSinc" ); option->SetUsageOption( 8, "LanczosWindowedSinc" ); option->SetUsageOption( 9, "GenericLabel[]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Several transform options are available. The gradientStep or" ) + std::string( "learningRate characterizes the gradient descent optimization and is scaled appropriately " ) + std::string( "for each transform using the shift scales estimator. Subsequent parameters are " ) + std::string( "transform-specific and can be determined from the usage. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "transform" ); option->SetShortName( 't' ); option->SetUsageOption( 0, "Translation[gradientStep]" ); option->SetUsageOption( 1, "Rigid[gradientStep]" ); option->SetUsageOption( 2, "Similarity[gradientStep]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the number of iterations at each level." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "iterations" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "MxNx0..." ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the amount of smoothing at each level." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "smoothingSigmas" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "MxNx0..." ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the shrink factor for the virtual domain (typically the fixed image) at each level." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "shrinkFactors" ); option->SetShortName( 'f' ); option->SetUsageOption( 0, "MxNx0..." ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Specify the output transform prefix (output format is .nii.gz )." ) + std::string( "Optionally, one can choose to warp the moving image to the fixed space and, if the " ) + std::string( "inverse transform exists, one can also output the warped fixed image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "[outputTransformPrefix,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetShortName( 'h' ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "verbose option" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "verbose" ); option->SetShortName( 'v' ); option->SetDescription( description ); option->AddFunction( std::string( "0" ) ); parser->AddOption( option ); } { std::string description = std::string( "degree of polynomial - up to zDimension-2. ") + std::string( "Controls the polynomial degree. 0 means no regularization. This may be a vector ") + std::string( "denoted by 2x2x1 for a 3-parameter transform ( e.g. rigid ). This would ") + std::string( "regularize the translation by 2nd degree polynomial and the rotation by a ") + std::string( "linear function."); OptionType::Pointer option = OptionType::New(); option->SetLongName( "polydegree" ); option->SetShortName( 'p' ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsSliceRegularizedRegistration( std::vector args, std::ostream * /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsSliceRegularizedRegistration" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "antsSliceRegularizedRegistration ") + std::string("This program is a user-level application for slice-by-slice translation registration. " ) + std::string( "Results are regularized in z using polynomial regression. The program is targeted at spinal cord MRI. ") + std::string( "Only one stage is supported where a stage consists of a transform; an image metric; " ) + std::string( "and iterations, shrink factors, and smoothing sigmas for each level. " ) + std::string( "Specialized for 3D data: fixed image is 3D, moving image is 3D. ") + std::string( "Registration is performed slice-by-slice then regularized in z. ") + std::string(" The parameter -p controls the polynomial degree. -p 0 means no regularization.") + std::string( "Implemented by B. Avants and conceived by Julien Cohen-Adad.\n") + std::string("Outputs: \n\n") + std::string(" OutputPrefixTxTy_poly.csv: polynomial fit to Tx & Ty \n") + std::string(" OutputPrefix.nii.gz: transformed image \n") + std::string("Example call: \n\n") + std::string(" antsSliceRegularizedRegistration -p 4 --output [OutputPrefix,OutputPrefix.nii.gz] ") + std::string("--transform Translation[0.1] --metric MI[ fixed.nii.gz, moving.nii.gz , 1 , 16 , Regular , 0.2 ] ") + std::string("--iterations 20 --shrinkFactors 1 --smoothingSigmas 0 \n\n"); parser->SetCommandDescription( commandDescription ); antsSliceRegularizedRegistrationInitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc < 2 || parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } else if( parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Get dimensionality unsigned int dimension = 3; typedef double RealType; typedef itk::TranslationTransform TranslationTransformType; typedef itk::Euler2DTransform EulerTransformType; typedef itk::Similarity2DTransform SimilarityTransformType; itk::ants::CommandLineParser::OptionType::Pointer transformOption = parser->GetOption( "transform" ); if( ! (transformOption && transformOption->GetNumberOfFunctions() ) ) { std::cerr << "No transformations are specified." << std::endl; return EXIT_FAILURE; } std::string whichTransform = transformOption->GetFunction( 0 )->GetName(); ConvertToLowerCase( whichTransform ); if( std::strcmp( whichTransform.c_str(), "translation" ) == 0 ) { switch( dimension ) { case 3: { return ants_slice_regularized_registration<3,TranslationTransformType>( parser ); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } else if( std::strcmp( whichTransform.c_str(), "rigid" ) == 0 ) { switch( dimension ) { case 3: { return ants_slice_regularized_registration<3,EulerTransformType>( parser ); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } else if( std::strcmp( whichTransform.c_str(), "similarity" ) == 0 ) { switch( dimension ) { case 3: { return ants_slice_regularized_registration<3,SimilarityTransformType>( parser ); } break; default: std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsSurf.cxx000066400000000000000000001154461311104306400165730ustar00rootroot00000000000000#include "antsCommandLineParser.h" #include "antsUtilities.h" #include "ReadWriteData.h" #include "itkAffineTransform.h" #include "itkAntiAliasBinaryImageFilter.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "vtkSTLReader.h" #include "vtkSTLWriter.h" #include "vtkPLYReader.h" #include "vtkPLYWriter.h" #include "itkImageToVTKImageFilter.h" #include "vtkActor.h" #include "vtkCallbackCommand.h" #include "vtkExtractEdges.h" #include "vtkGraphicsFactory.h" #include "vtkImageData.h" #include "vtkImageStencil.h" #include "vtkLookupTable.h" #include "vtkMarchingCubes.h" #include "vtkMetaImageWriter.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkPolyDataConnectivityFilter.h" #include "vtkPolyDataMapper.h" #include "vtkPolyDataNormals.h" #include "vtkProperty.h" #include "vtkSmartPointer.h" #include "vtkTriangleFilter.h" #include "vtkUnsignedCharArray.h" #include "vtkWindowedSincPolyDataFilter.h" #include "vtkPolyDataWriter.h" #include "vtkPolyDataReader.h" #include "vtkPolyDataToImageStencil.h" #include "vtkPNGWriter.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkScalarBarActor.h" #include "vtkSmoothPolyDataFilter.h" #include "vtkTextProperty.h" #include "vtkWindowToImageFilter.h" #include "vnl/vnl_math.h" #include #include namespace ants { float CalculateGenus( vtkPolyData *mesh, bool verbose ) { vtkSmartPointer extractEdges = vtkSmartPointer::New(); extractEdges->SetInputData( mesh ); extractEdges->Update(); float numberOfEdges = static_cast( extractEdges->GetOutput()->GetNumberOfLines() ); float numberOfVertices = static_cast( mesh->GetNumberOfPoints() ); float numberOfFaces = static_cast( mesh->GetNumberOfPolys() ); float genus = 0.5 * ( 2.0 - numberOfVertices + numberOfEdges - numberOfFaces ); if( verbose ) { std::cout << "Genus = " << genus << std::endl; std::cout << " number of vertices = " << numberOfVertices << std::endl; std::cout << " number of edges = " << numberOfEdges << std::endl; std::cout << " number of faces = " << numberOfFaces << std::endl; } return genus; } void Display( vtkPolyData *vtkMesh, const std::vector rotationAngleInDegrees, const std::vector backgroundColor, const std::string screenCaptureFileName, const bool renderScalarBar = false, vtkLookupTable *scalarBarLookupTable = NULL, const std::string scalarBarTitle = std::string( "" ), unsigned int scalarBarNumberOfLabels = 5, unsigned int scalarBarWidthInPixels = 0, unsigned int scalarBarHeightInPixels = 0 ) { vtkSmartPointer graphicsFactory = vtkSmartPointer::New(); graphicsFactory->SetOffScreenOnlyMode( false ); graphicsFactory->SetUseMesaClasses( 1 ); vtkSmartPointer mapper = vtkSmartPointer::New(); mapper->SetInputData( vtkMesh ); mapper->ScalarVisibilityOn(); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper( mapper ); actor->GetProperty()->SetInterpolationToFlat(); actor->GetProperty()->ShadingOff(); actor->GetProperty()->SetSpecular( 0.0 ); actor->GetProperty()->SetSpecularPower( 0 ); actor->RotateX( rotationAngleInDegrees[0] ); actor->RotateY( rotationAngleInDegrees[1] ); actor->RotateZ( rotationAngleInDegrees[2] ); vtkSmartPointer renderer = vtkSmartPointer::New(); renderer->SetBackground( backgroundColor[0] / 255.0, backgroundColor[1] / 255.0, backgroundColor[2] / 255.0 ); vtkSmartPointer renderWindow = vtkSmartPointer::New(); renderWindow->AddRenderer( renderer ); vtkSmartPointer callback = vtkSmartPointer::New(); renderer->AddObserver( vtkCommand::KeyPressEvent, callback ); vtkSmartPointer renderWindowInteractor = vtkSmartPointer::New(); renderWindowInteractor->SetRenderWindow( renderWindow ); renderer->AddActor( actor ); if( renderScalarBar ) { vtkSmartPointer scalarBar = vtkSmartPointer::New(); scalarBar->SetLookupTable( scalarBarLookupTable ); scalarBar->SetTitle( scalarBarTitle.c_str() ); scalarBar->SetMaximumNumberOfColors( 256 ); scalarBar->SetNumberOfLabels( scalarBarNumberOfLabels ); scalarBar->SetLabelFormat( "%.2g" ); if( scalarBarWidthInPixels > 0 && scalarBarHeightInPixels > 0 ) { if( scalarBarWidthInPixels > scalarBarHeightInPixels ) { scalarBar->SetOrientationToHorizontal(); } else { scalarBar->SetOrientationToVertical(); } scalarBar->SetMaximumWidthInPixels( scalarBarWidthInPixels ); scalarBar->SetMaximumHeightInPixels( scalarBarHeightInPixels ); } vtkSmartPointer titleTextProperty = vtkSmartPointer::New(); titleTextProperty->ItalicOff(); titleTextProperty->BoldOn(); titleTextProperty->SetColor( 0.0, 0.0, 0.0 ); titleTextProperty->SetJustificationToCentered(); // titleTextProperty->SetFontSize( 50 ); scalarBar->SetTitleTextProperty( titleTextProperty ); vtkSmartPointer labelTextProperty = vtkSmartPointer::New(); labelTextProperty->ItalicOff(); labelTextProperty->BoldOff(); labelTextProperty->SetColor( 0.0, 0.0, 0.0 ); // labelTextProperty->SetFontSize( 5 ); scalarBar->SetLabelTextProperty( labelTextProperty ); scalarBar->VisibilityOn(); renderer->AddActor2D( scalarBar ); } renderWindow->Render(); if( screenCaptureFileName.empty() ) { renderWindowInteractor->Start(); } else { vtkSmartPointer windowToImageFilter = vtkSmartPointer::New(); windowToImageFilter->SetInput( renderWindow ); windowToImageFilter->SetMagnification( 5 ); windowToImageFilter->Update(); vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetFileName( screenCaptureFileName.c_str() ); writer->SetInputConnection( windowToImageFilter->GetOutputPort() ); writer->Write(); } } int antsImageToSurface( itk::ants::CommandLineParser *parser ) { const unsigned int ImageDimension = 3; typedef float RealType; typedef itk::Image ImageType; typedef itk::Image MaskImageType; typedef unsigned char RgbComponentType; typedef itk::RGBPixel RgbPixelType; typedef itk::Image RgbImageType; ImageType::PointType zeroOrigin; zeroOrigin.Fill( 0.0 ); // Read in input surface image ImageType::Pointer inputImage = NULL; RealType defaultColorRed = 255.0; RealType defaultColorGreen = 255.0; RealType defaultColorBlue = 255.0; RealType defaultAlpha = 1.0; itk::ants::CommandLineParser::OptionType::Pointer inputImageOption = parser->GetOption( "surface-image" ); if( inputImageOption && inputImageOption->GetNumberOfFunctions() ) { if( inputImageOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { std::string inputFile = inputImageOption->GetFunction( 0 )->GetName(); ReadImage( inputImage, inputFile.c_str() ); inputImage->SetOrigin( zeroOrigin ); } else { std::string inputFile = inputImageOption->GetFunction( 0 )->GetParameter( 0 ); ReadImage( inputImage, inputFile.c_str() ); inputImage->SetOrigin( zeroOrigin ); if( inputImageOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { std::vector defaultColors = parser->ConvertVector( inputImageOption->GetFunction( 0 )->GetParameter( 1 ) ); if( defaultColors.size() == 1 ) { defaultColorRed = defaultColors[0]; defaultColorGreen = defaultColors[0]; defaultColorBlue = defaultColors[0]; defaultAlpha = 1.0; } else if( defaultColors.size() == 3 ) { defaultColorRed = defaultColors[0]; defaultColorGreen = defaultColors[1]; defaultColorBlue = defaultColors[2]; defaultAlpha = 1.0; } else if( defaultColors.size() == 4 ) { defaultColorRed = defaultColors[0]; defaultColorGreen = defaultColors[1]; defaultColorBlue = defaultColors[2]; defaultAlpha = defaultColors[3]; } else { std::cerr << "Incorrect color format specified." << std::endl; return EXIT_FAILURE; } } } } else { std::cerr << "Input image not specified." << std::endl; return EXIT_FAILURE; } // There's a reorientation issue between itk image physical space and the mesh space // for which we have to account. See // http://www.vtk.org/pipermail/vtkusers/2011-July/068595.html // and // http://www.vtk.org/Wiki/VTK/ExamplesBoneYard/Cxx/VolumeRendering/itkVtkImageConvert typedef itk::AffineTransform RigidTransformType; RigidTransformType::Pointer meshToItkImageTransform = RigidTransformType::New(); RigidTransformType::OutputVectorType offset; offset[0] = -inputImage->GetOrigin()[0]; offset[1] = -inputImage->GetOrigin()[1]; offset[2] = -inputImage->GetOrigin()[2]; RigidTransformType::MatrixType matrix; for( unsigned int i = 0; i < ImageDimension; i++ ) { for( unsigned int j = 0; j < ImageDimension; j++ ) { matrix( i, j ) = inputImage->GetDirection()( i, j ); } } meshToItkImageTransform->SetMatrix( matrix ); meshToItkImageTransform->SetOffset( offset ); // Get anti-alias RMSE parameter RealType antiAliasRmseParameter = 0.03; itk::ants::CommandLineParser::OptionType::Pointer antiAliasRmseOption = parser->GetOption( "anti-alias-rmse" ); if( antiAliasRmseOption && antiAliasRmseOption->GetNumberOfFunctions() ) { antiAliasRmseParameter = parser->Convert( antiAliasRmseOption->GetFunction( 0 )->GetName() ); } typedef itk::AntiAliasBinaryImageFilter AntiAliasFilterType; AntiAliasFilterType::Pointer antiAlias = AntiAliasFilterType::New(); antiAlias->SetMaximumRMSError( antiAliasRmseParameter ); antiAlias->SetInput( inputImage ); antiAlias->Update(); // Reconstruct binary surface. typedef itk::ImageToVTKImageFilter ConnectorType; ConnectorType::Pointer connector = ConnectorType::New(); connector->SetInput( antiAlias->GetOutput() ); connector->Update(); vtkSmartPointer marchingCubes = vtkSmartPointer::New(); marchingCubes->SetInputData( connector->GetOutput() ); marchingCubes->ComputeScalarsOff(); marchingCubes->ComputeGradientsOff(); marchingCubes->SetNumberOfContours( 1 ); marchingCubes->SetValue( 0, 0.0 ); marchingCubes->Update(); vtkSmartPointer connectivityFilter = vtkSmartPointer::New(); connectivityFilter->SetExtractionModeToLargestRegion(); connectivityFilter->SetInputData( marchingCubes->GetOutput() ); connectivityFilter->Update(); vtkSmartPointer triangularizer = vtkSmartPointer::New(); triangularizer->SetInputData( connectivityFilter->GetOutput() ); triangularizer->Update(); vtkPolyData *vtkMesh = triangularizer->GetOutput(); CalculateGenus( vtkMesh, true ); // Add the functional overlays std::vector functionalRgbImages; std::vector functionalMaskImages; std::vector functionalAlphaValues; itk::ants::CommandLineParser::OptionType::Pointer functionalOverlayOption = parser->GetOption( "functional-overlay" ); if( functionalOverlayOption && functionalOverlayOption->GetNumberOfFunctions() ) { for( unsigned int n = 0; n < functionalOverlayOption->GetNumberOfFunctions(); n++ ) { if( functionalOverlayOption->GetFunction( n )->GetNumberOfParameters() < 2 ) { std::cerr << "Error: each functional overlay must have an RGB image and mask." << "See help menu." << std::endl; return EXIT_FAILURE; } // read RGB image std::string rgbFileName = functionalOverlayOption->GetFunction( n )->GetParameter( 0 ); typedef itk::ImageFileReader RgbReaderType; RgbReaderType::Pointer rgbReader = RgbReaderType::New(); rgbReader->SetFileName( rgbFileName.c_str() ); try { rgbReader->Update(); rgbReader->GetOutput()->SetOrigin( zeroOrigin ); } catch( ... ) { std::cerr << "Error reading RGB file " << rgbFileName << std::endl; return EXIT_FAILURE; } functionalRgbImages.push_back( rgbReader->GetOutput() ); // read mask std::string maskFileName = functionalOverlayOption->GetFunction( n )->GetParameter( 1 ); typedef itk::ImageFileReader MaskReaderType; MaskReaderType::Pointer maskReader = MaskReaderType::New(); maskReader->SetFileName( maskFileName.c_str() ); try { maskReader->Update(); maskReader->GetOutput()->SetOrigin( zeroOrigin ); } catch( ... ) { std::cerr << "Error reading mask file " << maskFileName << std::endl; return EXIT_FAILURE; } functionalMaskImages.push_back( maskReader->GetOutput() ); if( functionalOverlayOption->GetFunction( n )->GetNumberOfParameters() > 2 ) { RealType alpha = parser->Convert( functionalOverlayOption->GetFunction( n )->GetParameter( 2 ) ); functionalAlphaValues.push_back( alpha ); } else { functionalAlphaValues.push_back( 1.0 ); } } } // Reset mesh points to physical space of ITK images vtkSmartPointer meshPoints = vtkMesh->GetPoints(); int numberOfPoints = meshPoints->GetNumberOfPoints(); for( int n = 0; n < numberOfPoints; n++ ) { RigidTransformType::InputPointType inputTransformPoint; RigidTransformType::OutputPointType outputTransformPoint; for( unsigned int d = 0; d < ImageDimension; d++ ) { inputTransformPoint[d] = meshPoints->GetPoint( n )[d]; } outputTransformPoint = meshToItkImageTransform->TransformPoint( inputTransformPoint ); meshPoints->SetPoint( n, outputTransformPoint[0], outputTransformPoint[1], outputTransformPoint[2] ); } // Do the painting vtkSmartPointer colors = vtkSmartPointer::New(); colors->SetNumberOfComponents( 4 ); // R, G, B, and alpha components colors->SetName( "Colors" ); for( int n = 0; n < numberOfPoints; n++ ) { ImageType::IndexType index; ImageType::PointType imagePoint; for( unsigned int d = 0; d < ImageDimension; d++ ) { imagePoint[d] = meshPoints->GetPoint( n )[d]; } RealType currentRed = defaultColorRed / 255.0; RealType currentGreen = defaultColorGreen / 255.0; RealType currentBlue = defaultColorBlue / 255.0; RealType currentAlpha = defaultAlpha; for( int i = functionalAlphaValues.size() - 1; i >= 0; i-- ) { bool isInsideImage = functionalMaskImages[i]->TransformPhysicalPointToIndex( imagePoint, index ); if( isInsideImage && functionalMaskImages[i]->GetPixel( index ) != 0 ) { // http://stackoverflow.com/questions/726549/algorithm-for-additive-color-mixing-for-rgb-values // or // http://en.wikipedia.org/wiki/Alpha_compositing RgbPixelType rgbPixel = functionalRgbImages[i]->GetPixel( index ); RealType functionalRed = rgbPixel.GetRed() / 255.0; RealType functionalGreen = rgbPixel.GetGreen() / 255.0; RealType functionalBlue = rgbPixel.GetBlue() / 255.0; RealType functionalAlpha = functionalAlphaValues[i]; RealType backgroundRed = currentRed; RealType backgroundGreen = currentGreen; RealType backgroundBlue = currentBlue; RealType backgroundAlpha = currentAlpha; currentAlpha = 1.0 - ( 1.0 - functionalAlpha ) * ( 1.0 - backgroundAlpha ); currentRed = functionalRed * functionalAlpha / currentAlpha + backgroundRed * backgroundAlpha * ( 1.0 - functionalAlpha ) / currentAlpha; currentGreen = functionalGreen * functionalAlpha / currentAlpha + backgroundGreen * backgroundAlpha * ( 1.0 - functionalAlpha ) / currentAlpha; currentBlue = functionalBlue * functionalAlpha / currentAlpha + backgroundBlue * backgroundAlpha * ( 1.0 - functionalAlpha ) / currentAlpha; } } unsigned char currentColor[4]; currentColor[0] = static_cast( currentRed * 255.0 ); currentColor[1] = static_cast( currentGreen * 255.0 ); currentColor[2] = static_cast( currentBlue * 255.0 ); currentColor[3] = static_cast( currentAlpha * 255.0 ); colors->InsertNextTupleValue( currentColor ); } vtkMesh->GetPointData()->SetScalars( colors ); // Inflation vtkSmartPointer inflater = vtkSmartPointer::New(); itk::ants::CommandLineParser::OptionType::Pointer inflationOption = parser->GetOption( "inflation" ); if( inflationOption && inflationOption->GetNumberOfFunctions() ) { unsigned int numberOfIterations = 0; if( inflationOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { numberOfIterations = parser->Convert( inflationOption->GetFunction( 0 )->GetName() ); } else { numberOfIterations = parser->Convert( inflationOption->GetFunction( 0 )->GetParameter( 0 ) ); } if( numberOfIterations > 0 ) { inflater->SetInputData( vtkMesh ); inflater->SetNumberOfIterations( numberOfIterations ); inflater->BoundarySmoothingOn(); inflater->FeatureEdgeSmoothingOff(); inflater->SetFeatureAngle( 180.0 ); inflater->SetEdgeAngle( 180.0 ); inflater->SetPassBand( 0.001 ); inflater->NonManifoldSmoothingOn(); inflater->NormalizeCoordinatesOff(); inflater->Update(); vtkMesh = inflater->GetOutput(); } } // Write the vtk mesh to file. itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { std::string outputFile = outputOption->GetFunction( 0 )->GetName(); std::string ext = itksys::SystemTools::GetFilenameExtension( outputFile ); if( strcmp( ext.c_str(), ".stl" ) == 0 ) { vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetInputData( vtkMesh ); writer->SetFileName( outputFile.c_str() ); writer->Write(); } if( strcmp( ext.c_str(), ".ply" ) == 0 ) { vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetInputData( vtkMesh ); writer->SetFileName( outputFile.c_str() ); writer->Write(); } if( strcmp( ext.c_str(), ".vtk" ) == 0 ) { vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetInputData( vtkMesh ); writer->SetFileName( outputFile.c_str() ); writer->Write(); } } vtkSmartPointer lookupTable = vtkSmartPointer::New(); std::string scalarBarTitle( "antsSurf" ); unsigned int scalarBarNumberOfLabels = 5; unsigned int scalarBarWidthInPixels = 0; unsigned int scalarBarHeightInPixels = 0; bool renderScalarBar = false; itk::ants::CommandLineParser::OptionType::Pointer scalarBarOption = parser->GetOption( "scalar-bar" ); if( scalarBarOption && scalarBarOption->GetNumberOfFunctions() ) { renderScalarBar = true; std::string lookupTableFile; if( scalarBarOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { lookupTableFile = scalarBarOption->GetFunction( 0 )->GetName(); } else { lookupTableFile = scalarBarOption->GetFunction( 0 )->GetParameter( 0 ); if( scalarBarOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { scalarBarTitle = scalarBarOption->GetFunction( 0 )->GetParameter( 1 ); } if( scalarBarOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { scalarBarNumberOfLabels = parser->Convert( scalarBarOption->GetFunction( 0 )->GetParameter( 2 ) ); } if( scalarBarOption->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { std::vector dimensions = parser->ConvertVector( scalarBarOption->GetFunction( 0 )->GetParameter( 3 ) ); scalarBarWidthInPixels = dimensions[0]; scalarBarHeightInPixels = dimensions[1]; } } // Read in color table std::ifstream fileStr( lookupTableFile.c_str() ); if( !fileStr.is_open() ) { std::cerr << " Could not open file " << lookupTableFile << '\n'; renderScalarBar = false; } int tableSize = std::count( std::istreambuf_iterator( fileStr ), std::istreambuf_iterator(), '\n' ); fileStr.clear(); fileStr.seekg( 0, std::ios::beg ); lookupTable->SetNumberOfTableValues( tableSize ); lookupTable->Build(); RealType value; RealType redComponent; RealType greenComponent; RealType blueComponent; RealType alphaComponent; char comma; RealType minValue = itk::NumericTraits::max(); RealType maxValue = itk::NumericTraits::min(); unsigned int index = 0; while( fileStr >> value >> comma >> redComponent >> comma >> greenComponent >> comma >> blueComponent >> comma >> alphaComponent ) { lookupTable->SetTableValue( index++, redComponent / 255.0, greenComponent / 255.0, blueComponent / 255.0, alphaComponent ); if( value < minValue ) { minValue = value; } if( value > maxValue ) { maxValue = value; } } lookupTable->SetTableRange( minValue, maxValue ); fileStr.close(); } // Display vtk mesh itk::ants::CommandLineParser::OptionType::Pointer displayOption = parser->GetOption( "display" ); if( displayOption && displayOption->GetNumberOfFunctions() ) { std::vector rotationAnglesInDegrees; rotationAnglesInDegrees.push_back( 0.0 ); rotationAnglesInDegrees.push_back( 0.0 ); rotationAnglesInDegrees.push_back( 0.0 ); std::vector backgroundColor; backgroundColor.push_back( 255.0 ); backgroundColor.push_back( 255.0 ); backgroundColor.push_back( 255.0 ); std::string screenCaptureFileName = std::string( "" ); screenCaptureFileName = displayOption->GetFunction( 0 )->GetName(); if( strcmp( screenCaptureFileName.c_str(), "false" ) == 0 || strcmp( screenCaptureFileName.c_str(), "0" ) == 0 ) { // do not render and exit return EXIT_SUCCESS; } std::size_t position = screenCaptureFileName.find( "png" ); if( position == std::string::npos ) { screenCaptureFileName.clear(); } else { std::cout << "Writing surface to image file " << screenCaptureFileName << "." << std::endl; } if( displayOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { Display( vtkMesh, rotationAnglesInDegrees, backgroundColor, screenCaptureFileName, renderScalarBar, lookupTable, scalarBarTitle, scalarBarNumberOfLabels, scalarBarWidthInPixels, scalarBarHeightInPixels ); } else { if( displayOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { rotationAnglesInDegrees = parser->ConvertVector( displayOption->GetFunction( 0 )->GetParameter( 0 ) ); } if( displayOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { backgroundColor = parser->ConvertVector( displayOption->GetFunction( 0 )->GetParameter( 1 ) ); if( backgroundColor.size() == 1 ) { backgroundColor.push_back( backgroundColor[0] ); backgroundColor.push_back( backgroundColor[0] ); } } Display( vtkMesh, rotationAnglesInDegrees, backgroundColor, screenCaptureFileName, renderScalarBar, lookupTable, scalarBarTitle, scalarBarNumberOfLabels, scalarBarWidthInPixels, scalarBarHeightInPixels ); } } return EXIT_SUCCESS; } int antsSurfaceToImage( itk::ants::CommandLineParser *parser ) { itk::ants::CommandLineParser::OptionType::Pointer surfaceOption = parser->GetOption( "mesh" ); vtkSmartPointer vtkMesh; std::string inputFile; if( surfaceOption && surfaceOption->GetNumberOfFunctions() > 0 ) { inputFile = surfaceOption->GetFunction( 0 )->GetName(); std::string ext = itksys::SystemTools::GetFilenameExtension( inputFile ); try { if( strcmp( ext.c_str(), ".stl" ) == 0 ) { vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName( inputFile.c_str() ); reader->Update(); vtkMesh = reader->GetOutput(); } if( strcmp( ext.c_str(), ".ply" ) == 0 ) { vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName( inputFile.c_str() ); reader->Update(); vtkMesh = reader->GetOutput(); } if( strcmp( ext.c_str(), ".vtk" ) == 0 ) { vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName( inputFile.c_str() ); reader->Update(); vtkMesh = reader->GetOutput(); } } catch( ... ) { std::cerr << "Error. Unable to read mesh input file." << std::endl; return EXIT_FAILURE; } } else { std::cerr << "No mesh file specified." << std::endl; return EXIT_FAILURE; } double bounds[6]; vtkMesh->GetBounds( bounds ); std::string outputFile; std::vector spacing; itk::ants::CommandLineParser::OptionType::Pointer outputOption = parser->GetOption( "output" ); if( outputOption && outputOption->GetNumberOfFunctions() ) { outputFile = outputOption->GetFunction( 0 )->GetName(); if( outputOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { spacing.push_back( 1.0 ); std::cout << "Warning. No spacing is specified---defaulting to 1.0." << std::endl; } else { spacing = parser->ConvertVector( outputOption->GetFunction( 0 )->GetParameter( 0 ) ); } } else { std::cerr << "Error. No output specified." << std::endl; return EXIT_FAILURE; } vtkSmartPointer whiteImage = vtkSmartPointer::New(); double spacing2[3]; // desired volume spacing if( spacing.size() == 1 ) { spacing2[0] = spacing[0]; spacing2[1] = spacing[0]; spacing2[2] = spacing[0]; } else if( spacing.size() == 3 ) { spacing2[0] = spacing[0]; spacing2[1] = spacing[1]; spacing2[2] = spacing[2]; } else { std::cerr << "Error. Incorrect spacing specified." << std::endl; return EXIT_FAILURE; } whiteImage->SetSpacing( spacing2 ); // compute dimensions int dim[3]; for( unsigned int i = 0; i < 3; i++ ) { dim[i] = static_cast( std::ceil( ( bounds[i * 2 + 1] - bounds[i * 2] ) / spacing2[i] ) ); } whiteImage->SetDimensions( dim ); whiteImage->SetExtent( 0, dim[0] - 1, 0, dim[1] - 1, 0, dim[2] - 1 ); double origin[3]; origin[0] = bounds[0] + spacing2[0] / 2; origin[1] = bounds[2] + spacing2[1] / 2; origin[2] = bounds[4] + spacing2[2] / 2; whiteImage->SetOrigin( origin ); whiteImage->AllocateScalars( VTK_UNSIGNED_CHAR, 1 ); // fill the image with foreground voxels: unsigned char inval = 1; unsigned char outval = 0; vtkIdType count = whiteImage->GetNumberOfPoints(); for( vtkIdType i = 0; i < count; ++i ) { whiteImage->GetPointData()->GetScalars()->SetTuple1( i, inval ); } // polygonal data --> image stencil: vtkSmartPointer pol2stenc = vtkSmartPointer::New(); pol2stenc->SetInputData( vtkMesh ); pol2stenc->SetOutputOrigin( origin ); pol2stenc->SetOutputSpacing( spacing2 ); pol2stenc->SetOutputWholeExtent( whiteImage->GetExtent() ); pol2stenc->Update(); // cut the corresponding white image and set the background: vtkSmartPointer imgstenc = vtkSmartPointer::New(); imgstenc->SetInputData( whiteImage ); imgstenc->SetStencilConnection( pol2stenc->GetOutputPort() ); imgstenc->ReverseStencilOff(); imgstenc->SetBackgroundValue( outval ); imgstenc->Update(); // Write the vtk mesh to image file. if( outputOption && outputOption->GetNumberOfFunctions() ) { vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetFileName( outputFile.c_str() ); writer->SetInputData( imgstenc->GetOutput() ); writer->Write(); } return EXIT_SUCCESS; } void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "Main input binary image for 3-D rendering. One can also " ) + std::string( "set a default color value in the range [0,255]. The " ) + std::string( "fourth default color element is the alpha value in " ) + std::string( "the range [0,1]." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "surface-image" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "surfaceImageFilename" ); option->SetUsageOption( 1, "[surfaceImageFilename,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "The user can also specify a vtk polydata file to be converted " ) + std::string( "to a binary image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mesh" ); option->SetShortName( 'm' ); option->SetUsageOption( 0, "meshFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "A functional overlay can be specified using both " ) + std::string( "and rgb image and a mask specifying where that " ) + std::string( "rgb image should be applied. Both images must " ) + std::string( "have the same image geometry as the input image. " ) + std::string( "Optionally, an alpha parameter can be specified." ) + std::string( "Note that more than one functional overlays can " ) + std::string( "be rendered, the order in which they are specified " ) + std::string( "on the command line matters, and rgb images are " ) + std::string( "assumed to be unsigned char [0,255]." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "functional-overlay" ); option->SetShortName( 'f' ); option->SetUsageOption( 0, "[rgbImageFileName,maskImageFileName,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Anti-alias maximum RMSE parameter for surface reconstruction " ) + std::string( "(default = 0.03)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "anti-alias-rmse" ); option->SetShortName( 'a' ); option->SetUsageOption( 0, "value" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Perform inflation of the mesh." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "inflation" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "numberOfIterations" ); option->SetUsageOption( 1, "[numberOfIterations]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Display output surface function in VTK window. Rotation " ) + std::string( "angles are in degrees and the default background color " ) + std::string( "is white (255x255x255). Note that the filename, to be " ) + std::string( "considered such, must have a \"png\" extension. If the " ) + std::string( "filename is omitted in the third usage option, then the " ) + std::string( "window is displayed." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "display" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "doWindowDisplay" ); option->SetUsageOption( 1, "filename" ); option->SetUsageOption( 2, "[rotateXxrotateYxrotateZ,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Given a binary image input, the output is a vtk polydata file (possible " ) + std::string( "extensions include .stl, .ply, and .vtk). " ) + std::string( "Alternatively, if a mesh file is specified as input, the output " ) + std::string( "is an itk binary image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "surfaceFilename" ); option->SetUsageOption( 1, "imageFilename[spacing]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Add a scalar bar to the rendering for the final overlay. One can tailor " ) + std::string( "the aesthetic by changing the number of labels and/or the orientation and ") + std::string( "size of the scalar bar. If the \'width\' > \'height\' (in pixels) then the ") + std::string( "orientation is horizontal. Otherwise it is vertical (default)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "scalar-bar" ); option->SetShortName( 'b' ); option->SetUsageOption( 0, "lookupTable" ); option->SetUsageOption( 1, "[lookupTable,,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsSurf( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsSurf" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "Produce a 3-D surface rendering with optional RGB overlay. Alternatively, " ) + std::string( "one can input a mesh which can then be converted to a binary image. " ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc == 1 ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Get dimensionality itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "surface-image" ); itk::ants::CommandLineParser::OptionType::Pointer surfaceOption = parser->GetOption( "mesh" ); if( imageOption && imageOption->GetNumberOfFunctions() > 0 ) { std::string inputFile; if( imageOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { inputFile = imageOption->GetFunction( 0 )->GetName(); } else if( imageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { inputFile = imageOption->GetFunction( 0 )->GetParameter( 0 ); } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( inputFile.c_str(), itk::ImageIOFactory::ReadMode ); unsigned int dimension = imageIO->GetNumberOfDimensions(); if( dimension == 3 ) { antsImageToSurface( parser ); } else { std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else if( surfaceOption && surfaceOption->GetNumberOfFunctions() > 0 ) { antsSurfaceToImage( parser ); } else { std::cerr << "Input not specified. See help menu." << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsTransformInfo.cxx000066400000000000000000000052121311104306400204300ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 "antsUtilities.h" #include "antsCommandLineParser.h" #include "itkTransformFileReader.h" #include namespace ants { // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsTransformInfo( std::vector args, std::ostream * /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsTransformInfo" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); for (int i=1; iSetFileName( argv[i] ); reader->Update(); std::cout << *(reader->GetTransformList()->begin()) << std::endl; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsUtilities.cxx000066400000000000000000000113751311104306400176230ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include #include #include #include #include #include namespace ants { // We need to ensure that only one of these exists! // boost::iostreams::stream std::cout( ( ants_Sink() ) ); } TRAN_FILE_TYPE CheckFileType(const char * const str) { std::string filename = str; std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); if( pos != std::string::npos ) { std::string extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); extension = std::string( filepre, pos, filepre.length() - 1 ); } if( extension == ".txt" || extension == ".mat" || extension == ".hdf5" || extension == ".hdf" || extension == ".xfm") { return AFFINE_FILE; } else { return DEFORMATION_FILE; } } else { return INVALID_FILE; } return AFFINE_FILE; } TRAN_FILE_TYPE CheckFileType(const std::string & str) { return CheckFileType( str.c_str() ); } void SetAffineInvFlag(TRAN_OPT & opt, bool & set_current_affine_inv) { opt.do_affine_inv = set_current_affine_inv; if( set_current_affine_inv ) { set_current_affine_inv = false; } } void FilePartsWithgz(const std::string & filename, std::string & path, std::string & name, std::string & ext) { std::string extension; std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); if( pos != std::string::npos ) { extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); if( pos != std::string::npos ) { extension = std::string( filepre, pos, filepre.length() - 1 ) + ".gz"; filepre = std::string(filepre, 0, pos); } } } else { extension = std::string(""); } ext = extension; pos = filepre.rfind('/'); if( pos != std::string::npos ) { path = std::string(filepre, 0, pos + 1); name = std::string(filepre, pos + 1, filepre.length() - 1); } else { path = std::string(""); name = filepre; } } bool CheckFileExistence(const char * const str) { std::ifstream myfile(str); bool b = myfile.is_open(); myfile.close(); return b; } // adapted from http://stackoverflow.com/questions/194465/how-to-parse-a-string-to-an-int-in-c bool get_a_double_number(const char * const str, double & v) { errno = 0; char *end; v = strtod(str, &end); if( (errno == ERANGE && v == HUGE_VAL) ) { return false; // OVERFLOW } if( (errno == ERANGE && v == 0.0) ) { return false; // UNDERFLOW } if( *str == '\0' || *end != '\0' ) { return false; // INCONVERTIBLE; } return true; } void DisplayOptQueue(const TRAN_OPT_QUEUE & opt_queue) { const int kQueueSize = opt_queue.size(); for( int i = 0; i < kQueueSize; i++ ) { std::cout << "[" << i << "/" << kQueueSize << "]: "; switch( opt_queue[i].file_type ) { case AFFINE_FILE: { std::cout << "AFFINE"; } break; case DEFORMATION_FILE: { std::cout << "FIELD"; } break; case IDENTITY_TRANSFORM: { std::cout << "IDENTITY"; } break; case IMAGE_AFFINE_HEADER: { std::cout << "HEADER"; } break; default: { std::cout << "Invalid Format!!!"; } break; } if( opt_queue[i].do_affine_inv ) { std::cout << "-INV"; } std::cout << ": " << opt_queue[i].filename << std::endl; } } void DisplayOpt(const TRAN_OPT & opt) { switch( opt.file_type ) { case AFFINE_FILE: { std::cout << "AFFINE"; } break; case DEFORMATION_FILE: { std::cout << "FIELD"; } break; case IDENTITY_TRANSFORM: { std::cout << "IDENTITY"; } break; case IMAGE_AFFINE_HEADER: { std::cout << "HEADER"; } break; default: { std::cout << "Invalid Format!!!"; } break; } if( opt.do_affine_inv ) { std::cout << "-INV"; } std::cout << ": " << opt.filename << std::endl; } std::string GetPreferredTransformFileType(void) { // return ".mat"; return ".txt"; } void ConvertToLowerCase( std::string& str ) { std::transform( str.begin(), str.end(), str.begin(), tolower ); // You may need to cast the above line to (int(*)(int)) // tolower - this works as is on VC 7.1 but may not work on // other compilers } ants-2.2.0/Examples/antsUtilities.h000066400000000000000000000506121311104306400172450ustar00rootroot00000000000000/** * There were several functions that had been copied and * pasted over and over again in this library. This * header files is contains a common definitoin for * those file. * \author Hans J. Johnson */ #ifndef __antsUtilities_h__ #define __antsUtilities_h__ // #include "antscout.hxx" #include "antsAllocImage.h" #include #include #include #include #include #include #include #include "itkVector.h" #include "itkBinaryThresholdImageFilter.h" #include "itkBinaryBallStructuringElement.h" #include "itkBinaryDilateImageFilter.h" #include "itkBinaryErodeImageFilter.h" #include "itkGrayscaleDilateImageFilter.h" #include "itkGrayscaleErodeImageFilter.h" // We need to ensure that only one of these exists! namespace ants { // extern boost::iostreams::stream std::cout; template bool IsInside( typename TImage::Pointer input, typename TImage::IndexType index ) { /** FIXME - should use StartIndex - */ typedef TImage ImageType; enum { ImageDimension = ImageType::ImageDimension }; bool isinside = true; for( unsigned int i = 0; i < ImageDimension; i++ ) { float shifted = index[i]; if( shifted < 0 || shifted > input->GetLargestPossibleRegion().GetSize()[i] - 1 ) { isinside = false; } } return isinside; } // ########################################################################## // ########################################################################## // ########################################################################## // ########################################################################## // ########################################################################## // ########################################################################## // ########################################################################## // Templates template typename TImage::Pointer Morphological( typename TImage::Pointer input, float rad, unsigned int option, float dilateval) { typedef TImage ImageType; enum { ImageDimension = TImage::ImageDimension }; typedef typename TImage::PixelType PixelType; if( option == 0 ) { // std::cout << " binary eroding the image " << std::endl; } else if( option == 1 ) { // std::cout << " binary dilating the image " << std::endl; } else if( option == 2 ) { // std::cout << " binary opening the image " << std::endl; } else if( option == 3 ) { // std::cout << " binary closing the image " << std::endl; } else if( option == 4 ) { // std::cout << " grayscale eroding the image " << std::endl; } else if( option == 5 ) { // std::cout << " grayscale dilating the image " << std::endl; } else if( option == 6 ) { // std::cout << " grayscale opening the image " << std::endl; } else if( option == 7 ) { // std::cout << " grayscale closing the image " << std::endl; } typedef itk::BinaryBallStructuringElement< PixelType, ImageDimension> StructuringElementType; typedef itk::BinaryErodeImageFilter< TImage, TImage, StructuringElementType> ErodeFilterType; typedef itk::BinaryDilateImageFilter< TImage, TImage, StructuringElementType> DilateFilterType; typename ErodeFilterType::Pointer binaryErode = ErodeFilterType::New(); typename DilateFilterType::Pointer binaryDilate = DilateFilterType::New(); StructuringElementType structuringElement; structuringElement.SetRadius( (unsigned long) rad ); // 3x3x3 structuring element structuringElement.CreateStructuringElement(); binaryErode->SetKernel( structuringElement ); binaryDilate->SetKernel( structuringElement ); // binaryOpen->SetKernal( structuringElement ); // binaryClose->SetKernel( structuringElement ); // // typename OpeningFilterType::Pointer binaryOpen = OpeningFilterType::New(); // typename ClosingFilterType::Pointer binaryClose = ClosingFilterType::New(); typedef itk::GrayscaleErodeImageFilter< TImage, TImage, StructuringElementType> GrayscaleErodeFilterType; typedef itk::GrayscaleDilateImageFilter< TImage, TImage, StructuringElementType> GrayscaleDilateFilterType; typename GrayscaleErodeFilterType::Pointer grayscaleErode = GrayscaleErodeFilterType::New(); typename GrayscaleDilateFilterType::Pointer grayscaleDilate = GrayscaleDilateFilterType::New(); grayscaleErode->SetKernel( structuringElement ); grayscaleDilate->SetKernel( structuringElement ); // It is necessary to define what could be considered objects on the binary // images. This is specified with the methods \code{SetErodeValue()} and // \code{SetDilateValue()}. The value passed to these methods will be // considered the value over which the dilation and erosion rules will apply binaryErode->SetErodeValue( (unsigned int ) dilateval ); binaryDilate->SetDilateValue( (unsigned int ) dilateval ); typename TImage::Pointer temp; if( option == 1 ) { // std::cout << " Dilate " << rad << std::endl; binaryDilate->SetInput( input ); binaryDilate->Update(); temp = binaryDilate->GetOutput(); } else if( option == 0 ) { // std::cout << " Erode " << rad << std::endl; binaryErode->SetInput( input ); // binaryDilate->GetOutput() ); binaryErode->Update(); temp = binaryErode->GetOutput(); } else if( option == 2 ) { // dilate(erode(img)) // std::cout << " Binary Open " << rad << std::endl; // binaryOpen->SetInput( input );//binaryDilate->GetOutput() ); // binaryOpen->Update(); binaryErode->SetInput( input ); binaryDilate->SetInput( binaryErode->GetOutput() ); binaryDilate->Update(); temp = binaryDilate->GetOutput(); } else if( option == 3 ) { // std::cout << " Binary Close " << rad << std::endl; // binaryClose->SetInput( input );//binaryDilate->GetOutput() ); // binaryClose->Update(); binaryDilate->SetInput( input ); binaryErode->SetInput( binaryDilate->GetOutput() ); binaryErode->Update(); temp = binaryErode->GetOutput(); } else if( option == 4 ) { // std::cout << " Grayscale Erode " << rad << std::endl; grayscaleErode->SetInput( input ); // binaryDilate->GetOutput() ); grayscaleErode->Update(); temp = grayscaleErode->GetOutput(); } else if( option == 5 ) { // std::cout << " Grayscale Dilate " << rad << std::endl; grayscaleDilate->SetInput( input ); // binaryDilate->GetOutput() ); grayscaleDilate->Update(); temp = grayscaleDilate->GetOutput(); // std::cout << " Grayscale Dilate Done " << temp << std::endl; } else if( option == 6 ) { // std::cout << " Grayscale Open " << rad << std::endl; grayscaleErode->SetInput( input ); // binaryDilate->GetOutput() ); grayscaleErode->Update(); grayscaleDilate->SetInput( grayscaleErode->GetOutput() ); grayscaleDilate->Update(); temp = grayscaleDilate->GetOutput(); } else if( option == 7 ) { // std::cout << " Grayscale Close " << rad << std::endl; grayscaleDilate->SetInput( input ); // binaryDilate->GetOutput() ); grayscaleDilate->Update(); grayscaleErode->SetInput( grayscaleDilate->GetOutput() ); grayscaleErode->Update(); temp = grayscaleErode->GetOutput(); } if( option == 0 ) { // FIXME - replace with threshold filter? typedef itk::ImageRegionIteratorWithIndex ImageIteratorType; ImageIteratorType o_iter( temp, temp->GetLargestPossibleRegion() ); o_iter.GoToBegin(); while( !o_iter.IsAtEnd() ) { if( o_iter.Get() > 0.5 && input->GetPixel(o_iter.GetIndex() ) > 0.5 ) { o_iter.Set(1); } else { o_iter.Set(0); } ++o_iter; } } return temp; } #if 0 // TODO: I am pretty sure that this can be completely // replaced by the Morphological template above // with option = true, flase, and template typename TImage::Pointer MorphologicalBinary( typename TImage::Pointer input, float rad, bool option) { typedef TImage ImageType; enum { ImageDimension = TImage::ImageDimension }; typedef typename TImage::PixelType PixelType; if( !option ) { // std::cout << " eroding the image " << std::endl; } else { // std::cout << " dilating the image " << std::endl; } typedef itk::BinaryBallStructuringElement< PixelType, ImageDimension> StructuringElementType; typedef itk::BinaryErodeImageFilter< TImage, TImage, StructuringElementType> ErodeFilterType; typedef itk::BinaryDilateImageFilter< TImage, TImage, StructuringElementType> DilateFilterType; typename ErodeFilterType::Pointer binaryErode = ErodeFilterType::New(); typename DilateFilterType::Pointer binaryDilate = DilateFilterType::New(); StructuringElementType structuringElement; structuringElement.SetRadius( (unsigned long) rad ); // 3x3x3 structuring element structuringElement.CreateStructuringElement(); binaryErode->SetKernel( structuringElement ); binaryDilate->SetKernel( structuringElement ); // It is necessary to define what could be considered objects on the binary // images. This is specified with the methods \code{SetErodeValue()} and // \code{SetDilateValue()}. The value passed to these methods will be // considered the value over which the dilation and erosion rules will apply binaryErode->SetErodeValue( 1 ); binaryDilate->SetDilateValue( 1 ); typename TImage::Pointer temp; if( option ) { binaryDilate->SetInput( input ); binaryDilate->Update(); temp = binaryDilate->GetOutput(); } else { binaryErode->SetInput( input ); // binaryDilate->GetOutput() ); binaryErode->Update(); temp = binaryErode->GetOutput(); typedef itk::ImageRegionIteratorWithIndex ImageIteratorType; ImageIteratorType o_iter( temp, temp->GetLargestPossibleRegion() ); o_iter.GoToBegin(); while( !o_iter.IsAtEnd() ) { if( o_iter.Get() > 0.5 && input->GetPixel(o_iter.GetIndex() ) > 0.5 ) { o_iter.Set(1); } else { o_iter.Set(0); } ++o_iter; } } return temp; } #endif template typename TImage::Pointer BinaryThreshold( typename TImage::PixelType low, typename TImage::PixelType high, typename TImage::PixelType replaceval, typename TImage::Pointer input) { typedef typename TImage::PixelType PixelType; // Begin Threshold Image typedef itk::BinaryThresholdImageFilter InputThresholderType; typename InputThresholderType::Pointer inputThresholder = InputThresholderType::New(); inputThresholder->SetInput( input ); inputThresholder->SetInsideValue( replaceval ); int outval = 0; if( (float) replaceval == (float) -1 ) { outval = 1; } inputThresholder->SetOutsideValue( outval ); if( high < low ) { high = 255; } inputThresholder->SetLowerThreshold( (PixelType) low ); inputThresholder->SetUpperThreshold( (PixelType) high ); inputThresholder->Update(); return inputThresholder->GetOutput(); } template class VectorPixelCompare { public: bool operator()( const itk::Vector & v1, const itk::Vector & v2 ) const { // Ordering of vectors based on 1st component, then second, etc. for( size_t i = 0; i < VDim; i++ ) { if( v1[i] < v2[i] ) { return true; } else if( v1[i] > v2[i] ) { return false; } } return false; } }; template void GetAffineTransformFromImage(const typename ImageType::Pointer& img, typename AffineTransform::Pointer & aff) { typedef typename ImageType::DirectionType DirectionType; typedef typename ImageType::PointType PointType; typedef typename AffineTransform::TranslationType VectorType; DirectionType direction = img->GetDirection(); VectorType translation; // translation.Fill(0); for( unsigned int i = 0; i < ImageType::GetImageDimension(); i++ ) { translation[i] = img->GetOrigin()[i]; } aff->SetMatrix(direction); // aff->SetCenter(pt); PointType pt; pt.Fill(0); aff->SetOffset(translation); aff->SetCenter(pt); // std::cout << "aff from image:" << aff << std::endl; } template void GetLargestSizeAfterWarp(typename WarperType::Pointer & warper, typename ImageType::Pointer & img, typename ImageType::SizeType & largest_size, typename ImageType::PointType & origin_warped) { typedef typename ImageType::PointType PointType; const int ImageDimension = ImageType::GetImageDimension(); // typedef typename ImageType::PointType PointType; typedef typename std::vector PointList; typedef typename ImageType::IndexType IndexType; // PointList pts_orig; PointList pts_warped; typename ImageType::SizeType imgsz; imgsz = img->GetLargestPossibleRegion().GetSize(); typename ImageType::SpacingType spacing; spacing = img->GetSpacing(); pts_warped.clear(); if( ImageDimension == 3 ) { for( int i = 0; i < 8; i++ ) { IndexType ind; switch( i ) { case 0: { ind[0] = 0; ind[1] = 0; ind[2] = 0; } break; case 1: { ind[0] = imgsz[0] - 1; ind[1] = 0; ind[2] = 0; } break; case 2: { ind[0] = 0; ind[1] = imgsz[1] - 1; ind[2] = 0; } break; case 3: { ind[0] = imgsz[0] - 1; ind[1] = imgsz[1] - 1; ind[2] = 0; } break; case 4: { ind[0] = 0; ind[1] = 0; ind[2] = imgsz[2] - 1; } break; case 5: { ind[0] = imgsz[0] - 1; ind[1] = 0; ind[2] = imgsz[2] - 1; } break; case 6: { ind[0] = 0; ind[1] = imgsz[1] - 1; ind[2] = imgsz[2] - 1; } break; case 7: { ind[0] = imgsz[0] - 1; ind[1] = imgsz[1] - 1; ind[2] = imgsz[2] - 1; } break; } PointType pt_orig, pt_warped; img->TransformIndexToPhysicalPoint(ind, pt_orig); if( warper->MultiInverseAffineOnlySinglePoint(pt_orig, pt_warped) == false ) { // std::cout << "ERROR: outside of numeric boundary with affine transform." << std::endl; throw std::exception(); } pts_warped.push_back(pt_warped); // std::cout << '[' << i << ']' << ind << ',' << pt_orig << "->" << pt_warped << std::endl; } } else if( ImageDimension == 2 ) { for( int i = 0; i < 4; i++ ) { IndexType ind; switch( i ) { case 0: { ind[0] = 0; ind[1] = 0; } break; case 1: { ind[0] = imgsz[0] - 1; ind[1] = 0; } break; case 2: { ind[0] = 0; ind[1] = imgsz[1] - 1; } break; case 3: { ind[0] = imgsz[0] - 1; ind[1] = imgsz[1] - 1; } break; } PointType pt_orig, pt_warped; img->TransformIndexToPhysicalPoint(ind, pt_orig); if( warper->MultiInverseAffineOnlySinglePoint(pt_orig, pt_warped) == false ) { // std::cout << "ERROR: outside of numeric boundary with affine transform." << std::endl; throw std::exception(); } pts_warped.push_back(pt_warped); // std::cout << '[' << i << ']' << ind << ',' << pt_orig << "->" << pt_warped << std::endl; } } else { // std::cout << "could not determine the dimension after warping for non 2D/3D volumes" << std::endl; throw std::exception(); } PointType pt_min, pt_max; pt_min = pts_warped[0]; pt_max = pts_warped[0]; for( unsigned int k = 0; k < pts_warped.size(); k++ ) { for( int i = 0; i < ImageDimension; i++ ) { pt_min[i] = (pt_min[i] < pts_warped[k][i]) ? (pt_min[i]) : (pts_warped[k][i]); pt_max[i] = (pt_max[i] > pts_warped[k][i]) ? (pt_max[i]) : (pts_warped[k][i]); } } for( int i = 0; i < ImageDimension; i++ ) { largest_size[i] = (int) (ceil( (pt_max[i] - pt_min[i]) / spacing[i]) + 1); } origin_warped = pt_min; // std::cout << "origin_warped: " << origin_warped << std::endl; // std::cout << "pt_min: " << pt_min << " pt_max:" << pt_max << " largest_size:" << largest_size << std::endl; } template typename TImageOut::Pointer arCastImage( typename TImageIn::Pointer Rimage ) { typedef itk::CastImageFilter CastFilterType; typename CastFilterType::Pointer caster = CastFilterType::New(); caster->SetInput( Rimage ); caster->Update(); return caster->GetOutput(); } template TValue Convert( std::string optionString ) { TValue value; std::istringstream iss( optionString ); iss >> value; return value; } template std::vector ConvertVector( std::string optionString ) { std::vector values; std::string::size_type crosspos = optionString.find( 'x', 0 ); if( crosspos == std::string::npos ) { values.push_back( Convert( optionString ) ); } else { std::string element = optionString.substr( 0, crosspos ) ; TValue value; std::istringstream iss( element ); iss >> value; values.push_back( value ); while ( crosspos != std::string::npos ) { std::string::size_type crossposfrom = crosspos; crosspos = optionString.find( 'x', crossposfrom + 1 ); if( crosspos == std::string::npos ) { element = optionString.substr( crossposfrom + 1, optionString.length() ); } else { element = optionString.substr( crossposfrom + 1, crosspos - ( crossposfrom + 1 ) ); } std::istringstream iss2( element ); iss2 >> value; values.push_back( value ); } } return values; } // void ANTsStringReplace( std::string &s, const std::string &search, const std::string &replace ) // { // for( size_t pos = 0; ; pos += replace.length() ) // { // pos = s.find( search, pos ); // if( pos == std::string::npos ) break; // // s.erase( pos, search.length() ); // s.insert( pos, replace ); // } // } } // end namespace // ########################################################################## // TODO: KENT: This block feels like it could be better encapsulated as a c++ class // typedef enum { INVALID_FILE = 1, AFFINE_FILE, DEFORMATION_FILE, IMAGE_AFFINE_HEADER, IDENTITY_TRANSFORM } TRAN_FILE_TYPE; // TODO: This should be a class. typedef struct { // char *filename; std::string filename; TRAN_FILE_TYPE file_type; bool do_affine_inv; // void SetValue(char *filename, TRAN_FILE_TYPE file_type, bool do_affine_inv){ // this.filename = filename; // this.file_type = file_type; // this.do_affine_inv = do_affine_inv; // }; double weight; // for average } TRAN_OPT; typedef std::vector TRAN_OPT_QUEUE; typedef struct { bool physical_units; std::vector sigma; } MLINTERP_OPT; typedef struct { bool use_NN_interpolator; bool use_MultiLabel_interpolator; bool use_BSpline_interpolator; bool use_TightestBoundingBox; char * reference_image_filename; bool use_RotationHeader; MLINTERP_OPT opt_ML; } MISC_OPT; extern TRAN_FILE_TYPE CheckFileType(const char * const str); extern TRAN_FILE_TYPE CheckFileType(const std::string & str); extern void SetAffineInvFlag(TRAN_OPT & opt, bool & set_current_affine_inv); extern void DisplayOptQueue(const TRAN_OPT_QUEUE & opt_queue); extern void DisplayOpt(const TRAN_OPT & opt); // ########################################################################## extern bool get_a_double_number(const char * const str, double & v); // TODO: KENT: These two functions have cross-platform-equivalent versions from kwSys and could be replaced. extern void FilePartsWithgz(const std::string & filename, std::string & path, std::string & name, std::string & ext); extern bool CheckFileExistence(const char * const str); extern std::string GetPreferredTransformFileType(); extern void ConvertToLowerCase( std::string& str ); #endif // __antsUtilities_h__ ants-2.2.0/Examples/antsUtilitiesTesting.cxx000066400000000000000000000311151311104306400211530ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include #include "ReadWriteData.h" #include #include "itkAffineTransform.h" #include "itkCenteredTransformInitializer.h" #include "itkCorrelationImageToImageMetricv4.h" #include "itkConjugateGradientLineSearchOptimizerv4.h" #include "itkGradientDescentOptimizerv4.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkJointHistogramMutualInformationImageToImageMetricv4.h" #include "itkMattesMutualInformationImageToImageMetricv4.h" #include "itkRegistrationParameterScalesFromPhysicalShift.h" #include "itkResampleImageFilter.h" #include "itkTransformFileWriter.h" #include "itkSimilarity2DTransform.h" #include #include namespace ants { int antsUtilitiesTesting( std::vector args, std::ostream* itkNotUsed( out_stream ) ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsUtilitiesTesting" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 3 ) { std::cerr << "Usage: " << argv[0] << " testImage whichMetric[GCorMIorMattes] numberOfIterations lowerScalexUpperScalexNumberOfScaleSamples numberOfRotationSamples doPrintMatchingFileName transformName setOfTrainingImages" << std::endl; std::cerr << "Notes: if transformName='none', no transform is printed" << std::endl; std::cerr << "Example call: " << std::endl; std::cerr << " " << argv[0] << "test.nii.gz CC 10 0.25x3x11 15 testTransform.txt 0 training*.nii.gz" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// const unsigned int ImageDimension = 2; typedef double PixelType; typedef itk::Image ImageType; // read in test image ImageType::Pointer testImage = ImageType::New(); ReadImage( testImage, argv[1] ); // read in training image file names std::vector trainingImageFileNames; for( int n = 8; n < argc; n++ ) { trainingImageFileNames.push_back( std::string( argv[n] ) ); } // Get number of iterations unsigned int numberOfIterations = atoi( argv[3] ); // Get scale parameters std::vector scaleParameters = ConvertVector( std::string( argv[4] ) ); if( scaleParameters.size() != 3 ) { std::cerr << "The scale parameters were improperly specified. See usage." << std::endl; return EXIT_FAILURE; } double scaleLowerBoundLog = std::log( scaleParameters[0] ); double scaleUpperBoundLog = std::log( scaleParameters[1] ); unsigned int scaleNumberOfSamples = static_cast( scaleParameters[2] ); double scaleDelta = ( scaleUpperBoundLog - scaleLowerBoundLog ) / static_cast( scaleNumberOfSamples - 1 ); // Get rotation sampling resolution unsigned int rotationNumberOfSamples = static_cast( atoi( argv[5] ) ); double rotationDelta = ( 2.0 * vnl_math::pi - 0.0 ) / static_cast( rotationNumberOfSamples - 1 ); // Set up metric typedef itk::ImageToImageMetricv4 ImageMetricType; typedef ImageMetricType::FixedSampledPointSetType PointSetType; ImageMetricType::Pointer imageMetric = ITK_NULLPTR; if( strcmp( argv[2], "Mattes" ) == 0 ) { typedef itk::MattesMutualInformationImageToImageMetricv4 MattesMetricType; MattesMetricType::Pointer mattesMetric = MattesMetricType::New(); mattesMetric->SetNumberOfHistogramBins( 20 ); imageMetric = mattesMetric; } else if( strcmp( argv[2], "GC" ) == 0 ) { typedef itk::CorrelationImageToImageMetricv4 GCMetricType; GCMetricType::Pointer gcMetric = GCMetricType::New(); imageMetric = gcMetric; } else if( strcmp( argv[2], "MI" ) == 0 ) { typedef itk::JointHistogramMutualInformationImageToImageMetricv4 MIMetricType; MIMetricType::Pointer miMetric = MIMetricType::New(); miMetric->SetNumberOfHistogramBins( 20 ); imageMetric = miMetric; } else { std::cerr << "Unrecognized metric option." << std::endl; return EXIT_FAILURE; } imageMetric->SetFixedImage( testImage ); imageMetric->SetVirtualDomainFromImage( testImage ); imageMetric->SetUseMovingImageGradientFilter( false ); imageMetric->SetUseFixedImageGradientFilter( false ); // identity transform for fixed image typedef itk::IdentityTransform IdentityTransformType; IdentityTransformType::Pointer identityTransform = IdentityTransformType::New(); identityTransform->SetIdentity(); imageMetric->SetFixedTransform( identityTransform ); // Do a random sampling unsigned int index = 0; unsigned int count = 0; PointSetType::Pointer pointSet = PointSetType::New(); pointSet->Initialize(); itk::ImageRegionIteratorWithIndex It( testImage, testImage->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { // take every N^th point if ( count % 20 == 0 ) { PointSetType::PointType point; testImage->TransformIndexToPhysicalPoint( It.GetIndex(), point ); pointSet->SetPoint( index++, point ); } count++; } imageMetric->SetFixedSampledPointSet( pointSet ); imageMetric->SetUseFixedSampledPointSet( true ); // Now go through the rotations + scalings to find the optimal pose. typedef itk::AffineTransform AffineTransformType; typedef itk::Similarity2DTransform SimilarityTransformType; double optimalMetricValue = itk::NumericTraits::max(); SimilarityTransformType::Pointer optimalTransform = ITK_NULLPTR; unsigned int optimalMetricIndex = 0; for( unsigned int n = 0; n < trainingImageFileNames.size(); n++ ) { double optimalMetricValuePerImage = itk::NumericTraits::max(); ImageType::Pointer trainingImage = ImageType::New(); ReadImage( trainingImage, trainingImageFileNames[n].c_str() ); imageMetric->SetMovingImage( trainingImage ); // Initialize centered transform (based on the center of the image) AffineTransformType::Pointer initialTransform = AffineTransformType::New(); typedef itk::CenteredTransformInitializer TransformInitializerType; TransformInitializerType::Pointer initializer = TransformInitializerType::New(); initializer->SetTransform( initialTransform ); initializer->SetFixedImage( testImage ); initializer->SetMovingImage( trainingImage ); initializer->GeometryOn(); initializer->InitializeTransform(); for( double angle = 0.0; angle < 2.0 * vnl_math::pi; angle += rotationDelta ) { AffineTransformType::MatrixType rotationMatrix; rotationMatrix( 0, 0 ) = rotationMatrix( 1, 1 ) = std::cos( angle ); rotationMatrix( 1, 0 ) = std::sin( angle ); rotationMatrix( 0, 1 ) = -rotationMatrix( 1, 0 ); for( double scaleLog = scaleLowerBoundLog; scaleLog <= scaleUpperBoundLog; scaleLog += scaleDelta ) { double scale = std::exp( scaleLog ); SimilarityTransformType::Pointer similarityTransform = SimilarityTransformType::New(); similarityTransform->SetCenter( initialTransform->GetCenter() ); similarityTransform->SetTranslation( initialTransform->GetTranslation() ); similarityTransform->SetMatrix( initialTransform->GetMatrix() * rotationMatrix ); similarityTransform->SetScale( scale ); imageMetric->SetMovingTransform( similarityTransform ); imageMetric->Initialize(); if( numberOfIterations > 0 ) { typedef itk::RegistrationParameterScalesFromPhysicalShift ScalesEstimatorType; ScalesEstimatorType::Pointer scalesEstimator = ScalesEstimatorType::New(); scalesEstimator->SetMetric( imageMetric ); scalesEstimator->SetTransformForward( true ); typedef itk::ConjugateGradientLineSearchOptimizerv4Template ConjugateGradientDescentOptimizerType; ConjugateGradientDescentOptimizerType::Pointer optimizer = ConjugateGradientDescentOptimizerType::New(); optimizer->SetLowerLimit( 0 ); optimizer->SetUpperLimit( 2 ); optimizer->SetEpsilon( 0.2 ); optimizer->SetLearningRate( 0.15 ); optimizer->SetMaximumStepSizeInPhysicalUnits( 0.15 ); optimizer->SetNumberOfIterations( numberOfIterations ); optimizer->SetScalesEstimator( scalesEstimator ); optimizer->SetMinimumConvergenceValue( 1e-10 ); optimizer->SetConvergenceWindowSize( 10 ); optimizer->SetDoEstimateLearningRateAtEachIteration( false ); optimizer->SetDoEstimateLearningRateOnce( true ); optimizer->SetMetric( imageMetric ); // typedef itk::GradientDescentOptimizerv4Template GradientDescentOptimizerType; // GradientDescentOptimizerType::Pointer optimizer2 = GradientDescentOptimizerType::New(); // optimizer2->SetLearningRate( 0.15 ); // optimizer2->SetMaximumStepSizeInPhysicalUnits( 0.15 ); // optimizer2->SetNumberOfIterations( numberOfIterations ); // optimizer2->SetScalesEstimator( scalesEstimator ); // optimizer2->SetMinimumConvergenceValue( 1e-6 ); // optimizer2->SetConvergenceWindowSize( 10 ); // optimizer2->SetDoEstimateLearningRateAtEachIteration( true ); // optimizer2->SetDoEstimateLearningRateOnce( false ); // optimizer2->SetMetric( imageMetric ); try { optimizer->StartOptimization(); } catch( ... ) { continue; } } // typedef itk::ResampleImageFilter ResamplerType; // ResamplerType::Pointer resampleFilter = ResamplerType::New(); // resampleFilter->SetInput( trainingImage ); // resampleFilter->SetOutputParametersFromImage( testImage ); // resampleFilter->SetTransform( similarityTransform ); // resampleFilter->SetDefaultPixelValue( 255 ); // resampleFilter->Update(); // WriteImage( resampleFilter->GetOutput(), "training.nii.gz" ); double metricValue = imageMetric->GetValue(); if( metricValue < optimalMetricValue ) { optimalMetricValue = metricValue; optimalTransform = similarityTransform; optimalMetricIndex = n; } if( metricValue < optimalMetricValuePerImage ) { optimalMetricValuePerImage = metricValue; } } } // std::cout << trainingImageFileNames[n] << " -> " << optimalMetricValuePerImage << std::endl; } if( strcmp( argv[7], "none" ) != 0 ) { typedef itk::TransformFileWriter TransformWriterType; TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetInput( optimalTransform ); transformWriter->SetFileName( argv[7] ); transformWriter->Update(); } if( static_cast( atoi( argv[6] ) ) ) { std::cout << trainingImageFileNames[optimalMetricIndex] << std::endl; } std::cout << optimalMetricValue << std::endl; return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/antsVol.cxx000066400000000000000000000470401311104306400164060ustar00rootroot00000000000000#include "antsCommandLineParser.h" #include "antsUtilities.h" #include "ReadWriteData.h" #include "itkImageToVTKImageFilter.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkIntensityWindowingImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "vtkCamera.h" #include "vtkColorTransferFunction.h" #include "vtkDataArray.h" #include "vtkImageData.h" #include "vtkImageShiftScale.h" #include "vtkMultiThreader.h" #include "vtkPiecewiseFunction.h" #include "vtkPointData.h" #include "vtkPNGWriter.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkSampleFunction.h" #include "vtkSmartPointer.h" #include "vtkSmartVolumeMapper.h" #include "vtkSphere.h" #include "vtkVolume.h" #include "vtkVolumeProperty.h" #include "vtkWindowToImageFilter.h" #include "vnl/vnl_math.h" #include #include namespace ants { int antsVolumetricRendering( itk::ants::CommandLineParser *parser ) { const unsigned int ImageDimension = 3; typedef float RealType; typedef itk::Image ImageType; typedef itk::Image MaskImageType; typedef unsigned char RgbComponentType; typedef itk::RGBPixel RgbPixelType; typedef itk::Image RgbImageType; typedef itk::RGBAPixel RgbaPixelType; typedef itk::Image RgbaImageType; // Read in input image ImageType::Pointer inputImage = ITK_NULLPTR; itk::ants::CommandLineParser::OptionType::Pointer inputImageOption = parser->GetOption( "input-image" ); if( inputImageOption && inputImageOption->GetNumberOfFunctions() ) { std::string inputFile = std::string( "" ); std::vector clipPercentage; clipPercentage.push_back( 0.0 ); clipPercentage.push_back( 1.0 ); if( inputImageOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { inputFile = inputImageOption->GetFunction( 0 )->GetName(); } else if( inputImageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { inputFile = inputImageOption->GetFunction( 0 )->GetParameter( 0 ); if( inputImageOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { clipPercentage = parser->ConvertVector( inputImageOption->GetFunction( 0 )->GetParameter( 1 ) ); } } ImageType::Pointer readImage = ITK_NULLPTR; ReadImage( readImage, inputFile.c_str() ); typedef itk::RescaleIntensityImageFilter RescaleFilterType; RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( 0.0 ); rescaler->SetOutputMaximum( 1.0 ); rescaler->SetInput( readImage ); typedef itk::IntensityWindowingImageFilter IntensityWindowingImageFilterType; IntensityWindowingImageFilterType::Pointer windower = IntensityWindowingImageFilterType::New(); windower->SetInput( rescaler->GetOutput() ); windower->SetWindowMinimum( clipPercentage[0] ); windower->SetWindowMaximum( clipPercentage[1] ); windower->SetOutputMinimum( 0.0 ); windower->SetOutputMaximum( 255.0 ); inputImage = windower->GetOutput(); inputImage->Update(); inputImage->DisconnectPipeline(); } else { std::cerr << "Input image not specified." << std::endl; return EXIT_FAILURE; } // Read in input image MaskImageType::Pointer maskImage = ITK_NULLPTR; itk::ants::CommandLineParser::OptionType::Pointer maskImageOption = parser->GetOption( "mask-image" ); if( maskImageOption && maskImageOption->GetNumberOfFunctions() ) { std::string maskFile = maskImageOption->GetFunction( 0 )->GetName(); ReadImage( maskImage, maskFile.c_str() ); } // Read in the functional overlays and alpha values std::vector functionalRgbImages; std::vector functionalMaskImages; itk::ants::CommandLineParser::OptionType::Pointer functionalOverlayOption = parser->GetOption( "functional-overlay" ); if( functionalOverlayOption && functionalOverlayOption->GetNumberOfFunctions() ) { for( unsigned int n = 0; n < functionalOverlayOption->GetNumberOfFunctions(); n++ ) { // read RGB and mask image std::string rgbFileName = std::string( "" ); std::string maskFileName = std::string( "" ); if( functionalOverlayOption->GetFunction( n )->GetNumberOfParameters() == 0 ) { rgbFileName = functionalOverlayOption->GetFunction( n )->GetName(); } else { rgbFileName = functionalOverlayOption->GetFunction( n )->GetParameter( 0 ); if( functionalOverlayOption->GetFunction( n )->GetNumberOfParameters() > 1 ) { maskFileName = functionalOverlayOption->GetFunction( n )->GetParameter( 1 ); } } typedef itk::ImageFileReader RgbReaderType; RgbReaderType::Pointer rgbReader = RgbReaderType::New(); rgbReader->SetFileName( rgbFileName.c_str() ); try { rgbReader->Update(); } catch( ... ) { std::cerr << "Error reading RGB file " << rgbFileName << std::endl; return EXIT_FAILURE; } functionalRgbImages.push_back( rgbReader->GetOutput() ); if( ! maskFileName.empty() ) { typedef itk::ImageFileReader MaskReaderType; MaskReaderType::Pointer maskReader = MaskReaderType::New(); maskReader->SetFileName( maskFileName.c_str() ); maskReader->Update(); functionalMaskImages.push_back( maskReader->GetOutput() ); } else { functionalMaskImages.push_back( ITK_NULLPTR ); } } } // Combine the functional overlays and alpha values RgbaImageType::Pointer rgbaImage = RgbaImageType::New(); rgbaImage->CopyInformation( inputImage ); rgbaImage->SetRegions( inputImage->GetRequestedRegion() ); rgbaImage->Allocate(); itk::ImageRegionConstIteratorWithIndex It( inputImage, inputImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { ImageType::IndexType index = It.GetIndex(); ImageType::PixelType pixel = It.Get(); if( maskImage.IsNotNull() && maskImage->GetPixel( index ) == 0 ) { pixel = 0.0; } // The rgb values are in the range 0,255 but we manipulate alpha values in the range // [0,1] since that is what is specified on the command line and simply renormalize // to the range [0,255] when setting the voxel. RealType currentRed = pixel / 255.0; RealType currentGreen = pixel / 255.0; RealType currentBlue = pixel / 255.0; RealType currentAlpha = pixel / 255.0; for( int i = functionalRgbImages.size() - 1; i >= 0; i-- ) { if( functionalMaskImages[i].IsNotNull() && functionalMaskImages[i]->GetPixel( index ) == 0 ) { continue; } RgbPixelType rgbPixel = functionalRgbImages[i]->GetPixel( index ); RealType functionalRed = rgbPixel.GetRed() / 255.0; RealType functionalGreen = rgbPixel.GetGreen() / 255.0; RealType functionalBlue = rgbPixel.GetBlue() / 255.0; if( functionalRed + functionalGreen + functionalBlue > 0.0 ) { currentRed = functionalRed; currentGreen = functionalGreen; currentBlue = functionalBlue; } } RgbaPixelType currentColor; currentColor.SetRed( static_cast( currentRed * 255.0 ) ); currentColor.SetGreen( static_cast( currentGreen * 255.0 ) ); currentColor.SetBlue( static_cast( currentBlue * 255.0 ) ); currentColor.SetAlpha( static_cast( currentAlpha * 255.0 ) ); rgbaImage->SetPixel( index, currentColor ); } // Get display options float magnificationFactor = 3.0; std::vector rotationAnglesInDegrees; rotationAnglesInDegrees.push_back( 0.0 ); rotationAnglesInDegrees.push_back( 0.0 ); rotationAnglesInDegrees.push_back( 0.0 ); std::vector backgroundColor; backgroundColor.push_back( 255.0 ); backgroundColor.push_back( 255.0 ); backgroundColor.push_back( 255.0 ); std::string screenCaptureFileName = std::string( "" ); bool writeScreenCaptureToFile = false; itk::ants::CommandLineParser::OptionType::Pointer displayOption = parser->GetOption( "display" ); if( displayOption && displayOption->GetNumberOfFunctions() ) { screenCaptureFileName = displayOption->GetFunction( 0 )->GetName(); if( strcmp( screenCaptureFileName.c_str(), "false" ) == 0 || strcmp( screenCaptureFileName.c_str(), "0" ) == 0 ) { // do not render and exit return EXIT_SUCCESS; } std::size_t position = screenCaptureFileName.find( "png" ); if( position == std::string::npos ) { screenCaptureFileName.clear(); } else { writeScreenCaptureToFile = true; std::cout << "Writing screenshot to image file " << screenCaptureFileName << "." << std::endl; } if( displayOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { magnificationFactor = parser->Convert( displayOption->GetFunction( 0 )->GetParameter( 0 ) ); } if( displayOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { rotationAnglesInDegrees = parser->ConvertVector( displayOption->GetFunction( 0 )->GetParameter( 1 ) ); } if( displayOption->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { backgroundColor = parser->ConvertVector( displayOption->GetFunction( 0 )->GetParameter( 2 ) ); if( backgroundColor.size() == 1 ) { backgroundColor.push_back( backgroundColor[0] ); backgroundColor.push_back( backgroundColor[0] ); } } } // Set up rendering window vtkSmartPointer renderer = vtkSmartPointer::New(); renderer->SetBackground( backgroundColor[0] / 255.0, backgroundColor[1] / 255.0, backgroundColor[2] / 255.0 ); vtkSmartPointer renderWindow = vtkSmartPointer::New(); renderWindow->AddRenderer( renderer ); renderWindow->SetSize( 301, 300 ); vtkSmartPointer renderWindowInteractor = vtkSmartPointer::New(); renderWindowInteractor->SetRenderWindow( renderWindow ); renderWindow->Render(); // Turn off multi-threading? vtkSmartPointer multiThreader = vtkSmartPointer::New(); multiThreader->SetGlobalMaximumNumberOfThreads( 1 ); // Do volumetric rendering typedef itk::ImageToVTKImageFilter ConnectorType; ConnectorType::Pointer connector = ConnectorType::New(); connector->SetInput( rgbaImage ); connector->Update(); vtkSmartPointer imageData = vtkSmartPointer::New(); imageData->ShallowCopy( connector->GetOutput() ); vtkSmartPointer volumeMapper = vtkSmartPointer::New(); volumeMapper->SetInputData( imageData ); vtkSmartPointer compositeOpacity = vtkSmartPointer::New(); compositeOpacity->AddPoint( 0.0, 0.0 ); compositeOpacity->AddPoint( 124.0, 0.25 ); compositeOpacity->AddPoint( 125.0, 0.5 ); compositeOpacity->AddPoint( 255.0, 1.0 ); vtkSmartPointer volumeProperty = vtkSmartPointer::New(); volumeProperty->ShadeOff(); volumeProperty->SetInterpolationType( VTK_LINEAR_INTERPOLATION ); volumeProperty->SetScalarOpacity( 0, compositeOpacity ); volumeProperty->IndependentComponentsOff(); vtkSmartPointer volume = vtkSmartPointer::New(); volume->SetMapper( volumeMapper ); volume->SetProperty( volumeProperty ); renderer->AddViewProp( volume ); renderer->ResetCamera(); renderer->GetActiveCamera()->Azimuth( rotationAnglesInDegrees[0] ); renderer->GetActiveCamera()->Elevation( rotationAnglesInDegrees[1] ); renderer->GetActiveCamera()->Roll( rotationAnglesInDegrees[2] ); renderer->GetActiveCamera()->Zoom( magnificationFactor ); renderWindow->Render(); // Screenshot if( writeScreenCaptureToFile ) { vtkSmartPointer windowToImageFilter = vtkSmartPointer::New(); windowToImageFilter->SetInput( renderWindow ); windowToImageFilter->SetMagnification( magnificationFactor ); windowToImageFilter->SetInputBufferTypeToRGBA(); windowToImageFilter->Update(); vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetFileName( screenCaptureFileName.c_str() ); writer->SetInputConnection( windowToImageFilter->GetOutputPort() ); writer->Write(); } else { renderWindowInteractor->Start(); } return EXIT_SUCCESS; } void InitializeCommandLineOptions( itk::ants::CommandLineParser *parser ) { typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "Main input image for 3-D rendering." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "input-image" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "imageFilename" ); option->SetUsageOption( 1, "[imageFilename,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Mask for determining volumetric rendering of main input image." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask-image" ); option->SetShortName( 'x' ); option->SetUsageOption( 0, "maskFilename" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "A functional overlay can be specified using an rgb image." ) + std::string( "Note that more than one functional overlays can " ) + std::string( "be rendered, the order in which they are specified " ) + std::string( "on the command line matters, and rgb images are " ) + std::string( "assumed to be unsigned char [0,255]. An optional mask " ) + std::string( "can also be provided." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "functional-overlay" ); option->SetShortName( 'f' ); option->SetUsageOption( 0, "rgbImageFileName" ); option->SetUsageOption( 1, "[rgbImageFileName,rgbMaskFileName]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Display output volume rendering in VTK window. Rotation " ) + std::string( "angles are in degrees and the default background color " ) + std::string( "is white (255x255x255). Note that the filename, to be " ) + std::string( "considered such, must have a \"png\" extension. If the " ) + std::string( "filename is omitted in the third usage option, then the " ) + std::string( "window is displayed." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "display" ); option->SetShortName( 'd' ); option->SetUsageOption( 0, "doWindowDisplay" ); option->SetUsageOption( 1, "filename" ); option->SetUsageOption( 2, "[,,]" ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); parser->AddOption( option ); } { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); parser->AddOption( option ); } } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int antsVol( std::vector args, std::ostream* /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "antsVol" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer parser = itk::ants::CommandLineParser::New(); parser->SetCommand( argv[0] ); std::string commandDescription = std::string( "Produce a 3-D volume rendering with optional RGB overlay. " ); parser->SetCommandDescription( commandDescription ); InitializeCommandLineOptions( parser ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( argc == 1 ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_FAILURE; } else if( parser->GetOption( "help" )->GetFunction() && parser->Convert( parser->GetOption( "help" )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } else if( parser->GetOption( 'h' )->GetFunction() && parser->Convert( parser->GetOption( 'h' )->GetFunction()->GetName() ) ) { parser->PrintMenu( std::cout, 5, true ); return EXIT_SUCCESS; } // Get dimensionality itk::ants::CommandLineParser::OptionType::Pointer imageOption = parser->GetOption( "input-image" ); if( imageOption && imageOption->GetNumberOfFunctions() > 0 ) { std::string inputFile; if( imageOption->GetFunction( 0 )->GetNumberOfParameters() == 0 ) { inputFile = imageOption->GetFunction( 0 )->GetName(); } else if( imageOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { inputFile = imageOption->GetFunction( 0 )->GetParameter( 0 ); } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( inputFile.c_str(), itk::ImageIOFactory::ReadMode ); unsigned int dimension = imageIO->GetNumberOfDimensions(); if( dimension == 3 ) { antsVolumetricRendering( parser ); } else { std::cerr << "Unsupported dimension" << std::endl; return EXIT_FAILURE; } } else { std::cerr << "Input image not specified. See help menu." << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/compareTwoTransforms.cxx000066400000000000000000000241641311104306400211610ustar00rootroot00000000000000#include "itkImage.h" #include "itkTransform.h" #include "itkCompositeTransform.h" #include "itkantsReadWriteTransform.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkBSplineTransform.h" #include #include #include #include "antsUtilities.h" namespace ants { template int compareTransforms( const typename itk::Transform::Pointer & firstTransform, const typename itk::Transform::Pointer & secondTransform ) { typedef typename itk::CompositeTransform CompositeTransformType; typedef typename CompositeTransformType::ScalarType RealType; typedef typename itk::DisplacementFieldTransform DisplacementFieldTransformType; typedef typename DisplacementFieldTransformType::DisplacementFieldType DisplacementFieldType; const std::string CompositeTransformID ("CompositeTransform"); if( firstTransform->GetNameOfClass() == CompositeTransformID && secondTransform->GetNameOfClass() == CompositeTransformID ) { std::cout << "The input transforms are composite transform types." << std::endl; const typename CompositeTransformType::ConstPointer Comp1 = dynamic_cast *>( firstTransform.GetPointer() ); const typename CompositeTransformType::ConstPointer Comp2 = dynamic_cast *>( secondTransform.GetPointer() ); if( Comp1->GetNumberOfTransforms() == Comp2->GetNumberOfTransforms() ) { const float coordinateTolerance = 1e-0; const unsigned int N = Comp1->GetNumberOfTransforms(); for(unsigned int i = 0; i < N; ++i) { if( Comp1->GetNthTransform(i)->GetNameOfClass() == Comp2->GetNthTransform(i)->GetNameOfClass() ) { if( strcmp( Comp1->GetNthTransform(i)->GetNameOfClass(), "DisplacementFieldTransform" ) ) { if( !Comp1->GetNthTransform(i)->GetFixedParameters().is_equal( Comp2->GetNthTransform(i)->GetFixedParameters(), coordinateTolerance ) || !Comp1->GetNthTransform(i)->GetParameters().is_equal( Comp2->GetNthTransform(i)->GetParameters(), coordinateTolerance ) ) { std::cerr << i << ": " << std::endl; std::cerr << Comp1->GetNthTransform(i)->GetParameters() << std::endl; std::cerr << Comp2->GetNthTransform(i)->GetParameters() << std::endl; std::cerr << "The input composite transforms are not equal! The transform parameters are different!" << std::endl; return EXIT_FAILURE; } } else { // compare two displacement field transforms by considering a tolerance const typename DisplacementFieldTransformType::ConstPointer DispTrans1 = dynamic_cast( Comp1->GetNthTransform(i).GetPointer() ); const typename DisplacementFieldTransformType::ConstPointer DispTrans2 = dynamic_cast( Comp2->GetNthTransform(i).GetPointer() ); const typename DisplacementFieldType::ConstPointer DispField1 = DispTrans1->GetDisplacementField(); const typename DisplacementFieldType::ConstPointer DispField2 = DispTrans2->GetDisplacementField(); typedef itk::ImageRegionConstIteratorWithIndex DispIteratorType; DispIteratorType dit1( DispField1, DispField1->GetLargestPossibleRegion() ); DispIteratorType dit2( DispField2, DispField2->GetLargestPossibleRegion() ); dit1.GoToBegin(); dit2.GoToBegin(); while( !dit1.IsAtEnd() && !dit2.IsAtEnd() ) { typename DisplacementFieldType::PixelType v1 = dit1.Get(); typename DisplacementFieldType::PixelType v2 = dit2.Get(); for (unsigned int index=0; indexGetNameOfClass() == DisplacementFieldTransformID && secondTransform->GetNameOfClass() == DisplacementFieldTransformID ) { std::cout << "The input transforms are displacement field transform type." << std::endl; // compare two displacement field transforms by considering a tolerance const typename DisplacementFieldTransformType::ConstPointer DispTrans1 = dynamic_cast( firstTransform.GetPointer() ); const typename DisplacementFieldTransformType::ConstPointer DispTrans2 = dynamic_cast( secondTransform.GetPointer() ); const typename DisplacementFieldType::ConstPointer DispField1 = DispTrans1->GetDisplacementField(); const typename DisplacementFieldType::ConstPointer DispField2 = DispTrans2->GetDisplacementField(); typedef itk::ImageRegionConstIteratorWithIndex DispIteratorType; DispIteratorType dit1( DispField1, DispField1->GetLargestPossibleRegion() ); DispIteratorType dit2( DispField2, DispField2->GetLargestPossibleRegion() ); dit1.GoToBegin(); dit2.GoToBegin(); while( !dit1.IsAtEnd() && !dit2.IsAtEnd() ) { typename DisplacementFieldType::PixelType v1 = dit1.Get(); typename DisplacementFieldType::PixelType v2 = dit2.Get(); for (unsigned int index=0; indexGetNameOfClass() << std::endl; std::cout << "Second Transform Type: " << secondTransform->GetNameOfClass() << std::endl; if( firstTransform->GetFixedParameters() != secondTransform->GetFixedParameters() || firstTransform->GetParameters() != secondTransform->GetParameters() ) { std::cerr << "The input transforms are not equal! The transform parameters are different!" << std::endl; return EXIT_FAILURE; } } } std::cout << "Two input transforms are the same!" << std::endl; return EXIT_SUCCESS; } int compareTwoTransforms( std::vector args, std::ostream* /* out_stream = NULL */ ) { // the arguments coming in as 'args' is a replacement for the standard (argc,argv) format // Just notice that the argv[i] equals to args[i-1] // and the argc equals: int argc = args.size() + 1; if( argc != 3 ) { std::cerr << "Usage: compareTwoTransforms\n" << " , " << std::endl; return EXIT_FAILURE; } { itk::Transform::Pointer firstTransform = itk::ants::ReadTransform(args[0]); itk::Transform::Pointer secondTransform = itk::ants::ReadTransform(args[1]); if( firstTransform.IsNotNull() && secondTransform.IsNotNull() ) { typedef itk::BSplineTransform< double, 2, 2> BSplineTransformType; BSplineTransformType::Pointer bsplineInput1 = dynamic_cast( firstTransform.GetPointer() ); BSplineTransformType::Pointer bsplineInput2 = dynamic_cast( secondTransform.GetPointer() ); if( bsplineInput1.IsNull() && bsplineInput2.IsNull() ) { return compareTransforms<2>(firstTransform, secondTransform); } else { std::cerr << "BSpline transform type is not supported." << std::endl; return EXIT_FAILURE; } } } { itk::Transform::Pointer firstTransform = itk::ants::ReadTransform(args[0]); itk::Transform::Pointer secondTransform = itk::ants::ReadTransform(args[1]); if( firstTransform.IsNotNull() && secondTransform.IsNotNull() ) { typedef itk::BSplineTransform< double, 3, 3> BSplineTransformType; BSplineTransformType::Pointer bsplineInput1 = dynamic_cast(firstTransform.GetPointer() ); BSplineTransformType::Pointer bsplineInput2 = dynamic_cast(secondTransform.GetPointer() ); if( bsplineInput1.IsNull() && bsplineInput2.IsNull() ) { return compareTransforms<3>(firstTransform, secondTransform); } else { std::cerr << "BSpline transform type is not supported." << std::endl; return EXIT_FAILURE; } } } std::cerr << "Can't read input transforms" << std::endl; return EXIT_FAILURE; } } // namespace ants ants-2.2.0/Examples/iMath.cxx000066400000000000000000000777061311104306400160360ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "iMathFunctions.h" #include "ReadWriteData.h" #include "antsUtilities.h" namespace ants { void WIP(int argc, char **argv) { std::cout << "You have reached an umimplemented section of code by calling:" << std::endl; for (int i=0; i bool from_string(T& t, const std::string& s, std::ios_base & (*f)(std::ios_base &) ) { std::istringstream iss(s); iss >> f >> t; // Check to see that there is nothing left over if( !iss.eof() ) { return false; } return true; } template std::string ants_to_string(T t) { std::stringstream istream; istream << t; return istream.str(); } std::string ANTSOptionName(const char *str) { std::string filename = str; std::string::size_type pos = filename.rfind( "=" ); std::string name = std::string( filename, 0, pos ); return name; } std::string ANTSOptionValue(const char *str) { std::string filename = str; std::string::size_type pos = filename.rfind( "=" ); std::string value = std::string( filename, pos + 1, filename.length() ); return value; } std::string ANTSGetFilePrefix(const char *str) { const std::string filename = str; std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); #if 0 // HACK: This does nothing useful if( pos != std::string::npos ) { std::string extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); // extension = std::string( filepre, pos, filepre.length() - 1 ); } } #endif return filepre; } // // iMath was a gigantic switch statement that had 3 duplicated // lists of 'if (operation == )' clauses for 2d, 3d, and 4d. I // figured out which functions were 2D only, 3D only and 4D Only, // which were valid for all dimensions, which were 2d and 3d, and // which were 3d and 4d. // So there's a template method for each case, and they're assembled // for each dimension in an Explicit Template Function below. template int iMathHelper2DOnly(int argc, char **argv) { std::string operation = std::string(argv[3]); WIP(argc, argv); return EXIT_FAILURE; } template int iMathHelper2DOr3D(int argc, char **argv) { std::string operation = std::string(argv[3]); WIP(argc, argv); return EXIT_FAILURE; } template int iMathHelper3DOr4D(int argc, char **argv) { std::string operation = std::string(argv[3]); WIP(argc, argv); return EXIT_FAILURE; } template int iMathHelper3DOnly(int argc, char **argv) { std::string operation = std::string(argv[3]); WIP(argc, argv); return EXIT_FAILURE; } template int iMathHelper4DOnly(int argc, char **argv) { std::string operation = std::string(argv[3]); WIP(argc, argv); return EXIT_FAILURE; } template int iMathHelperAll(int argc, char **argv) { std::string operation = std::string(argv[3]); std::string inName = std::string(argv[4]); std::string outName = std::string(argv[2]); typedef float PixelType; if( operation == "BlobDetector" ) { typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; if ( argc < 6 ) { std::cout << "BlobDetector: Not enough input parameters" << std::endl; return EXIT_FAILURE; } unsigned int nBlobs = atoi( argv[5] ); ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathBlobDetector(input,nBlobs); } catch( itk::ExceptionObject & excep ) { std::cout << "BlobDetector: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "Canny" ) { typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; if ( argc < 8 ) { std::cout << "Canny: Not enough input parameters" << std::endl; return EXIT_FAILURE; } double sigma = atof( argv[5] ); double lower = atof( argv[6] ); double upper = atof( argv[7] ); ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathCanny(input,sigma,lower,upper); } catch( itk::ExceptionObject & excep ) { std::cout << "Canny: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "DistanceMap" ) { typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; bool useSpacing = iMathDistanceMapUseSpacing; if ( argc >= 6 ) { useSpacing = atoi(argv[5]); } ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathDistanceMap(input, useSpacing); } catch( itk::ExceptionObject & excep ) { std::cout << "DistanceMap: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "FillHoles" ) { typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; double holeType = iMathFillHolesHoleParam; if ( argc >= 6 ) { holeType = atof(argv[5]); } ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathFillHoles(input, holeType); } catch( itk::ExceptionObject & excep ) { std::cout << "FillHoles: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "GC" ) { unsigned long radius = iMathGCRadius; if ( argc >= 6 ) { radius = atoi(argv[5]); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathGC(input, radius); } catch( itk::ExceptionObject & excep ) { std::cout << "GC: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "GD" ) { unsigned long radius = iMathGDRadius; if ( argc >= 6 ) { radius = atoi(argv[5]); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathGD(input, radius); } catch( itk::ExceptionObject & excep ) { std::cout << "GD: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "GE" ) { unsigned long radius = iMathGERadius; if ( argc >= 6 ) { radius = atoi(argv[5]); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathGE(input, radius); } catch( itk::ExceptionObject & excep ) { std::cout << "GE: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "GO" ) { unsigned long radius = iMathMORadius; if ( argc >= 6 ) { radius = atoi(argv[5]); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathGO(input, radius); } catch( itk::ExceptionObject & excep ) { std::cout << "GO: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "GetLargestComponent" ) { unsigned long minSize = iMathGetLargestComponentMinSize; if ( argc > 5) { minSize = atoi( argv[5] ); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathGetLargestComponent(input, minSize); } catch( itk::ExceptionObject & excep ) { std::cout << "GetLargestComponents: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "Grad" ) { double sigma = iMathGradSigma; bool normalize = iMathGradNormalize; if ( argc >= 6 ) { sigma = atof(argv[5]); } if ( argc >= 7 ) { normalize = (bool) atoi(argv[6]); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathGrad(input, sigma, normalize); } catch( itk::ExceptionObject & excep ) { std::cout << "Grad: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "HistogramEqualization" ) { typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; float alpha = 0; float beta = 1; if ( argc >= 6 ) { alpha = atof(argv[5]); } if ( argc >= 7 ) { beta = atof(argv[6]); } ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { std::cout << " a " << alpha << " b " << beta << std::endl; output = iMathHistogramEqualization(input, alpha, beta, 1 ); } catch( itk::ExceptionObject & excep ) { std::cout << "HistogramEqualization: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "Laplacian" ) { double sigma = iMathLaplacianSigma; bool normalize = iMathLaplacianNormalize; if ( argc >= 6 ) { sigma = atof(argv[5]); } if ( argc >= 7 ) { normalize = (bool) atoi(argv[6]); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathLaplacian(input, sigma, normalize); } catch( itk::ExceptionObject & excep ) { std::cout << "Laplacian: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "MC" ) { unsigned long radius = iMathMCRadius; PixelType value = iMathMCValue; unsigned int shape = iMathGetFlatStructuringElementShape; bool parametric = iMathGetFlatStructuringElementRadiusIsParametric; unsigned int lines = iMathGetFlatStructuringElementLines; unsigned int thickness = iMathGetFlatStructuringElementThickness; bool includeCenter = iMathGetFlatStructuringElementIncludeCenter; if ( argc >= 6 ) { radius = atoi(argv[5]); } if ( argc >= 7 ) { value = (PixelType)( atof( argv[6]) ); } if ( argc >= 8 ) { shape = morph_shape_flag( argv[7] ); //shape = atoi(argv[7]); } if ( argc >= 9 ) { if (shape==5) { lines = atoi(argv[8]); } else { parametric = ( atoi(argv[8])==1 ); } } if ( argc >= 10 ) //shape = 4 (annulus) only { thickness = atoi(argv[9]); } if ( argc >= 11 ) { includeCenter = ( atoi(argv[10])==1 ); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathMC(input, radius, value, shape, parametric, lines, thickness, includeCenter); } catch( itk::ExceptionObject & excep ) { std::cout << "MC: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "MD" ) { unsigned long radius = iMathMDRadius; PixelType value = iMathMDValue; unsigned int shape = iMathGetFlatStructuringElementShape; bool parametric = iMathGetFlatStructuringElementRadiusIsParametric; unsigned int lines = iMathGetFlatStructuringElementLines; unsigned int thickness = iMathGetFlatStructuringElementThickness; bool includeCenter = iMathGetFlatStructuringElementIncludeCenter; if ( argc >= 6 ) { radius = atoi(argv[5]); } if ( argc >= 7 ) { value = (PixelType)( atof( argv[6]) ); } if ( argc >= 8 ) { shape = morph_shape_flag( argv[7] ); } if ( argc >= 9 ) { if (shape==5) { lines = atoi(argv[8]); } else { parametric = ( atoi(argv[8])==1 ); } } if ( argc >= 10 ) //shape = 4 (annulus) only { thickness = atoi(argv[9]); } if ( argc >= 11 ) { includeCenter = ( atoi(argv[10])==1 ); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathMD(input, radius, value, shape, parametric, lines, thickness, includeCenter); } catch( itk::ExceptionObject & excep ) { std::cout << "MD: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "ME" ) { unsigned long radius = iMathMERadius; PixelType value = iMathMEValue; unsigned int shape = iMathGetFlatStructuringElementShape; bool parametric = iMathGetFlatStructuringElementRadiusIsParametric; unsigned int lines = iMathGetFlatStructuringElementLines; unsigned int thickness = iMathGetFlatStructuringElementThickness; bool includeCenter = iMathGetFlatStructuringElementIncludeCenter; if ( argc >= 6 ) { radius = atoi(argv[5]); } if ( argc >= 7 ) { value = (PixelType)( atof( argv[6]) ); } if ( argc >= 8 ) { shape = morph_shape_flag( argv[7] ); } if ( argc >= 9 ) { if (shape==5) { lines = atoi(argv[8]); } else { parametric = ( atoi(argv[8])==1 ); } } if ( argc >= 10 ) //shape = 4 (annulus) only { thickness = atoi(argv[9]); } if ( argc >= 11 ) { includeCenter = ( atoi(argv[10])==1 ); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathME(input, radius, value, shape, parametric, lines, thickness, includeCenter); } catch( itk::ExceptionObject & excep ) { std::cout << "ME: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "MO" ) { unsigned long radius = iMathMORadius; PixelType value = iMathMOValue; unsigned int shape = iMathGetFlatStructuringElementShape; bool parametric = iMathGetFlatStructuringElementRadiusIsParametric; unsigned int lines = iMathGetFlatStructuringElementLines; unsigned int thickness = iMathGetFlatStructuringElementThickness; bool includeCenter = iMathGetFlatStructuringElementIncludeCenter; if ( argc >= 6 ) { radius = atoi(argv[5]); } if ( argc >= 7 ) { value = (PixelType)( atof( argv[6]) ); } if ( argc >= 8 ) { shape = morph_shape_flag( argv[7] ); } if ( argc >= 9 ) { if (shape==5) { lines = atoi(argv[8]); } else { parametric = ( atoi(argv[8])==1 ); } } if ( argc >= 10 ) //shape = 4 (annulus) only { thickness = atoi(argv[9]); } if ( argc >= 11 ) { includeCenter = ( atoi(argv[10])==1 ); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathMO(input, radius, value, shape, parametric, lines, thickness, includeCenter); } catch( itk::ExceptionObject & excep ) { std::cout << "MO: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "MaurerDistance" ) { PixelType foreground = iMathMaurerDistanceForeground; if ( argc >= 6 ) { foreground = (PixelType) atof(argv[5]); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathMaurerDistance(input, foreground); } catch( itk::ExceptionObject & excep ) { std::cout << "MaurerDistance: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "Normalize" ) { typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathNormalize(input); } catch( itk::ExceptionObject & excep ) { std::cout << "Normalize: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "Pad" ) { int padding = atoi(argv[5]); typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathPad(input, padding); } catch( itk::ExceptionObject & excep ) { std::cout << "Pad: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "PeronaMalik" ) { double conductance = iMathPeronaMalikConductance; unsigned long nIterations = iMathPeronaMalikNIterations; if ( argc >= 6 ) { nIterations = atoi(argv[5]); } if ( argc >= 7 ) { conductance = (PixelType) atof(argv[6]); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathPeronaMalik(input, nIterations, conductance); } catch( itk::ExceptionObject & excep ) { std::cout << "PeronaMalik: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "Sharpen" ) { typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathSharpen(input); } catch( itk::ExceptionObject & excep ) { std::cout << "Sharpen: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if (operation == "PropagateLabelsThroughMask" ) { typedef itk::Image ImageType; if ( argc < 6 ) { std::cerr << "PropogateLabelsThroughMask needs a mask and an image of labels" << std::endl; return EXIT_FAILURE; } typename ImageType::Pointer mask = NULL; typename ImageType::Pointer labels = NULL; ReadImage( mask, inName.c_str() ); ReadImage( labels, argv[5] ); double stoppingValue = iMathPropagateLabelsThroughMaskStoppingValue; unsigned int propagationMethod = iMathPropagateLabelsThroughMaskMethod; if ( argc > 6 ) { stoppingValue = (double) atof(argv[6]); } if ( argc > 7) { propagationMethod = atoi(argv[7]); } if ( propagationMethod > 2 ) { std::cerr << "Propogation method must be one of: 0,1,2" << std::endl; return EXIT_FAILURE; } typename ImageType::Pointer output = iMathPropagateLabelsThroughMask(mask,labels,stoppingValue,propagationMethod); WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } else if( operation == "TruncateIntensity" ) { typedef itk::Image ImageType; typedef itk::Image MaskType; int nBins = iMathTruncateIntensityNBins; if ( argc < 7 ) { std::cerr << "TruncateIntensity needs a lower and upper quantile" << std::endl; return EXIT_FAILURE; } double lowerQ = atof( argv[5] ); double upperQ = atof( argv[6] ); if ( argc >= 8 ) { nBins= atoi(argv[7]); } typename MaskType::Pointer mask = NULL; if ( argc >= 9 ) { ReadImage( mask, argv[8] ); } typedef itk::Image ImageType; typename ImageType::Pointer input = NULL; typename ImageType::Pointer output = NULL; ReadImage( input, inName.c_str() ); if ( input.IsNull() ) { return EXIT_FAILURE; } try { output = iMathTruncateIntensity(input, lowerQ, upperQ, nBins, mask); } catch( itk::ExceptionObject & excep ) { std::cout << "TruncateIntensity: exception caught !" << std::endl; std::cout << excep << std::endl; } WriteImage( output, outName.c_str() ); return EXIT_SUCCESS; } return EXIT_FAILURE; } template int iMathHelper(int , char **) { return 1; } template <> int iMathHelper<2>(int argc, char **argv) { int returnval = iMathHelperAll<2>(argc,argv); if(returnval == EXIT_FAILURE) { returnval = iMathHelper2DOnly<2>(argc,argv); } if(returnval == EXIT_FAILURE) { returnval = iMathHelper2DOr3D<2>(argc,argv); } return returnval; } template <> int iMathHelper<3>(int argc, char **argv) { int returnval = iMathHelperAll<3>(argc,argv); if(returnval == EXIT_FAILURE) { returnval = iMathHelper3DOnly<3>(argc,argv); } if(returnval == EXIT_FAILURE) { returnval = iMathHelper2DOr3D<3>(argc,argv); } if(returnval == EXIT_FAILURE) { returnval = iMathHelper3DOr4D<3>(argc,argv); } return returnval; } template <> int iMathHelper<4>(int argc, char **argv) { int returnval = iMathHelperAll<4>(argc,argv); if(returnval == EXIT_FAILURE) { returnval = iMathHelper4DOnly<4>(argc,argv); } if(returnval == EXIT_FAILURE) { returnval = iMathHelper3DOr4D<4>(argc,argv); } return returnval; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int iMath( std::vector args, std::ostream * itkNotUsed( out_stream ) ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "iMath" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); if( argc < 5 ) { std::cout << "\nUsage: " << argv[0] << " ImageDimension [operations and inputs] " << std::endl; std::cout << "\nUsage Information " << std::endl; std::cout << " ImageDimension: 2 or 3 (for 2 or 3 dimensional operations)." << std::endl; std::cout << " ImageDimension: 4 (for operations on 4D file, e.g. time-series data)." << std::endl; std::cout << " Operator: See list of valid operators below." << std::endl; std::cout << std::endl; std::cout << "Mask and Label set operations" << std::endl; std::cout << "-----------------------------" << std::endl; std::cout << " GetLargestComponent : Get the single largest labeled object in an image" << std::endl; std::cout << " Usage : GetLargestComponent InputImage.ext {MinObjectSize=50}" << std::endl; std::cout << std::endl; std::cout << "Morphological operations" << std::endl; std::cout << "-----------------------------" << std::endl; std::cout << "Possible operations are:" << std::endl; std::cout << " MC = Closing" << std::endl; std::cout << " MD = Dilation" << std::endl; std::cout << " ME = Erosion" << std::endl; std::cout << " MO = Opening" << std::endl; std::cout << "Possible values for the shape parameter and associated settings are:" << std::endl; std::cout << " ball {RadiusIsParmetric=0}" << std::endl; std::cout << " box" << std::endl; std::cout << " cross" << std::endl; std::cout << " annulus {RadiusIsParametric=0} {Thickness=1} {IncludeCenter=0}" << std::endl; std::cout << " polygon {Lines=3}" << std::endl; std::cout << "Description of Parameters" << std::endl; std::cout << " RadiusIsParametric: The 'ball' and 'annulus' structuring elements have an optional flag called 'radiusIsParametric' (off by default). Setting this flag to true will use the parametric definition of the object and will generate structuring elements with more accurate areas, which can be especially important when morphological operations are intended to remove or retain objects of particular sizes. When the mode is turned off (default), the radius is the same, but the object diameter is set to (radius*2)+1, which is the size of the neighborhood region. Thus, the original ball and annulus structuring elements have a systematic bias in the radius of +0.5 voxels in each dimension relative to the parametric definition of the radius. Thus, we recommend turning this mode on for more accurate structuring elements, but this mode is turned off by default for backward compatibility." << std::endl; std::cout << std::endl << " Usage : Operation InputImage.ext {Radius=1} {ObjectValue=1} {Shape=1} {RadiusIsParametric=0 or Lines=3} {Thickness=1} {IncludeCenter=0}" << std::endl; if( argc >= 2 && ( std::string( argv[1] ) == std::string("--help") || std::string( argv[1] ) == std::string("-h") ) ) { return EXIT_SUCCESS; } return EXIT_FAILURE; } int returnvalue = EXIT_SUCCESS; std::string operation = std::string(argv[3]); unsigned int imageDimension = atoi(argv[1]); switch( imageDimension ) { case 2: returnvalue = iMathHelper<2>(argc,argv); break; case 3: returnvalue = iMathHelper<3>(argc,argv); break; case 4: returnvalue = iMathHelper<4>(argc,argv); break; default: std::cout << " Dimension " << imageDimension << " is not supported " << std::endl; return EXIT_FAILURE; } if ( returnvalue == EXIT_FAILURE ) { std::cout << " Operation " << operation << " not found or not supported for dimension " << imageDimension << std::endl; } return returnvalue; } } // namespace ants ants-2.2.0/Examples/iMathFunctions.h000066400000000000000000000160241311104306400173360ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __iMathFunctions_h #define __iMathFunctions_h #include "antsUtilities.h" #include "itkFlatStructuringElement.h" namespace ants { // Templated functions that perform the work for // iMath.cxx and iMath.cpp (in ANTSR) // after each function, suggested default parameters are defined unsigned int morph_shape_flag( const char * shape ); template typename ImageType::Pointer iMathBlobDetector( typename ImageType::Pointer image, unsigned int nBlobs); // Canny Edge Filter template typename ImageType::Pointer iMathCanny(typename ImageType::Pointer image, double sigma, double lowerThreshold, double upperThreshold ); // Distance Map template typename ImageType::Pointer iMathDistanceMap(typename ImageType::Pointer image, bool useSpacing ); #define iMathDistanceMapUseSpacing true; // Fill Holes in objects template typename ImageType::Pointer iMathFillHoles(typename ImageType::Pointer image, double holeParam ); #define iMathFillHolesHoleParam 2; // Return the largest connected component in a mask template typename ImageType::Pointer iMathGetLargestComponent(typename ImageType::Pointer image, unsigned long minSize ); #define iMathGetLargestComponentMinSize 50; template typename itk::FlatStructuringElement iMathGetFlatStructuringElement( unsigned int shape, unsigned long radius, bool radiusIsParametric, unsigned int lines, unsigned int thickness, bool includeCenter ); #define iMathGetFlatStructuringElementShape 1; #define iMathGetFlatStructuringElementRadius 1; #define iMathGetFlatStructuringElementLines 3; #define iMathGetFlatStructuringElementThickness 1; #define iMathGetFlatStructuringElementIncludeCenter false; #define iMathGetFlatStructuringElementRadiusIsParametric false; // Morphological Closing template typename ImageType::Pointer iMathMC(typename ImageType::Pointer image, unsigned long radius, typename ImageType::PixelType closeValue, unsigned int shape, bool radiusIsParametric, unsigned lines, unsigned int thickness, bool includeCenter ); #define iMathMCRadius 1; #define iMathMCValue 1; // Morphological dilation template typename ImageType::Pointer iMathMD(typename ImageType::Pointer image, unsigned long radius, typename ImageType::PixelType dilateValue, unsigned int shape, bool radiusIsParametric, unsigned lines, unsigned int thickness, bool includeCenter ); #define iMathMDRadius 1; #define iMathMDValue 1; // Morphological erosion template typename ImageType::Pointer iMathME(typename ImageType::Pointer image, unsigned long radius, typename ImageType::PixelType erodeValue, unsigned int shape, bool radiusIsParametric, unsigned lines, unsigned int thickness, bool includeCenter ); #define iMathMERadius 1; #define iMathMEValue 1; // Morphological opening template typename ImageType::Pointer iMathMO(typename ImageType::Pointer image, unsigned long radius, typename ImageType::PixelType openValue, unsigned int shape, bool radiusIsParametric, unsigned lines, unsigned int thickness, bool includeCenter ); #define iMathMORadius 1; #define iMathMOValue 1; // Maurer distance - returns Euclidean distance to binary object template typename ImageType::Pointer iMathMaurerDistance(typename ImageType::Pointer image, typename ImageType::PixelType foreground ); #define iMathMaurerDistanceForeground 1; // Grayscale morphological closing template typename ImageType::Pointer iMathGC(typename ImageType::Pointer image, unsigned long radius); #define iMathGCRadius 1; #define iMathGCValue 1; // Grayscale morphological dilation template typename ImageType::Pointer iMathGD(typename ImageType::Pointer image, unsigned long radius); #define iMathGDRadius 1; #define iMathGDValue 1; // Grayscale morphological erosion template typename ImageType::Pointer iMathGE(typename ImageType::Pointer image, unsigned long radius); #define iMathGERadius 1; #define iMathGEValue 1; // Grayscale morphological opening template typename ImageType::Pointer iMathGO(typename ImageType::Pointer image, unsigned long radius); #define iMathGORadius 1; #define iMathGOValue 1; template typename ImageType::Pointer iMathGrad( typename ImageType::Pointer image, double sigma, bool normalize ); #define iMathGradSigma 0.5; #define iMathGradNormalize false; template typename ImageType::Pointer iMathHistogramEqualization( typename ImageType::Pointer image, double, double, unsigned int ); template typename ImageType::Pointer iMathLaplacian( typename ImageType::Pointer image, double sigma, bool normalize ); #define iMathLaplacianSigma 0.5; #define iMathLaplacianNormalize false; // Normalize intensity values to lie in [0,1] template typename ImageType::Pointer iMathNormalize( typename ImageType::Pointer image ); template typename ImageType::Pointer iMathPad( typename ImageType::Pointer image, int padding ); template typename ImageType::Pointer iMathPeronaMalik( typename ImageType::Pointer image, unsigned long nIterations, double conductance ); #define iMathPeronaMalikConductance 0.25; #define iMathPeronaMalikNIterations 1; template typename ImageType::Pointer iMathPropagateLabelsThroughMask( typename ImageType::Pointer mask, typename ImageType::Pointer lables, double stoppingValue, unsigned int propagationMethod ); #define iMathPropagateLabelsThroughMaskStoppingValue 100.0; #define iMathPropagateLabelsThroughMaskMethod 0; template typename ImageType::Pointer iMathSharpen( typename ImageType::Pointer image ); template typename ImageType::Pointer iMathTruncateIntensity( typename ImageType::Pointer image, double lowerQ, double upperQ, int nBins, typename itk::Image::Pointer maskImage ); #define iMathTruncateIntensityNBins 64; } #include "iMathFunctions.hxx" #endif ants-2.2.0/Examples/iMathFunctions.hxx000066400000000000000000001162771311104306400177310ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "iMathFunctions.h" #include "ReadWriteData.h" #include "antsUtilities.h" #include "itkAdaptiveHistogramEqualizationImageFilter.h" #include "itkBinaryBallStructuringElement.h" #include "itkBinaryErodeImageFilter.h" #include "itkBinaryDilateImageFilter.h" #include "itkBinaryMorphologicalClosingImageFilter.h" #include "itkBinaryMorphologicalOpeningImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkCannyEdgeDetectionImageFilter.h" #include "itkCastImageFilter.h" #include "itkConnectedComponentImageFilter.h" #include "itkDanielssonDistanceMapImageFilter.h" #include "itkFastMarchingImageFilterBase.h" #include "itkFastMarchingThresholdStoppingCriterion.h" #include "itkFlatStructuringElement.h" #include "itkGradientAnisotropicDiffusionImageFilter.h" #include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" #include "itkGrayscaleDilateImageFilter.h" #include "itkGrayscaleErodeImageFilter.h" #include "itkGrayscaleMorphologicalClosingImageFilter.h" #include "itkGrayscaleMorphologicalOpeningImageFilter.h" #include "itkIdentityTransform.h" #include "itkIntensityWindowingImageFilter.h" #include "itkLabelContourImageFilter.h" #include "itkLabelStatisticsImageFilter.h" #include "itkLaplacianRecursiveGaussianImageFilter.h" #include "itkLaplacianSharpeningImageFilter.h" #include "itkMultiScaleLaplacianBlobDetectorImageFilter.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkRelabelComponentImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkSignedMaurerDistanceMapImageFilter.h" #include "itkImageFileWriter.h" namespace ants { /* template typename ImageType::Pointer BlobCorrespondence( typename ImageType::Pointer image, unsigned int nBlobs, typename ImageType::Pointer itkNotUsed(image2), double itkNotUsed(corrThresh), double itkNotUsed(radius), double itkNotUsed(distanceThresh) ) { typedef float RealType; // sensitive parameters are set here - begin //RealType gradsig = 1.0; // sigma for gradient filter unsigned int stepsperoctave = 10; // number of steps between doubling of scale RealType minscale = std::pow( 1.0, 1.0 ); RealType maxscale = std::pow( 2.0, 10.0 ); //RealType uniqfeat_thresh = 0.01; //RealType smallval = 1.e-2; // assumes images are normalizes in [ 0, 1 ] //bool dosinkhorn = false; //RealType maxradiusdiffallowed = 0.25; // IMPORTANT feature size difference //RealType kneighborhoodval = 3; // IMPORTANT - defines how many nhood nodes to use in k-hood definition //unsigned int radval = 20; // IMPORTANT radius for correlation //RealType dthresh = 0.02; // IMPORTANT distance preservation threshold // sensitive parameters are set here - end } */ unsigned int morph_shape_flag( const char * shape ) { std::string shapeStr( shape ); std::transform(shapeStr.begin(), shapeStr.end(), shapeStr.begin(), ::tolower); unsigned int flag = 1; if ( !shapeStr.compare("ball") ) { flag = 1; } else if ( !shapeStr.compare("box") ) { flag = 2; } if ( !shapeStr.compare("cross") ) { flag = 3; } if ( !shapeStr.compare("annulus") ) { flag = 4; } if ( !shapeStr.compare("polygon") ) { flag = 5; } return flag; } template typename ImageType::Pointer iMathBlobDetector( typename ImageType::Pointer image, unsigned int nBlobs ) { typedef float RealType; unsigned int stepsperoctave = 10; // number of steps between doubling of scale RealType minscale = std::pow( 1.0, 1.0 ); RealType maxscale = std::pow( 2.0, 10.0 ); typedef itk::MultiScaleLaplacianBlobDetectorImageFilter BlobFilterType; typename BlobFilterType::Pointer blobFilter = BlobFilterType::New(); blobFilter->SetStartT( minscale ); blobFilter->SetEndT( maxscale ); blobFilter->SetStepsPerOctave( stepsperoctave ); blobFilter->SetNumberOfBlobs( nBlobs ); blobFilter->SetInput( image ); blobFilter->Update(); typedef typename BlobFilterType::BlobRadiusImageType BlobRadiusImageType; typename BlobRadiusImageType::Pointer labimg = blobFilter->GetBlobRadiusImage(); return( labimg ); } template typename ImageType::Pointer iMathCanny( typename ImageType::Pointer image, double sigma, double lowerThreshold, double upperThreshold ) { typedef typename ImageType::PixelType PixelType; typedef itk::CannyEdgeDetectionImageFilter< ImageType, ImageType > FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetVariance( sigma ); filter->SetUpperThreshold( (PixelType) upperThreshold ); filter->SetLowerThreshold( (PixelType) lowerThreshold ); filter->Update(); return filter->GetOutput(); } template typename ImageType::Pointer iMathDistanceMap( typename ImageType::Pointer image, bool useSpacing ) { typedef itk::DanielssonDistanceMapImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->InputIsBinaryOff(); filter->SetUseImageSpacing(useSpacing); filter->SetInput(image); filter->Update(); return filter->GetOutput(); } // algorithm : // 1. get distance map of object // 2. threshold // 3. label connected components // 4. label surface // 5. if everywhere on surface is next to object then it's a hole // 6. make sure it's not the background template typename ImageType::Pointer iMathFillHoles( typename ImageType::Pointer image, double holeParam ) { if ( (holeParam < 0) || (holeParam > 2) ) { //itk::itkExceptionMacro("FillHoles: holeParam value must lie in [0,2]"); } typedef typename ImageType::Pointer ImagePointerType; typedef itk::Image MaskType; typedef typename ImageType::PixelType PixelType; typedef typename MaskType::PixelType LabelType; const PixelType imageMax = itk::NumericTraits::max(); const LabelType labelMax = itk::NumericTraits::max(); const PixelType objectMin = 0.5; const PixelType distanceMin = 0.001; typedef itk::CastImageFilter MaskToImage; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::BinaryThresholdImageFilter ThresholdMaskFilterType; typename ThresholdFilterType::Pointer threshold = ThresholdFilterType::New(); threshold->SetInput( image ); threshold->SetInsideValue(1); threshold->SetOutsideValue(0); threshold->SetLowerThreshold(objectMin); threshold->SetUpperThreshold(imageMax); typedef itk::DanielssonDistanceMapImageFilter FilterType; typename FilterType::Pointer distance = FilterType::New(); distance->InputIsBinaryOff(); distance->SetUseImageSpacing(false); distance->SetInput(threshold->GetOutput()); typename ThresholdFilterType::Pointer dThreshold = ThresholdFilterType::New(); dThreshold->SetInput( distance->GetOutput() ); dThreshold->SetInsideValue(1); dThreshold->SetOutsideValue(0); dThreshold->SetLowerThreshold(distanceMin); dThreshold->SetUpperThreshold(imageMax); dThreshold->Update(); typedef itk::ConnectedComponentImageFilter ConnectedFilterType; typename ConnectedFilterType::Pointer connected = ConnectedFilterType::New(); connected->SetInput( dThreshold->GetOutput() ); connected->SetFullyConnected( false ); typedef itk::RelabelComponentImageFilter RelabelFilterType; typename RelabelFilterType::Pointer relabel = RelabelFilterType::New(); relabel->SetInput( connected->GetOutput() ); relabel->SetMinimumObjectSize( 0 ); relabel->Update(); if( holeParam == 2 ) { typename ThresholdMaskFilterType::Pointer oThreshold = ThresholdMaskFilterType::New(); oThreshold->SetInput( relabel->GetOutput() ); oThreshold->SetInsideValue(1); oThreshold->SetOutsideValue(0); oThreshold->SetLowerThreshold(2); oThreshold->SetUpperThreshold(labelMax); typedef itk::AddImageFilter AddFilterType; typename AddFilterType::Pointer add = AddFilterType::New(); add->SetInput1( threshold->GetOutput() ); add->SetInput2( oThreshold->GetOutput() ); typename MaskToImage::Pointer maskToImage = MaskToImage::New(); maskToImage->SetInput( add->GetOutput() ); maskToImage->Update(); return maskToImage->GetOutput(); } // FIXME - add filter for below -- avoid iterators in these functions typename MaskToImage::Pointer caster = MaskToImage::New(); caster->SetInput( threshold->GetOutput() ); caster->Update(); ImagePointerType imageout = caster->GetOutput(); typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; for( unsigned int j = 0; j < ImageType::ImageDimension; j++ ) { rad[j] = 1; } iteratorType GHood(rad, relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); float maximum = relabel->GetNumberOfObjects(); // now we have the exact number of objects labeled independently for( int lab = 2; lab <= maximum; lab++ ) { float erat = 2; if( holeParam <= 1 ) { GHood.GoToBegin(); unsigned long objectedge = 0; unsigned long backgroundedge = 0; unsigned long totaledge = 0; unsigned long volume = 0; while( !GHood.IsAtEnd() ) { typename ImageType::PixelType p = GHood.GetCenterPixel(); typename ImageType::IndexType ind2; if( p == lab ) { volume++; for( unsigned int i = 0; i < GHood.Size(); i++ ) { ind2 = GHood.GetIndex(i); float val2 = threshold->GetOutput()->GetPixel(ind2); if( (val2 == 1) && GHood.GetPixel(i) != lab ) { objectedge++; totaledge++; } else if( (val2 == 1) && GHood.GetPixel(i) != lab ) { backgroundedge++; totaledge++; } } } ++GHood; } erat = (float)objectedge / (float)totaledge; } if( erat > holeParam ) // fill the hole { // std::cout << " Filling " << lab << " of " << maximum << std::endl; typedef itk::ImageRegionIteratorWithIndex RelabelIterator; RelabelIterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() == lab ) { imageout->SetPixel(vfIter.GetIndex(), 1); } } } } return imageout; } template typename ImageType::Pointer iMathGC(typename ImageType::Pointer image, unsigned long radius) { const unsigned int ImageDimension = ImageType::ImageDimension; typedef typename ImageType::PixelType PixelType; typedef itk::BinaryBallStructuringElement StructuringElementType; typedef itk::GrayscaleMorphologicalClosingImageFilter< ImageType, ImageType, StructuringElementType > FilterType; StructuringElementType structuringElement; structuringElement.SetRadius(radius); structuringElement.CreateStructuringElement(); typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetKernel( structuringElement ); filter->Update(); return filter->GetOutput(); } template typename ImageType::Pointer iMathGD(typename ImageType::Pointer image, unsigned long radius) { const unsigned int ImageDimension = ImageType::ImageDimension; typedef typename ImageType::PixelType PixelType; typedef itk::BinaryBallStructuringElement StructuringElementType; typedef itk::GrayscaleDilateImageFilter< ImageType, ImageType, StructuringElementType > FilterType; StructuringElementType structuringElement; structuringElement.SetRadius(radius); structuringElement.CreateStructuringElement(); typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetKernel( structuringElement ); filter->Update(); return filter->GetOutput(); } template typename ImageType::Pointer iMathGE( typename ImageType::Pointer image, unsigned long radius) { const unsigned int ImageDimension = ImageType::ImageDimension; typedef typename ImageType::PixelType PixelType; typedef itk::BinaryBallStructuringElement StructuringElementType; typedef itk::GrayscaleErodeImageFilter< ImageType, ImageType, StructuringElementType > FilterType; StructuringElementType structuringElement; structuringElement.SetRadius(radius); structuringElement.CreateStructuringElement(); typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetKernel( structuringElement ); filter->Update(); return filter->GetOutput(); } template typename ImageType::Pointer iMathGO( typename ImageType::Pointer image, unsigned long radius) { const unsigned int ImageDimension = ImageType::ImageDimension; typedef typename ImageType::PixelType PixelType; typedef itk::BinaryBallStructuringElement StructuringElementType; typedef itk::GrayscaleMorphologicalOpeningImageFilter< ImageType, ImageType, StructuringElementType > FilterType; StructuringElementType structuringElement; structuringElement.SetRadius(radius); structuringElement.CreateStructuringElement(); typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetKernel( structuringElement ); filter->Update(); return filter->GetOutput(); } template typename ImageType::Pointer iMathGetLargestComponent( typename ImageType::Pointer image, unsigned long smallest ) { const unsigned int ImageDimension = ImageType::ImageDimension; if ( image->GetNumberOfComponentsPerPixel() != 1 ) { // NOPE } typedef itk::ImageRegionIteratorWithIndex Iterator; // compute the voxel volume typename ImageType::SpacingType spacing = image->GetSpacing(); float volumeelement = 1.0; for( unsigned int i = 0; i < spacing.Size(); i++ ) { volumeelement *= spacing[i]; } typedef itk::Image LabelImageType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::ConnectedComponentImageFilter FilterType; typedef itk::RelabelComponentImageFilter RelabelType; typename ThresholdFilterType::Pointer threshold = ThresholdFilterType::New(); typename FilterType::Pointer filter = FilterType::New(); typename RelabelType::Pointer relabel = RelabelType::New(); threshold->SetInput(image); threshold->SetInsideValue(1); threshold->SetOutsideValue(0); threshold->SetLowerThreshold(0.25); //FIXME - why these values? threshold->SetUpperThreshold(1.e9); threshold->Update(); filter->SetInput(threshold->GetOutput() ); filter->SetFullyConnected( 0 ); filter->Update(); relabel->SetInput( filter->GetOutput() ); relabel->SetMinimumObjectSize( smallest ); // relabel->SetUseHistograms(true); try { relabel->Update(); } catch( itk::ExceptionObject & excep ) { // std::cout << "Relabel: exception caught !" << std::endl; // std::cout << excep << std::endl; } // WriteImage(relabel->GetOutput(),outname.c_str()); // return 0; typename ImageType::Pointer Clusters = MakeNewImage(relabel->GetOutput(), 0); // typename ImageType::Pointer Clusters=relabel->GetOutput(); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); float maximum = relabel->GetNumberOfObjects(); float maxtstat = 0; std::vector histogram( (int)maximum + 1); std::vector clustersum( (int)maximum + 1); for( int i = 0; i <= maximum; i++ ) { histogram[i] = 0; clustersum[i] = 0; } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 0 ) { float vox = image->GetPixel(vfIter.GetIndex() ); histogram[(unsigned int)vfIter.Get()] = histogram[(unsigned int)vfIter.Get()] + 1; clustersum[(unsigned int)vfIter.Get()] += vox; if( vox > maxtstat ) { maxtstat = vox; } } } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( vfIter.Get() > 0 ) { Clusters->SetPixel( vfIter.GetIndex(), histogram[(unsigned int)vfIter.Get()] ); // if ( Clusters->GetPixel( vfIter.GetIndex() ) > maximgval ) // maximgval=Clusters->GetPixel( vfIter.GetIndex()); } else { Clusters->SetPixel(vfIter.GetIndex(), 0); } } float maximgval = 0; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( Clusters->GetPixel( vfIter.GetIndex() ) > maximgval ) { maximgval = Clusters->GetPixel( vfIter.GetIndex() ); } } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { if( Clusters->GetPixel( vfIter.GetIndex() ) >= maximgval ) { image->SetPixel( vfIter.GetIndex(), 1); } else { image->SetPixel( vfIter.GetIndex(), 0); } } return image; } template typename ImageType::Pointer iMathGrad(typename ImageType::Pointer image, double sigma, bool normalize ) { typedef itk::GradientMagnitudeRecursiveGaussianImageFilter FilterType; typename FilterType::Pointer grad = FilterType::New(); grad->SetInput( image ); grad->SetSigma( sigma ); grad->Update(); typename ImageType::Pointer output = grad->GetOutput(); if ( normalize ) { typedef itk::RescaleIntensityImageFilter RescaleFilterType; typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( 0 ); rescaler->SetOutputMaximum( 1 ); rescaler->SetInput( grad->GetOutput() ); rescaler->Update(); output = rescaler->GetOutput(); } return output; } template typename ImageType::Pointer iMathHistogramEqualization( typename ImageType::Pointer image, double alpha, double beta, unsigned int r ) { if ( image->GetNumberOfComponentsPerPixel() != 1 ) { // NOPE } typedef itk::AdaptiveHistogramEqualizationImageFilter< ImageType > AdaptiveHistogramEqualizationImageFilterType; typename AdaptiveHistogramEqualizationImageFilterType::Pointer adaptiveHistogramEqualizationImageFilter = AdaptiveHistogramEqualizationImageFilterType::New(); adaptiveHistogramEqualizationImageFilter->SetInput( image ); typename AdaptiveHistogramEqualizationImageFilterType::RadiusType radius; radius.Fill( r ); adaptiveHistogramEqualizationImageFilter->SetRadius(radius); adaptiveHistogramEqualizationImageFilter->SetAlpha(alpha); adaptiveHistogramEqualizationImageFilter->SetBeta(beta); adaptiveHistogramEqualizationImageFilter->Update( ); return adaptiveHistogramEqualizationImageFilter->GetOutput(); } template typename ImageType::Pointer iMathLaplacian(typename ImageType::Pointer image, double sigma, bool normalize ) { typedef itk::LaplacianRecursiveGaussianImageFilter FilterType; typename FilterType::Pointer laplacian = FilterType::New(); laplacian->SetInput( image ); laplacian->SetSigma( sigma ); laplacian->Update(); typename ImageType::Pointer output = laplacian->GetOutput(); if ( normalize ) { typedef itk::RescaleIntensityImageFilter RescaleFilterType; typename RescaleFilterType::Pointer rescaler = RescaleFilterType::New(); rescaler->SetOutputMinimum( 0 ); rescaler->SetOutputMaximum( 1 ); rescaler->SetInput( laplacian->GetOutput() ); rescaler->Update(); output = rescaler->GetOutput(); } return output; } template typename ImageType::Pointer iMathMaurerDistance(typename ImageType::Pointer image, typename ImageType::PixelType foreground ) { typedef itk::BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( image); thresholder->SetLowerThreshold( foreground ); thresholder->SetUpperThreshold( foreground ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); typedef itk::SignedMaurerDistanceMapImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( thresholder->GetOutput() ); filter->SetSquaredDistance( false ); filter->SetUseImageSpacing( true ); filter->SetInsideIsPositive( false ); filter->Update(); return filter->GetOutput(); } // // shape (1=ball, 2=box, 3=cross, 4=annulus, 5=polygon) template typename itk::FlatStructuringElement iMathGetFlatStructuringElement( unsigned int shape, unsigned long radius, bool radiusIsParametric, unsigned int lines, unsigned int thickness, bool includeCenter ) { typedef typename itk::FlatStructuringElement ElementType; ElementType element; typename ElementType::RadiusType elRadius; elRadius.Fill( radius ); switch( shape ) { case 1: element = ElementType::Ball(elRadius,radiusIsParametric); break; case 2: element = ElementType::Box(elRadius); break; case 3: element = ElementType::Cross(elRadius); break; case 4: element = ElementType::Annulus(elRadius,thickness,includeCenter,radiusIsParametric); break; case 5: element = ElementType::Polygon(elRadius, lines); break; default: break; } return element; } template typename ImageType::Pointer iMathMC(typename ImageType::Pointer image, unsigned long radius, typename ImageType::PixelType closeValue, unsigned int shape, bool radiusIsParametric, unsigned int lines, unsigned int thickness, bool includeCenter ) { const unsigned int ImageDimension = ImageType::ImageDimension; typedef typename itk::FlatStructuringElement ElementType; ElementType element = iMathGetFlatStructuringElement(shape,radius,radiusIsParametric, lines,thickness,includeCenter); typedef itk::BinaryMorphologicalClosingImageFilter< ImageType, ImageType, ElementType > FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetKernel( element ); filter->SetForegroundValue( closeValue ); //filter->SetBackgroundValue(0); filter->Update(); return filter->GetOutput(); } template typename ImageType::Pointer iMathMD(typename ImageType::Pointer image, unsigned long radius, typename ImageType::PixelType dilateValue, unsigned int shape, bool radiusIsParametric, unsigned int lines, unsigned int thickness, bool includeCenter ) { const unsigned int ImageDimension = ImageType::ImageDimension; typedef typename itk::FlatStructuringElement ElementType; ElementType element = iMathGetFlatStructuringElement(shape,radius,radiusIsParametric, lines,thickness,includeCenter); typedef itk::BinaryDilateImageFilter< ImageType, ImageType, ElementType > FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetKernel( element ); filter->SetDilateValue( dilateValue ); filter->SetBackgroundValue(0); filter->Update(); return filter->GetOutput(); } template typename ImageType::Pointer iMathME(typename ImageType::Pointer image, unsigned long radius, typename ImageType::PixelType erodeValue, unsigned int shape, bool radiusIsParametric, unsigned int lines, unsigned int thickness, bool includeCenter ) { const unsigned int ImageDimension = ImageType::ImageDimension; typedef typename itk::FlatStructuringElement ElementType; ElementType element = iMathGetFlatStructuringElement(shape,radius,radiusIsParametric, lines,thickness,includeCenter); typedef itk::BinaryErodeImageFilter< ImageType, ImageType, ElementType > FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetKernel( element ); filter->SetErodeValue( erodeValue ); filter->SetBackgroundValue(0); filter->Update(); return filter->GetOutput(); } template typename ImageType::Pointer iMathMO(typename ImageType::Pointer image, unsigned long radius, typename ImageType::PixelType openValue, unsigned int shape, bool radiusIsParametric, unsigned int lines, unsigned int thickness, bool includeCenter ) { const unsigned int ImageDimension = ImageType::ImageDimension; typedef typename itk::FlatStructuringElement ElementType; ElementType element = iMathGetFlatStructuringElement(shape,radius,radiusIsParametric, lines,thickness,includeCenter); typedef itk::BinaryMorphologicalOpeningImageFilter< ImageType, ImageType, ElementType > FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetKernel( element ); filter->SetForegroundValue( openValue ); filter->SetBackgroundValue( 0 ); filter->Update(); return filter->GetOutput(); } template typename ImageType::Pointer iMathNormalize( typename ImageType::Pointer image ) { if ( image->GetNumberOfComponentsPerPixel() != 1 ) { // NOPE } typedef typename ImageType::PixelType PixelType; typedef itk::RescaleIntensityImageFilter NormFilterType; typename NormFilterType::Pointer normFilter = NormFilterType::New(); normFilter->SetInput( image ); normFilter->SetOutputMinimum( itk::NumericTraits::ZeroValue() ); normFilter->SetOutputMaximum( itk::NumericTraits::OneValue() ); normFilter->Update(); return normFilter->GetOutput(); } template typename ImageType::Pointer iMathPad( typename ImageType::Pointer image1, int padvalue ) { typedef itk::ImageRegionIteratorWithIndex Iterator; typename ImageType::SizeType size = image1->GetLargestPossibleRegion().GetSize(); typename ImageType::PointType origin2 = image1->GetOrigin(); typename ImageType::SizeType newsize = image1->GetLargestPossibleRegion().GetSize(); typename ImageType::RegionType newregion; // determine new image size for( unsigned int i = 0; i < ImageType::ImageDimension; i++ ) { float dimsz = (float)size[i]; newsize[i] = (unsigned int)(dimsz + padvalue * 2); } newregion.SetSize(newsize); newregion.SetIndex(image1->GetLargestPossibleRegion().GetIndex() ); typename ImageType::Pointer padimage = AllocImage(newregion, image1->GetSpacing(), origin2, image1->GetDirection(), 0); typename ImageType::IndexType index; typename ImageType::IndexType index2; if( padvalue > 0 ) { index.Fill(0); index2.Fill( (unsigned int)fabs( static_cast( padvalue ) ) ); } else { index2.Fill(0); index.Fill( (unsigned int)fabs( static_cast( padvalue ) ) ); } typename ImageType::PointType point1, pointpad; image1->TransformIndexToPhysicalPoint(index, point1); padimage->TransformIndexToPhysicalPoint(index2, pointpad); for( unsigned int i = 0; i < ImageType::ImageDimension; i++ ) { origin2[i] += (point1[i] - pointpad[i]); } padimage->SetOrigin(origin2); Iterator iter( image1, image1->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { typename ImageType::IndexType oindex = iter.GetIndex(); typename ImageType::IndexType padindex = iter.GetIndex(); bool isinside = true; for( unsigned int i = 0; i < ImageType::ImageDimension; i++ ) { float shifted = ( (float)oindex[i] + padvalue); if( shifted < 0 || shifted > newsize[i] - 1 ) { isinside = false; } // if (shifted < 0) shifted=0; // padindex[i]= } if( isinside ) { for( unsigned int i = 0; i < ImageType::ImageDimension; i++ ) { float shifted = ( (float)oindex[i] + padvalue); padindex[i] = (unsigned int)shifted; } padimage->SetPixel(padindex, iter.Get() ); } } return padimage; } template typename ImageType::Pointer iMathPeronaMalik( typename ImageType::Pointer image, unsigned long nIterations, double conductance ) { if ( image->GetNumberOfComponentsPerPixel() != 1 ) { // NOPE } typedef itk::GradientAnisotropicDiffusionImageFilter< ImageType, ImageType > FilterType; typedef typename FilterType::TimeStepType TimeStepType; // Select time step size. TimeStepType spacingsize = 0; for( unsigned int d = 0; d < ImageType::ImageDimension; d++ ) { TimeStepType sp = image->GetSpacing()[d]; spacingsize += sp * sp; } spacingsize = sqrt( spacingsize ); // FIXME - cite reason for this step double dimPlusOne = ImageType::ImageDimension + 1; TimeStepType mytimestep = spacingsize / std::pow( 2.0 , dimPlusOne ); TimeStepType reftimestep = 0.4 / std::pow( 2.0 , dimPlusOne ); if ( mytimestep > reftimestep ) { mytimestep = reftimestep; } typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetConductanceParameter( conductance ); // might need to change this filter->SetNumberOfIterations( nIterations ); filter->SetTimeStep( mytimestep ); filter->Update(); return filter->GetOutput(); } template typename ImageType::Pointer iMathPropagateLabelsThroughMask( typename ImageType::Pointer speedimage, typename ImageType::Pointer labimage, double stoppingValue, unsigned int propagationMethod ) { typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::FastMarchingThresholdStoppingCriterion< ImageType, ImageType > CriterionType; typedef typename CriterionType::Pointer CriterionPointer; typedef itk::FastMarchingImageFilterBase FastMarchingFilterType; typedef typename FastMarchingFilterType::LabelImageType LabelImageType; typedef itk::BinaryThresholdImageFilter ThresholderType; typedef itk::LabelContourImageFilter ContourFilterType; typedef typename FastMarchingFilterType::NodePairContainerType NodeContainer; typedef typename FastMarchingFilterType::NodePairType NodePairType; typedef itk::CastImageFilter CastFilterType; typename ImageType::Pointer fastimage = ImageType::New(); fastimage->SetRegions( speedimage->GetLargestPossibleRegion() ); fastimage->SetSpacing( speedimage->GetSpacing() ); fastimage->SetOrigin( speedimage->GetOrigin() ); fastimage->SetDirection( speedimage->GetDirection() ); fastimage->Allocate(); fastimage->FillBuffer(1.e9); /* typename ImageType::Pointer outlabimage = ImageType::New(); outlabimage->SetRegions( speedimage->GetLargestPossibleRegion() ); outlabimage->SetSpacing( speedimage->GetSpacing() ); outlabimage->SetOrigin( speedimage->GetOrigin() ); outlabimage->SetDirection( speedimage->GetDirection() ); outlabimage->Allocate(); outlabimage->FillBuffer(0); */ typename CastFilterType::Pointer caster = CastFilterType::New(); caster->SetInput( labimage ); caster->Update(); typename ImageType::Pointer outlabimage = caster->GetOutput(); // FIXME - why is thresh set to 0.5? double maxlabel = 0; double thresh = 0.5; Iterator vfIter2( labimage, labimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { bool isinside = true; double speedval = speedimage->GetPixel(vfIter2.GetIndex() ); double labval = labimage->GetPixel(vfIter2.GetIndex() ); if( speedval < thresh ) { isinside = false; } if( isinside ) { if( labval > maxlabel ) { maxlabel = labval; } } } CriterionPointer criterion = CriterionType::New(); criterion->SetThreshold( stoppingValue ); typename FastMarchingFilterType::Pointer fastMarching; for( unsigned int lab = 1; lab <= (unsigned int)maxlabel; lab++ ) { // Create binary mask for each label typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( labimage ); thresholder->SetLowerThreshold( lab ); thresholder->SetUpperThreshold( lab ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); // Get pixels on border of the label mask typename ContourFilterType::Pointer contour = ContourFilterType::New(); contour->SetInput( thresholder->GetOutput() ); contour->FullyConnectedOff(); contour->SetBackgroundValue( itk::NumericTraits::ZeroValue() ); contour->Update(); typename ImageType::Pointer contourimage = contour->GetOutput(); fastMarching = FastMarchingFilterType::New(); fastMarching->SetInput( speedimage ); fastMarching->SetStoppingCriterion( criterion ); if( propagationMethod == 1 ) // Strict { // std::cout << " strict " << std::endl; fastMarching->SetTopologyCheck( FastMarchingFilterType::Strict ); } if( propagationMethod == 2 ) // No handles { // std::cout << " no handles " << std::endl; fastMarching->SetTopologyCheck( FastMarchingFilterType::NoHandles ); } typename NodeContainer::Pointer seeds = NodeContainer::New(); seeds->Initialize(); typename NodeContainer::Pointer alivePoints = NodeContainer::New(); alivePoints->Initialize(); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { double labval = labimage->GetPixel( vfIter2.GetIndex() ); double contourval = contourimage->GetPixel( vfIter2.GetIndex() ); if( ( (unsigned int) contourval == 1 ) && ( (unsigned int) labval == lab ) ) { seeds->push_back( NodePairType( vfIter2.GetIndex(), 0.0 ) ); } if( ( (unsigned int) contourval == 0 ) && ( (unsigned int) labval == lab ) ) { alivePoints->push_back( NodePairType( vfIter2.GetIndex(), 0.0 ) ); } } fastMarching->SetTrialPoints( seeds ); fastMarching->SetAlivePoints( alivePoints ); fastMarching->Update(); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { bool isinside = true; double speedval = speedimage->GetPixel(vfIter2.GetIndex() ); double labval = labimage->GetPixel(vfIter2.GetIndex() ); if( speedval < thresh ) { isinside = false; } if( isinside && labval == 0 ) { double fmarrivaltime = fastMarching->GetOutput()->GetPixel( vfIter2.GetIndex() ); double mmm = fastimage->GetPixel(vfIter2.GetIndex() ); if( fmarrivaltime < mmm ) { fastimage->SetPixel(vfIter2.GetIndex(), fmarrivaltime ); outlabimage->SetPixel(vfIter2.GetIndex(), lab ); } } else if( !isinside ) { outlabimage->SetPixel(vfIter2.GetIndex(), 0 ); } } } return outlabimage; } template typename ImageType::Pointer iMathSharpen( typename ImageType::Pointer image ) { if ( image->GetNumberOfComponentsPerPixel() != 1 ) { // NOPE } typedef itk::LaplacianSharpeningImageFilter FilterType; typename FilterType::Pointer sharpenFilter = FilterType::New(); sharpenFilter->SetInput( image ); sharpenFilter->Update(); return sharpenFilter->GetOutput(); } template typename ImageType::Pointer iMathTruncateIntensity( typename ImageType::Pointer image, double lowerQ, double upperQ, int nBins, typename itk::Image::Pointer mask ) { typedef typename ImageType::PixelType PixelType; typedef unsigned int LabelType; typedef itk::Image MaskType; if( mask.IsNull() ) { typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer thresh = ThresholdFilterType::New(); thresh->SetInput( image ); thresh->SetLowerThreshold( 1e-6 ); thresh->SetUpperThreshold( itk::NumericTraits::max() ); thresh->SetInsideValue(1); thresh->SetOutsideValue(0); thresh->Update(); mask = thresh->GetOutput(); } typedef itk::LabelStatisticsImageFilter HistogramFilterType; typename HistogramFilterType::Pointer stats = HistogramFilterType::New(); stats->SetInput( image ); stats->SetLabelInput( mask ); stats->Update(); PixelType minValue = stats->GetMinimum( 1 ); PixelType maxValue = stats->GetMaximum( 1 ); // Hack increment by delta if (minValue == 0) { minValue = (PixelType) (minValue + 1e-6); } if (minValue == 0) { minValue++; } stats->SetUseHistograms( true ); stats->SetHistogramParameters( nBins, minValue, maxValue ); stats->Update(); typedef typename HistogramFilterType::HistogramPointer HistogramPointer; HistogramPointer histogram = stats->GetHistogram( 1 ); PixelType lowerQuantile = histogram->Quantile( 0, lowerQ ); PixelType upperQuantile = histogram->Quantile( 0, upperQ ); typedef itk::IntensityWindowingImageFilter WindowFilterType; typename WindowFilterType::Pointer windowFilter = WindowFilterType::New(); windowFilter->SetInput( image ); windowFilter->SetWindowMinimum( lowerQuantile ); windowFilter->SetOutputMinimum( lowerQuantile ); windowFilter->SetWindowMaximum( upperQuantile ); windowFilter->SetOutputMaximum( upperQuantile ); windowFilter->Update(); return windowFilter->GetOutput(); } } // namespace ants ants-2.2.0/Examples/include/000077500000000000000000000000001311104306400156525ustar00rootroot00000000000000ants-2.2.0/Examples/include/ANTSConformalMapping.h000066400000000000000000000005631311104306400217510ustar00rootroot00000000000000 #ifndef ANTSCONFORMALMAPPING_H #define ANTSCONFORMALMAPPING_H namespace ants { extern int ANTSConformalMapping( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSCONFORMALMAPPING_H ants-2.2.0/Examples/include/ANTSIntegrateVectorField.h000066400000000000000000000006131311104306400225620ustar00rootroot00000000000000 #ifndef ANTSINTEGRATEVECTORFIELD_H #define ANTSINTEGRATEVECTORFIELD_H namespace ants { extern int ANTSIntegrateVectorField( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSINTEGRATEVECTORFIELD_H ants-2.2.0/Examples/include/ANTSIntegrateVelocityField.h000066400000000000000000000006271311104306400231230ustar00rootroot00000000000000 #ifndef ANTSINTEGRATEVELOCITYFIELD_H #define ANTSINTEGRATEVELOCITYFIELD_H namespace ants { extern int ANTSIntegrateVelocityField( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSINTEGRATEVELOCITYFIELD_H ants-2.2.0/Examples/include/ANTSJacobian.h000066400000000000000000000005051311104306400202170ustar00rootroot00000000000000 #ifndef ANTSJACOBIAN_H #define ANTSJACOBIAN_H namespace ants { extern int ANTSJacobian( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSJACOBIAN_H ants-2.2.0/Examples/include/ANTSUseDeformationFieldToGetAffineTransform.h000066400000000000000000000011131311104306400263450ustar00rootroot00000000000000 #ifndef ANTSUSEDEFORMATIONFIELDTOGETAFFINETRANSFORM_H #define ANTSUSEDEFORMATIONFIELDTOGETAFFINETRANSFORM_H namespace ants { extern int ANTSUseDeformationFieldToGetAffineTransform( std::vector, // equivalent to argv of command line // parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSUSEDEFORMATIONFIELDTOGETAFFINETRANSFORM_H ants-2.2.0/Examples/include/ANTSUseLandmarkImagesToGetAffineTransform.h000066400000000000000000000010751311104306400260200ustar00rootroot00000000000000 #ifndef ANTSUSELANDMARKIMAGESTOGETAFFINETRANSFORM_H #define ANTSUSELANDMARKIMAGESTOGETAFFINETRANSFORM_H namespace ants { extern int ANTSUseLandmarkImagesToGetAffineTransform( std::vector, // equivalent to argv of command line // parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSUSELANDMARKIMAGESTOGETAFFINETRANSFORM_H ants-2.2.0/Examples/include/ANTSUseLandmarkImagesToGetBSplineDisplacementField.h000066400000000000000000000011741311104306400275650ustar00rootroot00000000000000 #ifndef ANTSUSELANDMARKIMAGESTOGETBSPLINEDISPLACEMENTFIELD_H #define ANTSUSELANDMARKIMAGESTOGETBSPLINEDISPLACEMENTFIELD_H namespace ants { extern int ANTSUseLandmarkImagesToGetBSplineDisplacementField( std::vector, // equivalent to argv of command line // parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSUSELANDMARKIMAGESTOGETBSPLINEDISPLACEMENTFIELD_H ants-2.2.0/Examples/include/ANTS_.h000066400000000000000000000004241311104306400167270ustar00rootroot00000000000000#ifndef ANTS_H #define ANTS_H namespace ants { extern int ANTS( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTS_H ants-2.2.0/Examples/include/Atropos.h000066400000000000000000000004471311104306400174570ustar00rootroot00000000000000 #ifndef ATROPOS_H #define ATROPOS_H namespace ants { extern int Atropos( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ATROPOS_H ants-2.2.0/Examples/include/AverageAffineTransform.h000066400000000000000000000005771311104306400224130ustar00rootroot00000000000000 #ifndef AVERAGEAFFINETRANSFORM_H #define AVERAGEAFFINETRANSFORM_H namespace ants { extern int AverageAffineTransform( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // AVERAGEAFFINETRANSFORM_H ants-2.2.0/Examples/include/AverageAffineTransformNoRigid.h000066400000000000000000000006511311104306400236600ustar00rootroot00000000000000 #ifndef AVERAGEAFFINETRANSFORMNORIGID_H #define AVERAGEAFFINETRANSFORMNORIGID_H namespace ants { extern int AverageAffineTransformNoRigid( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // AVERAGEAFFINETRANSFORMNORIGID_H ants-2.2.0/Examples/include/AverageImages.h000066400000000000000000000005131311104306400205220ustar00rootroot00000000000000 #ifndef AVERAGEIMAGES_H #define AVERAGEIMAGES_H namespace ants { extern int AverageImages( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // AVERAGEIMAGES_H ants-2.2.0/Examples/include/AverageTensorImages.h000066400000000000000000000005551311104306400217230ustar00rootroot00000000000000 #ifndef AVERAGETENSORIMAGES_H #define AVERAGETENSORIMAGES_H namespace ants { extern int AverageTensorImages( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // AVERAGETENSORIMAGES_H ants-2.2.0/Examples/include/CheckTopology.h000066400000000000000000000005131311104306400205740ustar00rootroot00000000000000 #ifndef CHECKTOPOLOGY_H #define CHECKTOPOLOGY_H namespace ants { extern int CheckTopology( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CHECKTOPOLOGY_H ants-2.2.0/Examples/include/ClusterImageStatistics.h000066400000000000000000000005771311104306400224730ustar00rootroot00000000000000 #ifndef CLUSTERIMAGESTATISTICS_H #define CLUSTERIMAGESTATISTICS_H namespace ants { extern int ClusterImageStatistics( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CLUSTERIMAGESTATISTICS_H ants-2.2.0/Examples/include/ComposeMultiTransform.h000066400000000000000000000005711311104306400223420ustar00rootroot00000000000000 #ifndef COMPOSEMULTITRANSFORM_H #define COMPOSEMULTITRANSFORM_H namespace ants { extern int ComposeMultiTransform( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // COMPOSEMULTITRANSFORM_H ants-2.2.0/Examples/include/CompositeTransformUtil.h000066400000000000000000000005771311104306400225300ustar00rootroot00000000000000 #ifndef COMPOSITETRANSFORMUTIL_H #define COMPOSITETRANSFORMUTIL_H namespace ants { extern int CompositeTransformUtil( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // COMPOSITETRANSFORMUTIL_H ants-2.2.0/Examples/include/ComputeSimilarityMetric.h000066400000000000000000000006051311104306400226530ustar00rootroot00000000000000 #ifndef COMPUTESIMILARITYMETRIC_H #define COMPUTESIMILARITYMETRIC_H namespace ants { extern int ComputeSimilarityMetric( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // COMPUTESIMILARITYMETRIC_H ants-2.2.0/Examples/include/ConformalMapping.h000066400000000000000000000005331311104306400212600ustar00rootroot00000000000000 #ifndef CONFORMALMAPPING_H #define CONFORMALMAPPING_H namespace ants { extern int ConformalMapping( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CONFORMALMAPPING_H ants-2.2.0/Examples/include/ConvertImage.h000066400000000000000000000004771311104306400204160ustar00rootroot00000000000000 #ifndef CONVERTIMAGE_H #define CONVERTIMAGE_H namespace ants { extern int ConvertImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // TILEIMAGES_H ants-2.2.0/Examples/include/ConvertImagePixelType.h000066400000000000000000000005711311104306400222550ustar00rootroot00000000000000 #ifndef CONVERTIMAGEPIXELTYPE_H #define CONVERTIMAGEPIXELTYPE_H namespace ants { extern int ConvertImagePixelType( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CONVERTIMAGEPIXELTYPE_H ants-2.2.0/Examples/include/ConvertInputImagePixelTypeToFloat.h000066400000000000000000000006511311104306400245650ustar00rootroot00000000000000 #ifndef CONVERTINPUTIMAGEPIXELTYPETOFLOAT_H #define CONVERTINPUTIMAGEPIXELTYPETOFLOAT_H namespace ants { extern int ConvertInputImagePixelTypeToFloat( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CONVERTINPUTIMAGEPIXELTYPETOFLOAT_H ants-2.2.0/Examples/include/ConvertScalarImageToRGB.h000066400000000000000000000006051311104306400223730ustar00rootroot00000000000000 #ifndef CONVERTSCALARIMAGETORGB_H #define CONVERTSCALARIMAGETORGB_H namespace ants { extern int ConvertScalarImageToRGB( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CONVERTSCALARIMAGETORGB_H ants-2.2.0/Examples/include/ConvertToJpg.h000066400000000000000000000005051311104306400204070ustar00rootroot00000000000000 #ifndef CONVERTTOJPG_H #define CONVERTTOJPG_H namespace ants { extern int ConvertToJpg( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CONVERTTOJPG_H ants-2.2.0/Examples/include/ConvertTransformFile.h000066400000000000000000000005541311104306400221430ustar00rootroot00000000000000 #ifndef CONVERTTRANSFORMFILE_H #define CONVERTTRANSFORMFILE_H namespace ants { extern int ConvertTransformFile( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // PRINTHEADER_H ants-2.2.0/Examples/include/ConvertVectorFieldToVTK.h000066400000000000000000000006051311104306400224630ustar00rootroot00000000000000 #ifndef CONVERTVECTORFIELDTOVTK_H #define CONVERTVECTORFIELDTOVTK_H namespace ants { extern int ConvertVectorFieldToVTK( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CONVERTVECTORFIELDTOVTK_H ants-2.2.0/Examples/include/CopyImageHeaderInformation.h000066400000000000000000000006271311104306400232240ustar00rootroot00000000000000 #ifndef COPYIMAGEHEADERINFORMATION_H #define COPYIMAGEHEADERINFORMATION_H namespace ants { extern int CopyImageHeaderInformation( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // COPYIMAGEHEADERINFORMATION_H ants-2.2.0/Examples/include/CreateDTICohort.h000066400000000000000000000005251311104306400207500ustar00rootroot00000000000000 #ifndef CREATEDTICOHORT_H #define CREATEDTICOHORT_H namespace ants { extern int CreateDTICohort( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CREATEDTICOHORT_H ants-2.2.0/Examples/include/CreateDisplacementField.h000066400000000000000000000006051311104306400225240ustar00rootroot00000000000000 #ifndef CREATEDISPLACEMENTFIELD_H #define CREATEDISPLACEMENTFIELD_H namespace ants { extern int CreateDisplacementField( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CREATEDISPLACEMENTFIELD_H ants-2.2.0/Examples/include/CreateImage.h000066400000000000000000000004771311104306400202010ustar00rootroot00000000000000 #ifndef CREATEIMAGE_H #define CREATEIMAGE_H namespace ants { extern int CreateImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CREATEIMAGE_H ants-2.2.0/Examples/include/CreateJacobianDeterminantImage.h000066400000000000000000000006571311104306400240230ustar00rootroot00000000000000 #ifndef CREATEJACOBIANDETERMINANTIMAGE_H #define CREATEJACOBIANDETERMINANTIMAGE_H namespace ants { extern int CreateJacobianDeterminantImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CREATEJACOBIANDETERMINANTIMAGE_H ants-2.2.0/Examples/include/CreateTiledMosaic.h000066400000000000000000000005251311104306400213460ustar00rootroot00000000000000 #ifndef CREATETILEDMOSAIC_H #define CREATETILEDMOSAIC_H namespace ants { extern int CreateTiledMosaic( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CREATETILEDMOSAIC_H ants-2.2.0/Examples/include/CreateWarpedGridImage.h000066400000000000000000000005711311104306400221450ustar00rootroot00000000000000 #ifndef CREATEWARPEDGRIDIMAGE_H #define CREATEWARPEDGRIDIMAGE_H namespace ants { extern int CreateWarpedGridImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // CREATEWARPEDGRIDIMAGE_H ants-2.2.0/Examples/include/DeNrrd.h000066400000000000000000000004651311104306400172060ustar00rootroot00000000000000 #ifndef DENRRD_H #define DENRRD_H namespace ants { extern int DeNrrd( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // DENRRD_H ants-2.2.0/Examples/include/DenoiseImage.h000066400000000000000000000005251311104306400203560ustar00rootroot00000000000000 #ifndef DENOISEIMAGE_H #define DENOISEIMAGE_H namespace ants { extern int DenoiseImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // DENOISEIMAGE_H ants-2.2.0/Examples/include/ExtractRegionFromImage.h000066400000000000000000000005771311104306400224010ustar00rootroot00000000000000 #ifndef EXTRACTREGIONFROMIMAGE_H #define EXTRACTREGIONFROMIMAGE_H namespace ants { extern int ExtractRegionFromImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // EXTRACTREGIONFROMIMAGE_H ants-2.2.0/Examples/include/ExtractRegionFromImageByMask.h000066400000000000000000000006431311104306400235020ustar00rootroot00000000000000 #ifndef EXTRACTREGIONFROMIMAGEBYMASK_H #define EXTRACTREGIONFROMIMAGEBYMASK_H namespace ants { extern int ExtractRegionFromImageByMask( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // EXTRACTREGIONFROMIMAGEBYMASK_H ants-2.2.0/Examples/include/ExtractSliceFromImage.h000066400000000000000000000005711311104306400222070ustar00rootroot00000000000000 #ifndef EXTRACTSLICEFROMIMAGE_H #define EXTRACTSLICEFROMIMAGE_H namespace ants { extern int ExtractSliceFromImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // EXTRACTSLICEFROMIMAGE_H ants-2.2.0/Examples/include/FitBSplineToPoints.h000066400000000000000000000005501311104306400215220ustar00rootroot00000000000000 #ifndef FITBSPLINECURVETOPOINTS_H #define FITBSPLINECURVETOPOINTS_H namespace ants { extern int FitBSplineToPoints( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // FITBSPLINECURVETOPOINTS_H ants-2.2.0/Examples/include/GetConnectedComponentsFeatureImages.h000066400000000000000000000006371311104306400251030ustar00rootroot00000000000000 #ifndef GETCONNECTEDCOMPONENTSFEATUREIMAGES_H #define GETCONNECTEDCOMPONENTSFEATUREIMAGES_H namespace ants { extern int GetConnectedComponentsFeatureImages( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // GETCONNECTEDCOMPONENTSFEATUREIMAGES_H ants-2.2.0/Examples/include/GetMeshAndTopology.h000066400000000000000000000005471311104306400215450ustar00rootroot00000000000000 #ifndef GETMESHANDTOPOLOGY_H #define GETMESHANDTOPOLOGY_H namespace ants { extern int GetMeshAndTopology( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // GETMESHANDTOPOLOGY_H ants-2.2.0/Examples/include/ImageCompare.h000066400000000000000000000005051311104306400203540ustar00rootroot00000000000000 #ifndef IMAGECOMPARE_H #define IMAGECOMPARE_H namespace ants { extern int ImageCompare( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // IMAGECOMPARE_H ants-2.2.0/Examples/include/ImageIntensityStatistics.h000066400000000000000000000006041311104306400230270ustar00rootroot00000000000000#ifndef IMAGEINTENSITYSTATISTICS_H #define IMAGEINTENSITYSTATISTICS_H namespace ants { extern int ImageIntensityStatistics( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // IMAGEINTENSITYSTATISTICS_H ants-2.2.0/Examples/include/ImageMath.h000066400000000000000000000004631311104306400176620ustar00rootroot00000000000000 #ifndef IMAGEMATH_H #define IMAGEMATH_H namespace ants { extern int ImageMath( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // IMAGEMATH_H ants-2.2.0/Examples/include/ImageSetStatistics.h000066400000000000000000000005471311104306400216020ustar00rootroot00000000000000 #ifndef IMAGESETSTATISTICS_H #define IMAGESETSTATISTICS_H namespace ants { extern int ImageSetStatistics( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // IMAGESETSTATISTICS_H ants-2.2.0/Examples/include/KellyKapowski.h000066400000000000000000000005131311104306400206130ustar00rootroot00000000000000 #ifndef KELLYKAPOWSKI_H #define KELLYKAPOWSKI_H namespace ants { extern int KellyKapowski( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // KELLYKAPOWSKI_H ants-2.2.0/Examples/include/KellySlater.h000066400000000000000000000004771311104306400202660ustar00rootroot00000000000000 #ifndef KELLYSLATER_H #define KELLYSLATER_H namespace ants { extern int KellySlater( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // KELLYSLATER_H ants-2.2.0/Examples/include/LabelClustersUniquely.h000066400000000000000000000005711311104306400223260ustar00rootroot00000000000000 #ifndef LABELCLUSTERSUNIQUELY_H #define LABELCLUSTERSUNIQUELY_H namespace ants { extern int LabelClustersUniquely( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // LABELCLUSTERSUNIQUELY_H ants-2.2.0/Examples/include/LabelGeometryMeasures.h000066400000000000000000000005711311104306400222660ustar00rootroot00000000000000 #ifndef LABELGEOMETRYMEASURES_H #define LABELGEOMETRYMEASURES_H namespace ants { extern int LabelGeometryMeasures( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // LABELGEOMETRYMEASURES_H ants-2.2.0/Examples/include/LabelOverlapMeasures.h000066400000000000000000000005631311104306400221040ustar00rootroot00000000000000 #ifndef LABELOVERLAPMEASURES_H #define LABELOVERLAPMEASURES_H namespace ants { extern int LabelOverlapMeasures( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // LABELOVERLAPMEASURES_H ants-2.2.0/Examples/include/LaplacianThickness.h000066400000000000000000000005471311104306400215710ustar00rootroot00000000000000 #ifndef LAPLACIANTHICKNESS_H #define LAPLACIANTHICKNESS_H namespace ants { extern int LaplacianThickness( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // LAPLACIANTHICKNESS_H ants-2.2.0/Examples/include/LesionFilling.h000066400000000000000000000005311311104306400205600ustar00rootroot00000000000000 #ifndef LESIONFILLING_H #define LESIONFILLING_H namespace ants { extern int LesionFilling( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // LESIONFILLING_H ants-2.2.0/Examples/include/MeasureImageSimilarity.h000066400000000000000000000005771311104306400224470ustar00rootroot00000000000000 #ifndef MEASUREIMAGESIMILARITY_H #define MEASUREIMAGESIMILARITY_H namespace ants { extern int MeasureImageSimilarity( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // MEASUREIMAGESIMILARITY_H ants-2.2.0/Examples/include/MeasureMinMaxMean.h000066400000000000000000000005411311104306400213370ustar00rootroot00000000000000 #ifndef MEASUREMINMAXMEAN_H #define MEASUREMINMAXMEAN_H namespace ants { extern int MeasureMinMaxMean( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // MEASUREMINMAXMEAN_H ants-2.2.0/Examples/include/MemoryTest.h000066400000000000000000000004711311104306400201350ustar00rootroot00000000000000 #ifndef MEMORYTEST_H #define MEMORYTEST_H namespace ants { extern int MemoryTest( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // MEMORYTEST_H ants-2.2.0/Examples/include/MultiplyImages.h000066400000000000000000000005211311104306400207660ustar00rootroot00000000000000 #ifndef MULTIPLYIMAGES_H #define MULTIPLYIMAGES_H namespace ants { extern int MultiplyImages( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // MULTIPLYIMAGES_H ants-2.2.0/Examples/include/N3BiasFieldCorrection.h000066400000000000000000000005711311104306400221010ustar00rootroot00000000000000 #ifndef N3BIASFIELDCORRECTION_H #define N3BIASFIELDCORRECTION_H namespace ants { extern int N3BiasFieldCorrection( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // N3BIASFIELDCORRECTION_H ants-2.2.0/Examples/include/N4BiasFieldCorrection.h000066400000000000000000000005711311104306400221020ustar00rootroot00000000000000 #ifndef N4BIASFIELDCORRECTION_H #define N4BIASFIELDCORRECTION_H namespace ants { extern int N4BiasFieldCorrection( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // N4BIASFIELDCORRECTION_H ants-2.2.0/Examples/include/NonLocalSuperResolution.h000066400000000000000000000006011311104306400226300ustar00rootroot00000000000000 #ifndef NONLOCALSUPERRESOLUTION_H #define NONLOCALSUPERRESOLUTION_H namespace ants { extern int NonLocalSuperResolution( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // NONLOCALSUPERRESOLUTION_H ants-2.2.0/Examples/include/PasteImageIntoImage.h000066400000000000000000000005551311104306400216440ustar00rootroot00000000000000 #ifndef PASTEIMAGEINTOIMAGE_H #define PASTEIMAGEINTOIMAGE_H namespace ants { extern int PasteImageIntoImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // PASTEIMAGEINTOIMAGE_H ants-2.2.0/Examples/include/PermuteFlipImageOrientationAxes.h000066400000000000000000000007671311104306400242710ustar00rootroot00000000000000 #ifndef PERMUTEFLIPIMAGEORIENTATIONAXES_H #define PERMUTEFLIPIMAGEORIENTATIONAXES_H namespace ants { extern int PermuteFlipImageOrientationAxes( std::vector, // equivalent to argv of command line parameters to // main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // PERMUTEFLIPIMAGEORIENTATIONAXES_H ants-2.2.0/Examples/include/PrintHeader.h000066400000000000000000000004771311104306400202400ustar00rootroot00000000000000 #ifndef PRINTHEADER_H #define PRINTHEADER_H namespace ants { extern int PrintHeader( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // PRINTHEADER_H ants-2.2.0/Examples/include/RebaseTensorImage.h000066400000000000000000000005411311104306400213620ustar00rootroot00000000000000 #ifndef REBASETENSORIMAGE_H #define REBASETENSORIMAGE_H namespace ants { extern int RebaseTensorImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // REBASETENSORIMAGE_H ants-2.2.0/Examples/include/ReorientTensorImage.h000066400000000000000000000005551311104306400217550ustar00rootroot00000000000000 #ifndef REORIENTTENSORIMAGE_H #define REORIENTTENSORIMAGE_H namespace ants { extern int ReorientTensorImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // REORIENTTENSORIMAGE_H ants-2.2.0/Examples/include/ResampleImage.h000066400000000000000000000005131311104306400205350ustar00rootroot00000000000000 #ifndef RESAMPLEIMAGE_H #define RESAMPLEIMAGE_H namespace ants { extern int ResampleImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // RESAMPLEIMAGE_H ants-2.2.0/Examples/include/ResampleImageBySpacing.h000066400000000000000000000005771311104306400223470ustar00rootroot00000000000000 #ifndef RESAMPLEIMAGEBYSPACING_H #define RESAMPLEIMAGEBYSPACING_H namespace ants { extern int ResampleImageBySpacing( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // RESAMPLEIMAGEBYSPACING_H ants-2.2.0/Examples/include/ResetDirection.h000066400000000000000000000005211311104306400207440ustar00rootroot00000000000000 #ifndef RESETDIRECTION_H #define RESETDIRECTION_H namespace ants { extern int ResetDirection( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // RESETDIRECTION_H ants-2.2.0/Examples/include/SetDirectionByMatrix.h000066400000000000000000000005631311104306400221030ustar00rootroot00000000000000 #ifndef SETDIRECTIONBYMATRIX_H #define SETDIRECTIONBYMATRIX_H namespace ants { extern int SetDirectionByMatrix( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // SETDIRECTIONBYMATRIX_H ants-2.2.0/Examples/include/SetOrigin.h000066400000000000000000000004631311104306400177310ustar00rootroot00000000000000 #ifndef SETORIGIN_H #define SETORIGIN_H namespace ants { extern int SetOrigin( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // SETORIGIN_H ants-2.2.0/Examples/include/SetSpacing.h000066400000000000000000000004711311104306400200650ustar00rootroot00000000000000 #ifndef SETSPACING_H #define SETSPACING_H namespace ants { extern int SetSpacing( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // SETSPACING_H ants-2.2.0/Examples/include/SmoothDisplacementField.h000066400000000000000000000005421311104306400225720ustar00rootroot00000000000000#ifndef SMOOTHDISPLACEMENTFIELD_H #define SMOOTHDISPLACEMENTFIELD_H namespace ants { extern int SmoothDisplacementField( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // SMOOTHIMAGE_H ants-2.2.0/Examples/include/SmoothImage.h000066400000000000000000000004771311104306400202470ustar00rootroot00000000000000 #ifndef SMOOTHIMAGE_H #define SMOOTHIMAGE_H namespace ants { extern int SmoothImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // SMOOTHIMAGE_H ants-2.2.0/Examples/include/StackSlices.h000066400000000000000000000004771311104306400202430ustar00rootroot00000000000000 #ifndef STACKSLICES_H #define STACKSLICES_H namespace ants { extern int StackSlices( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // STACKSLICES_H ants-2.2.0/Examples/include/StudentsTestOnImages.h000066400000000000000000000005631311104306400221230ustar00rootroot00000000000000 #ifndef STUDENTSTESTONIMAGES_H #define STUDENTSTESTONIMAGES_H namespace ants { extern int StudentsTestOnImages( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // STUDENTSTESTONIMAGES_H ants-2.2.0/Examples/include/SuperResolution.h000066400000000000000000000005151311104306400212060ustar00rootroot00000000000000 #ifndef SUPERRESOLUTION_H #define SUPERRESOLUTION_H namespace ants { extern int SuperResolution( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // SUPERRESOLUTION_H ants-2.2.0/Examples/include/SurfaceBasedSmoothing.h000066400000000000000000000005711311104306400222450ustar00rootroot00000000000000 #ifndef SURFACEBASEDSMOOTHING_H #define SURFACEBASEDSMOOTHING_H namespace ants { extern int SurfaceBasedSmoothing( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // SURFACEBASEDSMOOTHING_H ants-2.2.0/Examples/include/SurfaceCurvature.h000066400000000000000000000005331311104306400213150ustar00rootroot00000000000000 #ifndef SURFACECURVATURE_H #define SURFACECURVATURE_H namespace ants { extern int SurfaceCurvature( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // SURFACECURVATURE_H ants-2.2.0/Examples/include/TensorDerivedImage.h000066400000000000000000000005471311104306400215510ustar00rootroot00000000000000 #ifndef TENSORDERIVEDIMAGE_H #define TENSORDERIVEDIMAGE_H namespace ants { extern int TensorDerivedImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // TENSORDERIVEDIMAGE_H ants-2.2.0/Examples/include/TextureCooccurrenceFeatures.h000066400000000000000000000005701311104306400235170ustar00rootroot00000000000000 #ifndef TEXTURECOOCCURRENCEFEATURES_H #define TEXTURECOOCCURRENCEFEATURES_H namespace ants { extern int TextureCooccurrenceFeatures( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // THRESHOLDIMAGE_H ants-2.2.0/Examples/include/TextureRunLengthFeatures.h000066400000000000000000000005571311104306400230200ustar00rootroot00000000000000 #ifndef TEXTURERUNLENGTHFEATURES_H #define TEXTURERUNLENGTHFEATURES_H namespace ants { extern int TextureRunLengthFeatures( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // THRESHOLDIMAGE_H ants-2.2.0/Examples/include/ThresholdImage.h000066400000000000000000000005211311104306400207200ustar00rootroot00000000000000 #ifndef THRESHOLDIMAGE_H #define THRESHOLDIMAGE_H namespace ants { extern int ThresholdImage( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // THRESHOLDIMAGE_H ants-2.2.0/Examples/include/TileImages.h000066400000000000000000000004711311104306400200500ustar00rootroot00000000000000 #ifndef TILEIMAGES_H #define TILEIMAGES_H namespace ants { extern int TileImages( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // TILEIMAGES_H ants-2.2.0/Examples/include/TimeSCCAN.h000066400000000000000000000004461311104306400174750ustar00rootroot00000000000000 #ifndef TIMESCCAN_H #define TIMESCCAN_H namespace ants { extern int TimeSCCAN( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // TIMESCCAN_H ants-2.2.0/Examples/include/WarpImageMultiTransform.h000066400000000000000000000006051311104306400226070ustar00rootroot00000000000000 #ifndef WARPIMAGEMULTITRANSFORM_H #define WARPIMAGEMULTITRANSFORM_H namespace ants { extern int WarpImageMultiTransform( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // WARPIMAGEMULTITRANSFORM_H ants-2.2.0/Examples/include/WarpTensorImageMultiTransform.h000066400000000000000000000006511311104306400240030ustar00rootroot00000000000000 #ifndef WARPTENSORIMAGEMULTITRANSFORM_H #define WARPTENSORIMAGEMULTITRANSFORM_H namespace ants { extern int WarpTensorImageMultiTransform( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // WARPTENSORIMAGEMULTITRANSFORM_H ants-2.2.0/Examples/include/WarpTimeSeriesImageMultiTransform.h000066400000000000000000000010051311104306400245740ustar00rootroot00000000000000 #ifndef WARPTIMESERIESIMAGEMULTITRANSFORM_H #define WARPTIMESERIESIMAGEMULTITRANSFORM_H namespace ants { extern int WarpTimeSeriesImageMultiTransform( std::vector, // equivalent to argv of command line parameters to // main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // WARPTIMESERIESIMAGEMULTITRANSFORM_H ants-2.2.0/Examples/include/WarpVTKPolyDataMultiTransform.h000066400000000000000000000006511311104306400236700ustar00rootroot00000000000000 #ifndef WARPVTKPOLYDATAMULTITRANSFORM_H #define WARPVTKPOLYDATAMULTITRANSFORM_H namespace ants { extern int WarpVTKPolyDataMultiTransform( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // WARPVTKPOLYDATAMULTITRANSFORM_H ants-2.2.0/Examples/include/ants.h000066400000000000000000000101171311104306400167700ustar00rootroot00000000000000#ifndef ants_h #define ants_h #include "../../Utilities/itkSurfaceCurvatureBase.h" #include "../../Utilities/itkSurfaceImageCurvature.h" #include "../../Utilities/itkAlternatingValueSimpleSubtractionImageFilter.h" #include "../../Utilities/itkAverageOverDimensionImageFilter.h" #include "../../Utilities/itkAlternatingValueDifferenceImageFilter.h" #include "antsAffineInitializer.h" #include "antsAI.h" #include "antsApplyTransforms.h" #include "antsAlignOrigin.h" #include "antsApplyTransformsToPoints.h" #include "antsJointFusion.h" #include "antsJointTensorFusion.h" #include "antsTransformInfo.h" #include "ANTSConformalMapping.h" #include "ANTS_.h" #include "ANTSIntegrateVectorField.h" #include "ANTSIntegrateVelocityField.h" #include "ANTSJacobian.h" #include "ants_moco.h" #include "antsMotionCorr.h" #include "antsMotionCorrStats.h" #include "antsMotionCorrDiffusionDirection.h" #include "antsSliceRegularizedRegistration.h" #include "antsRegistration.h" #include "antsSurf.h" #include "antsUtilitiesTesting.h" #include "antsVol.h" #include "ANTSUseDeformationFieldToGetAffineTransform.h" #include "ANTSUseLandmarkImagesToGetAffineTransform.h" #include "ANTSUseLandmarkImagesToGetBSplineDisplacementField.h" #include "antsLandmarkBasedTransformInitializer.h" #include "Atropos.h" #include "AverageAffineTransform.h" #include "AverageAffineTransformNoRigid.h" #include "AverageImages.h" #include "AverageTensorImages.h" #include "CheckTopology.h" #include "ClusterImageStatistics.h" #include "ComposeMultiTransform.h" #include "CompositeTransformUtil.h" #include "ComputeSimilarityMetric.h" #include "ConformalMapping.h" #include "ConvertImage.h" #include "ConvertImagePixelType.h" #include "ConvertInputImagePixelTypeToFloat.h" #include "ConvertScalarImageToRGB.h" #include "ConvertToJpg.h" #include "ConvertVectorFieldToVTK.h" #include "CopyImageHeaderInformation.h" #include "CreateDisplacementField.h" #include "CreateDTICohort.h" #include "CreateImage.h" #include "CreateJacobianDeterminantImage.h" #include "CreateTiledMosaic.h" #include "CreateWarpedGridImage.h" #include "DeNrrd.h" #include "DenoiseImage.h" #include "ExtractRegionFromImageByMask.h" #include "ExtractRegionFromImage.h" #include "ExtractSliceFromImage.h" #include "FitBSplineToPoints.h" #include "GetMeshAndTopology.h" #include "iMath.h" #include "ImageCompare.h" #include "ImageMath.h" #include "ImageIntensityStatistics.h" // #include "antsSimilarityInitializer.h" #include "ImageSetStatistics.h" #include "itkCommandLineParserTest.h" #include "KellyKapowski.h" #include "KellySlater.h" #include "LabelClustersUniquely.h" #include "LabelGeometryMeasures.h" #include "LabelOverlapMeasures.h" #include "LaplacianThickness.h" #include "LesionFilling.h" #include "MeasureImageSimilarity.h" #include "MeasureMinMaxMean.h" #include "MemoryTest.h" #include "MultiplyImages.h" #include "N3BiasFieldCorrection.h" #include "N4BiasFieldCorrection.h" #include "NonLocalSuperResolution.h" #include "PasteImageIntoImage.h" #include "PermuteFlipImageOrientationAxes.h" #include "PrintHeader.h" #include "RebaseTensorImage.h" #include "ReorientTensorImage.h" #include "ResampleImageBySpacing.h" #include "ResampleImage.h" #include "ResetDirection.h" #include "sccan.h" #include "simpleSynRegistration.h" #include "SetDirectionByMatrix.h" #include "SetOrigin.h" #include "SetSpacing.h" #include "SmoothImage.h" #include "SmoothDisplacementField.h" #include "StackSlices.h" #include "StudentsTestOnImages.h" #include "SuperResolution.h" #include "SurfaceBasedSmoothing.h" #include "SurfaceCurvature.h" #include "TensorDerivedImage.h" #include "ThresholdImage.h" #include "TileImages.h" #include "TimeSCCAN.h" #include "WarpImageMultiTransform.h" #include "WarpTensorImageMultiTransform.h" #include "WarpTimeSeriesImageMultiTransform.h" #include "WarpVTKPolyDataMultiTransform.h" #include "ConvertTransformFile.h" #include "compareTwoTransforms.h" #include "GetConnectedComponentsFeatureImages.h" #include "TextureRunLengthFeatures.h" #include "TextureCooccurrenceFeatures.h" #endif // ants_h ants-2.2.0/Examples/include/antsAI.h000066400000000000000000000004741311104306400172070ustar00rootroot00000000000000#ifndef ANTSAI_H #define ANTSAI_H namespace ants { extern int antsAI( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSAI_H ants-2.2.0/Examples/include/antsAffineInitializer.h000066400000000000000000000005711311104306400223100ustar00rootroot00000000000000 #ifndef ANTSAFFINEINITIALIZER_H #define ANTSAFFINEINITIALIZER_H namespace ants { extern int antsAffineInitializer( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSAFFINEINITIALIZER_H ants-2.2.0/Examples/include/antsAlignOrigin.h000066400000000000000000000005241311104306400211140ustar00rootroot00000000000000 #ifndef ANTSALIGNORIGIN_H #define ANTSALIGNORIGIN_H namespace ants { extern int antsAlignOrigin( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSLIGNORIGIN_H ants-2.2.0/Examples/include/antsApplyTransforms.h000066400000000000000000000005551311104306400220620ustar00rootroot00000000000000 #ifndef ANTSAPPLYTRANSFORMS_H #define ANTSAPPLYTRANSFORMS_H namespace ants { extern int antsApplyTransforms( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSAPPLYTRANSFORMS_H ants-2.2.0/Examples/include/antsApplyTransformsToPoints.h000066400000000000000000000006351311104306400235610ustar00rootroot00000000000000 #ifndef ANTSAPPLYTRANSFORMSTOPOINTS_H #define ANTSAPPLYTRANSFORMSTOPOINTS_H namespace ants { extern int antsApplyTransformsToPoints( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSAPPLYTRANSFORMSTOPOINTS_H ants-2.2.0/Examples/include/antsImageRead.h000066400000000000000000000004421311104306400205270ustar00rootroot00000000000000 #ifndef ANTSIMAGEREAD_H #define ANTSIMAGEREAD_H #include "itkImage.h" namespace ants { extern itk::Image::Pointer antsImageRead( std::string // filename of the image to be read ); } // namespace ants #endif // ANTSIMAGEREAD_H ants-2.2.0/Examples/include/antsImageWrite.h000066400000000000000000000005011311104306400207420ustar00rootroot00000000000000 #ifndef ANTSIMAGEWRITE_H #define ANTSIMAGEWRITE_H #include "itkImage.h" namespace ants { extern int antsImageWrite( itk::Image::Pointer, // image to write std::string // filename of the target file ); } // namespace ants #endif // ANTSIMAGEWRITE_H ants-2.2.0/Examples/include/antsJointFusion.h000066400000000000000000000005401311104306400211570ustar00rootroot00000000000000#ifndef ANTSJOINTFUSION_H #define ANTSJOINTFUSION_H namespace ants { extern int antsJointFusion( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSJOINTFUSION_H ants-2.2.0/Examples/include/antsJointTensorFusion.h000066400000000000000000000006051311104306400223540ustar00rootroot00000000000000#ifndef ANTSJOINTTENSORFUSION_H #define ANTSJOINTTENSORFUSION_H namespace ants { extern int antsJointTensorFusion( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSJOINTTENSORFUSION_H ants-2.2.0/Examples/include/antsLandmarkBasedTransformInitializer.h000066400000000000000000000011121311104306400254740ustar00rootroot00000000000000 #ifndef ANTSLANDMARKBASEDTRANSFORMINITIALIZER_H #define ANTSLANDMARKBASEDTRANSFORMINITIALIZER_H namespace ants { extern int antsLandmarkBasedTransformInitializer( std::vector, // equivalent to argv of command line // parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSUSELANDMARKIMAGESTOGETBSPLINEDISPLACEMENTFIELD_H ants-2.2.0/Examples/include/antsMotionCorr.h000066400000000000000000000005211311104306400210020ustar00rootroot00000000000000 #ifndef ANTSMOTIONCORR_H #define ANTSMOTIONCORR_H namespace ants { extern int antsMotionCorr( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSMOTIONCORR_H ants-2.2.0/Examples/include/antsMotionCorrDiffusionDirection.h000066400000000000000000000006311311104306400245140ustar00rootroot00000000000000 #ifndef ANTSMOTIONCORRDIFFUSIONDIRECTION_H #define ANTSMOTIONCORRDIFFUSIONDIRECTION_H namespace ants { extern int antsMotionCorrDiffusionDirection( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSMOTIONCORRDIFFUSIONDIRECTION_H ants-2.2.0/Examples/include/antsMotionCorrStats.h000066400000000000000000000005451311104306400220270ustar00rootroot00000000000000 #ifndef ANTSMOTIONCORRSTATS_H #define ANTSMOTIONCORRSTATS_H namespace ants { extern int antsMotionCorrStats( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSMOTIONCORRSTATS_H ants-2.2.0/Examples/include/antsRegistration.h000066400000000000000000000005331311104306400213640ustar00rootroot00000000000000 #ifndef ANTSREGISTRATION_H #define ANTSREGISTRATION_H namespace ants { extern int antsRegistration( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSREGISTRATION_H ants-2.2.0/Examples/include/antsSliceRegularizedRegistration.h000066400000000000000000000006261311104306400245450ustar00rootroot00000000000000 #ifndef ANTSLICEREGULARIZEDREGISTRATION_H #define ANTSLICEREGULARIZEDREGISTRATION_H namespace ants { extern int antsSliceRegularizedRegistration( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSLICEREGULARIZEDREGISTRATION_H ants-2.2.0/Examples/include/antsSurf.h000066400000000000000000000005001311104306400176230ustar00rootroot00000000000000#ifndef ANTSSURF_H #define ANTSSURF_H namespace ants { extern int antsSurf( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSSURF_H ants-2.2.0/Examples/include/antsTransformInfo.h000066400000000000000000000005371311104306400215050ustar00rootroot00000000000000 #ifndef ANTSTRANSFORMINFO_H #define ANTSTRANSFORMINFO_H namespace ants { extern int antsTransformInfo( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTSMOTIONCORRSTATS_H ants-2.2.0/Examples/include/antsUtilitiesTesting.h000066400000000000000000000005661311104306400222310ustar00rootroot00000000000000 #ifndef ANTSUTILITIESTESTING_H #define ANTSUTILITIESTESTING_H namespace ants { extern int antsUtilitiesTesting( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // LABELGEOMETRYMEASURES_H ants-2.2.0/Examples/include/antsVol.h000066400000000000000000000004731311104306400174550ustar00rootroot00000000000000#ifndef ANTSVOL_H #define ANTSVOL_H namespace ants { extern int antsVol( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTVOL_H ants-2.2.0/Examples/include/ants_moco.h000066400000000000000000000004631311104306400200100ustar00rootroot00000000000000 #ifndef ANTS_MOCO_H #define ANTS_MOCO_H namespace ants { extern int ants_moco( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ANTS_MOCO_H ants-2.2.0/Examples/include/compareTwoTransforms.h000066400000000000000000000006061311104306400222240ustar00rootroot00000000000000#ifndef COMPARETWOTRANSFORMS_H #define COMPARETWOTRANSFORMS_H namespace ants { extern int compareTwoTransforms( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // COMPARETWOTRANSFORMS_H ants-2.2.0/Examples/include/iMath.h000066400000000000000000000004431311104306400170660ustar00rootroot00000000000000 #ifndef IMATH_H #define IMATH_H namespace ants { extern int iMath( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // IMATH_H ants-2.2.0/Examples/include/itkCommandLineParserTest.h000066400000000000000000000006131311104306400227360ustar00rootroot00000000000000 #ifndef ITKCOMMANDLINEPARSERTEST_H #define ITKCOMMANDLINEPARSERTEST_H namespace ants { extern int itkCommandLineParserTest( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // ITKCOMMANDLINEPARSERTEST_H ants-2.2.0/Examples/include/sccan.h000066400000000000000000000004331311104306400171120ustar00rootroot00000000000000 #ifndef SCCAN_H #define SCCAN_H namespace ants { extern int sccan( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // SCCAN_H ants-2.2.0/Examples/include/simpleSynRegistration.h000066400000000000000000000006041311104306400224010ustar00rootroot00000000000000#ifndef SIMPLESYNREGISTRATION_H #define SIMPLESYNREGISTRATION_H namespace ants { extern int simpleSynRegistration( std::vector, // equivalent to argv of command line parameters to main() std::ostream* out_stream // [optional] output stream to write ); } // namespace ants #endif // SIMPLESYNREGISTRATION_H ants-2.2.0/Examples/itkCommandLineParserTest.cxx000066400000000000000000000071611311104306400216730ustar00rootroot00000000000000 #include "antsUtilities.h" #include #include "itkPICSLAdvancedNormalizationToolKit.h" #include "itkCommandLineParser.h" namespace ants { // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int itkCommandLineParserTest( std::vector args, std::ostream* out_stream = NULL ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the parser should handle args.insert( args.begin(), "itkCommandLineParserTest" ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = 0; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); typedef itk::PICSLAdvancedNormalizationToolKit<3> RegistrationType; RegistrationType::Pointer registration = RegistrationType::New(); registration->ParseCommandLine( argc, argv ); typedef itk::CommandLineParser ParserType; ParserType::Pointer parser = ParserType::New(); typedef ParserType::OptionType OptionType; OptionType::Pointer fileOption = OptionType::New(); fileOption->SetShortName( 'f' ); fileOption->SetLongName( "file" ); fileOption->SetDescription( "The fixed image file used in the image registration algorithm." ); parser->AddOption( fileOption ); typedef ParserType::OptionType OptionType; OptionType::Pointer metricOption = OptionType::New(); metricOption->SetShortName( 'm' ); metricOption->SetLongName( "metric" ); metricOption->SetDescription( "The metric used by the image registration algorithm." ); parser->AddOption( metricOption ); typedef ParserType::OptionType OptionType; OptionType::Pointer verbosityOption = OptionType::New(); verbosityOption->SetLongName( "verbosity" ); verbosityOption->SetDescription( "Mundi vult decipi. " ); parser->AddOption( verbosityOption ); typedef ParserType::OptionType OptionType; OptionType::Pointer dirOption = OptionType::New(); dirOption->SetLongName( "directionality" ); // dirOption->SetShortName( 'd' ); dirOption->SetDescription( "Mundi vult decipi. " ); parser->AddOption( dirOption ); if( parser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( !parser->GetOption( "directionality" ) ) { std::cout << "N ULL" << std::endl; } else { for( unsigned int j = 0; j < 3; j++ ) { std::cout << parser->ConvertVector( parser->GetOption( "directionality" )->GetValue( 0 ) )[j] << " x "; } } parser->PrintMenu( std::cout, 7 ); // std::cout << std::endl << std::endl << "--------------------------------------------" // << std::endl << std::endl; // parser->Print( std::cout << 7 ); return 0; }; } // namespace ants ants-2.2.0/Examples/itkTestMain.h000066400000000000000000000406011311104306400166350ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. Portions of this code are covered under the VTK copyright. See VTKCopyright.txt or http://www.kitware.com/VTKCopyright.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 __itkTestMain_h #define __itkTestMain_h // This file is used to create TestDriver executables // These executables are able to register a function pointer to a string name // in a lookup table. By including this file, it creates a main function // that calls RegisterTests() then looks up the function pointer for the test // specified on the command line. #include "itkWin32Header.h" #include #include #include #include #include "itkNumericTraits.h" #include "itkMultiThreader.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionConstIterator.h" #include "itkSubtractImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkExtractImageFilter.h" #include "LOCAL_itkDifferenceImageFilter.h" #include "itkImageRegion.h" #include "itksys/SystemTools.hxx" #include #define ITK_TEST_DIMENSION_MAX 6 typedef int (*MainFuncPointer)(int, char * [] ); std::map StringToTestFunctionMap; #define REGISTER_TEST(test) \ extern int test(int, char * [] ); \ StringToTestFunctionMap[#test] = test int RegressionTestImage(const char *testImageFilename, const char *baselineImageFilename, int reportErrors, double intensityTolerance = 2.0, unsigned int numberOfPixelsTolerance = 0, unsigned int radiusTolerance = 0); std::map RegressionTestBaselines(char *); void RegisterTests(); void PrintAvailableTests() { std::cout << "Available tests:\n"; std::map::iterator j = StringToTestFunctionMap.begin(); int i = 0; while( j != StringToTestFunctionMap.end() ) { std::cout << i << ". " << j->first << "\n"; ++i; ++j; } } static void sigfpe_handl(int sig) { printf("Argggh..Division by zero ! (or other fpe): SIGNAL=%d\n", sig); // ..some more exit(-1); } // void sigint_handl(int sig) // { // printf("Program is exiting..: SIGNAL=%d\n",sig); // exit(0); // } int main(int ac, char* av[] ) { std::signal(SIGFPE, sigfpe_handl); double intensityTolerance = 2.0; unsigned int numberOfPixelsTolerance = 0; unsigned int radiusTolerance = 0; typedef std::pair ComparePairType; std::vector compareList; RegisterTests(); std::string testToRun; if( ac < 2 ) { PrintAvailableTests(); std::cout << "To run a test, enter the test number: "; int testNum = 0; std::cin >> testNum; std::map::iterator j = StringToTestFunctionMap.begin(); int i = 0; while( j != StringToTestFunctionMap.end() && i < testNum ) { ++i; ++j; } if( j == StringToTestFunctionMap.end() ) { std::cerr << testNum << " is an invalid test number\n"; return -1; } testToRun = j->first; } else { while( ac > 0 && testToRun.empty() ) { if( strcmp(av[1], "--with-threads") == 0 ) { int numThreads = atoi(av[2]); itk::MultiThreader::SetGlobalDefaultNumberOfThreads(numThreads); av += 2; ac -= 2; } else if( strcmp(av[1], "--without-threads") == 0 ) { itk::MultiThreader::SetGlobalDefaultNumberOfThreads(1); av += 1; ac -= 1; } else if( ac > 3 && strcmp(av[1], "--compare") == 0 ) { compareList.push_back( ComparePairType( av[2], av[3] ) ); av += 3; ac -= 3; } else if( ac > 2 && strcmp(av[1], "--compareNumberOfPixelsTolerance") == 0 ) { numberOfPixelsTolerance = atoi( av[2] ); av += 2; ac -= 2; } else if( ac > 2 && strcmp(av[1], "--compareRadiusTolerance") == 0 ) { radiusTolerance = atoi( av[2] ); av += 2; ac -= 2; } else if( ac > 2 && strcmp(av[1], "--compareIntensityTolerance") == 0 ) { intensityTolerance = atof( av[2] ); av += 2; ac -= 2; } else { testToRun = av[1]; } } } std::map::iterator j = StringToTestFunctionMap.find(testToRun); if( j != StringToTestFunctionMap.end() ) { MainFuncPointer f = j->second; int result; try { // Invoke the test's "main" function. result = (*f)(ac - 1, av + 1); // Make a list of possible baselines for( int i = 0; i < static_cast(compareList.size() ); ++i ) { char * baselineFilename = compareList[i].first; char * testFilename = compareList[i].second; std::map baselines = RegressionTestBaselines(baselineFilename); std::map::iterator baseline = baselines.begin(); std::string bestBaseline; int bestBaselineStatus = itk::NumericTraits::max(); while( baseline != baselines.end() ) { baseline->second = RegressionTestImage(testFilename, (baseline->first).c_str(), 0, intensityTolerance, numberOfPixelsTolerance, radiusTolerance ); if( baseline->second < bestBaselineStatus ) { bestBaseline = baseline->first; bestBaselineStatus = baseline->second; } if( baseline->second == 0 ) { break; } ++baseline; } // if the best we can do still has errors, generate the error images if( bestBaselineStatus ) { RegressionTestImage(testFilename, bestBaseline.c_str(), 1, intensityTolerance, numberOfPixelsTolerance, radiusTolerance ); } // output the matching baseline std::cout << ""; std::cout << itksys::SystemTools::GetFilenameName(bestBaseline); std::cout << "" << std::endl; result += bestBaselineStatus; } } catch( const itk::ExceptionObject& e ) { std::cerr << "ITK test driver caught an ITK exception:\n"; e.Print(std::cerr); result = -1; } catch( const std::exception& e ) { std::cerr << "ITK test driver caught an exception:\n"; std::cerr << e.what() << "\n"; result = -1; } catch( ... ) { std::cerr << "ITK test driver caught an unknown exception!!!\n"; result = -1; } return result; } PrintAvailableTests(); std::cerr << "Failed: " << testToRun << ": No test registered with name " << testToRun << "\n"; return -1; } // Regression Testing Code int RegressionTestImage(const char *testImageFilename, const char *baselineImageFilename, int reportErrors, double intensityTolerance, unsigned int numberOfPixelsTolerance, unsigned int radiusTolerance ) { // Use the factory mechanism to read the test and baseline files and convert // them to double typedef itk::Image ImageType; typedef itk::Image OutputType; typedef itk::Image DiffOutputType; typedef itk::ImageFileReader ReaderType; // Read the baseline file ReaderType::Pointer baselineReader = ReaderType::New(); baselineReader->SetFileName(baselineImageFilename); try { baselineReader->UpdateLargestPossibleRegion(); } catch( itk::ExceptionObject& e ) { std::cerr << "Exception detected while reading " << baselineImageFilename << " : " << e.GetDescription(); return 1000; } // Read the file generated by the test ReaderType::Pointer testReader = ReaderType::New(); testReader->SetFileName(testImageFilename); try { testReader->UpdateLargestPossibleRegion(); } catch( itk::ExceptionObject& e ) { std::cerr << "Exception detected while reading " << testImageFilename << " : " << e.GetDescription() << std::endl; return 1000; } // The sizes of the baseline and test image must match ImageType::SizeType baselineSize; baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize(); ImageType::SizeType testSize; testSize = testReader->GetOutput()->GetLargestPossibleRegion().GetSize(); if( baselineSize != testSize ) { std::cerr << "The size of the Baseline image and Test image do not match!" << std::endl; std::cerr << "Baseline image: " << baselineImageFilename << " has size " << baselineSize << std::endl; std::cerr << "Test image: " << testImageFilename << " has size " << testSize << std::endl; return 1; } // Now compare the two images typedef itk::LOCAL_DifferenceImageFilter DiffType; DiffType::Pointer diff = DiffType::New(); diff->SetValidInput(baselineReader->GetOutput() ); diff->SetTestInput(testReader->GetOutput() ); diff->SetDifferenceThreshold( intensityTolerance ); diff->SetToleranceRadius( radiusTolerance ); diff->UpdateLargestPossibleRegion(); unsigned long status = 0; status = diff->GetNumberOfPixelsWithDifferences(); // if there are discrepencies, create an diff image if( (status > numberOfPixelsTolerance) && reportErrors ) { typedef itk::RescaleIntensityImageFilter RescaleType; typedef itk::ExtractImageFilter ExtractType; typedef itk::ImageFileWriter WriterType; typedef itk::ImageRegion RegionType; OutputType::SizeType size; size.Fill(0); RescaleType::Pointer rescale = RescaleType::New(); rescale->SetOutputMinimum(itk::NumericTraits::NonpositiveMin() ); rescale->SetOutputMaximum(itk::NumericTraits::max() ); rescale->SetInput(diff->GetOutput() ); rescale->UpdateLargestPossibleRegion(); size = rescale->GetOutput()->GetLargestPossibleRegion().GetSize(); // Get the center slice of the image, In 3D, the first slice // is often a black slice with little debugging information. OutputType::IndexType index; index.Fill(0); for( unsigned int i = 2; i < ITK_TEST_DIMENSION_MAX; ++i ) { index[i] = size[i] / 2; // NOTE: Integer Divide used to get approximately // the center slice size[i] = 0; } RegionType region; region.SetIndex(index); region.SetSize(size); ExtractType::Pointer extract = ExtractType::New(); extract->SetInput(rescale->GetOutput() ); extract->SetExtractionRegion(region); extract->SetDirectionCollapseToIdentity(); WriterType::Pointer writer = WriterType::New(); writer->SetInput(extract->GetOutput() ); std::cout << ""; std::cout << status; std::cout << "" << std::endl; std::ostringstream diffName; diffName << testImageFilename << ".diff.png"; try { rescale->SetInput(diff->GetOutput() ); rescale->Update(); } catch( const std::exception& e ) { std::cerr << "Error during rescale of " << diffName.str() << std::endl; std::cerr << e.what() << "\n"; } catch( ... ) { std::cerr << "Error during rescale of " << diffName.str() << std::endl; } writer->SetFileName(diffName.str().c_str() ); try { writer->Update(); } catch( const std::exception& e ) { std::cerr << "Error during write of " << diffName.str() << std::endl; std::cerr << e.what() << "\n"; } catch( ... ) { std::cerr << "Error during write of " << diffName.str() << std::endl; } std::cout << ""; std::cout << diffName.str(); std::cout << "" << std::endl; std::ostringstream baseName; baseName << testImageFilename << ".base.png"; try { rescale->SetInput(baselineReader->GetOutput() ); rescale->Update(); } catch( const std::exception& e ) { std::cerr << "Error during rescale of " << baseName.str() << std::endl; std::cerr << e.what() << "\n"; } catch( ... ) { std::cerr << "Error during rescale of " << baseName.str() << std::endl; } try { writer->SetFileName(baseName.str().c_str() ); writer->Update(); } catch( const std::exception& e ) { std::cerr << "Error during write of " << baseName.str() << std::endl; std::cerr << e.what() << "\n"; } catch( ... ) { std::cerr << "Error during write of " << baseName.str() << std::endl; } std::cout << ""; std::cout << baseName.str(); std::cout << "" << std::endl; std::ostringstream testName; testName << testImageFilename << ".test.png"; try { rescale->SetInput(testReader->GetOutput() ); rescale->Update(); } catch( const std::exception& e ) { std::cerr << "Error during rescale of " << testName.str() << std::endl; std::cerr << e.what() << "\n"; } catch( ... ) { std::cerr << "Error during rescale of " << testName.str() << std::endl; } try { writer->SetFileName(testName.str().c_str() ); writer->Update(); } catch( const std::exception& e ) { std::cerr << "Error during write of " << testName.str() << std::endl; std::cerr << e.what() << "\n"; } catch( ... ) { std::cerr << "Error during write of " << testName.str() << std::endl; } std::cout << ""; std::cout << testName.str(); std::cout << "" << std::endl; } return (status > numberOfPixelsTolerance) ? 1 : 0; } // // Generate all of the possible baselines // The possible baselines are generated fromn the baselineFilename using the // following algorithm: // 1) strip the suffix // 2) append a digit .x // 3) append the original suffix. // It the file exists, increment x and continue // std::map RegressionTestBaselines(char *baselineFilename) { std::map baselines; baselines[std::string(baselineFilename)] = 0; std::string originalBaseline(baselineFilename); int x = 0; std::string::size_type suffixPos = originalBaseline.rfind("."); std::string suffix; if( suffixPos != std::string::npos ) { suffix = originalBaseline.substr(suffixPos, originalBaseline.length() ); originalBaseline.erase(suffixPos, originalBaseline.length() ); } while( ++x ) { std::ostringstream filename; filename << originalBaseline << "." << x << suffix; std::ifstream filestream(filename.str().c_str() ); if( !filestream ) { break; } baselines[filename.str()] = 0; filestream.close(); } return baselines; } // Needed for explicit instantiation #include "LOCAL_itkDifferenceImageFilter.hxx" #endif ants-2.2.0/Examples/itkantsRegistrationHelper.h000066400000000000000000001472611311104306400216230ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __antsRegistrationHelper_h #define __antsRegistrationHelper_h #include "ReadWriteData.h" #include "itkANTSAffine3DTransform.h" #include "itkANTSCenteredAffine2DTransform.h" #include "itkANTSNeighborhoodCorrelationImageToImageMetricv4.h" #include "itkAffineTransform.h" #include "itkArray.h" #include "itkBSplineExponentialDiffeomorphicTransform.h" #include "itkBSplineExponentialDiffeomorphicTransformParametersAdaptor.h" #include "itkBSplineSmoothingOnUpdateDisplacementFieldTransform.h" #include "itkBSplineSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor.h" #include "itkBSplineSyNImageRegistrationMethod.h" #include "itkBSplineTransform.h" #include "itkBSplineTransformParametersAdaptor.h" #include "itkCastImageFilter.h" #include "itkCenteredAffineTransform.h" #include "itkCenteredTransformInitializer.h" #include "itkCommand.h" #include "itkComposeDisplacementFieldsImageFilter.h" #include "itkCompositeTransform.h" #include "itkConjugateGradientLineSearchOptimizerv4.h" #include "itkCorrelationImageToImageMetricv4.h" #include "itkDemonsImageToImageMetricv4.h" #include "itkDisplacementFieldTransform.h" #include "itkEuclideanDistancePointSetToPointSetMetricv4.h" #include "itkEuler2DTransform.h" #include "itkEuler3DTransform.h" #include "itkExpectationBasedPointSetToPointSetMetricv4.h" #include "itkGaussianExponentialDiffeomorphicTransform.h" #include "itkGaussianExponentialDiffeomorphicTransformParametersAdaptor.h" #include "itkGaussianSmoothingOnUpdateDisplacementFieldTransform.h" #include "itkGaussianSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor.h" #include "itkGradientDescentOptimizerv4.h" #include "itkHistogramMatchingImageFilter.h" #include "itkIdentityTransform.h" #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageMaskSpatialObject.h" #include "itkImageRegistrationMethodv4.h" #include "itkImageToHistogramFilter.h" #include "itkImageToImageMetricv4.h" #include "itkIntensityWindowingImageFilter.h" #include "itkJensenHavrdaCharvatTsallisPointSetToPointSetMetricv4.h" #include "itkJointHistogramMutualInformationImageToImageMetricv4.h" #include "itkLabeledPointSetToPointSetMetricv4.h" #include "itkLinearInterpolateImageFunction.h" #include "itkMatrixOffsetTransformBase.h" #include "itkMattesMutualInformationImageToImageMetricv4.h" #include "itkMeanSquaresImageToImageMetricv4.h" #include "itkMeanSquaresPointSetToPointSetIntensityMetricv4.h" #include "itkMultiGradientOptimizerv4.h" #include "itkObject.h" #include "itkObjectToObjectMetric.h" #include "itkObjectToObjectMultiMetricv4.h" #include "itkQuaternionRigidTransform.h" #include "itkRegistrationParameterScalesFromPhysicalShift.h" #include "itkSimilarity2DTransform.h" #include "itkSimilarity3DTransform.h" #include "itkSyNImageRegistrationMethod.h" #include "itkTimeProbe.h" #include "itkTimeVaryingBSplineVelocityFieldImageRegistrationMethod.h" #include "itkTimeVaryingBSplineVelocityFieldTransformParametersAdaptor.h" #include "itkTimeVaryingVelocityFieldImageRegistrationMethodv4.h" #include "itkTimeVaryingVelocityFieldTransform.h" #include "itkTimeVaryingVelocityFieldTransformParametersAdaptor.h" #include "itkTransform.h" #include "itkTransformFactory.h" #include "itkTranslationTransform.h" #include "itkVersorRigid3DTransform.h" #include "itkWeakPointer.h" #include "itkantsReadWriteTransform.h" #include "antsAllocImage.h" #include "antsCommandLineParser.h" #include #include #include #include #include #include "antsRegistrationCommandIterationUpdate.h" #include "antsRegistrationOptimizerCommandIterationUpdate.h" #include "antsDisplacementAndVelocityFieldRegistrationCommandIterationUpdate.h" namespace ants { typedef itk::ants::CommandLineParser ParserType; typedef ParserType::OptionType OptionType; template class RegistrationHelper : public itk::Object { public: /** Standard class typedefs */ typedef RegistrationHelper Self; typedef itk::Object Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; typedef itk::WeakPointer ConstWeakPointer; typedef TComputeType RealType; typedef TComputeType PixelType; typedef itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; typedef itk::ImageBase ImageBaseType; typedef typename ImageType::SpacingType ImageSpacingType; typedef itk::Array IntensityAndGradientArrayType; typedef itk::PointSet LabeledPointSetType; typedef typename LabeledPointSetType::Pointer LabeledPointSetPointer; typedef itk::PointSet IntensityPointSetType; typedef typename IntensityPointSetType::Pointer IntensityPointSetPointer; typedef itk::Transform TransformType; typedef itk::AffineTransform AffineTransformType; typedef itk::ImageRegistrationMethodv4 AffineRegistrationType; typedef typename AffineRegistrationType::ShrinkFactorsPerDimensionContainerType ShrinkFactorsPerDimensionContainerType; typedef typename AffineTransformType::Superclass MatrixOffsetTransformBaseType; typedef typename MatrixOffsetTransformBaseType::Pointer MatrixOffsetTransformBasePointer; typedef itk::CompositeTransform CompositeTransformType; typedef typename CompositeTransformType::Pointer CompositeTransformPointer; typedef itk::DisplacementFieldTransform DisplacementFieldTransformType; typedef typename DisplacementFieldTransformType::Pointer DisplacementFieldTransformPointer; typedef typename DisplacementFieldTransformType::DisplacementFieldType DisplacementFieldType; typedef itk::TimeVaryingVelocityFieldTransform TimeVaryingVelocityFieldTransformType; typedef itk::ObjectToObjectMetric ObjectMetricType; typedef itk::ObjectToObjectMultiMetricv4 MultiMetricType; typedef itk::ImageToImageMetricv4 ImageMetricType; typedef itk::ImageMaskSpatialObject ImageMaskSpatialObjectType; typedef typename ImageMaskSpatialObjectType::ImageType MaskImageType; typedef itk::InterpolateImageFunction InterpolatorType; typedef itk::ConjugateGradientLineSearchOptimizerv4Template ConjugateGradientDescentOptimizerType; typedef itk::GradientDescentOptimizerv4Template GradientDescentOptimizerType; enum MetricEnumeration { CC = 0, MI = 1, Mattes = 2, MeanSquares = 3, Demons = 4, GC = 5, ICP = 6, PSE = 7, JHCT = 8, IGDM = 9, IllegalMetric = 10 }; enum SamplingStrategy { none = 0, // aka dense regular = 1, // regularly spaced sub-sampling random = 2, // irregularly spaced sub-sampling invalid = 17 }; bool IsPointSetMetric( const MetricEnumeration metricType ) const { if( metricType == ICP || metricType == PSE || metricType == JHCT || metricType == IGDM ) { return true; } else { return false; } } class Metric { public: Metric( MetricEnumeration metricType, ImageType *fixedImage, ImageType *movingImage, LabeledPointSetType *fixedLabeledPointSet, LabeledPointSetType *movingLabeledPointSet, IntensityPointSetType *fixedIntensityPointSet, IntensityPointSetType *movingIntensityPointSet, unsigned int stageID, RealType weighting, SamplingStrategy samplingStrategy, int numberOfBins, unsigned int radius, bool useBoundaryPointsOnly, RealType pointSetSigma, unsigned int evaluationKNeighborhood, RealType alpha, bool useAnisotropicCovariances, RealType samplingPercentage, RealType intensityDistanceSigma, RealType euclideanDistanceSigma ) : m_MetricType( metricType ), m_FixedImage( fixedImage ), m_MovingImage( movingImage ), m_StageID( stageID ), m_Weighting( weighting ), m_SamplingStrategy( samplingStrategy ), m_NumberOfBins( numberOfBins ), m_Radius( radius ), m_FixedLabeledPointSet( fixedLabeledPointSet ), m_MovingLabeledPointSet( movingLabeledPointSet ), m_FixedIntensityPointSet( fixedIntensityPointSet ), m_MovingIntensityPointSet( movingIntensityPointSet ), m_UseBoundaryPointsOnly( useBoundaryPointsOnly ), m_PointSetSigma( pointSetSigma ), m_EvaluationKNeighborhood( evaluationKNeighborhood ), m_Alpha( alpha ), m_UseAnisotropicCovariances( useAnisotropicCovariances ), m_SamplingPercentage( samplingPercentage ), m_IntensityDistanceSigma( intensityDistanceSigma ), m_EuclideanDistanceSigma( euclideanDistanceSigma ) { } const std::string GetMetricAsString() const { switch( this->m_MetricType ) { case CC: { return std::string( "CC" ); } case MI: { return std::string( "MI" ); } case Mattes: { return std::string( "Mattes" ); } case MeanSquares: { return std::string( "MeanSquares" ); } case Demons: { return std::string( "Demons" ); } case GC: { return std::string( "GC" ); } case ICP: { return std::string( "ICP" ); } case PSE: { return std::string( "PSE" ); } case JHCT: { return std::string( "JHCT" ); } case IGDM: { return std::string( "IGDM" ); } default: { } break; } return std::string( "Illegal" ); } MetricEnumeration m_MetricType; // Variables for image metrics ImagePointer m_FixedImage; ImagePointer m_MovingImage; unsigned int m_StageID; RealType m_Weighting; SamplingStrategy m_SamplingStrategy; int m_NumberOfBins; unsigned int m_Radius; // Only for CC metric // Variables for point-set metrics LabeledPointSetPointer m_FixedLabeledPointSet; LabeledPointSetPointer m_MovingLabeledPointSet; IntensityPointSetPointer m_FixedIntensityPointSet; IntensityPointSetPointer m_MovingIntensityPointSet; bool m_UseBoundaryPointsOnly; RealType m_PointSetSigma; // Only for PSE,JHCT metrics RealType m_EvaluationKNeighborhood; // Only for PSE,JHCT metrics RealType m_Alpha; // Only for JHCT metric bool m_UseAnisotropicCovariances; // Only for JHCT metric RealType m_SamplingPercentage; // Only for PSE,JHCT metrics RealType m_IntensityDistanceSigma; // Only for IGDM metric RealType m_EuclideanDistanceSigma; // Only for IGDM metric }; typedef std::deque MetricListType; enum XfrmMethod { Rigid = 0, Affine = 1, CompositeAffine = 2, Similarity = 3, Translation = 4, BSpline = 5, GaussianDisplacementField = 6, BSplineDisplacementField = 7, TimeVaryingVelocityField = 8, TimeVaryingBSplineVelocityField = 9, SyN = 10, BSplineSyN = 11, Exponential = 12, BSplineExponential = 13, UnknownXfrm = 14 }; class TransformMethod { public: TransformMethod() : m_XfrmMethod( Rigid ), m_GradientStep( 0 ), m_UpdateFieldVarianceInVarianceSpace( 0.0 ), m_TotalFieldVarianceInVarianceSpace( 0.0 ), m_SplineOrder( 3 ), m_UpdateFieldTimeSigma( 0.0 ), m_TotalFieldTimeSigma( 0.0 ), m_NumberOfTimeIndices( 0 ), m_NumberOfTimePointSamples( 4 ), m_VelocityFieldVarianceInVarianceSpace( 0.0 ) { } std::string XfrmMethodAsString() const { switch( this->m_XfrmMethod ) { case Rigid: { return std::string( "Rigid" ); } case Affine: { return std::string( "Affine" ); } case CompositeAffine: { return std::string( "CompositeAffine" ); } case Similarity: { return std::string( "Similarity" ); } case Translation: { return std::string( "Translation" ); } case BSpline: { return std::string( "BSpline" ); } case GaussianDisplacementField: { return std::string( "GaussianDisplacementField" ); } case BSplineDisplacementField: { return std::string( "BSplineDisplacementField" ); } case TimeVaryingVelocityField: { return std::string( "TimeVaryingVelocityField" ); } case TimeVaryingBSplineVelocityField: { return std::string( "TimeVaryingBSplineVelocityField" ); } case SyN: { return std::string( "SyN" ); } case BSplineSyN: { return std::string( "BSplineSyN" ); } case Exponential: { return std::string( "Exponential" ); } case BSplineExponential: { return std::string( "BSplineExponential" ); } case UnknownXfrm: return std::string( "UnknownXfrm" ); } return std::string( "Impossible" ); } XfrmMethod m_XfrmMethod; // all transforms RealType m_GradientStep; // BSpline std::vector m_MeshSizeAtBaseLevel; // GaussianDisplacementField RealType m_UpdateFieldVarianceInVarianceSpace; RealType m_TotalFieldVarianceInVarianceSpace; // BSplineDisplacementField std::vector m_TotalFieldMeshSizeAtBaseLevel; std::vector m_UpdateFieldMeshSizeAtBaseLevel; unsigned int m_SplineOrder; // also anything B-spline // TimeVaryingVelocityField RealType m_UpdateFieldTimeSigma; RealType m_TotalFieldTimeSigma; unsigned int m_NumberOfTimeIndices; // TimeVaryingBSplineVelocityField std::vector m_VelocityFieldMeshSize; unsigned int m_NumberOfTimePointSamples; // Exponential RealType m_VelocityFieldVarianceInVarianceSpace; // BSplineExponential std::vector m_VelocityFieldMeshSizeAtBaseLevel; }; typedef std::deque TransformMethodListType; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( RegistrationHelper, Object ); /** Dimension of the image. This constant is used by functions that are * templated over image type (as opposed to being templated over pixel type * and dimension) when they need compile time access to the dimension of * the image. */ itkStaticConstMacro( ImageDimension, unsigned int, VImageDimension ); /** * add a metric, corresponding to the registration stage */ void AddMetric( MetricEnumeration metricType, ImageType *fixedImage, ImageType *movingImage, LabeledPointSetType *fixedLabeledPointSet, LabeledPointSetType *movingLabeledPointSet, IntensityPointSetType *fixedIntensityPointSet, IntensityPointSetType *movingIntensityPointSet, unsigned int stageID, RealType weighting, SamplingStrategy samplingStrategy, int numberOfBins, unsigned int radius, bool useBoundaryPointsOnly, RealType pointSetSigma, unsigned int evaluationKNeighborhood, RealType alpha, bool useAnisotropicCovariances, RealType samplingPercentage, RealType intensityDistanceSigma, RealType euclideanDistanceSigma ); /** For backwards compatibility */ inline void AddMetric( MetricEnumeration metricType, ImageType *fixedImage, ImageType *movingImage, unsigned int stageID, RealType weighting, SamplingStrategy samplingStrategy, int numberOfBins, unsigned int radius, RealType samplingPercentage ) { this->AddMetric( metricType, fixedImage, movingImage, ITK_NULLPTR, ITK_NULLPTR, ITK_NULLPTR, ITK_NULLPTR, stageID, weighting, samplingStrategy, numberOfBins, radius, false, 1.0, 50, 1.1, false, samplingPercentage, std::sqrt( 5 ), std::sqrt( 5 ) ); } /** * Get set of metrics per stage. If we have more than one, we have * to use the MultiMetricType */ MetricListType GetMetricListPerStage( unsigned int stageID ); /** * return the enumerated Metric type based on a string representation */ MetricEnumeration StringToMetricType( const std::string & str ) const; /** * return the enumerated transform method specified by str */ XfrmMethod StringToXfrmMethod( const std::string & str ) const; /** * set the fixed initial transform. */ void SetFixedInitialTransform( const TransformType *initialTransform ); /** * set the moving initial transform. */ void SetMovingInitialTransform( const TransformType *initialTransform ); /** * restore the state transform and set that as an initial transform. */ void SetRestoreStateTransform( const TransformType *initialTransform ); /** * add a rigid transform */ void AddRigidTransform( RealType GradientStep ); /** * add an affine transform */ void AddAffineTransform( RealType GradientStep ); /** * add a composite affine transform */ void AddCompositeAffineTransform( RealType GradientStep ); /** * add a similarity transform */ void AddSimilarityTransform( RealType GradientStep ); /** * add a translation transform */ void AddTranslationTransform( RealType GradientStep ); /** * add a spline transform */ void AddBSplineTransform( RealType GradientStep, std::vector & MeshSizeAtBaseLevel ); /** * add gaussian displacement transform */ void AddGaussianDisplacementFieldTransform( RealType GradientStep, RealType UpdateFieldVarianceInVarianceSpace, RealType TotalFieldVarianceInVarianceSpace ); /** * add bspline displacement transform */ void AddBSplineDisplacementFieldTransform( RealType GradientStep, std::vector & UpdateFieldMeshSizeAtBaseLevel, std::vector & TotalFieldMeshSizeAtBaseLevel, unsigned int SplineOrder ); /** * add a time varying velocity field transform */ void AddTimeVaryingVelocityFieldTransform( RealType GradientStep, unsigned int NumberOfTimeIndices, RealType UpdateFieldVarianceInVarianceSpace, RealType UpdateFieldTimeSigma, RealType TotalFieldVarianceInVarianceSpace, RealType TotalFieldTimeSigma ); /** * add a time varying b spline velocity field transform */ void AddTimeVaryingBSplineVelocityFieldTransform( RealType GradientStep, std::vector VelocityFieldMeshSize, unsigned int NumberOfTimePointSamples, unsigned int SplineOrder ); /** * add a SyN transform */ void AddSyNTransform( RealType GradientStep, RealType UpdateFieldVarianceInVarianceSpace, RealType TotalFieldVarianceInVarianceSpace ); /** * add a B-spline SyN transform */ void AddBSplineSyNTransform( RealType GradientStep, std::vector & UpdateFieldMeshSizeAtBaseLevel, std::vector & TotalFieldMeshSizeAtBaseLevel, unsigned int SplineOrder ); /** * add an exponential transform */ void AddExponentialTransform( RealType GradientStep, RealType UpdateFieldVarianceInVarianceSpace, RealType VelocityFieldVarianceInVarianceSpace, unsigned int NumberOfIntegrationSteps ); /** * add a B-spline exponential transform */ void AddBSplineExponentialTransform( RealType GradientStep, std::vector & UpdateFieldMeshSizeAtBaseLevel, std::vector & VelocityFieldMeshSizeAtBaseLevel, unsigned int NumberOfIntegrationSteps, unsigned int SplineOrder ); /** * Add the collected iterations list */ void SetIterations( const std::vector > & Iterations ); /** * Add the collected convergence thresholds */ void SetConvergenceThresholds( const std::vector & thresholds ); /** * Add the collected convergence window sizes */ void SetConvergenceWindowSizes( const std::vector & windowSizes ); /** * Add the collected smoothing sigmas list */ void SetSmoothingSigmas( const std::vector > & SmoothingSigmas ); /** * Add the restrict deformation optimizer weights */ void SetRestrictDeformationOptimizerWeights( const std::vector > & restrictDeformationWeights ); /** * Add the collected bool smoothing sigmas in voxel units list */ void SetSmoothingSigmasAreInPhysicalUnits( const std::vector & SmoothingSigmasAreInPhysicalUnits ); /** * Add the collected shrink factors list */ void SetShrinkFactors( const std::vector > & ShrinkFactors ); /** * Given an image and a specified shrink factor, calculate the shrink factor for * each dimension. The heuristic we use is to apply the specified shrink factor * to the image dimension with the highest resolution (i.e. smallest spacing). * Then, for each other dimension, we find the factor ( < specified factor ) which * makes the subsampled image as close to isotropic (in terms of spacing) as possible. */ ShrinkFactorsPerDimensionContainerType CalculateShrinkFactorsPerDimension( unsigned int, ImageSpacingType ); /** * turn on histogram matching of the input images */ itkSetMacro( UseHistogramMatching, bool ); itkGetConstMacro( UseHistogramMatching, bool ); itkBooleanMacro( UseHistogramMatching ); /** * turn on the option that lets you estimate the learning rate step size only at the beginning of each level. * useful as a second stage of fine-scale registration. */ itkSetMacro( DoEstimateLearningRateAtEachIteration, bool ); itkGetConstMacro( DoEstimateLearningRateAtEachIteration, bool ); itkBooleanMacro( DoEstimateLearningRateAtEachIteration ); /** * turn on the option that pushes initial linear transforms to the fixed * image header for faster processing. */ itkSetMacro( ApplyLinearTransformsToFixedImageHeader, bool ); itkGetConstMacro( ApplyLinearTransformsToFixedImageHeader, bool ); itkBooleanMacro( ApplyLinearTransformsToFixedImageHeader ); /** * turn on the option that prints the CC similarity measure * between the full-size fixed and moving input images at each iteraton. */ itkSetMacro( PrintSimilarityMeasureInterval, unsigned int ); itkGetConstMacro( PrintSimilarityMeasureInterval, unsigned int ); /** * turn on the option that writes the output volume in intervals of iterations. */ itkSetMacro( WriteIntervalVolumes, unsigned int ); itkGetConstMacro( WriteIntervalVolumes, unsigned int ); /** * turn on the option that cause the direct initialization of the linear transforms at each stage. */ itkSetMacro( InitializeTransformsPerStage, bool ); itkGetConstMacro( InitializeTransformsPerStage, bool ); itkBooleanMacro( InitializeTransformsPerStage ); /** * turn on winsorize image intensity normalization */ void SetWinsorizeImageIntensities( bool Winsorize, float LowerQuantile = 0.0, float UpperQuantile = 1.0 ); itkGetModifiableObjectMacro( CompositeTransform, CompositeTransformType ); itkGetModifiableObjectMacro( RegistrationState, CompositeTransformType ); /** * Set/Get the interpolator. Linear is default. */ itkSetObjectMacro( Interpolator, InterpolatorType ); itkGetModifiableObjectMacro( Interpolator, InterpolatorType ); /** * Get the Warped Image & Inverse Warped Images */ typename ImageType::Pointer GetWarpedImage() const; typename ImageType::Pointer GetInverseWarpedImage() const; /** * Set the fixed/moving image masks with a spatial object */ void AddFixedImageMask( typename ImageMaskSpatialObjectType::Pointer & fixedImageMask ) { this->m_FixedImageMasks.push_back( fixedImageMask ); } void AddMovingImageMask( typename ImageMaskSpatialObjectType::Pointer & movingImageMask ) { this->m_MovingImageMasks.push_back( movingImageMask ); } /** * Set the fixed/moving mask image. this will be used to instantiate * ImageMaskSpatialObject masks. */ void AddFixedImageMask( typename MaskImageType::Pointer & fixedImageMask ); void AddMovingImageMask( typename MaskImageType::Pointer & movingImageMask ); /** * Collapse a composite transform by adjacent linear or displacement field transforms. */ CompositeTransformPointer CollapseCompositeTransform( const CompositeTransformType * ); void ApplyCompositeLinearTransformToImageHeader( const CompositeTransformType *, ImageBaseType * const, const bool applyInverse ); /** * Collapse a composite transform composed of displacement field transforms. * We return a composite transform since we don't combine mixed displacement field * transforms (i.e., transforms that do and do not have inverses). */ typename CompositeTransformType::Pointer CollapseDisplacementFieldTransforms( const CompositeTransformType * ); /** * Collapse a composite linear transform to a generic affine transform. */ typename AffineTransformType::Pointer CollapseLinearTransforms( const CompositeTransformType * ); /** * */ template bool InitializeWithPreviousLinearTransform(const CompositeTransformType *, const std::string, typename TTransformType::Pointer &); /** * Compute approximate mesh size for a specified isotropic knot spacing */ std::vector CalculateMeshSizeForSpecifiedKnotSpacing( ImageBaseType * const, const RealType, const unsigned int ); /** * Do the registration. Will return EXIT_FAILURE if there is any * problem completing the registration. */ int DoRegistration(); /** * print out the internal registration helper state */ void PrintState() const; void SetLogStream(std::ostream & logStream) { this->m_LogStream = &logStream; } protected: RegistrationHelper(); virtual ~RegistrationHelper(); private: typename itk::ImageBase::Pointer GetShrinkImageOutputInformation(const itk::ImageBase * inputImageInformation, const typename RegistrationHelper::ShrinkFactorsPerDimensionContainerType &shrinkFactorsPerDimensionForCurrentLevel) const; int ValidateParameters(); std::ostream & Logger() const { return *m_LogStream; } template typename RegistrationMethodType::Pointer PrepareRegistrationMethod( CompositeTransformType *compositeTransform, const unsigned int currentStageNumber, const unsigned int parametersDimensionSize, std::vector preprocessedFixedImagesPerStage, std::vector preprocessedMovingImagesPerStage, std::vector fixedPointSetsPerStage, std::vector movingPointSetsPerStage, const MetricListType stageMetricList, ObjectMetricType *singleMetric, MultiMetricType *multiMetric, ConjugateGradientDescentOptimizerType *optimizer, const unsigned int numberOfLevels, const std::vector shrinkFactorsPerDimensionForAllLevels, const typename RegistrationMethodType::SmoothingSigmasArrayType smoothingSigmasPerLevel, typename AffineRegistrationType::MetricSamplingStrategyType metricSamplingStrategy, const float samplingPercentage ) { typename RegistrationMethodType::Pointer registrationMethod = RegistrationMethodType::New(); typedef typename RegistrationMethodType::OutputTransformType RegistrationMethodTransformType; for( unsigned int n = 0; n < stageMetricList.size(); n++ ) { if( !this->IsPointSetMetric( stageMetricList[n].m_MetricType ) ) { registrationMethod->SetFixedImage( n, preprocessedFixedImagesPerStage[n] ); registrationMethod->SetMovingImage( n, preprocessedMovingImagesPerStage[n] ); } else { registrationMethod->SetFixedPointSet( n, fixedPointSetsPerStage[n] ); registrationMethod->SetMovingPointSet( n, movingPointSetsPerStage[n] ); } } if( multiMetric ) { registrationMethod->SetMetric( multiMetric ); } else { registrationMethod->SetMetric( singleMetric ); } registrationMethod->SetNumberOfLevels( numberOfLevels ); for( unsigned int level = 0; level < numberOfLevels; ++level ) { registrationMethod->SetShrinkFactorsPerDimension( level, shrinkFactorsPerDimensionForAllLevels[level] ); } registrationMethod->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); registrationMethod->SetSmoothingSigmasAreSpecifiedInPhysicalUnits( this->m_SmoothingSigmasAreInPhysicalUnits[currentStageNumber] ); registrationMethod->SetMetricSamplingStrategy( static_cast( metricSamplingStrategy ) ); registrationMethod->SetMetricSamplingPercentage( samplingPercentage ); if( this->m_RestrictDeformationOptimizerWeights.size() > currentStageNumber ) { if( this->m_RestrictDeformationOptimizerWeights[currentStageNumber].size() == parametersDimensionSize ) { typename RegistrationMethodType::OptimizerWeightsType optimizerWeights( parametersDimensionSize ); for( unsigned int d = 0; d < parametersDimensionSize; d++ ) { optimizerWeights[d] = this->m_RestrictDeformationOptimizerWeights[currentStageNumber][d]; } registrationMethod->SetOptimizerWeights( optimizerWeights ); } } registrationMethod->SetOptimizer( optimizer ); typename RegistrationMethodTransformType::Pointer currentTransform = RegistrationMethodTransformType::New(); std::string t = currentTransform->GetNameOfClass(); std::string s = "Transform"; std::string::size_type index = t.find( s ); if( index != std::string::npos ) { t.erase( index, s.length() ); } if( compositeTransform->GetNumberOfTransforms() > 0 ) { if( this->m_InitializeTransformsPerStage ) { const unsigned int numOfTransforms = compositeTransform->GetNumberOfTransforms(); this->Logger() << "Current number of transforms in the composite transform: " << numOfTransforms << std::endl; for( unsigned int i = 0; i < numOfTransforms; i++ ) { this->Logger() << i+1 << ") " << compositeTransform->GetNthTransform( i )->GetNameOfClass() << std::endl; } if( this->InitializeWithPreviousLinearTransform( compositeTransform, t.c_str(), currentTransform ) ) { this->Logger() << "Registration process is run using direct initialization!" << std::endl; compositeTransform->RemoveTransform(); // Remove previous initial transform, // since it is included in current results. registrationMethod->SetInitialTransform( currentTransform ); } } } if( compositeTransform->GetNumberOfTransforms() > 0 ) { registrationMethod->SetMovingInitialTransform( compositeTransform ); } if( this->m_FixedInitialTransform->GetNumberOfTransforms() > 0 ) { registrationMethod->SetFixedInitialTransform( this->m_FixedInitialTransform ); } return registrationMethod; } template int AddLinearTransformToCompositeTransform( CompositeTransformType *compositeTransform, const unsigned int currentStageNumber, const unsigned int parametersDimensionSize, std::vector preprocessedFixedImagesPerStage, std::vector preprocessedMovingImagesPerStage, std::vector fixedPointSetsPerStage, std::vector movingPointSetsPerStage, const MetricListType stageMetricList, ObjectMetricType *singleMetric, MultiMetricType *multiMetric, ConjugateGradientDescentOptimizerType *optimizer, const unsigned int numberOfLevels, const std::vector shrinkFactorsPerDimensionForAllLevels, const typename RegistrationMethodType::SmoothingSigmasArrayType smoothingSigmasPerLevel, typename AffineRegistrationType::MetricSamplingStrategyType metricSamplingStrategy, const float samplingPercentage ) { typename RegistrationMethodType::Pointer registrationMethod = this->PrepareRegistrationMethod( compositeTransform, currentStageNumber, parametersDimensionSize, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedPointSetsPerStage, movingPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); typedef antsRegistrationCommandIterationUpdate TransformCommandType; typename TransformCommandType::Pointer transformObserver = TransformCommandType::New(); transformObserver->SetLogStream( *this->m_LogStream ); transformObserver->SetNumberOfIterations( this->m_Iterations[currentStageNumber] ); registrationMethod->AddObserver( itk::IterationEvent(), transformObserver ); registrationMethod->AddObserver( itk::InitializeEvent(), transformObserver ); try { typedef typename RegistrationMethodType::OutputTransformType RegistrationMethodTransformType; typename RegistrationMethodTransformType::Pointer currentTransform = RegistrationMethodTransformType::New(); this->Logger() << std::endl << "*** Running " << currentTransform->GetNameOfClass() << " registration ***" << std::endl << std::endl; transformObserver->Execute( registrationMethod, itk::StartEvent() ); registrationMethod->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform or add it to the composite transform // which is incorporated into the fixed image header. if( this->m_ApplyLinearTransformsToFixedImageHeader && this->m_AllPreviousTransformsAreLinear ) { this->m_CompositeLinearTransformForFixedImageHeader->AddTransform( registrationMethod->GetModifiableTransform() ); } else { compositeTransform->AddTransform( registrationMethod->GetModifiableTransform() ); } return EXIT_SUCCESS; } typename CompositeTransformType::Pointer m_CompositeTransform; typename CompositeTransformType::Pointer m_RegistrationState; typename CompositeTransformType::Pointer m_FixedInitialTransform; std::vector m_FixedImageMasks; std::vector m_MovingImageMasks; typename InterpolatorType::Pointer m_Interpolator; unsigned int m_NumberOfStages; MetricListType m_Metrics; TransformMethodListType m_TransformMethods; std::vector > m_Iterations; std::vector m_ConvergenceThresholds; std::vector m_ConvergenceWindowSizes; std::vector > m_SmoothingSigmas; std::vector m_SmoothingSigmasAreInPhysicalUnits; std::vector > m_RestrictDeformationOptimizerWeights; std::vector > m_ShrinkFactors; bool m_UseHistogramMatching; bool m_WinsorizeImageIntensities; bool m_DoEstimateLearningRateAtEachIteration; RealType m_LowerQuantile; RealType m_UpperQuantile; std::ostream * m_LogStream; bool m_ApplyLinearTransformsToFixedImageHeader; unsigned int m_PrintSimilarityMeasureInterval; unsigned int m_WriteIntervalVolumes; bool m_InitializeTransformsPerStage; bool m_AllPreviousTransformsAreLinear; typename CompositeTransformType::Pointer m_CompositeLinearTransformForFixedImageHeader; }; // ########################################################################## // ########################################################################## /** * Transform traits to generalize the rigid transform */ template class RigidTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class RigidTransformTraits { public: typedef itk::Euler2DTransform TransformType; }; template <> class RigidTransformTraits { public: typedef itk::Euler2DTransform TransformType; }; template <> class RigidTransformTraits { public: // typedef itk::VersorRigid3DTransform TransformType; // typedef itk::QuaternionRigidTransform TransformType; typedef itk::Euler3DTransform TransformType; }; template <> class RigidTransformTraits { public: // typedef itk::VersorRigid3DTransform TransformType; // typedef itk::QuaternionRigidTransform TransformType; typedef itk::Euler3DTransform TransformType; }; template class SimilarityTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity2DTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity2DTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity3DTransform TransformType; }; template <> class SimilarityTransformTraits { public: typedef itk::Similarity3DTransform TransformType; }; template class CompositeAffineTransformTraits { // Don't worry about the fact that the default option is the // affine Transform, that one will not actually be instantiated. public: typedef itk::AffineTransform TransformType; }; template <> class CompositeAffineTransformTraits { public: typedef itk::ANTSCenteredAffine2DTransform TransformType; }; template <> class CompositeAffineTransformTraits { public: typedef itk::ANTSCenteredAffine2DTransform TransformType; }; template <> class CompositeAffineTransformTraits { public: typedef itk::ANTSAffine3DTransform TransformType; }; template <> class CompositeAffineTransformTraits { public: typedef itk::ANTSAffine3DTransform TransformType; }; // ########################################################################## // ########################################################################## // Provide common way of reading transforms. template typename ants::RegistrationHelper::CompositeTransformType::Pointer GetCompositeTransformFromParserOption( typename ParserType::Pointer & parser, typename ParserType::OptionType::Pointer initialTransformOption, std::vector & derivedTransforms, bool useStaticCastForR = false ) { typedef typename ants::RegistrationHelper RegistrationHelperType; typedef typename RegistrationHelperType::CompositeTransformType CompositeTransformType; typename CompositeTransformType::Pointer compositeTransform = CompositeTransformType::New(); typedef typename RegistrationHelperType::ImageType ImageType; typename ImageType::Pointer fixedImage = ITK_NULLPTR; typename ImageType::Pointer movingImage = ITK_NULLPTR; bool verbose = false; typename itk::ants::CommandLineParser::OptionType::Pointer verboseOption = parser->GetOption( "verbose" ); if( verboseOption && verboseOption->GetNumberOfFunctions() ) { verbose = parser->Convert( verboseOption->GetFunction( 0 )->GetName() ); } std::deque transformNames; std::deque transformTypes; derivedTransforms.resize( initialTransformOption->GetNumberOfFunctions() ); for( unsigned int n = 0; n < initialTransformOption->GetNumberOfFunctions(); n++ ) { std::string initialTransformName; bool useInverse = false; bool calculatedTransformFromImages = false; derivedTransforms[n] = false; if( initialTransformOption->GetFunction( n )->GetNumberOfParameters() == 0 ) { initialTransformName = initialTransformOption->GetFunction( n )->GetName(); } else if( initialTransformOption->GetFunction( n )->GetNumberOfParameters() > 2 ) { std::string fixedImageFileName = initialTransformOption->GetFunction( n )->GetParameter( 0 ); ReadImage( fixedImage, fixedImageFileName.c_str() ); if( fixedImage ) { std::string movingImageFileName = initialTransformOption->GetFunction( n )->GetParameter( 1 ); ReadImage( movingImage, movingImageFileName.c_str() ); } // initialization feature types: // 0: image centers // 1: center of (intensity) mass // 2: image origins unsigned short initializationFeature = parser->Convert( initialTransformOption->GetFunction( n )->GetParameter( 2 ) ); typedef typename RigidTransformTraits::TransformType TransformType; typename TransformType::Pointer transform = TransformType::New(); if( initializationFeature == 0 || initializationFeature == 1 ) { typedef itk::CenteredTransformInitializer TransformInitializerType; typename TransformInitializerType::Pointer initializer = TransformInitializerType::New(); initializer->SetTransform( transform ); initializer->SetFixedImage( fixedImage ); initializer->SetMovingImage( movingImage ); if( initializationFeature == 0 ) { initializer->GeometryOn(); initialTransformName = std::string( "Image center alignment using " ); } else { initializer->MomentsOn(); initialTransformName = std::string( "Center of mass alignment using " ); } initializer->InitializeTransform(); } else { initialTransformName = std::string( "Image origin alignment using " ); typename TransformType::InputPointType rotationCenter; typename TransformType::OutputVectorType translationVector; typename ImageType::PointType fixedOrigin = fixedImage->GetOrigin(); typename ImageType::PointType movingOrigin = movingImage->GetOrigin(); for ( unsigned int d = 0; d < VImageDimension; d++ ) { rotationCenter[d] = fixedOrigin[d]; translationVector[d] = movingOrigin[d] - fixedOrigin[d]; } transform->SetCenter( rotationCenter ); transform->SetTranslation( translationVector ); } initialTransformName += std::string( "fixed image: " ) + initialTransformOption->GetFunction( n )->GetParameter( 0 ) + std::string( " and moving image: " ) + initialTransformOption->GetFunction( n )->GetParameter( 1 ); compositeTransform->AddTransform( transform ); calculatedTransformFromImages = true; derivedTransforms[n] = true; transformNames.push_back( initialTransformName ); transformTypes.push_back( transform->GetNameOfClass() ); } if( !calculatedTransformFromImages ) { if( initialTransformOption->GetFunction( n )->GetNumberOfParameters() == 0 ) { initialTransformName = initialTransformOption->GetFunction( n )->GetName(); useInverse = false; } else { initialTransformName = initialTransformOption->GetFunction( n )->GetParameter( 0 ); if( initialTransformOption->GetFunction( n )->GetNumberOfParameters() > 1 ) { useInverse = parser->Convert( initialTransformOption->GetFunction( n )->GetParameter( 1 ) ); } } } static bool MatOffRegistered( false ); // Only register once for each template dimension. if( !MatOffRegistered ) { MatOffRegistered = true; // Register the matrix offset transform base class to the // transform factory for compatibility with the current ANTs. typedef itk::MatrixOffsetTransformBase MatrixOffsetTransformType; itk::TransformFactory::RegisterTransform(); } if( !calculatedTransformFromImages ) { typedef typename RegistrationHelperType::TransformType TransformType; typename TransformType::Pointer initialTransform; if( std::strcmp( initialTransformName.c_str(), "identity" ) == 0 || std::strcmp( initialTransformName.c_str(), "Identity" ) == 0 ) { typedef itk::MatrixOffsetTransformBase MatrixOffsetTransformType; typename MatrixOffsetTransformType::Pointer identityTransform = MatrixOffsetTransformType::New(); identityTransform->SetIdentity(); initialTransform = identityTransform; } else { initialTransform = itk::ants::ReadTransform( initialTransformName, useStaticCastForR ); } if( initialTransform.IsNull() ) { if( verbose ) { std::cout << "Can't read initial transform " << initialTransformName << std::endl; } return ITK_NULLPTR; } if( useInverse ) { initialTransform = dynamic_cast( initialTransform->GetInverseTransform().GetPointer() ); if( initialTransform.IsNull() ) { if( verbose ) { std::cout << "Inverse does not exist for " << initialTransformName << std::endl; } return ITK_NULLPTR; } initialTransformName = std::string( "inverse of " ) + initialTransformName; } static const std::string CompositeTransformID("CompositeTransform"); if( initialTransform->GetNameOfClass() == CompositeTransformID ) { const typename itk::CompositeTransform::ConstPointer tempComp = dynamic_cast *>( initialTransform.GetPointer() ); for( unsigned int i = 0; i < tempComp->GetNumberOfTransforms(); ++i ) { std::stringstream tempstream; tempstream << initialTransformName << "[" << i << "]"; compositeTransform->AddTransform( tempComp->GetNthTransform( i ) ); transformNames.push_back( tempstream.str() ); transformTypes.push_back( tempComp->GetNthTransform( i )->GetNameOfClass() ); } } else { compositeTransform->AddTransform( initialTransform ); transformNames.push_back( initialTransformName ); transformTypes.push_back( initialTransform->GetNameOfClass() ); } } } if( verbose ) { std::cout << "=============================================================================" << std::endl; std::cout << "The composite transform comprises the following transforms (in order): " << std::endl; for( unsigned int n = 0; n < transformNames.size(); n++ ) { std::cout << " " << n + 1 << ". " << transformNames[n] << " (type = " << transformTypes[n] << ")" << std::endl; } std::cout << "=============================================================================" << std::endl; } return compositeTransform; } // #################### } // namespace ants #include "itkantsRegistrationHelper.hxx" #endif ants-2.2.0/Examples/itkantsRegistrationHelper.hxx000066400000000000000000005766641311104306400222200ustar00rootroot00000000000000#ifndef __itkantsRegistrationHelper_hxx #define __itkantsRegistrationHelper_hxx #include #include namespace ants { /** * GetShrinkImageOutputInformation provides a consistent way to compute the * outputImage space for each level of a registration in a consistent way. * By always using the same reference image, we can ensure that the same * shrink results always are produced. */ template typename itk::ImageBase::Pointer RegistrationHelper::GetShrinkImageOutputInformation(const itk::ImageBase * inputImageInformation, const typename RegistrationHelper::ShrinkFactorsPerDimensionContainerType &shrinkFactorsPerDimensionForCurrentLevel) const { typedef itk::Image DummyImageType; typename DummyImageType::Pointer dummyImage = AllocImage( inputImageInformation, 0 ); // We use the shrink image filter to calculate the fixed parameters of the virtual // domain at each level. To speed up calculation and avoid unnecessary memory // usage, we could calculate these fixed parameters directly. typedef itk::ShrinkImageFilter ShrinkFilterType; typename ShrinkFilterType::Pointer shrinkFilter = ShrinkFilterType::New(); shrinkFilter->SetShrinkFactors( shrinkFactorsPerDimensionForCurrentLevel ); shrinkFilter->SetInput( dummyImage ); shrinkFilter->GenerateOutputInformation(); //Don't need to allocate space or run the filter, just create output information typename itk::ImageBase::Pointer returnImageBase=shrinkFilter->GetOutput(); return returnImageBase; } template RegistrationHelper ::RegistrationHelper() : m_CompositeTransform( ITK_NULLPTR ), m_RegistrationState( ITK_NULLPTR ), m_FixedInitialTransform( ITK_NULLPTR ), m_NumberOfStages( 0 ), m_Metrics(), m_TransformMethods(), m_Iterations(), m_SmoothingSigmas(), m_RestrictDeformationOptimizerWeights(), m_ShrinkFactors(), m_UseHistogramMatching( true ), m_WinsorizeImageIntensities( false ), m_DoEstimateLearningRateAtEachIteration( true ), m_LowerQuantile( 0.0 ), m_UpperQuantile( 1.0 ), m_LogStream( &std::cout ), m_ApplyLinearTransformsToFixedImageHeader( true ), m_PrintSimilarityMeasureInterval( 0 ), m_WriteIntervalVolumes( 0 ), m_InitializeTransformsPerStage( false ), m_AllPreviousTransformsAreLinear( true ), m_CompositeLinearTransformForFixedImageHeader( ITK_NULLPTR ) { typedef itk::LinearInterpolateImageFunction LinearInterpolatorType; typename LinearInterpolatorType::Pointer linearInterpolator = LinearInterpolatorType::New(); this->m_Interpolator = linearInterpolator; } template RegistrationHelper ::~RegistrationHelper() { } template typename ImageType::Pointer PreprocessImage( typename ImageType::ConstPointer inputImage, typename ImageType::PixelType lowerScaleValue, typename ImageType::PixelType upperScaleValue, float winsorizeLowerQuantile, float winsorizeUpperQuantile, typename ImageType::ConstPointer histogramMatchSourceImage = NULL ) { typedef itk::Statistics::ImageToHistogramFilter HistogramFilterType; typedef typename HistogramFilterType::InputBooleanObjectType InputBooleanObjectType; typedef typename HistogramFilterType::HistogramSizeType HistogramSizeType; HistogramSizeType histogramSize( 1 ); histogramSize[0] = 256; typename InputBooleanObjectType::Pointer autoMinMaxInputObject = InputBooleanObjectType::New(); autoMinMaxInputObject->Set( true ); typename HistogramFilterType::Pointer histogramFilter = HistogramFilterType::New(); histogramFilter->SetInput( inputImage ); histogramFilter->SetAutoMinimumMaximumInput( autoMinMaxInputObject ); histogramFilter->SetHistogramSize( histogramSize ); histogramFilter->SetMarginalScale( 10.0 ); histogramFilter->Update(); float lowerValue = histogramFilter->GetOutput()->Quantile( 0, winsorizeLowerQuantile ); float upperValue = histogramFilter->GetOutput()->Quantile( 0, winsorizeUpperQuantile ); typedef itk::IntensityWindowingImageFilter IntensityWindowingImageFilterType; typename IntensityWindowingImageFilterType::Pointer windowingFilter = IntensityWindowingImageFilterType::New(); windowingFilter->SetInput( inputImage ); windowingFilter->SetWindowMinimum( lowerValue ); windowingFilter->SetWindowMaximum( upperValue ); windowingFilter->SetOutputMinimum( lowerScaleValue ); windowingFilter->SetOutputMaximum( upperScaleValue ); windowingFilter->Update(); typename ImageType::Pointer outputImage = ITK_NULLPTR; if( histogramMatchSourceImage ) { typedef itk::HistogramMatchingImageFilter HistogramMatchingFilterType; typename HistogramMatchingFilterType::Pointer matchingFilter = HistogramMatchingFilterType::New(); matchingFilter->SetSourceImage( windowingFilter->GetOutput() ); matchingFilter->SetReferenceImage( histogramMatchSourceImage ); matchingFilter->SetNumberOfHistogramLevels( 256 ); matchingFilter->SetNumberOfMatchPoints( 12 ); matchingFilter->ThresholdAtMeanIntensityOn(); matchingFilter->Update(); outputImage = matchingFilter->GetOutput(); outputImage->Update(); outputImage->DisconnectPipeline(); } else { outputImage = windowingFilter->GetOutput(); outputImage->Update(); outputImage->DisconnectPipeline(); } return outputImage; } template typename RegistrationHelper::MetricEnumeration RegistrationHelper ::StringToMetricType( const std::string & str ) const { if( str == "cc" ) { return CC; } else if( str == "mi2" ) { return MI; } else if( str == "mattes" || str == "mi" ) { return Mattes; } else if( str == "meansquares" || str == "msq" || str == "ssd" ) { return MeanSquares; } else if( str == "demons" ) { return Demons; } else if( str == "gc" ) { return GC; } else if( str == "icp" ) { return ICP; } else if( str == "pse" ) { return PSE; } else if( str == "jhct" ) { return JHCT; } else if( str == "igdm" ) { return IGDM; } return IllegalMetric; } template typename RegistrationHelper::XfrmMethod RegistrationHelper ::StringToXfrmMethod(const std::string & str) const { if( str == "rigid" ) { return Rigid; } else if( str == "affine" ) { return Affine; } if( str == "compositeaffine" || str == "compaff" ) { return CompositeAffine; } if( str == "similarity" ) { return Similarity; } if( str == "translation" ) { return Translation; } if( str == "bspline" || str == "ffd" ) { return BSpline; } if( str == "gaussiandisplacementfield" || str == "gdf" ) { return GaussianDisplacementField; } if( str == "bsplinedisplacementfield" || str == "dmffd" ) { return BSplineDisplacementField; } if( str == "timevaryingvelocityfield" || str == "tvf" ) { return TimeVaryingVelocityField; } if( str == "timevaryingbsplinevelocityfield" || str == "tvdmffd" ) { return TimeVaryingBSplineVelocityField; } if( str == "syn" || str == "symmetricnormalization" ) { return SyN; } if( str == "bsplinesyn" ) { return BSplineSyN; } if( str == "exp" || str == "exponential" ) { return Exponential; } if( str == "bsplineexponential" ) { return BSplineExponential; } return UnknownXfrm; } template void RegistrationHelper ::AddMetric( MetricEnumeration metricType, ImageType *fixedImage, ImageType *movingImage, LabeledPointSetType *fixedLabeledPointSet, LabeledPointSetType *movingLabeledPointSet, IntensityPointSetType *fixedIntensityPointSet, IntensityPointSetType *movingIntensityPointSet, unsigned int stageID, RealType weighting, SamplingStrategy samplingStrategy, int numberOfBins, unsigned int radius, bool useBoundaryPointsOnly, RealType pointSetSigma, unsigned int evaluationKNeighborhood, RealType alpha, bool useAnisotropicCovariances, RealType samplingPercentage, RealType intensityDistanceSigma, RealType euclideanDistanceSigma ) { Metric init( metricType, fixedImage, movingImage, fixedLabeledPointSet, movingLabeledPointSet, fixedIntensityPointSet, movingIntensityPointSet, stageID, weighting, samplingStrategy, numberOfBins, radius, useBoundaryPointsOnly, pointSetSigma, evaluationKNeighborhood, alpha, useAnisotropicCovariances, samplingPercentage, intensityDistanceSigma, euclideanDistanceSigma ); this->m_Metrics.push_back( init ); } template typename RegistrationHelper::MetricListType RegistrationHelper ::GetMetricListPerStage( unsigned int stageID ) { MetricListType stageMetricList; typename MetricListType::const_iterator it; for( it = this->m_Metrics.begin(); it != this->m_Metrics.end(); ++it ) { if( ( *it ).m_StageID == stageID ) { stageMetricList.push_back( *it ); } } return stageMetricList; } template void RegistrationHelper ::AddRigidTransform(RealType GradientStep) { TransformMethod init; init.m_XfrmMethod = Rigid; init.m_GradientStep = GradientStep; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddAffineTransform(RealType GradientStep) { TransformMethod init; init.m_XfrmMethod = Affine; init.m_GradientStep = GradientStep; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddCompositeAffineTransform(RealType GradientStep) { TransformMethod init; init.m_XfrmMethod = CompositeAffine; init.m_GradientStep = GradientStep; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddSimilarityTransform(RealType GradientStep) { TransformMethod init; init.m_XfrmMethod = Similarity; init.m_GradientStep = GradientStep; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddTranslationTransform(RealType GradientStep) { TransformMethod init; init.m_XfrmMethod = Translation; init.m_GradientStep = GradientStep; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddBSplineTransform(RealType GradientStep, std::vector & MeshSizeAtBaseLevel) { TransformMethod init; init.m_XfrmMethod = BSpline; init.m_GradientStep = GradientStep; init.m_MeshSizeAtBaseLevel = MeshSizeAtBaseLevel; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddGaussianDisplacementFieldTransform(RealType GradientStep, RealType UpdateFieldVarianceInVarianceSpace, RealType TotalFieldVarianceInVarianceSpace) { TransformMethod init; init.m_XfrmMethod = GaussianDisplacementField; init.m_GradientStep = GradientStep; init.m_UpdateFieldVarianceInVarianceSpace = UpdateFieldVarianceInVarianceSpace; init.m_TotalFieldVarianceInVarianceSpace = TotalFieldVarianceInVarianceSpace; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddBSplineDisplacementFieldTransform(RealType GradientStep, std::vector & UpdateFieldMeshSizeAtBaseLevel, std::vector & TotalFieldMeshSizeAtBaseLevel, unsigned int SplineOrder) { TransformMethod init; init.m_XfrmMethod = BSplineDisplacementField; init.m_GradientStep = GradientStep; init.m_UpdateFieldMeshSizeAtBaseLevel = UpdateFieldMeshSizeAtBaseLevel; init.m_TotalFieldMeshSizeAtBaseLevel = TotalFieldMeshSizeAtBaseLevel; init.m_SplineOrder = SplineOrder; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddTimeVaryingVelocityFieldTransform( RealType GradientStep, unsigned int NumberOfTimeIndices, RealType UpdateFieldVarianceInVarianceSpace, RealType UpdateFieldTimeSigma, RealType TotalFieldVarianceInVarianceSpace, RealType TotalFieldTimeSigma ) { TransformMethod init; init.m_XfrmMethod = TimeVaryingVelocityField; init.m_GradientStep = GradientStep; init.m_NumberOfTimeIndices = NumberOfTimeIndices; init.m_UpdateFieldVarianceInVarianceSpace = UpdateFieldVarianceInVarianceSpace; init.m_UpdateFieldTimeSigma = UpdateFieldTimeSigma; init.m_TotalFieldVarianceInVarianceSpace = TotalFieldVarianceInVarianceSpace; init.m_TotalFieldTimeSigma = TotalFieldTimeSigma; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddTimeVaryingBSplineVelocityFieldTransform( RealType GradientStep, std::vector VelocityFieldMeshSize, unsigned int NumberOfTimePointSamples, unsigned int SplineOrder ) { TransformMethod init; init.m_XfrmMethod = TimeVaryingBSplineVelocityField;; init.m_GradientStep = GradientStep; init.m_VelocityFieldMeshSize = VelocityFieldMeshSize; init.m_NumberOfTimePointSamples = NumberOfTimePointSamples; init.m_SplineOrder = SplineOrder; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddSyNTransform( RealType GradientStep, RealType UpdateFieldVarianceInVarianceSpace, RealType TotalFieldVarianceInVarianceSpace ) { TransformMethod init; init.m_XfrmMethod = SyN; init.m_GradientStep = GradientStep; init.m_UpdateFieldVarianceInVarianceSpace = UpdateFieldVarianceInVarianceSpace; init.m_TotalFieldVarianceInVarianceSpace = TotalFieldVarianceInVarianceSpace; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddBSplineSyNTransform( RealType GradientStep, std::vector & UpdateFieldMeshSizeAtBaseLevel, std::vector & TotalFieldMeshSizeAtBaseLevel, unsigned int SplineOrder ) { TransformMethod init; init.m_XfrmMethod = BSplineSyN; init.m_GradientStep = GradientStep; init.m_UpdateFieldMeshSizeAtBaseLevel = UpdateFieldMeshSizeAtBaseLevel; init.m_TotalFieldMeshSizeAtBaseLevel = TotalFieldMeshSizeAtBaseLevel; init.m_SplineOrder = SplineOrder; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddExponentialTransform( RealType GradientStep, RealType UpdateFieldVarianceInVarianceSpace, RealType VelocityFieldVarianceInVarianceSpace, unsigned int NumberOfIntegrationSteps ) { TransformMethod init; init.m_XfrmMethod = Exponential; init.m_GradientStep = GradientStep; init.m_UpdateFieldVarianceInVarianceSpace = UpdateFieldVarianceInVarianceSpace; init.m_VelocityFieldVarianceInVarianceSpace = VelocityFieldVarianceInVarianceSpace; init.m_NumberOfTimeIndices = NumberOfIntegrationSteps; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::AddBSplineExponentialTransform( RealType GradientStep, std::vector & UpdateFieldMeshSizeAtBaseLevel, std::vector & VelocityFieldMeshSizeAtBaseLevel, unsigned int NumberOfIntegrationSteps, unsigned int SplineOrder ) { TransformMethod init; init.m_XfrmMethod = BSplineExponential; init.m_GradientStep = GradientStep; init.m_UpdateFieldMeshSizeAtBaseLevel = UpdateFieldMeshSizeAtBaseLevel; init.m_VelocityFieldMeshSizeAtBaseLevel = VelocityFieldMeshSizeAtBaseLevel; init.m_SplineOrder = SplineOrder; init.m_NumberOfTimeIndices = NumberOfIntegrationSteps; this->m_TransformMethods.push_back( init ); } template void RegistrationHelper ::SetIterations( const std::vector > & Iterations ) { this->m_Iterations = Iterations; } template void RegistrationHelper ::SetConvergenceThresholds( const std::vector & thresholds ) { this->m_ConvergenceThresholds = thresholds; } template void RegistrationHelper ::SetConvergenceWindowSizes( const std::vector & windowSizes ) { this->m_ConvergenceWindowSizes = windowSizes; } template void RegistrationHelper ::SetSmoothingSigmas( const std::vector > & SmoothingSigmas ) { this->m_SmoothingSigmas = SmoothingSigmas; } template void RegistrationHelper ::SetRestrictDeformationOptimizerWeights( const std::vector > & restrictDeformationWeights ) { this->m_RestrictDeformationOptimizerWeights = restrictDeformationWeights; } template void RegistrationHelper ::SetSmoothingSigmasAreInPhysicalUnits( const std::vector & SmoothingSigmasAreInPhysicalUnits ) { this->m_SmoothingSigmasAreInPhysicalUnits = SmoothingSigmasAreInPhysicalUnits; } template void RegistrationHelper ::SetShrinkFactors( const std::vector > & ShrinkFactors ) { this->m_ShrinkFactors = ShrinkFactors; } template typename RegistrationHelper::ShrinkFactorsPerDimensionContainerType RegistrationHelper ::CalculateShrinkFactorsPerDimension( unsigned int factor, ImageSpacingType spacing ) { typedef RealType SpacingValueType; SpacingValueType minSpacing = spacing[0]; unsigned int minIndex = 0; for( unsigned int n = 1; n < VImageDimension; n++ ) { if( minSpacing > spacing[n] ) { minSpacing = spacing[n]; minIndex = n; } } ShrinkFactorsPerDimensionContainerType shrinkFactorsPerDimension; shrinkFactorsPerDimension.Fill( 0 ); shrinkFactorsPerDimension[minIndex] = factor; ImageSpacingType newSpacing; newSpacing[minIndex] = spacing[minIndex] * factor; for( unsigned int n = 0; n < VImageDimension; n++ ) { if( shrinkFactorsPerDimension[n] == 0 ) { SpacingValueType newMinSpacing = spacing[n] * static_cast( factor ); RealType minDifferenceFromMinSpacing = vnl_math_abs( newMinSpacing - newSpacing[minIndex] ); unsigned int minFactor = factor; for( unsigned int f = factor - 1; f > 0; f-- ) { newMinSpacing = spacing[n] * static_cast( f ); // We use <= such that the smaller factor is preferred if distances are the same if( vnl_math_abs( newMinSpacing - newSpacing[minIndex] ) <= minDifferenceFromMinSpacing ) { minDifferenceFromMinSpacing = vnl_math_abs( newMinSpacing - newSpacing[minIndex] ); minFactor = f; } } shrinkFactorsPerDimension[n] = minFactor; } } return shrinkFactorsPerDimension; } template void RegistrationHelper ::SetWinsorizeImageIntensities( bool Winsorize, float LowerQuantile, float UpperQuantile ) { this->m_WinsorizeImageIntensities = Winsorize; this->m_LowerQuantile = LowerQuantile; this->m_UpperQuantile = UpperQuantile; } template int RegistrationHelper ::ValidateParameters() { if( this->m_NumberOfStages == 0 ) { this->Logger() << "No transformations are specified." << std::endl; return EXIT_FAILURE; } if( this->m_Iterations.size() != this->m_NumberOfStages ) { this->Logger() << "The number of iteration sets specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } if( this->m_ShrinkFactors.size() != this->m_NumberOfStages ) { this->Logger() << "The number of shrinkFactors specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } if( this->m_SmoothingSigmas.size() != this->m_NumberOfStages ) { this->Logger() << "The number of smoothing sigma sets specified does not match the number of stages." << std::endl; return EXIT_FAILURE; } if( this->m_SmoothingSigmasAreInPhysicalUnits.size() != this->m_NumberOfStages ) { this->Logger() << "The number of smoothing sigma in physical units bool values does not match the number of stages." << std::endl; return EXIT_FAILURE; } for( unsigned int i = 0; i < this->m_Metrics.size(); i++ ) { if( !this->IsPointSetMetric( this->m_Metrics[i].m_MetricType ) ) { if( this->m_Metrics[i].m_FixedImage.IsNull() || this->m_Metrics[i].m_MovingImage.IsNull() ) { this->Logger() << "The image metric has no fixed and/or moving image." << std::endl; return EXIT_FAILURE; } } } // Check the number of masks. We are going to allow the user 2 options w.r.t. // mask specification: // 1. Either the user specifies a single mask to be used for all stages or // 2. the user specifies a mask for each stage. // Note that we handle the fixed and moving masks separately to enforce this constraint. if( this->m_FixedImageMasks.size() > 1 && this->m_FixedImageMasks.size() != this->m_NumberOfStages ) { this->Logger() << "The number of fixed masks must be equal to 1 (use the mask for all " << "stages) or the number of fixed masks must be equal to the number of stages." << std::endl; return EXIT_FAILURE; } if( this->m_MovingImageMasks.size() > 1 && this->m_MovingImageMasks.size() != this->m_NumberOfStages ) { this->Logger() << "The number of moving masks must be equal to 1 (i.e., use the mask for all " << "stages) or the number of moving masks must be equal to the number of stages." << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } template typename RegistrationHelper::ImageType::Pointer RegistrationHelper ::GetWarpedImage() const { typename ImageType::Pointer fixedImage = this->m_Metrics[0].m_FixedImage; typename ImageType::Pointer movingImage = this->m_Metrics[0].m_MovingImage; typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetTransform( this->m_CompositeTransform ); resampler->SetInput( movingImage ); resampler->SetOutputParametersFromImage( fixedImage ); resampler->SetInterpolator( this->m_Interpolator ); resampler->SetDefaultPixelValue( 0 ); resampler->Update(); typename ImageType::Pointer WarpedImage; WarpedImage = resampler->GetOutput(); return WarpedImage.GetPointer(); } template typename RegistrationHelper::ImageType::Pointer RegistrationHelper ::GetInverseWarpedImage() const { typename ImageType::Pointer fixedImage = this->m_Metrics[0].m_FixedImage; typename ImageType::Pointer movingImage = this->m_Metrics[0].m_MovingImage; if( this->m_CompositeTransform->GetInverseTransform().IsNull() ) { return ITK_NULLPTR; } typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer inverseResampler = ResampleFilterType::New(); inverseResampler->SetTransform( this->m_CompositeTransform->GetInverseTransform() ); inverseResampler->SetInput( fixedImage ); inverseResampler->SetOutputParametersFromImage( movingImage ); inverseResampler->SetInterpolator( this->m_Interpolator ); inverseResampler->SetDefaultPixelValue( 0 ); inverseResampler->Update(); typename ImageType::Pointer InverseWarpedImage; InverseWarpedImage = inverseResampler->GetOutput(); return InverseWarpedImage.GetPointer(); } template void RegistrationHelper ::AddFixedImageMask( typename MaskImageType::Pointer & fixedImageMask ) { typename ImageMaskSpatialObjectType::Pointer so = ITK_NULLPTR; if( fixedImageMask.IsNotNull() ) { so = ImageMaskSpatialObjectType::New(); so->SetImage( fixedImageMask.GetPointer() ); } this->AddFixedImageMask( so ); } template void RegistrationHelper ::AddMovingImageMask( typename MaskImageType::Pointer & movingImageMask ) { typename ImageMaskSpatialObjectType::Pointer so = ITK_NULLPTR; if( movingImageMask.IsNotNull() ) { so = ImageMaskSpatialObjectType::New(); so->SetImage( movingImageMask.GetPointer() ); } this->AddMovingImageMask( so ); } template int RegistrationHelper ::DoRegistration() { /** Can really impact performance */ const bool gradientfilter = false; itk::TimeProbe totalTimer; totalTimer.Start(); this->m_NumberOfStages = this->m_TransformMethods.size(); if( this->ValidateParameters() != EXIT_SUCCESS ) { return EXIT_FAILURE; } this->PrintState(); this->Logger() << "Registration using " << this->m_NumberOfStages << " total stages." << std::endl; // NOTE: the -1 is to ignore the initial identity identity transform if( this->m_CompositeTransform.IsNull() ) { this->m_CompositeTransform = CompositeTransformType::New(); } if( this->m_CompositeLinearTransformForFixedImageHeader.IsNull() ) { this->m_CompositeLinearTransformForFixedImageHeader = CompositeTransformType::New(); } if( this->m_FixedInitialTransform.IsNull() ) { this->m_FixedInitialTransform = CompositeTransformType::New(); } // ######################################################################################## // ######################################################################################## // ##The main loop for exstimating the total composite tranform // ######################################################################################## // ######################################################################################## for( unsigned int currentStageNumber = 0; currentStageNumber < this->m_NumberOfStages; currentStageNumber++ ) { itk::TimeProbe timer; timer.Start(); this->Logger() << std::endl << "Stage " << currentStageNumber << std::endl; std::stringstream currentStageString; currentStageString << currentStageNumber; // Get the number of iterations and use that information to specify the number of levels const std::vector & currentStageIterations = this->m_Iterations[currentStageNumber]; this->Logger() << " iterations = "; for( unsigned int m = 0; m < currentStageIterations.size(); m++ ) { this->Logger() << currentStageIterations[m]; if( m < currentStageIterations.size() - 1 ) { this->Logger() << 'x'; } } this->Logger() << std::endl; const RealType convergenceThreshold = this->m_ConvergenceThresholds[currentStageNumber]; this->Logger() << " convergence threshold = " << convergenceThreshold << std::endl; const unsigned int convergenceWindowSize = this->m_ConvergenceWindowSizes[currentStageNumber]; this->Logger() << " convergence window size = " << convergenceWindowSize << std::endl; const unsigned int numberOfLevels = currentStageIterations.size(); this->Logger() << " number of levels = " << numberOfLevels << std::endl; unsigned int fixedMaskIndex = -1; unsigned int movingMaskIndex = -1; bool useFixedImageMaskForThisStage = false; bool useMovingImageMaskForThisStage = false; // We already checked that number of masks = 1 or = number of stages if( this->m_FixedImageMasks.size() > 0 ) { useFixedImageMaskForThisStage = true; if( this->m_FixedImageMasks.size() == 1 ) { fixedMaskIndex = 0; } else { fixedMaskIndex = currentStageNumber; } } if( this->m_MovingImageMasks.size() > 0 ) { useMovingImageMaskForThisStage = true; if( this->m_MovingImageMasks.size() == 1 ) { movingMaskIndex = 0; } else { movingMaskIndex = currentStageNumber; } } // Get the number of metrics at the current stage. If more than one metric // then we need to use the MultiMetricType. Due to the way the metrics are // pulled off the command line stack, we need to iterate from the top down. MetricListType stageMetricList = this->GetMetricListPerStage( this->m_NumberOfStages - currentStageNumber - 1 ); typename ObjectMetricType::Pointer singleMetric; typename MultiMetricType::Pointer multiMetric; typename MultiMetricType::WeightsArrayType metricWeights( stageMetricList.size() ); metricWeights.Fill( 1.0 ); bool useMultiMetric = false; if( stageMetricList.size() > 1 ) { useMultiMetric = true; multiMetric = MultiMetricType::New(); } std::vector preprocessedFixedImagesPerStage; std::vector preprocessedMovingImagesPerStage; typename ImageBaseType::Pointer virtualDomainImage = ITK_NULLPTR; for( unsigned int currentMetricNumber = 0; currentMetricNumber < stageMetricList.size(); currentMetricNumber++ ) { MetricEnumeration currentMetricType = stageMetricList[currentMetricNumber].m_MetricType; typename ImageMetricType::Pointer imageMetric = ITK_NULLPTR; typedef itk::LabeledPointSetToPointSetMetricv4 LabeledPointSetMetricType; typename LabeledPointSetMetricType::Pointer labeledPointSetMetric = LabeledPointSetMetricType::New(); typedef itk::MeanSquaresPointSetToPointSetIntensityMetricv4 IntensityPointSetMetricType; typename IntensityPointSetMetricType::Pointer intensityPointSetMetric = ITK_NULLPTR; switch( currentMetricType ) { case CC: { const unsigned int radiusOption = stageMetricList[currentMetricNumber].m_Radius; this->Logger() << " using the CC metric (radius = " << radiusOption << ", weight = " << stageMetricList[currentMetricNumber].m_Weighting << ")" << std::endl; typedef itk::ANTSNeighborhoodCorrelationImageToImageMetricv4 CorrelationMetricType; typename CorrelationMetricType::Pointer correlationMetric = CorrelationMetricType::New(); { typename CorrelationMetricType::RadiusType radius; radius.Fill( radiusOption ); correlationMetric->SetRadius( radius ); } correlationMetric->SetUseMovingImageGradientFilter( gradientfilter ); correlationMetric->SetUseFixedImageGradientFilter( gradientfilter ); imageMetric = correlationMetric; } break; case Mattes: { const unsigned int binOption = stageMetricList[currentMetricNumber].m_NumberOfBins; this->Logger() << " using the Mattes MI metric (number of bins = " << binOption << ", weight = " << stageMetricList[currentMetricNumber].m_Weighting << ")" << std::endl; typedef itk::MattesMutualInformationImageToImageMetricv4 MutualInformationMetricType; typename MutualInformationMetricType::Pointer mutualInformationMetric = MutualInformationMetricType::New(); mutualInformationMetric = mutualInformationMetric; mutualInformationMetric->SetNumberOfHistogramBins( binOption ); mutualInformationMetric->SetUseMovingImageGradientFilter( gradientfilter ); mutualInformationMetric->SetUseFixedImageGradientFilter( gradientfilter ); mutualInformationMetric->SetUseFixedSampledPointSet( false ); imageMetric = mutualInformationMetric; } break; case MI: { const unsigned int binOption = stageMetricList[currentMetricNumber].m_NumberOfBins; this->Logger() << " using the joint histogram MI metric (number of bins = " << binOption << ", weight = " << stageMetricList[currentMetricNumber].m_Weighting << ")" << std::endl; typedef itk::JointHistogramMutualInformationImageToImageMetricv4 MutualInformationMetricType; typename MutualInformationMetricType::Pointer mutualInformationMetric = MutualInformationMetricType::New(); mutualInformationMetric = mutualInformationMetric; mutualInformationMetric->SetNumberOfHistogramBins( binOption ); mutualInformationMetric->SetUseMovingImageGradientFilter( gradientfilter ); mutualInformationMetric->SetUseFixedImageGradientFilter( gradientfilter ); mutualInformationMetric->SetUseFixedSampledPointSet( false ); mutualInformationMetric->SetVarianceForJointPDFSmoothing( 1.0 ); imageMetric = mutualInformationMetric; } break; case MeanSquares: { this->Logger() << " using the MeanSquares metric (weight = " << stageMetricList[currentMetricNumber].m_Weighting << ")" << std::endl; typedef itk::MeanSquaresImageToImageMetricv4 MeanSquaresMetricType; typename MeanSquaresMetricType::Pointer meanSquaresMetric = MeanSquaresMetricType::New(); meanSquaresMetric = meanSquaresMetric; imageMetric = meanSquaresMetric; } break; case Demons: { this->Logger() << " using the Demons metric (weight = " << stageMetricList[currentMetricNumber].m_Weighting << ")" << std::endl; typedef itk::DemonsImageToImageMetricv4 DemonsMetricType; typename DemonsMetricType::Pointer demonsMetric = DemonsMetricType::New(); imageMetric = demonsMetric; } break; case GC: { this->Logger() << " using the global correlation metric (weight = " << stageMetricList[currentMetricNumber].m_Weighting << ")" << std::endl; typedef itk::CorrelationImageToImageMetricv4 corrMetricType; typename corrMetricType::Pointer corrMetric = corrMetricType::New(); imageMetric = corrMetric; } break; case ICP: { this->Logger() << " using the ICP metric (weight = " << stageMetricList[currentMetricNumber].m_Weighting << ")" << std::endl; typedef itk::EuclideanDistancePointSetToPointSetMetricv4 IcpPointSetMetricType; typename IcpPointSetMetricType::Pointer icpMetric = IcpPointSetMetricType::New(); labeledPointSetMetric->SetPointSetMetric( icpMetric.GetPointer() ); } break; case PSE: { this->Logger() << " using the PSE metric (weight = " << stageMetricList[currentMetricNumber].m_Weighting << ")" << std::endl; typedef itk::ExpectationBasedPointSetToPointSetMetricv4 PsePointSetMetricType; typename PsePointSetMetricType::Pointer pseMetric = PsePointSetMetricType::New(); pseMetric->SetPointSetSigma( stageMetricList[currentMetricNumber].m_PointSetSigma ); pseMetric->SetEvaluationKNeighborhood( stageMetricList[currentMetricNumber].m_EvaluationKNeighborhood ); labeledPointSetMetric->SetPointSetMetric( pseMetric.GetPointer() ); } break; case JHCT: { this->Logger() << " using the JHCT metric (weight = " << stageMetricList[currentMetricNumber].m_Weighting << ")" << std::endl; typedef itk::JensenHavrdaCharvatTsallisPointSetToPointSetMetricv4 JhctPointSetMetricType; typename JhctPointSetMetricType::Pointer jhctMetric = JhctPointSetMetricType::New(); jhctMetric->SetPointSetSigma( stageMetricList[currentMetricNumber].m_PointSetSigma ); jhctMetric->SetKernelSigma( 10.0 ); jhctMetric->SetUseAnisotropicCovariances( stageMetricList[currentMetricNumber].m_UseAnisotropicCovariances ); jhctMetric->SetCovarianceKNeighborhood( 5 ); jhctMetric->SetEvaluationKNeighborhood( stageMetricList[currentMetricNumber].m_EvaluationKNeighborhood ); jhctMetric->SetAlpha( stageMetricList[currentMetricNumber].m_Alpha ); labeledPointSetMetric->SetPointSetMetric( jhctMetric.GetPointer() ); } break; case IGDM: { this->Logger() << " using the IGDM metric (weight = " << stageMetricList[currentMetricNumber].m_Weighting << ")" << std::endl; typedef itk::MeanSquaresPointSetToPointSetIntensityMetricv4 MsqPointSetMetricType; typename MsqPointSetMetricType::Pointer msqMetric = MsqPointSetMetricType::New(); msqMetric->SetIntensityDistanceSigma( stageMetricList[currentMetricNumber].m_IntensityDistanceSigma ); msqMetric->SetEuclideanDistanceSigma( stageMetricList[currentMetricNumber].m_EuclideanDistanceSigma ); if( msqMetric->GetEuclideanDistanceSigma() <= 0.0 ) { msqMetric->EstimateEuclideanDistanceSigmaAutomaticallyOn(); } else { msqMetric->EstimateEuclideanDistanceSigmaAutomaticallyOff(); } if( msqMetric->GetIntensityDistanceSigma() <= 0.0 ) { msqMetric->EstimateIntensityDistanceSigmaAutomaticallyOn(); } else { msqMetric->EstimateIntensityDistanceSigmaAutomaticallyOff(); } intensityPointSetMetric = msqMetric; } break; default: this->Logger() << "ERROR: Unrecognized metric. " << std::endl; return EXIT_FAILURE; } if( !this->IsPointSetMetric( currentMetricType ) ) { // Get the fixed and moving images const typename ImageType::ConstPointer fixedImage = stageMetricList[currentMetricNumber].m_FixedImage.GetPointer(); const typename ImageType::ConstPointer movingImage = stageMetricList[currentMetricNumber].m_MovingImage.GetPointer(); // Preprocess images std::string outputPreprocessingString = ""; PixelType lowerScaleValue = 0.0; PixelType upperScaleValue = 1.0; if( this->m_WinsorizeImageIntensities ) { outputPreprocessingString += " preprocessing: winsorizing the image intensities\n"; } typename ImageType::Pointer preprocessFixedImage = PreprocessImage( fixedImage.GetPointer(), lowerScaleValue, upperScaleValue, this->m_LowerQuantile, this->m_UpperQuantile, ITK_NULLPTR ); preprocessedFixedImagesPerStage.push_back( preprocessFixedImage.GetPointer() ); typename ImageType::Pointer preprocessMovingImage = PreprocessImage( movingImage.GetPointer(), lowerScaleValue, upperScaleValue, this->m_LowerQuantile, this->m_UpperQuantile, ITK_NULLPTR ); if( this->m_UseHistogramMatching ) { outputPreprocessingString += " preprocessing: histogram matching the images\n"; preprocessMovingImage = PreprocessImage( movingImage.GetPointer(), lowerScaleValue, upperScaleValue, this->m_LowerQuantile, this->m_UpperQuantile, preprocessFixedImage.GetPointer() ); } preprocessedMovingImagesPerStage.push_back( preprocessMovingImage.GetPointer() ); if( this->m_ApplyLinearTransformsToFixedImageHeader ) { this->ApplyCompositeLinearTransformToImageHeader( this->m_CompositeLinearTransformForFixedImageHeader, dynamic_cast( preprocessFixedImage. GetPointer() ), false ); if( useFixedImageMaskForThisStage ) { this->ApplyCompositeLinearTransformToImageHeader( this->m_CompositeLinearTransformForFixedImageHeader, dynamic_cast( const_cast( this->m_FixedImageMasks[fixedMaskIndex]-> GetImage() ) ), false ); } } this->Logger() << outputPreprocessingString << std::flush; // Set up the image metric and scales estimator imageMetric->SetVirtualDomainFromImage( fixedImage ); imageMetric->SetUseMovingImageGradientFilter( gradientfilter ); imageMetric->SetUseFixedImageGradientFilter( gradientfilter ); metricWeights[currentMetricNumber] = stageMetricList[currentMetricNumber].m_Weighting; if( useFixedImageMaskForThisStage ) { imageMetric->SetFixedImageMask( this->m_FixedImageMasks[fixedMaskIndex] ); } if( useMovingImageMaskForThisStage ) { imageMetric->SetMovingImageMask( this->m_MovingImageMasks[movingMaskIndex] ); } if( virtualDomainImage.IsNull() ) { virtualDomainImage = imageMetric->GetVirtualImage(); } if( useMultiMetric ) { multiMetric->AddMetric( imageMetric ); } if( !useMultiMetric || currentMetricNumber == 0 ) { singleMetric = static_cast( imageMetric ); } } else { preprocessedFixedImagesPerStage.push_back( ITK_NULLPTR ); preprocessedMovingImagesPerStage.push_back( ITK_NULLPTR ); metricWeights[currentMetricNumber] = stageMetricList[currentMetricNumber].m_Weighting; if( currentMetricType == IGDM ) { if( useFixedImageMaskForThisStage ) { typedef itk::CastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput( this->m_FixedImageMasks[fixedMaskIndex]->GetImage() ); caster->Update(); intensityPointSetMetric->SetVirtualDomainFromImage( caster->GetOutput() ); if( virtualDomainImage.IsNull() ) { virtualDomainImage = intensityPointSetMetric->GetVirtualImage(); } } if( useMultiMetric ) { multiMetric->AddMetric( intensityPointSetMetric ); } if( !useMultiMetric || currentMetricNumber == 0 ) { intensityPointSetMetric->SetFixedPointSet( stageMetricList[currentMetricNumber].m_FixedIntensityPointSet ); intensityPointSetMetric->SetMovingPointSet( stageMetricList[currentMetricNumber].m_MovingIntensityPointSet ); singleMetric = static_cast( intensityPointSetMetric ); } } else { if( useFixedImageMaskForThisStage ) { typedef itk::CastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput( this->m_FixedImageMasks[fixedMaskIndex]->GetImage() ); caster->Update(); labeledPointSetMetric->SetVirtualDomainFromImage( caster->GetOutput() ); if( virtualDomainImage.IsNull() ) { virtualDomainImage = labeledPointSetMetric->GetVirtualImage(); } } if( useMultiMetric ) { multiMetric->AddMetric( labeledPointSetMetric ); } if( !useMultiMetric || currentMetricNumber == 0 ) { labeledPointSetMetric->SetFixedPointSet( stageMetricList[currentMetricNumber].m_FixedLabeledPointSet ); labeledPointSetMetric->SetMovingPointSet( stageMetricList[currentMetricNumber].m_MovingLabeledPointSet ); singleMetric = static_cast( labeledPointSetMetric ); } } } } if( useMultiMetric ) { multiMetric->SetMetricWeights( metricWeights ); } // These two variables are specified in setting up the registration method. // However, for point set metrics, they are not required. std::vector shrinkFactorsPerDimensionForAllLevels; typename AffineRegistrationType::SmoothingSigmasArrayType smoothingSigmasPerLevel; // Get shrink factors and adjust according to the current image const std::vector factors( this->m_ShrinkFactors[currentStageNumber] ); if( factors.size() != numberOfLevels ) { this->Logger() << "\n\n\n" << "ERROR: The number of shrink factors does not match the number of levels." << "\nShrink Factors: " << factors.size() << "\nNumber Of Levels: " << numberOfLevels << "\n\n\n" << std::endl; return EXIT_FAILURE; } for( unsigned int n = 0; n < numberOfLevels; n++ ) { ShrinkFactorsPerDimensionContainerType shrinkFactorsPerDimension = this->CalculateShrinkFactorsPerDimension( factors[n], virtualDomainImage->GetSpacing() ); shrinkFactorsPerDimensionForAllLevels.push_back( shrinkFactorsPerDimension ); this->Logger() << " Shrink factors (level " << n+1 << " out of " << numberOfLevels << "): " << shrinkFactorsPerDimension << std::endl; } // Get smoothing sigmas const std::vector sigmas( this->m_SmoothingSigmas[currentStageNumber] ); smoothingSigmasPerLevel.SetSize( sigmas.size() ); if( sigmas.size() != numberOfLevels ) { this->Logger() << "ERROR: The number of smoothing sigmas " << "does not match the number of levels." << std::endl; return EXIT_FAILURE; } for( unsigned int n = 0; n < smoothingSigmasPerLevel.Size(); n++ ) { smoothingSigmasPerLevel[n] = sigmas[n]; } this->Logger() << " smoothing sigmas per level: " << smoothingSigmasPerLevel << std::endl; // The sampling strategy/percentage is only specified once for the image registration // method. We might need to change this in the future. const float samplingPercentage = stageMetricList[0].m_SamplingPercentage; const SamplingStrategy samplingStrategy = stageMetricList[0].m_SamplingStrategy; typename AffineRegistrationType::MetricSamplingStrategyType metricSamplingStrategy = AffineRegistrationType::NONE; if( samplingStrategy == random ) { this->Logger() << " random sampling (percentage = " << samplingPercentage << ")" << std::endl; metricSamplingStrategy = AffineRegistrationType::RANDOM; } else if( samplingStrategy == regular ) { this->Logger() << " regular sampling (percentage = " << samplingPercentage << ")" << std::endl; metricSamplingStrategy = AffineRegistrationType::REGULAR; } else if( samplingStrategy == none ) { this->Logger() << " Using default NONE metricSamplingStrategy " << std::endl; } else { this->Logger() << "ERROR: samplingStrategy is incorrectly specified" << std::endl; return EXIT_FAILURE; } // Set up the optimizers. To change the iteration number for each level we rely // on the command observer. const RealType learningRate = this->m_TransformMethods[currentStageNumber].m_GradientStep; // There's a scale issue here. Currently we are using the first metric to estimate the // scales but we might need to change this. typedef itk::RegistrationParameterScalesFromPhysicalShift ScalesEstimatorType; typename ScalesEstimatorType::Pointer scalesEstimator = ScalesEstimatorType::New(); scalesEstimator->SetMetric( singleMetric ); scalesEstimator->SetTransformForward( true ); typedef itk::LabeledPointSetToPointSetMetricv4 LabeledPointSetMetricType; typename LabeledPointSetMetricType::Pointer labeledPointSetMetric2 = dynamic_cast( singleMetric.GetPointer() ); if( labeledPointSetMetric2.IsNotNull() ) { typedef typename ScalesEstimatorType::VirtualPointSetType VirtualPointSetType; typename VirtualPointSetType::Pointer virtualPointSet = VirtualPointSetType::New(); virtualPointSet->Initialize(); virtualPointSet->SetPoints( const_cast( labeledPointSetMetric2->GetFixedPointSet()->GetPoints() ) ); scalesEstimator->SetVirtualDomainPointSet( virtualPointSet ); } else { typedef itk::MeanSquaresPointSetToPointSetIntensityMetricv4 IntensityPointSetMetricType; typename IntensityPointSetMetricType::Pointer intensityPointSetMetric2 = dynamic_cast( singleMetric.GetPointer() ); if( intensityPointSetMetric2.IsNotNull() ) { typedef typename ScalesEstimatorType::VirtualPointSetType VirtualPointSetType; typename VirtualPointSetType::Pointer virtualPointSet = VirtualPointSetType::New(); virtualPointSet->Initialize(); virtualPointSet->SetPoints( const_cast( intensityPointSetMetric2->GetFixedPointSet()->GetPoints() ) ); scalesEstimator->SetVirtualDomainPointSet( virtualPointSet ); } } typename ConjugateGradientDescentOptimizerType::Pointer optimizer = ConjugateGradientDescentOptimizerType::New(); optimizer->SetLowerLimit( 0 ); optimizer->SetUpperLimit( 2 ); optimizer->SetEpsilon( 0.2 ); // optimizer->SetMaximumLineSearchIterations( 20 ); optimizer->SetLearningRate( learningRate ); optimizer->SetMaximumStepSizeInPhysicalUnits( learningRate ); optimizer->SetNumberOfIterations( currentStageIterations[0] ); optimizer->SetScalesEstimator( scalesEstimator ); optimizer->SetMinimumConvergenceValue( convergenceThreshold ); optimizer->SetConvergenceWindowSize( convergenceWindowSize ); optimizer->SetDoEstimateLearningRateAtEachIteration( this->m_DoEstimateLearningRateAtEachIteration ); optimizer->SetDoEstimateLearningRateOnce( !this->m_DoEstimateLearningRateAtEachIteration ); typedef antsRegistrationOptimizerCommandIterationUpdate OptimizerCommandType; typename OptimizerCommandType::Pointer optimizerObserver = OptimizerCommandType::New(); optimizerObserver->SetLogStream( *this->m_LogStream ); optimizerObserver->SetNumberOfIterations( currentStageIterations ); optimizerObserver->SetOptimizer( optimizer ); if( !this->IsPointSetMetric( this->m_Metrics[0].m_MetricType ) ) { optimizerObserver->SetOrigFixedImage( this->m_Metrics[0].m_FixedImage ); optimizerObserver->SetOrigMovingImage( this->m_Metrics[0].m_MovingImage ); } if( this->m_PrintSimilarityMeasureInterval != 0 ) { optimizerObserver->SetComputeFullScaleCCInterval( this->m_PrintSimilarityMeasureInterval ); } if( this->m_WriteIntervalVolumes != 0 ) { optimizerObserver->SetWriteInterationsOutputsInIntervals( this->m_WriteIntervalVolumes ); optimizerObserver->SetCurrentStageNumber( currentStageNumber ); } typename GradientDescentOptimizerType::Pointer optimizer2 = GradientDescentOptimizerType::New(); // optimizer2->SetLowerLimit( 0 ); // optimizer2->SetUpperLimit( 2 ); // optimizer2->SetEpsilon( 0.2 ); // optimizer->SetMaximumLineSearchIterations( 20 ); optimizer2->SetLearningRate( learningRate ); optimizer2->SetMaximumStepSizeInPhysicalUnits( learningRate ); optimizer2->SetNumberOfIterations( currentStageIterations[0] ); optimizer2->SetScalesEstimator( ITK_NULLPTR ); optimizer2->SetMinimumConvergenceValue( convergenceThreshold ); optimizer2->SetConvergenceWindowSize( convergenceWindowSize ); optimizer2->SetDoEstimateLearningRateAtEachIteration( this->m_DoEstimateLearningRateAtEachIteration ); optimizer2->SetDoEstimateLearningRateOnce( !this->m_DoEstimateLearningRateAtEachIteration ); typedef antsRegistrationOptimizerCommandIterationUpdate OptimizerCommandType2; typename OptimizerCommandType2::Pointer optimizerObserver2 = OptimizerCommandType2::New(); optimizerObserver2->SetLogStream( *this->m_LogStream ); optimizerObserver2->SetNumberOfIterations( currentStageIterations ); optimizerObserver2->SetOptimizer( optimizer2 ); if( !this->IsPointSetMetric( this->m_Metrics[0].m_MetricType ) ) { optimizerObserver2->SetOrigFixedImage( this->m_Metrics[0].m_FixedImage ); optimizerObserver2->SetOrigMovingImage( this->m_Metrics[0].m_MovingImage ); } if( this->m_PrintSimilarityMeasureInterval != 0 ) { optimizerObserver2->SetComputeFullScaleCCInterval( this->m_PrintSimilarityMeasureInterval ); } if( this->m_WriteIntervalVolumes != 0 ) { optimizerObserver2->SetWriteInterationsOutputsInIntervals( this->m_WriteIntervalVolumes ); optimizerObserver2->SetCurrentStageNumber( currentStageNumber ); } std::vector fixedLabeledPointSetsPerStage; std::vector movingLabeledPointSetsPerStage; for( unsigned int n = 0; n < stageMetricList.size(); n++ ) { fixedLabeledPointSetsPerStage.push_back( stageMetricList[n].m_FixedLabeledPointSet.GetPointer() ); movingLabeledPointSetsPerStage.push_back( stageMetricList[n].m_MovingLabeledPointSet.GetPointer() ); } std::vector fixedIntensityPointSetsPerStage; std::vector movingIntensityPointSetsPerStage; for( unsigned int n = 0; n < stageMetricList.size(); n++ ) { fixedIntensityPointSetsPerStage.push_back( stageMetricList[n].m_FixedIntensityPointSet.GetPointer() ); movingIntensityPointSetsPerStage.push_back( stageMetricList[n].m_MovingIntensityPointSet.GetPointer() ); } // Set up the image registration methods along with the transforms const XfrmMethod whichTransform( this->m_TransformMethods[currentStageNumber].m_XfrmMethod ); switch( whichTransform ) { case Affine: { if( stageMetricList[0].m_MetricType != IGDM ) { this->AddLinearTransformToCompositeTransform( this->m_CompositeTransform, currentStageNumber, AffineTransformType::ParametersDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedLabeledPointSetsPerStage, movingLabeledPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); } else { typedef itk::ImageRegistrationMethodv4 AffineRegistrationType2; this->AddLinearTransformToCompositeTransform( this->m_CompositeTransform, currentStageNumber, AffineTransformType::ParametersDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedIntensityPointSetsPerStage, movingIntensityPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); } } break; case Rigid: { typedef typename RigidTransformTraits::TransformType RigidTransformType; if( stageMetricList[0].m_MetricType != IGDM ) { typedef itk::ImageRegistrationMethodv4 RigidRegistrationType; this->AddLinearTransformToCompositeTransform( this->m_CompositeTransform, currentStageNumber, RigidTransformType::ParametersDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedLabeledPointSetsPerStage, movingLabeledPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); } else { typedef itk::ImageRegistrationMethodv4 RigidRegistrationType; this->AddLinearTransformToCompositeTransform( this->m_CompositeTransform, currentStageNumber, RigidTransformType::ParametersDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedIntensityPointSetsPerStage, movingIntensityPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); } } break; case CompositeAffine: { typedef typename CompositeAffineTransformTraits::TransformType CompositeAffineTransformType; if( stageMetricList[0].m_MetricType != IGDM ) { typedef itk::ImageRegistrationMethodv4 CompositeAffineRegistrationType; this->AddLinearTransformToCompositeTransform( this->m_CompositeTransform, currentStageNumber, CompositeAffineTransformType::ParametersDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedLabeledPointSetsPerStage, movingLabeledPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); } else { typedef itk::ImageRegistrationMethodv4 CompositeAffineRegistrationType; this->AddLinearTransformToCompositeTransform( this->m_CompositeTransform, currentStageNumber, CompositeAffineTransformType::ParametersDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedIntensityPointSetsPerStage, movingIntensityPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); } } break; case Similarity: { typedef typename SimilarityTransformTraits::TransformType SimilarityTransformType; if( stageMetricList[0].m_MetricType != IGDM ) { typedef itk::ImageRegistrationMethodv4 SimilarityRegistrationType; this->AddLinearTransformToCompositeTransform( this->m_CompositeTransform, currentStageNumber, SimilarityTransformType::ParametersDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedLabeledPointSetsPerStage, movingLabeledPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); } else { typedef itk::ImageRegistrationMethodv4 SimilarityRegistrationType; this->AddLinearTransformToCompositeTransform( this->m_CompositeTransform, currentStageNumber, SimilarityTransformType::ParametersDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedIntensityPointSetsPerStage, movingIntensityPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); } } break; case Translation: { typedef itk::TranslationTransform TranslationTransformType; if( stageMetricList[0].m_MetricType != IGDM ) { typedef itk::ImageRegistrationMethodv4 TranslationRegistrationType; this->AddLinearTransformToCompositeTransform( this->m_CompositeTransform, currentStageNumber, TranslationTransformType::ParametersDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedLabeledPointSetsPerStage, movingLabeledPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); } else { typedef itk::ImageRegistrationMethodv4 TranslationRegistrationType; this->AddLinearTransformToCompositeTransform( this->m_CompositeTransform, currentStageNumber, TranslationTransformType::ParametersDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedIntensityPointSetsPerStage, movingIntensityPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); } } break; case GaussianDisplacementField: { if( stageMetricList[0].m_MetricType == IGDM ) { this->Logger() << "Intensity point set metric is not implemented yet for the specified transform." << std::endl; return EXIT_FAILURE; } typedef itk::GaussianSmoothingOnUpdateDisplacementFieldTransform GaussianDisplacementFieldTransformType; typedef itk::ImageRegistrationMethodv4 DisplacementFieldRegistrationType; typename DisplacementFieldRegistrationType::Pointer registrationMethod = this->PrepareRegistrationMethod( this->m_CompositeTransform, currentStageNumber, VImageDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedLabeledPointSetsPerStage, movingLabeledPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); typename GaussianDisplacementFieldTransformType::Pointer outputDisplacementFieldTransform = registrationMethod->GetModifiableTransform(); typedef itk::Vector VectorType; VectorType zeroVector( 0.0 ); typename DisplacementFieldType::Pointer displacementField = AllocImage( preprocessedFixedImagesPerStage[0], zeroVector ); outputDisplacementFieldTransform->SetDisplacementField( displacementField ); // Create the transform adaptors typedef itk::GaussianSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor< GaussianDisplacementFieldTransformType> DisplacementFieldTransformAdaptorType; typename DisplacementFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; RealType varianceForUpdateField = this->m_TransformMethods[currentStageNumber].m_UpdateFieldVarianceInVarianceSpace; RealType varianceForTotalField = this->m_TransformMethods[currentStageNumber].m_TotalFieldVarianceInVarianceSpace; outputDisplacementFieldTransform->SetGaussianSmoothingVarianceForTheUpdateField( varianceForUpdateField ); outputDisplacementFieldTransform->SetGaussianSmoothingVarianceForTheTotalField( varianceForTotalField ); // Create the transform adaptors // For the gaussian displacement field, the specified variances are in image spacing terms // and, in normal practice, we typically don't change these values at each level. However, // if the user wishes to add that option, they can use the class // GaussianSmoothingOnUpdateDisplacementFieldTransformAdaptor for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename itk::ImageBase::Pointer shrunkSpace= this->GetShrinkImageOutputInformation( virtualDomainImage.GetPointer(), shrinkFactorsPerDimensionForAllLevels[level] ); typename DisplacementFieldTransformAdaptorType::Pointer fieldTransformAdaptor = DisplacementFieldTransformAdaptorType::New(); fieldTransformAdaptor->SetRequiredSpacing( shrunkSpace->GetSpacing() ); fieldTransformAdaptor->SetRequiredSize( shrunkSpace->GetLargestPossibleRegion().GetSize() ); fieldTransformAdaptor->SetRequiredDirection( shrunkSpace->GetDirection() ); fieldTransformAdaptor->SetRequiredOrigin( shrunkSpace->GetOrigin() ); fieldTransformAdaptor->SetTransform( outputDisplacementFieldTransform ); adaptors.push_back( fieldTransformAdaptor.GetPointer() ); } registrationMethod->SetOptimizer( optimizer ); registrationMethod->SetTransformParametersAdaptorsPerLevel( adaptors ); typedef antsRegistrationCommandIterationUpdate DisplacementFieldCommandType; typename DisplacementFieldCommandType::Pointer displacementFieldRegistrationObserver = DisplacementFieldCommandType::New(); displacementFieldRegistrationObserver->SetLogStream( *this->m_LogStream ); displacementFieldRegistrationObserver->SetNumberOfIterations( currentStageIterations ); registrationMethod->AddObserver( itk::IterationEvent(), displacementFieldRegistrationObserver ); registrationMethod->AddObserver( itk::InitializeEvent(), displacementFieldRegistrationObserver ); try { this->Logger() << std::endl << "*** Running gaussian displacement field registration (varianceForUpdateField = " << varianceForUpdateField << ", varianceForTotalField = " << varianceForTotalField << ") ***" << std::endl << std::endl; displacementFieldRegistrationObserver->Execute( registrationMethod, itk::StartEvent() ); registrationMethod->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputDisplacementFieldTransform ); this->m_AllPreviousTransformsAreLinear = false; } break; case BSplineDisplacementField: { if( stageMetricList[0].m_MetricType == IGDM ) { this->Logger() << "Intensity point set metric is not implemented yet for the specified transform." << std::endl; return EXIT_FAILURE; } typedef itk::BSplineSmoothingOnUpdateDisplacementFieldTransform BSplineDisplacementFieldTransformType; typedef itk::ImageRegistrationMethodv4 DisplacementFieldRegistrationType; typename DisplacementFieldRegistrationType::Pointer registrationMethod = this->PrepareRegistrationMethod( this->m_CompositeTransform, currentStageNumber, VImageDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedLabeledPointSetsPerStage, movingLabeledPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); typename BSplineDisplacementFieldTransformType::Pointer outputDisplacementFieldTransform = registrationMethod->GetModifiableTransform(); typedef itk::Vector VectorType; VectorType zeroVector( 0.0 ); typename DisplacementFieldType::Pointer displacementField = AllocImage( preprocessedFixedImagesPerStage[0], zeroVector ); outputDisplacementFieldTransform->SetDisplacementField( displacementField ); // Create the transform adaptors typename DisplacementFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; const std::vector & meshSizeForTheUpdateField = this->m_TransformMethods[currentStageNumber].m_UpdateFieldMeshSizeAtBaseLevel; std::vector meshSizeForTheTotalField = this->m_TransformMethods[currentStageNumber].m_TotalFieldMeshSizeAtBaseLevel; outputDisplacementFieldTransform->SetSplineOrder( this->m_TransformMethods[currentStageNumber].m_SplineOrder ); if( meshSizeForTheUpdateField.size() != VImageDimension || meshSizeForTheTotalField.size() != VImageDimension ) { this->Logger() << "ERROR: The mesh size(s) don't match the ImageDimension." << std::endl; return EXIT_FAILURE; } typename BSplineDisplacementFieldTransformType::ArrayType updateMeshSize; typename BSplineDisplacementFieldTransformType::ArrayType totalMeshSize; for( unsigned int d = 0; d < VImageDimension; d++ ) { updateMeshSize[d] = meshSizeForTheUpdateField[d]; totalMeshSize[d] = meshSizeForTheTotalField[d]; } // Create the transform adaptors specific to B-splines for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename itk::ImageBase::Pointer shrunkSpace= this->GetShrinkImageOutputInformation( virtualDomainImage.GetPointer(), shrinkFactorsPerDimensionForAllLevels[level] ); typedef itk::BSplineSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor< BSplineDisplacementFieldTransformType> BSplineDisplacementFieldTransformAdaptorType; typename BSplineDisplacementFieldTransformAdaptorType::Pointer bsplineFieldTransformAdaptor = BSplineDisplacementFieldTransformAdaptorType::New(); bsplineFieldTransformAdaptor->SetRequiredSpacing( shrunkSpace->GetSpacing() ); bsplineFieldTransformAdaptor->SetRequiredSize( shrunkSpace->GetLargestPossibleRegion().GetSize() ); bsplineFieldTransformAdaptor->SetRequiredDirection( shrunkSpace->GetDirection() ); bsplineFieldTransformAdaptor->SetRequiredOrigin( shrunkSpace->GetOrigin() ); bsplineFieldTransformAdaptor->SetTransform( outputDisplacementFieldTransform ); // A good heuristic is to double the b-spline mesh resolution at each level typename BSplineDisplacementFieldTransformType::ArrayType newUpdateMeshSize = updateMeshSize; typename BSplineDisplacementFieldTransformType::ArrayType newTotalMeshSize = totalMeshSize; for( unsigned int d = 0; d < VImageDimension; d++ ) { newUpdateMeshSize[d] = newUpdateMeshSize[d] << ( level + 1 ); newTotalMeshSize[d] = newTotalMeshSize[d] << ( level + 1 ); } bsplineFieldTransformAdaptor->SetMeshSizeForTheUpdateField( newUpdateMeshSize ); bsplineFieldTransformAdaptor->SetMeshSizeForTheTotalField( newTotalMeshSize ); adaptors.push_back( bsplineFieldTransformAdaptor.GetPointer() ); } registrationMethod->SetOptimizer( optimizer ); registrationMethod->SetTransformParametersAdaptorsPerLevel( adaptors ); typedef antsRegistrationCommandIterationUpdate DisplacementFieldCommandType; typename DisplacementFieldCommandType::Pointer displacementFieldRegistrationObserver = DisplacementFieldCommandType::New(); displacementFieldRegistrationObserver->SetLogStream( *this->m_LogStream ); displacementFieldRegistrationObserver->SetNumberOfIterations( currentStageIterations ); registrationMethod->AddObserver( itk::IterationEvent(), displacementFieldRegistrationObserver ); registrationMethod->AddObserver( itk::InitializeEvent(), displacementFieldRegistrationObserver ); try { this->Logger() << std::endl << "*** Running bspline displacement field registration (updateMeshSizeAtBaseLevel = " << updateMeshSize << ", totalMeshSizeAtBaseLevel = " << totalMeshSize << ") ***" << std::endl << std::endl; displacementFieldRegistrationObserver->Execute( registrationMethod, itk::StartEvent() ); registrationMethod->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputDisplacementFieldTransform ); this->m_AllPreviousTransformsAreLinear = false; } break; case SyN: { if( stageMetricList[0].m_MetricType == IGDM ) { this->Logger() << "Intensity point set metric is not implemented yet for the specified transform." << std::endl; return EXIT_FAILURE; } typedef itk::Vector VectorType; VectorType zeroVector( 0.0 ); //typedef itk::Image DisplacementFieldType; typename DisplacementFieldType::Pointer displacementField = AllocImage( virtualDomainImage, zeroVector ); typename DisplacementFieldType::Pointer inverseDisplacementField = AllocImage( virtualDomainImage, zeroVector ); typedef itk::SyNImageRegistrationMethod DisplacementFieldRegistrationType; typename DisplacementFieldRegistrationType::Pointer displacementFieldRegistration = DisplacementFieldRegistrationType::New(); if( this->m_RestrictDeformationOptimizerWeights.size() > currentStageNumber ) { if( this->m_RestrictDeformationOptimizerWeights[currentStageNumber].size() == VImageDimension ) { typename DisplacementFieldRegistrationType::OptimizerWeightsType optimizerWeights( VImageDimension ); for( unsigned int d = 0; d < VImageDimension; d++ ) { optimizerWeights[d] = this->m_RestrictDeformationOptimizerWeights[currentStageNumber][d]; } displacementFieldRegistration->SetOptimizerWeights( optimizerWeights ); } } typename DisplacementFieldTransformType::Pointer outputDisplacementFieldTransform = displacementFieldRegistration->GetModifiableTransform(); // Create the transform adaptors typedef itk::DisplacementFieldTransformParametersAdaptor DisplacementFieldTransformAdaptorType; typename DisplacementFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; // Create the transform adaptors // For the gaussian displacement field, the specified variances are in image spacing terms // and, in normal practice, we typically don't change these values at each level. However, // if the user wishes to add that option, they can use the class // GaussianSmoothingOnUpdateDisplacementFieldTransformAdaptor for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename itk::ImageBase::Pointer shrunkSpace= this->GetShrinkImageOutputInformation( virtualDomainImage.GetPointer(), shrinkFactorsPerDimensionForAllLevels[level] ); typename DisplacementFieldTransformAdaptorType::Pointer fieldTransformAdaptor = DisplacementFieldTransformAdaptorType::New(); fieldTransformAdaptor->SetRequiredSpacing( shrunkSpace->GetSpacing() ); fieldTransformAdaptor->SetRequiredSize( shrunkSpace->GetLargestPossibleRegion().GetSize() ); fieldTransformAdaptor->SetRequiredDirection( shrunkSpace->GetDirection() ); fieldTransformAdaptor->SetRequiredOrigin( shrunkSpace->GetOrigin() ); fieldTransformAdaptor->SetTransform( outputDisplacementFieldTransform ); adaptors.push_back( fieldTransformAdaptor.GetPointer() ); } // Extract parameters typename DisplacementFieldRegistrationType::NumberOfIterationsArrayType numberOfIterationsPerLevel; numberOfIterationsPerLevel.SetSize( numberOfLevels ); for( unsigned int d = 0; d < numberOfLevels; d++ ) { numberOfIterationsPerLevel[d] = currentStageIterations[d]; } const RealType varianceForUpdateField = this->m_TransformMethods[currentStageNumber].m_UpdateFieldVarianceInVarianceSpace; const RealType varianceForTotalField = this->m_TransformMethods[currentStageNumber].m_TotalFieldVarianceInVarianceSpace; for( unsigned int n = 0; n < stageMetricList.size(); n++ ) { if( !this->IsPointSetMetric( stageMetricList[n].m_MetricType ) ) { displacementFieldRegistration->SetFixedImage( n, preprocessedFixedImagesPerStage[n] ); displacementFieldRegistration->SetMovingImage( n, preprocessedMovingImagesPerStage[n] ); } else { displacementFieldRegistration->SetFixedPointSet( n, stageMetricList[n].m_FixedLabeledPointSet.GetPointer() ); displacementFieldRegistration->SetMovingPointSet( n, stageMetricList[n].m_MovingLabeledPointSet.GetPointer() ); } } if( useMultiMetric ) { displacementFieldRegistration->SetMetric( multiMetric ); } else { displacementFieldRegistration->SetMetric( singleMetric ); } bool synIsInitialized = false; if( this->m_InitializeTransformsPerStage ) { if( this->m_RegistrationState.IsNotNull() ) { const unsigned int numOfTransforms = this->m_RegistrationState->GetNumberOfTransforms(); typename TransformType::Pointer oneToEndTransform = this->m_RegistrationState->GetNthTransform( numOfTransforms-2 ); typename TransformType::Pointer endTransform = this->m_RegistrationState->GetNthTransform( numOfTransforms-1 ); typename DisplacementFieldTransformType::Pointer fixedToMiddle = dynamic_cast( oneToEndTransform.GetPointer() ); typename DisplacementFieldTransformType::Pointer movingToMiddle = dynamic_cast( endTransform.GetPointer() ); if( fixedToMiddle.IsNotNull() && movingToMiddle.IsNotNull() && fixedToMiddle->GetInverseDisplacementField() && movingToMiddle->GetInverseDisplacementField() ) { this->Logger() << "Current SyN transform is directly initialized from the previous stage." << std::endl; displacementFieldRegistration->SetFixedToMiddleTransform( fixedToMiddle ); displacementFieldRegistration->SetMovingToMiddleTransform( movingToMiddle ); this->m_RegistrationState->RemoveTransform(); this->m_RegistrationState->RemoveTransform(); } // If there are components other than SyN state if( this->m_RegistrationState->GetNumberOfTransforms() > 0 ) { displacementFieldRegistration->SetMovingInitialTransform( this->m_RegistrationState ); } synIsInitialized = true; this->m_CompositeTransform->RemoveTransform(); } } if( this->m_CompositeTransform->GetNumberOfTransforms() > 0 && !synIsInitialized ) { displacementFieldRegistration->SetMovingInitialTransform( this->m_CompositeTransform ); } if( this->m_FixedInitialTransform->GetNumberOfTransforms() > 0 ) { displacementFieldRegistration->SetFixedInitialTransform( this->m_FixedInitialTransform ); } displacementFieldRegistration->SetDownsampleImagesForMetricDerivatives( true ); displacementFieldRegistration->SetAverageMidPointGradients( false ); displacementFieldRegistration->SetNumberOfLevels( numberOfLevels ); for( unsigned int level = 0; level < numberOfLevels; ++level ) { displacementFieldRegistration->SetShrinkFactorsPerDimension( level, shrinkFactorsPerDimensionForAllLevels[level] ); } displacementFieldRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); displacementFieldRegistration->SetSmoothingSigmasAreSpecifiedInPhysicalUnits( this->m_SmoothingSigmasAreInPhysicalUnits[currentStageNumber] ); displacementFieldRegistration->SetLearningRate( learningRate ); displacementFieldRegistration->SetConvergenceThreshold( convergenceThreshold ); displacementFieldRegistration->SetConvergenceWindowSize( convergenceWindowSize ); displacementFieldRegistration->SetNumberOfIterationsPerLevel( numberOfIterationsPerLevel ); displacementFieldRegistration->SetTransformParametersAdaptorsPerLevel( adaptors ); displacementFieldRegistration->SetGaussianSmoothingVarianceForTheUpdateField( varianceForUpdateField ); displacementFieldRegistration->SetGaussianSmoothingVarianceForTheTotalField( varianceForTotalField ); outputDisplacementFieldTransform->SetDisplacementField( displacementField ); outputDisplacementFieldTransform->SetInverseDisplacementField( inverseDisplacementField ); // For all Velocity field and Displacement field registration types that are not using generic // itkImageRegistrationMethodv4 we use following type of observer: typedef antsDisplacementAndVelocityFieldRegistrationCommandIterationUpdate DisplacementFieldCommandType2; typename DisplacementFieldCommandType2::Pointer displacementFieldRegistrationObserver2 = DisplacementFieldCommandType2::New(); displacementFieldRegistrationObserver2->SetLogStream(*this->m_LogStream); displacementFieldRegistrationObserver2->SetNumberOfIterations( currentStageIterations ); displacementFieldRegistrationObserver2->SetOrigFixedImage( this->m_Metrics[0].m_FixedImage ); displacementFieldRegistrationObserver2->SetOrigMovingImage( this->m_Metrics[0].m_MovingImage ); if( this->m_PrintSimilarityMeasureInterval != 0 ) { displacementFieldRegistrationObserver2->SetComputeFullScaleCCInterval( this->m_PrintSimilarityMeasureInterval ); } if( this->m_WriteIntervalVolumes != 0 ) { displacementFieldRegistrationObserver2->SetWriteInterationsOutputsInIntervals( this->m_WriteIntervalVolumes ); displacementFieldRegistrationObserver2->SetCurrentStageNumber( currentStageNumber ); } displacementFieldRegistration->AddObserver( itk::InitializeEvent(), displacementFieldRegistrationObserver2 ); displacementFieldRegistration->AddObserver( itk::IterationEvent(), displacementFieldRegistrationObserver2 ); try { this->Logger() << std::endl << "*** Running SyN registration (varianceForUpdateField = " << varianceForUpdateField << ", varianceForTotalField = " << varianceForTotalField << ") ***" << std::endl << std::endl; displacementFieldRegistrationObserver2->Execute( displacementFieldRegistration, itk::StartEvent() ); displacementFieldRegistration->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated internal transforms to the registration state if( this->m_RegistrationState.IsNull() ) { this->m_RegistrationState = CompositeTransformType::New(); } this->m_RegistrationState->ClearTransformQueue(); this->m_RegistrationState->AddTransform( this->m_CompositeTransform ); this->m_RegistrationState->AddTransform( displacementFieldRegistration->GetModifiableFixedToMiddleTransform() ); this->m_RegistrationState->AddTransform( displacementFieldRegistration->GetModifiableMovingToMiddleTransform() ); this->m_RegistrationState->FlattenTransformQueue(); // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputDisplacementFieldTransform ); this->m_AllPreviousTransformsAreLinear = false; } break; case BSplineSyN: { typedef itk::BSplineSmoothingOnUpdateDisplacementFieldTransform BSplineDisplacementFieldTransformType; typedef itk::Vector VectorType; VectorType zeroVector( 0.0 ); typename DisplacementFieldType::Pointer displacementField = AllocImage( virtualDomainImage, zeroVector ); typename DisplacementFieldType::Pointer inverseDisplacementField = AllocImage( virtualDomainImage, zeroVector ); const std::vector & meshSizeForTheUpdateField = this->m_TransformMethods[currentStageNumber].m_UpdateFieldMeshSizeAtBaseLevel; std::vector meshSizeForTheTotalField = this->m_TransformMethods[currentStageNumber].m_TotalFieldMeshSizeAtBaseLevel; if( meshSizeForTheUpdateField.size() != VImageDimension || meshSizeForTheTotalField.size() != VImageDimension ) { this->Logger() << "ERROR: The mesh size(s) don't match the ImageDimension." << std::endl; return EXIT_FAILURE; } typename BSplineDisplacementFieldTransformType::ArrayType updateMeshSize; typename BSplineDisplacementFieldTransformType::ArrayType totalMeshSize; for( unsigned int d = 0; d < VImageDimension; d++ ) { updateMeshSize[d] = meshSizeForTheUpdateField[d]; totalMeshSize[d] = meshSizeForTheTotalField[d]; } if( stageMetricList[0].m_MetricType != IGDM ) { typedef itk::BSplineSyNImageRegistrationMethod DisplacementFieldRegistrationType; typename DisplacementFieldRegistrationType::Pointer registrationMethod = this->PrepareRegistrationMethod( this->m_CompositeTransform, currentStageNumber, VImageDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedLabeledPointSetsPerStage, movingLabeledPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); typename BSplineDisplacementFieldTransformType::Pointer outputDisplacementFieldTransform = registrationMethod->GetModifiableTransform(); // Create the transform adaptors typename DisplacementFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; outputDisplacementFieldTransform->SetSplineOrder( this->m_TransformMethods[currentStageNumber].m_SplineOrder ); // Create the transform adaptors for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename itk::ImageBase::Pointer shrunkSpace= this->GetShrinkImageOutputInformation( virtualDomainImage.GetPointer(), shrinkFactorsPerDimensionForAllLevels[level] ); typedef itk::BSplineSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor< BSplineDisplacementFieldTransformType> BSplineDisplacementFieldTransformAdaptorType; typename BSplineDisplacementFieldTransformAdaptorType::Pointer bsplineFieldTransformAdaptor = BSplineDisplacementFieldTransformAdaptorType::New(); bsplineFieldTransformAdaptor->SetRequiredSpacing( shrunkSpace->GetSpacing() ); bsplineFieldTransformAdaptor->SetRequiredSize( shrunkSpace->GetLargestPossibleRegion().GetSize() ); bsplineFieldTransformAdaptor->SetRequiredDirection( shrunkSpace->GetDirection() ); bsplineFieldTransformAdaptor->SetRequiredOrigin( shrunkSpace->GetOrigin() ); bsplineFieldTransformAdaptor->SetTransform( outputDisplacementFieldTransform ); // A good heuristic is to RealType the b-spline mesh resolution at each level typename BSplineDisplacementFieldTransformType::ArrayType newUpdateMeshSize = updateMeshSize; typename BSplineDisplacementFieldTransformType::ArrayType newTotalMeshSize = totalMeshSize; for( unsigned int d = 0; d < VImageDimension; d++ ) { newUpdateMeshSize[d] = newUpdateMeshSize[d] << ( level ); newTotalMeshSize[d] = newTotalMeshSize[d] << ( level ); } bsplineFieldTransformAdaptor->SetMeshSizeForTheUpdateField( newUpdateMeshSize ); bsplineFieldTransformAdaptor->SetMeshSizeForTheTotalField( newTotalMeshSize ); adaptors.push_back( bsplineFieldTransformAdaptor.GetPointer() ); } registrationMethod->SetDownsampleImagesForMetricDerivatives( true ); registrationMethod->SetAverageMidPointGradients( false ); registrationMethod->SetNumberOfLevels( numberOfLevels ); typename DisplacementFieldRegistrationType::NumberOfIterationsArrayType numberOfIterationsPerLevel; numberOfIterationsPerLevel.SetSize( numberOfLevels ); for( unsigned int d = 0; d < numberOfLevels; d++ ) { numberOfIterationsPerLevel[d] = currentStageIterations[d]; } registrationMethod->SetLearningRate( learningRate ); registrationMethod->SetConvergenceThreshold( convergenceThreshold ); registrationMethod->SetConvergenceWindowSize( convergenceWindowSize ); registrationMethod->SetNumberOfIterationsPerLevel( numberOfIterationsPerLevel ); registrationMethod->SetTransformParametersAdaptorsPerLevel( adaptors ); outputDisplacementFieldTransform->SetDisplacementField( displacementField ); outputDisplacementFieldTransform->SetInverseDisplacementField( inverseDisplacementField ); typedef antsRegistrationCommandIterationUpdate DisplacementFieldCommandType; typename DisplacementFieldCommandType::Pointer displacementFieldRegistrationObserver = DisplacementFieldCommandType::New(); displacementFieldRegistrationObserver->SetLogStream( *this->m_LogStream ); displacementFieldRegistrationObserver->SetNumberOfIterations( currentStageIterations ); registrationMethod->AddObserver( itk::IterationEvent(), displacementFieldRegistrationObserver ); registrationMethod->AddObserver( itk::InitializeEvent(), displacementFieldRegistrationObserver ); try { this->Logger() << std::endl << "*** Running B-spline SyN registration (updateMeshSizeAtBaseLevel = " << updateMeshSize << ", totalMeshSizeAtBaseLevel = " << totalMeshSize << ") ***" << std::endl << std::endl; displacementFieldRegistrationObserver->Execute( registrationMethod, itk::StartEvent() ); registrationMethod->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputDisplacementFieldTransform ); } else { typedef itk::BSplineSyNImageRegistrationMethod DisplacementFieldRegistrationType; typename DisplacementFieldRegistrationType::Pointer registrationMethod = this->PrepareRegistrationMethod( this->m_CompositeTransform, currentStageNumber, VImageDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedIntensityPointSetsPerStage, movingIntensityPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); typename BSplineDisplacementFieldTransformType::Pointer outputDisplacementFieldTransform = registrationMethod->GetModifiableTransform(); // Create the transform adaptors typename DisplacementFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; outputDisplacementFieldTransform->SetSplineOrder( this->m_TransformMethods[currentStageNumber].m_SplineOrder ); // Create the transform adaptors for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename itk::ImageBase::Pointer shrunkSpace= this->GetShrinkImageOutputInformation( virtualDomainImage.GetPointer(), shrinkFactorsPerDimensionForAllLevels[level] ); typedef itk::BSplineSmoothingOnUpdateDisplacementFieldTransformParametersAdaptor< BSplineDisplacementFieldTransformType> BSplineDisplacementFieldTransformAdaptorType; typename BSplineDisplacementFieldTransformAdaptorType::Pointer bsplineFieldTransformAdaptor = BSplineDisplacementFieldTransformAdaptorType::New(); bsplineFieldTransformAdaptor->SetRequiredSpacing( shrunkSpace->GetSpacing() ); bsplineFieldTransformAdaptor->SetRequiredSize( shrunkSpace->GetLargestPossibleRegion().GetSize() ); bsplineFieldTransformAdaptor->SetRequiredDirection( shrunkSpace->GetDirection() ); bsplineFieldTransformAdaptor->SetRequiredOrigin( shrunkSpace->GetOrigin() ); bsplineFieldTransformAdaptor->SetTransform( outputDisplacementFieldTransform ); // A good heuristic is to RealType the b-spline mesh resolution at each level typename BSplineDisplacementFieldTransformType::ArrayType newUpdateMeshSize = updateMeshSize; typename BSplineDisplacementFieldTransformType::ArrayType newTotalMeshSize = totalMeshSize; for( unsigned int d = 0; d < VImageDimension; d++ ) { newUpdateMeshSize[d] = newUpdateMeshSize[d] << ( level ); newTotalMeshSize[d] = newTotalMeshSize[d] << ( level ); } bsplineFieldTransformAdaptor->SetMeshSizeForTheUpdateField( newUpdateMeshSize ); bsplineFieldTransformAdaptor->SetMeshSizeForTheTotalField( newTotalMeshSize ); adaptors.push_back( bsplineFieldTransformAdaptor.GetPointer() ); } registrationMethod->SetDownsampleImagesForMetricDerivatives( true ); registrationMethod->SetAverageMidPointGradients( false ); registrationMethod->SetNumberOfLevels( numberOfLevels ); typename DisplacementFieldRegistrationType::NumberOfIterationsArrayType numberOfIterationsPerLevel; numberOfIterationsPerLevel.SetSize( numberOfLevels ); for( unsigned int d = 0; d < numberOfLevels; d++ ) { numberOfIterationsPerLevel[d] = currentStageIterations[d]; } registrationMethod->SetLearningRate( learningRate ); registrationMethod->SetConvergenceThreshold( convergenceThreshold ); registrationMethod->SetConvergenceWindowSize( convergenceWindowSize ); registrationMethod->SetNumberOfIterationsPerLevel( numberOfIterationsPerLevel ); registrationMethod->SetTransformParametersAdaptorsPerLevel( adaptors ); outputDisplacementFieldTransform->SetDisplacementField( displacementField ); outputDisplacementFieldTransform->SetInverseDisplacementField( inverseDisplacementField ); typedef antsRegistrationCommandIterationUpdate DisplacementFieldCommandType; typename DisplacementFieldCommandType::Pointer displacementFieldRegistrationObserver = DisplacementFieldCommandType::New(); displacementFieldRegistrationObserver->SetLogStream( *this->m_LogStream ); displacementFieldRegistrationObserver->SetNumberOfIterations( currentStageIterations ); registrationMethod->AddObserver( itk::IterationEvent(), displacementFieldRegistrationObserver ); registrationMethod->AddObserver( itk::InitializeEvent(), displacementFieldRegistrationObserver ); try { this->Logger() << std::endl << "*** Running B-spline SyN registration (updateMeshSizeAtBaseLevel = " << updateMeshSize << ", totalMeshSizeAtBaseLevel = " << totalMeshSize << ") ***" << std::endl << std::endl; displacementFieldRegistrationObserver->Execute( registrationMethod, itk::StartEvent() ); registrationMethod->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputDisplacementFieldTransform ); } this->m_AllPreviousTransformsAreLinear = false; } break; case TimeVaryingVelocityField: { typedef itk::Vector VectorType; VectorType zeroVector( 0.0 ); // Determine the parameters (size, spacing, etc) for the time-varying velocity field typedef itk::Image TimeVaryingVelocityFieldType; typename TimeVaryingVelocityFieldType::IndexType velocityFieldIndex; typename TimeVaryingVelocityFieldType::SizeType velocityFieldSize; typename TimeVaryingVelocityFieldType::PointType velocityFieldOrigin; typename TimeVaryingVelocityFieldType::SpacingType velocityFieldSpacing; typename TimeVaryingVelocityFieldType::DirectionType velocityFieldDirection; typename TimeVaryingVelocityFieldType::RegionType velocityFieldRegion; typename ImageType::IndexType fixedImageIndex = preprocessedFixedImagesPerStage[0]->GetBufferedRegion().GetIndex(); typename ImageType::SizeType fixedImageSize = preprocessedFixedImagesPerStage[0]->GetBufferedRegion().GetSize(); typename ImageType::PointType fixedImageOrigin = preprocessedFixedImagesPerStage[0]->GetOrigin(); typename ImageType::SpacingType fixedImageSpacing = preprocessedFixedImagesPerStage[0]->GetSpacing(); typename ImageType::DirectionType fixedImageDirection = preprocessedFixedImagesPerStage[0]->GetDirection(); unsigned int numberOfTimeIndices = this->m_TransformMethods[currentStageNumber].m_NumberOfTimeIndices; velocityFieldIndex.Fill( 0 ); velocityFieldSize.Fill( numberOfTimeIndices ); velocityFieldOrigin.Fill( 0.0 ); velocityFieldSpacing.Fill( 1.0 ); velocityFieldDirection.SetIdentity(); for( unsigned int i = 0; i < VImageDimension; i++ ) { velocityFieldIndex[i] = fixedImageIndex[i]; velocityFieldSize[i] = fixedImageSize[i]; velocityFieldOrigin[i] = fixedImageOrigin[i]; velocityFieldSpacing[i] = fixedImageSpacing[i]; for( unsigned int j = 0; j < VImageDimension; j++ ) { velocityFieldDirection[i][j] = fixedImageDirection[i][j]; } } velocityFieldRegion.SetSize( velocityFieldSize ); velocityFieldRegion.SetIndex( velocityFieldIndex ); typename TimeVaryingVelocityFieldType::Pointer velocityField = AllocImage(velocityFieldRegion, velocityFieldSpacing, velocityFieldOrigin, velocityFieldDirection, zeroVector); typename DisplacementFieldType::Pointer displacementField = AllocImage( preprocessedFixedImagesPerStage[0]->GetBufferedRegion(), fixedImageSpacing, fixedImageOrigin, fixedImageDirection, zeroVector ); typename DisplacementFieldType::Pointer inverseDisplacementField = AllocImage( preprocessedFixedImagesPerStage[0]->GetBufferedRegion(), fixedImageSpacing, fixedImageOrigin, fixedImageDirection, zeroVector ); // Extract parameters RealType varianceForUpdateField = this->m_TransformMethods[currentStageNumber].m_UpdateFieldVarianceInVarianceSpace; RealType varianceForUpdateFieldTime = this->m_TransformMethods[currentStageNumber].m_UpdateFieldTimeSigma; RealType varianceForTotalField = this->m_TransformMethods[currentStageNumber].m_TotalFieldVarianceInVarianceSpace; RealType varianceForTotalFieldTime = this->m_TransformMethods[currentStageNumber].m_TotalFieldTimeSigma; typedef itk::GaussianSmoothingOnUpdateTimeVaryingVelocityFieldTransform TimeVaryingVelocityFieldOutputTransformType; typedef itk::TimeVaryingVelocityFieldImageRegistrationMethodv4 VelocityFieldRegistrationType; typename VelocityFieldRegistrationType::Pointer velocityFieldRegistration = VelocityFieldRegistrationType::New(); if( this->m_RestrictDeformationOptimizerWeights.size() > currentStageNumber ) { if( this->m_RestrictDeformationOptimizerWeights[currentStageNumber].size() == VImageDimension ) { typename VelocityFieldRegistrationType::OptimizerWeightsType optimizerWeights( VImageDimension ); for( unsigned int d = 0; d < VImageDimension; d++ ) { optimizerWeights[d] = this->m_RestrictDeformationOptimizerWeights[currentStageNumber][d]; } velocityFieldRegistration->SetOptimizerWeights( optimizerWeights ); } } typedef typename VelocityFieldRegistrationType::OutputTransformType OutputTransformType; typename OutputTransformType::Pointer outputTransform = velocityFieldRegistration->GetModifiableTransform(); for( unsigned int n = 0; n < stageMetricList.size(); n++ ) { if( !this->IsPointSetMetric( stageMetricList[n].m_MetricType ) ) { velocityFieldRegistration->SetFixedImage( n, preprocessedFixedImagesPerStage[n] ); velocityFieldRegistration->SetMovingImage( n, preprocessedMovingImagesPerStage[n] ); } else { velocityFieldRegistration->SetFixedPointSet( n, stageMetricList[n].m_FixedLabeledPointSet.GetPointer() ); velocityFieldRegistration->SetMovingPointSet( n, stageMetricList[n].m_MovingLabeledPointSet.GetPointer() ); } } if( useMultiMetric ) { velocityFieldRegistration->SetMetric( multiMetric ); } else { velocityFieldRegistration->SetMetric( singleMetric ); } if( this->m_CompositeTransform->GetNumberOfTransforms() > 0 ) { velocityFieldRegistration->SetMovingInitialTransform( this->m_CompositeTransform ); } if( this->m_FixedInitialTransform->GetNumberOfTransforms() > 0 ) { velocityFieldRegistration->SetFixedInitialTransform( this->m_FixedInitialTransform ); } velocityFieldRegistration->SetNumberOfLevels( numberOfLevels ); velocityFieldRegistration->SetMetricSamplingStrategy( static_cast( metricSamplingStrategy ) ); velocityFieldRegistration->SetMetricSamplingPercentage( samplingPercentage ); velocityFieldRegistration->SetLearningRate( learningRate ); velocityFieldRegistration->SetConvergenceThreshold( convergenceThreshold ); velocityFieldRegistration->SetConvergenceWindowSize( convergenceWindowSize ); outputTransform->SetGaussianSpatialSmoothingVarianceForTheTotalField( varianceForTotalField ); outputTransform->SetGaussianSpatialSmoothingVarianceForTheUpdateField( varianceForUpdateField ); outputTransform->SetGaussianTemporalSmoothingVarianceForTheTotalField( varianceForTotalFieldTime ); outputTransform->SetGaussianTemporalSmoothingVarianceForTheUpdateField( varianceForUpdateFieldTime ); outputTransform->SetTimeVaryingVelocityField( velocityField ); outputTransform->SetLowerTimeBound( 0.0 ); outputTransform->SetUpperTimeBound( 1.0 ); outputTransform->SetDisplacementField( displacementField ); outputTransform->SetInverseDisplacementField( inverseDisplacementField ); typename VelocityFieldRegistrationType::NumberOfIterationsArrayType numberOfIterationsPerLevel; numberOfIterationsPerLevel.SetSize( numberOfLevels ); for( unsigned int d = 0; d < numberOfLevels; d++ ) { numberOfIterationsPerLevel[d] = currentStageIterations[d]; } velocityFieldRegistration->SetNumberOfIterationsPerLevel( numberOfIterationsPerLevel ); for( unsigned int level = 0; level < numberOfLevels; ++level ) { velocityFieldRegistration->SetShrinkFactorsPerDimension( level, shrinkFactorsPerDimensionForAllLevels[level] ); } velocityFieldRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); velocityFieldRegistration->SetSmoothingSigmasAreSpecifiedInPhysicalUnits( this->m_SmoothingSigmasAreInPhysicalUnits[currentStageNumber] ); typedef itk::TimeVaryingVelocityFieldTransformParametersAdaptor VelocityFieldTransformAdaptorType; typename VelocityFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename itk::ImageBase::Pointer shrunkSpace= this->GetShrinkImageOutputInformation( virtualDomainImage.GetPointer(), shrinkFactorsPerDimensionForAllLevels[level] ); // Although we shrink the images for the given levels, // we keep the size in time the same velocityFieldSize.Fill( numberOfTimeIndices ); velocityFieldOrigin.Fill( 0.0 ); velocityFieldSpacing.Fill( 1.0 ); velocityFieldDirection.SetIdentity(); fixedImageSize = shrunkSpace->GetLargestPossibleRegion().GetSize(); fixedImageOrigin = shrunkSpace->GetOrigin(); fixedImageSpacing = shrunkSpace->GetSpacing(); fixedImageDirection = shrunkSpace->GetDirection(); for( unsigned int i = 0; i < VImageDimension; i++ ) { velocityFieldSize[i] = fixedImageSize[i]; velocityFieldOrigin[i] = fixedImageOrigin[i]; velocityFieldSpacing[i] = fixedImageSpacing[i]; for( unsigned int j = 0; j < VImageDimension; j++ ) { velocityFieldDirection[i][j] = fixedImageDirection[i][j]; } } typename VelocityFieldTransformAdaptorType::Pointer fieldTransformAdaptor = VelocityFieldTransformAdaptorType::New(); fieldTransformAdaptor->SetRequiredSpacing( velocityFieldSpacing ); fieldTransformAdaptor->SetRequiredSize( velocityFieldSize ); fieldTransformAdaptor->SetRequiredDirection( velocityFieldDirection ); fieldTransformAdaptor->SetRequiredOrigin( velocityFieldOrigin ); adaptors.push_back( fieldTransformAdaptor.GetPointer() ); } velocityFieldRegistration->SetTransformParametersAdaptorsPerLevel( adaptors ); typedef antsRegistrationCommandIterationUpdate VelocityFieldCommandType; typename VelocityFieldCommandType::Pointer velocityFieldRegistrationObserver = VelocityFieldCommandType::New(); velocityFieldRegistrationObserver->SetLogStream( *this->m_LogStream ); velocityFieldRegistrationObserver->SetNumberOfIterations( currentStageIterations ); velocityFieldRegistration->AddObserver( itk::IterationEvent(), velocityFieldRegistrationObserver ); velocityFieldRegistration->AddObserver( itk::InitializeEvent(), velocityFieldRegistrationObserver ); try { this->Logger() << std::endl << "*** Running time-varying velocity field registration (varianceForUpdateField = " << varianceForUpdateField << ", varianceForTotalField = " << varianceForTotalField << ", varianceForUpdateFieldTime = " << varianceForUpdateFieldTime << ", varianceForTotalFieldTime = " << varianceForTotalFieldTime << ") ***" << std::endl << std::endl; velocityFieldRegistrationObserver->Execute( velocityFieldRegistration, itk::StartEvent() ); velocityFieldRegistration->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputTransform ); this->m_AllPreviousTransformsAreLinear = false; } break; case TimeVaryingBSplineVelocityField: { typedef itk::Vector VectorType; VectorType zeroVector( 0.0 ); // Determine the parameters (size, spacing, etc) for the time-varying velocity field control point lattice const std::vector & meshSize = this->m_TransformMethods[currentStageNumber].m_VelocityFieldMeshSize; if( meshSize.size() != VImageDimension + 1 ) { this->Logger() << "The transform domain mesh size does not have the correct number of elements." << "For image dimension = " << VImageDimension << ", you need " << VImageDimension + 1 << "elements. " << std::endl; return EXIT_FAILURE; } unsigned int numberOfTimePointSamples = this->m_TransformMethods[currentStageNumber].m_NumberOfTimePointSamples; unsigned int splineOrder = this->m_TransformMethods[currentStageNumber].m_SplineOrder; typedef itk::Image TimeVaryingVelocityFieldControlPointLatticeType; typename ImageType::SizeType fixedImageSize = virtualDomainImage->GetBufferedRegion().GetSize(); typename ImageType::PointType fixedImageOrigin = virtualDomainImage->GetOrigin(); typename ImageType::SpacingType fixedImageSpacing = virtualDomainImage->GetSpacing(); typename ImageType::DirectionType fixedImageDirection = virtualDomainImage->GetDirection(); typename TimeVaryingVelocityFieldControlPointLatticeType::SizeType transformDomainMeshSize; typename TimeVaryingVelocityFieldControlPointLatticeType::PointType transformDomainOrigin; typename TimeVaryingVelocityFieldControlPointLatticeType::SpacingType transformDomainSpacing; typename TimeVaryingVelocityFieldControlPointLatticeType::SizeType transformDomainSize; typename TimeVaryingVelocityFieldControlPointLatticeType::DirectionType transformDomainDirection; transformDomainDirection.SetIdentity(); transformDomainOrigin.Fill( 0.0 ); transformDomainSpacing.Fill( 1.0 ); transformDomainSize.Fill( 2 ); for( unsigned int i = 0; i < VImageDimension; i++ ) { transformDomainOrigin[i] = fixedImageOrigin[i]; transformDomainMeshSize[i] = 3; transformDomainSpacing[i] = fixedImageSpacing[i]; transformDomainSize[i] = fixedImageSize[i]; for( unsigned int j = 0; j < VImageDimension; j++ ) { transformDomainDirection[i][j] = fixedImageDirection[i][j]; } } for( unsigned int i = 0; i < meshSize.size(); i++ ) { transformDomainMeshSize[i] = meshSize[i]; } typename TimeVaryingVelocityFieldControlPointLatticeType::SizeType initialTransformDomainMeshSize = transformDomainMeshSize; typedef itk::TimeVaryingBSplineVelocityFieldTransform TimeVaryingBSplineVelocityFieldOutputTransformType; if( stageMetricList[0].m_MetricType != IGDM ) { typedef itk::TimeVaryingBSplineVelocityFieldImageRegistrationMethod VelocityFieldRegistrationType; typename VelocityFieldRegistrationType::Pointer velocityFieldRegistration = this->PrepareRegistrationMethod( this->m_CompositeTransform, currentStageNumber, VImageDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedLabeledPointSetsPerStage, movingLabeledPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); typename TimeVaryingBSplineVelocityFieldOutputTransformType::Pointer outputTransform = velocityFieldRegistration->GetModifiableTransform(); if( useMultiMetric ) { velocityFieldRegistration->SetMetric( multiMetric ); } else { velocityFieldRegistration->SetMetric( singleMetric ); } velocityFieldRegistration->SetNumberOfTimePointSamples( numberOfTimePointSamples ); velocityFieldRegistration->SetLearningRate( learningRate ); velocityFieldRegistration->SetConvergenceThreshold( convergenceThreshold ); velocityFieldRegistration->SetConvergenceWindowSize( convergenceWindowSize ); outputTransform->SetSplineOrder( splineOrder ); outputTransform->SetLowerTimeBound( 0.0 ); outputTransform->SetUpperTimeBound( 1.0 ); typedef itk::TimeVaryingBSplineVelocityFieldTransformParametersAdaptor VelocityFieldTransformAdaptorType; typename VelocityFieldTransformAdaptorType::Pointer initialFieldTransformAdaptor = VelocityFieldTransformAdaptorType::New(); initialFieldTransformAdaptor->SetTransform( outputTransform ); initialFieldTransformAdaptor->SetRequiredTransformDomainOrigin( transformDomainOrigin ); initialFieldTransformAdaptor->SetRequiredTransformDomainSpacing( transformDomainSpacing ); initialFieldTransformAdaptor->SetRequiredTransformDomainSize( transformDomainSize ); initialFieldTransformAdaptor->SetRequiredTransformDomainMeshSize( transformDomainMeshSize ); initialFieldTransformAdaptor->SetRequiredTransformDomainDirection( transformDomainDirection ); typename TimeVaryingVelocityFieldControlPointLatticeType::Pointer velocityFieldLattice = AllocImage ( initialFieldTransformAdaptor->GetRequiredControlPointLatticeSize(), initialFieldTransformAdaptor->GetRequiredControlPointLatticeSpacing(), initialFieldTransformAdaptor->GetRequiredControlPointLatticeOrigin(), initialFieldTransformAdaptor->GetRequiredControlPointLatticeDirection(), zeroVector ); typename TimeVaryingBSplineVelocityFieldOutputTransformType::VelocityFieldPointType sampledVelocityFieldOrigin; typename TimeVaryingBSplineVelocityFieldOutputTransformType::VelocityFieldSpacingType sampledVelocityFieldSpacing; typename TimeVaryingBSplineVelocityFieldOutputTransformType::VelocityFieldSizeType sampledVelocityFieldSize; typename TimeVaryingBSplineVelocityFieldOutputTransformType::VelocityFieldDirectionType sampledVelocityFieldDirection; sampledVelocityFieldOrigin.Fill( 0.0 ); sampledVelocityFieldSpacing.Fill( 1.0 ); sampledVelocityFieldSize.Fill( numberOfTimePointSamples ); sampledVelocityFieldDirection.SetIdentity(); for( unsigned int i = 0; i < VImageDimension; i++ ) { sampledVelocityFieldOrigin[i] = virtualDomainImage->GetOrigin()[i]; sampledVelocityFieldSpacing[i] = virtualDomainImage->GetSpacing()[i]; sampledVelocityFieldSize[i] = virtualDomainImage->GetRequestedRegion().GetSize()[i]; for( unsigned int j = 0; j < VImageDimension; j++ ) { sampledVelocityFieldDirection[i][j] = virtualDomainImage->GetDirection()[i][j]; } } outputTransform->SetTimeVaryingVelocityFieldControlPointLattice( velocityFieldLattice ); outputTransform->SetVelocityFieldOrigin( sampledVelocityFieldOrigin ); outputTransform->SetVelocityFieldDirection( sampledVelocityFieldDirection ); outputTransform->SetVelocityFieldSpacing( sampledVelocityFieldSpacing ); outputTransform->SetVelocityFieldSize( sampledVelocityFieldSize ); typename VelocityFieldRegistrationType::NumberOfIterationsArrayType numberOfIterationsPerLevel; numberOfIterationsPerLevel.SetSize( numberOfLevels ); for( unsigned int d = 0; d < numberOfLevels; d++ ) { numberOfIterationsPerLevel[d] = currentStageIterations[d]; } velocityFieldRegistration->SetNumberOfIterationsPerLevel( numberOfIterationsPerLevel ); for( unsigned int level = 0; level < numberOfLevels; ++level ) { velocityFieldRegistration->SetShrinkFactorsPerDimension( level, shrinkFactorsPerDimensionForAllLevels[level] ); } velocityFieldRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); velocityFieldRegistration->SetSmoothingSigmasAreSpecifiedInPhysicalUnits( this->m_SmoothingSigmasAreInPhysicalUnits[currentStageNumber] ); typename VelocityFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename VelocityFieldTransformAdaptorType::Pointer fieldTransformAdaptor = VelocityFieldTransformAdaptorType::New(); fieldTransformAdaptor->SetTransform( outputTransform ); fieldTransformAdaptor->SetRequiredTransformDomainOrigin( transformDomainOrigin ); fieldTransformAdaptor->SetRequiredTransformDomainMeshSize( transformDomainMeshSize ); fieldTransformAdaptor->SetRequiredTransformDomainSpacing( transformDomainSpacing ); fieldTransformAdaptor->SetRequiredTransformDomainSize( transformDomainSize ); adaptors.push_back( fieldTransformAdaptor.GetPointer() ); for( unsigned int i = 0; i <= VImageDimension; i++ ) { transformDomainMeshSize[i] <<= 1; } } velocityFieldRegistration->SetTransformParametersAdaptorsPerLevel( adaptors ); typedef antsRegistrationCommandIterationUpdate VelocityFieldCommandType; typename VelocityFieldCommandType::Pointer velocityFieldRegistrationObserver = VelocityFieldCommandType::New(); velocityFieldRegistrationObserver->SetLogStream( *this->m_LogStream ); velocityFieldRegistrationObserver->SetNumberOfIterations( currentStageIterations ); velocityFieldRegistration->AddObserver( itk::IterationEvent(), velocityFieldRegistrationObserver ); velocityFieldRegistration->AddObserver( itk::InitializeEvent(), velocityFieldRegistrationObserver ); try { this->Logger() << std::endl << "*** Running time-varying b-spline velocity field registration (initial mesh size = " << initialTransformDomainMeshSize << ") ***" << std::endl << std::endl; velocityFieldRegistrationObserver->Execute( velocityFieldRegistration, itk::StartEvent() ); velocityFieldRegistration->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputTransform ); } else { typedef itk::TimeVaryingBSplineVelocityFieldImageRegistrationMethod VelocityFieldRegistrationType; typename VelocityFieldRegistrationType::Pointer velocityFieldRegistration = this->PrepareRegistrationMethod( this->m_CompositeTransform, currentStageNumber, VImageDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedIntensityPointSetsPerStage, movingIntensityPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); typename TimeVaryingBSplineVelocityFieldOutputTransformType::Pointer outputTransform = velocityFieldRegistration->GetModifiableTransform(); if( useMultiMetric ) { velocityFieldRegistration->SetMetric( multiMetric ); } else { velocityFieldRegistration->SetMetric( singleMetric ); } velocityFieldRegistration->SetNumberOfTimePointSamples( numberOfTimePointSamples ); velocityFieldRegistration->SetLearningRate( learningRate ); velocityFieldRegistration->SetConvergenceThreshold( convergenceThreshold ); velocityFieldRegistration->SetConvergenceWindowSize( convergenceWindowSize ); outputTransform->SetSplineOrder( splineOrder ); outputTransform->SetLowerTimeBound( 0.0 ); outputTransform->SetUpperTimeBound( 1.0 ); typedef itk::TimeVaryingBSplineVelocityFieldTransformParametersAdaptor VelocityFieldTransformAdaptorType; typename VelocityFieldTransformAdaptorType::Pointer initialFieldTransformAdaptor = VelocityFieldTransformAdaptorType::New(); initialFieldTransformAdaptor->SetTransform( outputTransform ); initialFieldTransformAdaptor->SetRequiredTransformDomainOrigin( transformDomainOrigin ); initialFieldTransformAdaptor->SetRequiredTransformDomainSpacing( transformDomainSpacing ); initialFieldTransformAdaptor->SetRequiredTransformDomainSize( transformDomainSize ); initialFieldTransformAdaptor->SetRequiredTransformDomainMeshSize( transformDomainMeshSize ); initialFieldTransformAdaptor->SetRequiredTransformDomainDirection( transformDomainDirection ); typename TimeVaryingVelocityFieldControlPointLatticeType::Pointer velocityFieldLattice = AllocImage ( initialFieldTransformAdaptor->GetRequiredControlPointLatticeSize(), initialFieldTransformAdaptor->GetRequiredControlPointLatticeSpacing(), initialFieldTransformAdaptor->GetRequiredControlPointLatticeOrigin(), initialFieldTransformAdaptor->GetRequiredControlPointLatticeDirection(), zeroVector ); typename TimeVaryingBSplineVelocityFieldOutputTransformType::VelocityFieldPointType sampledVelocityFieldOrigin; typename TimeVaryingBSplineVelocityFieldOutputTransformType::VelocityFieldSpacingType sampledVelocityFieldSpacing; typename TimeVaryingBSplineVelocityFieldOutputTransformType::VelocityFieldSizeType sampledVelocityFieldSize; typename TimeVaryingBSplineVelocityFieldOutputTransformType::VelocityFieldDirectionType sampledVelocityFieldDirection; sampledVelocityFieldOrigin.Fill( 0.0 ); sampledVelocityFieldSpacing.Fill( 1.0 ); sampledVelocityFieldSize.Fill( numberOfTimePointSamples ); sampledVelocityFieldDirection.SetIdentity(); for( unsigned int i = 0; i < VImageDimension; i++ ) { sampledVelocityFieldOrigin[i] = virtualDomainImage->GetOrigin()[i]; sampledVelocityFieldSpacing[i] = virtualDomainImage->GetSpacing()[i]; sampledVelocityFieldSize[i] = virtualDomainImage->GetRequestedRegion().GetSize()[i]; for( unsigned int j = 0; j < VImageDimension; j++ ) { sampledVelocityFieldDirection[i][j] = virtualDomainImage->GetDirection()[i][j]; } } outputTransform->SetTimeVaryingVelocityFieldControlPointLattice( velocityFieldLattice ); outputTransform->SetVelocityFieldOrigin( sampledVelocityFieldOrigin ); outputTransform->SetVelocityFieldDirection( sampledVelocityFieldDirection ); outputTransform->SetVelocityFieldSpacing( sampledVelocityFieldSpacing ); outputTransform->SetVelocityFieldSize( sampledVelocityFieldSize ); typename VelocityFieldRegistrationType::NumberOfIterationsArrayType numberOfIterationsPerLevel; numberOfIterationsPerLevel.SetSize( numberOfLevels ); for( unsigned int d = 0; d < numberOfLevels; d++ ) { numberOfIterationsPerLevel[d] = currentStageIterations[d]; } velocityFieldRegistration->SetNumberOfIterationsPerLevel( numberOfIterationsPerLevel ); for( unsigned int level = 0; level < numberOfLevels; ++level ) { velocityFieldRegistration->SetShrinkFactorsPerDimension( level, shrinkFactorsPerDimensionForAllLevels[level] ); } velocityFieldRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); velocityFieldRegistration->SetSmoothingSigmasAreSpecifiedInPhysicalUnits( this->m_SmoothingSigmasAreInPhysicalUnits[currentStageNumber] ); typename VelocityFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename VelocityFieldTransformAdaptorType::Pointer fieldTransformAdaptor = VelocityFieldTransformAdaptorType::New(); fieldTransformAdaptor->SetTransform( outputTransform ); fieldTransformAdaptor->SetRequiredTransformDomainOrigin( transformDomainOrigin ); fieldTransformAdaptor->SetRequiredTransformDomainMeshSize( transformDomainMeshSize ); fieldTransformAdaptor->SetRequiredTransformDomainSpacing( transformDomainSpacing ); fieldTransformAdaptor->SetRequiredTransformDomainSize( transformDomainSize ); adaptors.push_back( fieldTransformAdaptor.GetPointer() ); for( unsigned int i = 0; i <= VImageDimension; i++ ) { transformDomainMeshSize[i] <<= 1; } } velocityFieldRegistration->SetTransformParametersAdaptorsPerLevel( adaptors ); typedef antsRegistrationCommandIterationUpdate VelocityFieldCommandType; typename VelocityFieldCommandType::Pointer velocityFieldRegistrationObserver = VelocityFieldCommandType::New(); velocityFieldRegistrationObserver->SetLogStream( *this->m_LogStream ); velocityFieldRegistrationObserver->SetNumberOfIterations( currentStageIterations ); velocityFieldRegistration->AddObserver( itk::IterationEvent(), velocityFieldRegistrationObserver ); velocityFieldRegistration->AddObserver( itk::InitializeEvent(), velocityFieldRegistrationObserver ); try { this->Logger() << std::endl << "*** Running time-varying b-spline velocity field registration (initial mesh size = " << initialTransformDomainMeshSize << ") ***" << std::endl << std::endl; velocityFieldRegistrationObserver->Execute( velocityFieldRegistration, itk::StartEvent() ); velocityFieldRegistration->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputTransform ); } this->m_AllPreviousTransformsAreLinear = false; } break; case Exponential: { typedef itk::Vector VectorType; VectorType zeroVector( 0.0 ); typedef itk::Image ConstantVelocityFieldType; typename ConstantVelocityFieldType::Pointer constantVelocityField = AllocImage( preprocessedFixedImagesPerStage[0], zeroVector ); typedef itk::GaussianExponentialDiffeomorphicTransform GaussianDisplacementFieldTransformType; typedef itk::ImageRegistrationMethodv4 DisplacementFieldRegistrationType; typename DisplacementFieldRegistrationType::Pointer displacementFieldRegistration = DisplacementFieldRegistrationType::New(); if( this->m_RestrictDeformationOptimizerWeights.size() > currentStageNumber ) { if( this->m_RestrictDeformationOptimizerWeights[currentStageNumber].size() == VImageDimension ) { typename DisplacementFieldRegistrationType::OptimizerWeightsType optimizerWeights( VImageDimension ); for( unsigned int d = 0; d < VImageDimension; d++ ) { optimizerWeights[d] = this->m_RestrictDeformationOptimizerWeights[currentStageNumber][d]; } displacementFieldRegistration->SetOptimizerWeights( optimizerWeights ); } } typename GaussianDisplacementFieldTransformType::Pointer outputDisplacementFieldTransform = displacementFieldRegistration->GetModifiableTransform(); // Create the transform adaptors typedef itk::GaussianExponentialDiffeomorphicTransformParametersAdaptor DisplacementFieldTransformAdaptorType; typename DisplacementFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; // Extract parameters RealType varianceForUpdateField = this->m_TransformMethods[currentStageNumber].m_UpdateFieldVarianceInVarianceSpace; RealType varianceForVelocityField = this->m_TransformMethods[currentStageNumber].m_VelocityFieldVarianceInVarianceSpace; unsigned int numberOfIntegrationSteps = this->m_TransformMethods[currentStageNumber].m_NumberOfTimeIndices; outputDisplacementFieldTransform->SetGaussianSmoothingVarianceForTheUpdateField( varianceForUpdateField ); outputDisplacementFieldTransform->SetGaussianSmoothingVarianceForTheConstantVelocityField( varianceForVelocityField ); if( numberOfIntegrationSteps == 0 ) { outputDisplacementFieldTransform->SetCalculateNumberOfIntegrationStepsAutomatically( true ); } else { outputDisplacementFieldTransform->SetNumberOfIntegrationSteps( numberOfIntegrationSteps ); } outputDisplacementFieldTransform->SetConstantVelocityField( constantVelocityField ); outputDisplacementFieldTransform->SetDisplacementField( constantVelocityField ); // Create the transform adaptors // For the gaussian displacement field, the specified variances are in image spacing terms // and, in normal practice, we typically don't change these values at each level. However, // if the user wishes to add that option, they can use the class // GaussianSmoothingOnUpdateDisplacementFieldTransformAdaptor for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename itk::ImageBase::Pointer shrunkSpace= this->GetShrinkImageOutputInformation( virtualDomainImage.GetPointer(), shrinkFactorsPerDimensionForAllLevels[level] ); typename DisplacementFieldTransformAdaptorType::Pointer fieldTransformAdaptor = DisplacementFieldTransformAdaptorType::New(); fieldTransformAdaptor->SetRequiredSpacing( shrunkSpace->GetSpacing() ); fieldTransformAdaptor->SetRequiredSize( shrunkSpace->GetLargestPossibleRegion().GetSize() ); fieldTransformAdaptor->SetRequiredDirection( shrunkSpace->GetDirection() ); fieldTransformAdaptor->SetRequiredOrigin( shrunkSpace->GetOrigin() ); fieldTransformAdaptor->SetTransform( outputDisplacementFieldTransform ); fieldTransformAdaptor->SetGaussianSmoothingVarianceForTheUpdateField( varianceForUpdateField ); fieldTransformAdaptor->SetGaussianSmoothingVarianceForTheConstantVelocityField( varianceForVelocityField ); adaptors.push_back( fieldTransformAdaptor.GetPointer() ); } for( unsigned int n = 0; n < stageMetricList.size(); n++ ) { if( !this->IsPointSetMetric( stageMetricList[n].m_MetricType ) ) { displacementFieldRegistration->SetFixedImage( n, preprocessedFixedImagesPerStage[n] ); displacementFieldRegistration->SetMovingImage( n, preprocessedMovingImagesPerStage[n] ); } else { displacementFieldRegistration->SetFixedPointSet( n, stageMetricList[n].m_FixedLabeledPointSet.GetPointer() ); displacementFieldRegistration->SetMovingPointSet( n, stageMetricList[n].m_MovingLabeledPointSet.GetPointer() ); } } if( useMultiMetric ) { displacementFieldRegistration->SetMetric( multiMetric ); } else { displacementFieldRegistration->SetMetric( singleMetric ); } displacementFieldRegistration->SetNumberOfLevels( numberOfLevels ); for( unsigned int level = 0; level < numberOfLevels; ++level ) { displacementFieldRegistration->SetShrinkFactorsPerDimension( level, shrinkFactorsPerDimensionForAllLevels[level] ); } displacementFieldRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); displacementFieldRegistration->SetMetricSamplingStrategy( static_cast( metricSamplingStrategy ) ); displacementFieldRegistration->SetMetricSamplingPercentage( samplingPercentage ); displacementFieldRegistration->SetOptimizer( optimizer2 ); displacementFieldRegistration->SetTransformParametersAdaptorsPerLevel( adaptors ); if( this->m_CompositeTransform->GetNumberOfTransforms() > 0 ) { displacementFieldRegistration->SetMovingInitialTransform( this->m_CompositeTransform ); } if( this->m_FixedInitialTransform->GetNumberOfTransforms() > 0 ) { displacementFieldRegistration->SetFixedInitialTransform( this->m_FixedInitialTransform ); } typedef antsRegistrationCommandIterationUpdate DisplacementFieldCommandType; typename DisplacementFieldCommandType::Pointer displacementFieldRegistrationObserver = DisplacementFieldCommandType::New(); displacementFieldRegistrationObserver->SetLogStream(*this->m_LogStream ); displacementFieldRegistrationObserver->SetNumberOfIterations( currentStageIterations ); displacementFieldRegistration->AddObserver( itk::IterationEvent(), displacementFieldRegistrationObserver ); displacementFieldRegistration->AddObserver( itk::InitializeEvent(), displacementFieldRegistrationObserver ); try { this->Logger() << std::endl << "*** Running gaussian exponential field registration (varianceForUpdateField = " << varianceForUpdateField << ", varianceForVelocityField = " << varianceForVelocityField << ") ***" << std::endl << std::endl; displacementFieldRegistrationObserver->Execute( displacementFieldRegistration, itk::StartEvent() ); displacementFieldRegistration->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputDisplacementFieldTransform ); this->m_AllPreviousTransformsAreLinear = false; } break; case BSplineExponential: { typedef itk::Vector VectorType; VectorType zeroVector( 0.0 ); typedef itk::Image ConstantVelocityFieldType; typename ConstantVelocityFieldType::Pointer constantVelocityField = AllocImage( preprocessedFixedImagesPerStage[0], zeroVector ); typedef itk::BSplineExponentialDiffeomorphicTransform BSplineDisplacementFieldTransformType; typedef itk::ImageRegistrationMethodv4 DisplacementFieldRegistrationType; typename DisplacementFieldRegistrationType::Pointer displacementFieldRegistration = DisplacementFieldRegistrationType::New(); if( this->m_RestrictDeformationOptimizerWeights.size() > currentStageNumber ) { if( this->m_RestrictDeformationOptimizerWeights[currentStageNumber].size() == VImageDimension ) { typename DisplacementFieldRegistrationType::OptimizerWeightsType optimizerWeights( VImageDimension ); for( unsigned int d = 0; d < VImageDimension; d++ ) { optimizerWeights[d] = this->m_RestrictDeformationOptimizerWeights[currentStageNumber][d]; } displacementFieldRegistration->SetOptimizerWeights( optimizerWeights ); } } typename BSplineDisplacementFieldTransformType::Pointer outputDisplacementFieldTransform = displacementFieldRegistration->GetModifiableTransform(); // Create the transform adaptors typename DisplacementFieldRegistrationType::TransformParametersAdaptorsContainerType adaptors; // Extract parameters const std::vector & meshSizeForTheUpdateField = this->m_TransformMethods[currentStageNumber].m_UpdateFieldMeshSizeAtBaseLevel; std::vector meshSizeForTheVelocityField = this->m_TransformMethods[currentStageNumber].m_VelocityFieldMeshSizeAtBaseLevel; unsigned int numberOfIntegrationSteps = this->m_TransformMethods[currentStageNumber].m_NumberOfTimeIndices; if( numberOfIntegrationSteps == 0 ) { outputDisplacementFieldTransform->SetCalculateNumberOfIntegrationStepsAutomatically( true ); } else { outputDisplacementFieldTransform->SetNumberOfIntegrationSteps( numberOfIntegrationSteps ); } outputDisplacementFieldTransform->SetSplineOrder( this->m_TransformMethods[currentStageNumber].m_SplineOrder ); outputDisplacementFieldTransform->SetConstantVelocityField( constantVelocityField ); outputDisplacementFieldTransform->SetDisplacementField( constantVelocityField ); if( meshSizeForTheUpdateField.size() != VImageDimension || meshSizeForTheVelocityField.size() != VImageDimension ) { this->Logger() << "ERROR: The mesh size(s) don't match the ImageDimension." << std::endl; return EXIT_FAILURE; } typename BSplineDisplacementFieldTransformType::ArrayType updateMeshSize; typename BSplineDisplacementFieldTransformType::ArrayType velocityMeshSize; for( unsigned int d = 0; d < VImageDimension; d++ ) { updateMeshSize[d] = meshSizeForTheUpdateField[d]; velocityMeshSize[d] = meshSizeForTheVelocityField[d]; } // Create the transform adaptors specific to B-splines for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename itk::ImageBase::Pointer shrunkSpace= this->GetShrinkImageOutputInformation( virtualDomainImage.GetPointer(), shrinkFactorsPerDimensionForAllLevels[level] ); typedef itk::BSplineExponentialDiffeomorphicTransformParametersAdaptor BSplineDisplacementFieldTransformAdaptorType; typename BSplineDisplacementFieldTransformAdaptorType::Pointer bsplineFieldTransformAdaptor = BSplineDisplacementFieldTransformAdaptorType::New(); bsplineFieldTransformAdaptor->SetRequiredSpacing( shrunkSpace->GetSpacing() ); bsplineFieldTransformAdaptor->SetRequiredSize( shrunkSpace->GetLargestPossibleRegion().GetSize() ); bsplineFieldTransformAdaptor->SetRequiredDirection( shrunkSpace->GetDirection() ); bsplineFieldTransformAdaptor->SetRequiredOrigin( shrunkSpace->GetOrigin() ); bsplineFieldTransformAdaptor->SetTransform( outputDisplacementFieldTransform ); // A good heuristic is to RealType the b-spline mesh resolution at each level typename BSplineDisplacementFieldTransformType::ArrayType newUpdateMeshSize = updateMeshSize; typename BSplineDisplacementFieldTransformType::ArrayType newVelocityMeshSize = velocityMeshSize; for( unsigned int d = 0; d < VImageDimension; d++ ) { newUpdateMeshSize[d] = newUpdateMeshSize[d] << ( level + 1 ); newVelocityMeshSize[d] = newVelocityMeshSize[d] << ( level + 1 ); } bsplineFieldTransformAdaptor->SetMeshSizeForTheUpdateField( newUpdateMeshSize ); bsplineFieldTransformAdaptor->SetMeshSizeForTheConstantVelocityField( newVelocityMeshSize ); adaptors.push_back( bsplineFieldTransformAdaptor.GetPointer() ); } for( unsigned int n = 0; n < stageMetricList.size(); n++ ) { if( !this->IsPointSetMetric( stageMetricList[n].m_MetricType ) ) { displacementFieldRegistration->SetFixedImage( n, preprocessedFixedImagesPerStage[n] ); displacementFieldRegistration->SetMovingImage( n, preprocessedMovingImagesPerStage[n] ); } else { displacementFieldRegistration->SetFixedPointSet( n, stageMetricList[n].m_FixedLabeledPointSet.GetPointer() ); displacementFieldRegistration->SetMovingPointSet( n, stageMetricList[n].m_MovingLabeledPointSet.GetPointer() ); } } if( useMultiMetric ) { displacementFieldRegistration->SetMetric( multiMetric ); } else { displacementFieldRegistration->SetMetric( singleMetric ); } displacementFieldRegistration->SetNumberOfLevels( numberOfLevels ); for( unsigned int level = 0; level < numberOfLevels; ++level ) { displacementFieldRegistration->SetShrinkFactorsPerDimension( level, shrinkFactorsPerDimensionForAllLevels[level] ); } displacementFieldRegistration->SetSmoothingSigmasPerLevel( smoothingSigmasPerLevel ); if( this->m_CompositeTransform->GetNumberOfTransforms() > 0 ) { displacementFieldRegistration->SetMovingInitialTransform( this->m_CompositeTransform ); } if( this->m_FixedInitialTransform->GetNumberOfTransforms() > 0 ) { displacementFieldRegistration->SetFixedInitialTransform( this->m_FixedInitialTransform ); } displacementFieldRegistration->SetMetricSamplingStrategy( static_cast( metricSamplingStrategy ) ); displacementFieldRegistration->SetMetricSamplingPercentage( samplingPercentage ); displacementFieldRegistration->SetOptimizer( optimizer2 ); displacementFieldRegistration->SetTransformParametersAdaptorsPerLevel( adaptors ); typedef antsRegistrationCommandIterationUpdate DisplacementFieldCommandType; typename DisplacementFieldCommandType::Pointer displacementFieldRegistrationObserver = DisplacementFieldCommandType::New(); displacementFieldRegistrationObserver->SetLogStream(*this->m_LogStream); displacementFieldRegistrationObserver->SetNumberOfIterations( currentStageIterations ); displacementFieldRegistration->AddObserver( itk::IterationEvent(), displacementFieldRegistrationObserver ); displacementFieldRegistration->AddObserver( itk::InitializeEvent(), displacementFieldRegistrationObserver ); try { this->Logger() << std::endl << "*** Running bspline exponential field registration (updateMeshSizeAtBaseLevel = " << updateMeshSize << ", velocityMeshSizeAtBaseLevel = " << velocityMeshSize << ") ***" << std::endl << std::endl; displacementFieldRegistrationObserver->Execute( displacementFieldRegistration, itk::StartEvent() ); displacementFieldRegistration->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputDisplacementFieldTransform ); this->m_AllPreviousTransformsAreLinear = false; } break; case BSpline: { const unsigned int SplineOrder = 3; typedef itk::BSplineTransform BSplineTransformType; typedef itk::ImageRegistrationMethodv4 BSplineRegistrationType; typename BSplineRegistrationType::Pointer registrationMethod = this->PrepareRegistrationMethod( this->m_CompositeTransform, currentStageNumber, VImageDimension, preprocessedFixedImagesPerStage, preprocessedMovingImagesPerStage, fixedLabeledPointSetsPerStage, movingLabeledPointSetsPerStage, stageMetricList, singleMetric, multiMetric, optimizer, numberOfLevels, shrinkFactorsPerDimensionForAllLevels, smoothingSigmasPerLevel, metricSamplingStrategy, samplingPercentage ); typename BSplineTransformType::Pointer outputBSplineTransform = registrationMethod->GetModifiableTransform(); const std::vector & size = this->m_TransformMethods[currentStageNumber].m_MeshSizeAtBaseLevel; typename BSplineTransformType::PhysicalDimensionsType physicalDimensions; typename BSplineTransformType::MeshSizeType meshSize; for( unsigned int d = 0; d < VImageDimension; d++ ) { physicalDimensions[d] = preprocessedFixedImagesPerStage[0]->GetSpacing()[d] * static_cast( preprocessedFixedImagesPerStage[0]->GetLargestPossibleRegion().GetSize()[d] - 1 ); meshSize[d] = size[d]; } // Create the transform adaptors typename BSplineRegistrationType::TransformParametersAdaptorsContainerType adaptors; // Create the transform adaptors specific to B-splines for( unsigned int level = 0; level < numberOfLevels; level++ ) { typename itk::ImageBase::Pointer shrunkSpace= this->GetShrinkImageOutputInformation( virtualDomainImage.GetPointer(), shrinkFactorsPerDimensionForAllLevels[level] ); // A good heuristic is to RealType the b-spline mesh resolution at each level typename BSplineTransformType::MeshSizeType requiredMeshSize; for( unsigned int d = 0; d < VImageDimension; d++ ) { requiredMeshSize[d] = meshSize[d] << level; } typedef itk::BSplineTransformParametersAdaptor BSplineAdaptorType; typename BSplineAdaptorType::Pointer bsplineAdaptor = BSplineAdaptorType::New(); bsplineAdaptor->SetTransform( outputBSplineTransform ); bsplineAdaptor->SetRequiredTransformDomainMeshSize( requiredMeshSize ); bsplineAdaptor->SetRequiredTransformDomainOrigin( shrunkSpace->GetOrigin() ); bsplineAdaptor->SetRequiredTransformDomainDirection( shrunkSpace->GetDirection() ); bsplineAdaptor->SetRequiredTransformDomainPhysicalDimensions( physicalDimensions ); adaptors.push_back( bsplineAdaptor.GetPointer() ); } registrationMethod->SetTransformParametersAdaptorsPerLevel( adaptors ); outputBSplineTransform->SetTransformDomainOrigin( preprocessedFixedImagesPerStage[0]->GetOrigin() ); outputBSplineTransform->SetTransformDomainPhysicalDimensions( physicalDimensions ); outputBSplineTransform->SetTransformDomainMeshSize( meshSize ); outputBSplineTransform->SetTransformDomainDirection( preprocessedFixedImagesPerStage[0]->GetDirection() ); outputBSplineTransform->SetIdentity(); typedef antsRegistrationCommandIterationUpdate BSplineCommandType; typename BSplineCommandType::Pointer bsplineObserver = BSplineCommandType::New(); bsplineObserver->SetLogStream( *this->m_LogStream ); bsplineObserver->SetNumberOfIterations( currentStageIterations ); registrationMethod->AddObserver( itk::IterationEvent(), bsplineObserver ); registrationMethod->AddObserver( itk::InitializeEvent(), bsplineObserver ); try { this->Logger() << std::endl << "*** Running bspline registration (meshSizeAtBaseLevel = " << meshSize << ") ***" << std::endl << std::endl; bsplineObserver->Execute( registrationMethod, itk::StartEvent() ); registrationMethod->Update(); } catch( itk::ExceptionObject & e ) { this->Logger() << "Exception caught: " << e << std::endl; return EXIT_FAILURE; } // Add calculated transform to the composite transform this->m_CompositeTransform->AddTransform( outputBSplineTransform ); this->m_AllPreviousTransformsAreLinear = false; } break; default: this->Logger() << "ERROR: Unrecognized transform option - " << whichTransform << std::endl; return EXIT_FAILURE; } timer.Stop(); this->Logger() << " Elapsed time (stage " << currentStageNumber << "): " << timer.GetMean() << std::endl << std::endl; } if( this->m_ApplyLinearTransformsToFixedImageHeader && this->m_CompositeLinearTransformForFixedImageHeader->GetNumberOfTransforms() > 0 ) { this->m_CompositeTransform->PrependTransform( this->m_CompositeLinearTransformForFixedImageHeader ); this->m_CompositeTransform->FlattenTransformQueue(); } totalTimer.Stop(); this->Logger() << std::endl << "Total elapsed time: " << totalTimer.GetMean() << std::endl; return EXIT_SUCCESS; } template void RegistrationHelper ::SetMovingInitialTransform( const TransformType *initialTransform ) { // Since the initial transform might be linear (or a composition of // linear transforms), we might want to add those initial transforms // to the moving image header for faster processing. typename CompositeTransformType::Pointer compToAdd; typename CompositeTransformType::ConstPointer compXfrm = dynamic_cast( initialTransform ); if( compXfrm.IsNotNull() ) { compToAdd = compXfrm->Clone(); this->m_CompositeTransform = compToAdd; } else { compToAdd = CompositeTransformType::New(); typename TransformType::Pointer xfrm = initialTransform->Clone(); compToAdd->AddTransform( xfrm ); this->m_CompositeTransform = compToAdd; } } template void RegistrationHelper ::SetFixedInitialTransform( const TransformType *initialTransform ) { typename CompositeTransformType::Pointer compToAdd; typename CompositeTransformType::ConstPointer compXfrm = dynamic_cast( initialTransform ); if( compXfrm.IsNotNull() ) { compToAdd = compXfrm->Clone(); if( this->m_ApplyLinearTransformsToFixedImageHeader && compXfrm->IsLinear() ) { this->m_CompositeLinearTransformForFixedImageHeader = compToAdd; } else { this->m_FixedInitialTransform = compToAdd; this->m_AllPreviousTransformsAreLinear = false; } } else { compToAdd = CompositeTransformType::New(); typename TransformType::Pointer xfrm = initialTransform->Clone(); compToAdd->AddTransform( xfrm ); if( this->m_ApplyLinearTransformsToFixedImageHeader && initialTransform->IsLinear() ) { this->m_CompositeLinearTransformForFixedImageHeader = compToAdd; } else { this->m_FixedInitialTransform = compToAdd; this->m_AllPreviousTransformsAreLinear = false; } } } template void RegistrationHelper ::SetRestoreStateTransform( const TransformType *initialTransform ) { typename CompositeTransformType::Pointer compToRestore; typename CompositeTransformType::Pointer compToAdd; typename CompositeTransformType::ConstPointer compXfrm = dynamic_cast( initialTransform ); if( compXfrm.IsNotNull() ) { compToRestore = compXfrm->Clone(); // If the last four transforms are displacementFieldType, we assume that they are // forward and inverse displacement fields of the FixedToMiddle and MovingToMiddle // transforms for a SyN registration. // unsigned int numTransforms = compToRestore->GetNumberOfTransforms(); if( (compToRestore->GetNthTransform( numTransforms-1 )->GetTransformCategory() == TransformType::DisplacementField) && (compToRestore->GetNthTransform( numTransforms-2 )->GetTransformCategory() == TransformType::DisplacementField) && (compToRestore->GetNthTransform( numTransforms-3 )->GetTransformCategory() == TransformType::DisplacementField) && (compToRestore->GetNthTransform( numTransforms-4 )->GetTransformCategory() == TransformType::DisplacementField) ) { typename DisplacementFieldTransformType::Pointer fixedToMiddleForwardTx = dynamic_cast( compToRestore->GetNthTransform( numTransforms-4 ).GetPointer() ); typename DisplacementFieldTransformType::Pointer fixedToMiddleInverseTx = dynamic_cast( compToRestore->GetNthTransform( numTransforms-3 ).GetPointer() ); typename DisplacementFieldTransformType::Pointer movingToMiddleForwardTx = dynamic_cast( compToRestore->GetNthTransform( numTransforms-2 ).GetPointer() ); typename DisplacementFieldTransformType::Pointer movingToMiddleInverseTx = dynamic_cast( compToRestore->GetNthTransform( numTransforms-1 ).GetPointer() ); typename DisplacementFieldTransformType::Pointer fixedToMiddleTransform = DisplacementFieldTransformType::New(); fixedToMiddleTransform->SetDisplacementField( fixedToMiddleForwardTx->GetDisplacementField() ); fixedToMiddleTransform->SetInverseDisplacementField( fixedToMiddleInverseTx->GetDisplacementField() ); typename DisplacementFieldTransformType::Pointer movingToMiddleTransform = DisplacementFieldTransformType::New(); movingToMiddleTransform->SetDisplacementField( movingToMiddleForwardTx->GetDisplacementField() ); movingToMiddleTransform->SetInverseDisplacementField( movingToMiddleInverseTx->GetDisplacementField() ); this->Logger() << "Initial FixedToMiddle and MovingToMiddle transforms are restored from the registration state file." << std::endl; compToRestore->RemoveTransform(); compToRestore->RemoveTransform(); compToRestore->RemoveTransform(); compToRestore->RemoveTransform(); compToRestore->AddTransform( fixedToMiddleTransform ); compToRestore->AddTransform( movingToMiddleTransform ); // m_RegistrationState has initial linear transforms + fixedToMiddle + movingToMiddle this->m_RegistrationState = compToRestore; // Now we restore the SyN transform from FixedToMiddle and MovingToMiddle transforms compToAdd = compToRestore->Clone(); typename DisplacementFieldTransformType::Pointer initialSyNTransform = DisplacementFieldTransformType::New(); typedef itk::ComposeDisplacementFieldsImageFilter ComposerType; typename ComposerType::Pointer composer = ComposerType::New(); composer->SetDisplacementField( movingToMiddleTransform->GetInverseDisplacementField() ); composer->SetWarpingField( fixedToMiddleTransform->GetDisplacementField() ); composer->Update(); typename ComposerType::Pointer inverseComposer = ComposerType::New(); inverseComposer->SetDisplacementField( fixedToMiddleTransform->GetInverseDisplacementField() ); inverseComposer->SetWarpingField( movingToMiddleTransform->GetDisplacementField() ); inverseComposer->Update(); initialSyNTransform->SetDisplacementField( composer->GetOutput() ); initialSyNTransform->SetInverseDisplacementField( inverseComposer->GetOutput() ); compToAdd->RemoveTransform(); compToAdd->RemoveTransform(); compToAdd->AddTransform( initialSyNTransform ); } else { this->m_RegistrationState = ITK_NULLPTR; } if( compToAdd.IsNull() ) { compToAdd = compToRestore->Clone(); } // m_CompositeTransform has initial linear transforms + initial SyN transform this->m_CompositeTransform = compToAdd; } else { this->m_CompositeTransform = ITK_NULLPTR; } } template std::vector RegistrationHelper ::CalculateMeshSizeForSpecifiedKnotSpacing( ImageBaseType * const inputImage, const RealType knotSpacing, const unsigned int itkNotUsed( splineOrder ) ) { // The commented code is for use with itk::ConstantPadImageFilter. Right now // the mesh size is simply an approximation. std::vector meshSize; // unsigned long lowerBound[VImageDimension]; // unsigned long upperBound[VImageDimension]; for( unsigned int d = 0; d < ImageDimension; d++ ) { RealType domain = static_cast( inputImage->GetLargestPossibleRegion().GetSize()[d] - 1 ) * inputImage->GetSpacing()[d]; meshSize.push_back( static_cast( std::ceil( domain / knotSpacing ) ) ); // unsigned long extraPadding = static_cast( // ( numberOfSpans * splineDistance - domain ) / inputImage->GetSpacing()[d] + 0.5 ); // lowerBound[d] = static_cast( 0.5 * extraPadding ); // upperBound[d] = extraPadding - lowerBound[d]; // numberOfControlPoints[d] = meshSize[d] + splineOrder; } return meshSize; } template typename RegistrationHelper::AffineTransformType::Pointer RegistrationHelper ::CollapseLinearTransforms( const CompositeTransformType * compositeTransform ) { if( !compositeTransform->IsLinear() ) { itkExceptionMacro( "The composite transform is not linear." ); } typename AffineTransformType::Pointer totalTransform = AffineTransformType::New(); const unsigned int numberOfTransforms = compositeTransform->GetNumberOfTransforms(); // Find the last transform that has a center, and set that as the fixed parameters of the total transform. // It should be set only once. for( unsigned int n = numberOfTransforms; n > 0; n--) { typename TransformType::Pointer transform = compositeTransform->GetNthTransform( n-1 ); typename MatrixOffsetTransformBaseType::ConstPointer matrixOffsetTransform = dynamic_cast( transform.GetPointer() ); if( matrixOffsetTransform.IsNotNull() ) { totalTransform->SetCenter( matrixOffsetTransform->GetCenter() ); break; } } typedef itk::TranslationTransform TranslationTransformType; for( unsigned int n = 0; n < numberOfTransforms; n++ ) { typename TransformType::Pointer transform = compositeTransform->GetNthTransform( n ); typename AffineTransformType::Pointer nthTransform = AffineTransformType::New(); typename TranslationTransformType::Pointer translationTransform = dynamic_cast( transform.GetPointer() ); if( translationTransform.IsNotNull() ) { nthTransform->SetOffset( translationTransform->GetOffset() ); } else { typename MatrixOffsetTransformBaseType::ConstPointer matrixOffsetTransform = dynamic_cast( transform.GetPointer() ); nthTransform->SetCenter( matrixOffsetTransform->GetCenter() ); nthTransform->SetMatrix( matrixOffsetTransform->GetMatrix() ); nthTransform->SetTranslation( matrixOffsetTransform->GetTranslation() ); } totalTransform->Compose( nthTransform, true ); } return totalTransform; } template typename RegistrationHelper::CompositeTransformType::Pointer RegistrationHelper ::CollapseDisplacementFieldTransforms( const CompositeTransformType * compositeTransform ) { typename CompositeTransformType::Pointer combinedCompositeTransform = CompositeTransformType::New(); if( compositeTransform->GetTransformCategory() != TransformType::DisplacementField ) { itkExceptionMacro( "The composite transform is not composed strictly of displacement fields." ); } if( compositeTransform->GetNumberOfTransforms() == 0 ) { itkWarningMacro( "The composite transform is empty. Returning empty displacement field transform." ); return combinedCompositeTransform; } typename TransformType::Pointer transform = compositeTransform->GetNthTransform( 0 ); typename DisplacementFieldTransformType::Pointer currentTransform = dynamic_cast( transform.GetPointer() ); bool isCurrentTransformInvertible = false; if( currentTransform->GetInverseDisplacementField() ) { isCurrentTransformInvertible = true; } for( unsigned int n = 1; n < compositeTransform->GetNumberOfTransforms(); n++ ) { transform = compositeTransform->GetNthTransform( n ); typename DisplacementFieldTransformType::Pointer nthTransform = dynamic_cast( transform.GetPointer() ); if( ( isCurrentTransformInvertible && nthTransform->GetInverseDisplacementField() ) || ! ( isCurrentTransformInvertible || nthTransform->GetInverseDisplacementField() ) ) { // Adjacent transforms are the same so we can combine typedef itk::ComposeDisplacementFieldsImageFilter ComposerType; typename ComposerType::Pointer composer = ComposerType::New(); composer->SetWarpingField( nthTransform->GetDisplacementField() ); composer->SetDisplacementField( currentTransform->GetDisplacementField() ); typename DisplacementFieldType::Pointer totalField = composer->GetOutput(); totalField->Update(); totalField->DisconnectPipeline(); typename DisplacementFieldType::Pointer totalInverseField = ITK_NULLPTR; if( isCurrentTransformInvertible ) { typename ComposerType::Pointer inverseComposer = ComposerType::New(); inverseComposer->SetWarpingField( currentTransform->GetInverseDisplacementField() ); inverseComposer->SetDisplacementField( nthTransform->GetInverseDisplacementField() ); totalInverseField = inverseComposer->GetOutput(); totalInverseField->Update(); totalInverseField->DisconnectPipeline(); } currentTransform->SetDisplacementField( totalField ); currentTransform->SetInverseDisplacementField( totalInverseField ); } else { DisplacementFieldTransformPointer displacementFieldTransform = DisplacementFieldTransformType::New(); displacementFieldTransform->SetDisplacementField( currentTransform->GetModifiableDisplacementField() ); if( isCurrentTransformInvertible ) { displacementFieldTransform->SetInverseDisplacementField( currentTransform->GetModifiableInverseDisplacementField() ); } combinedCompositeTransform->AddTransform( displacementFieldTransform ); currentTransform->SetDisplacementField( nthTransform->GetModifiableDisplacementField() ); currentTransform->SetInverseDisplacementField( nthTransform->GetModifiableInverseDisplacementField() ); if( currentTransform->GetInverseDisplacementField() ) { isCurrentTransformInvertible = true; } else { isCurrentTransformInvertible = false; } } } combinedCompositeTransform->AddTransform( currentTransform ); return combinedCompositeTransform; } template typename RegistrationHelper::CompositeTransformPointer RegistrationHelper ::CollapseCompositeTransform( const CompositeTransformType * compositeTransform ) { CompositeTransformPointer collapsedCompositeTransform = CompositeTransformType::New(); // Check for the simple cases where the composite transform is composed entirely // of linear transforms or displacement field transforms. if( compositeTransform->IsLinear() ) { collapsedCompositeTransform->AddTransform( this->CollapseLinearTransforms( compositeTransform ) ); return collapsedCompositeTransform; } else if( compositeTransform->GetTransformCategory() == TransformType::DisplacementField ) { collapsedCompositeTransform->AddTransform( this->CollapseDisplacementFieldTransforms( compositeTransform ) ); collapsedCompositeTransform->FlattenTransformQueue(); return collapsedCompositeTransform; } // Find the first linear or displacement field transform typename TransformType::TransformCategoryType currentTransformCategory = TransformType::UnknownTransformCategory; unsigned int startIndex = 0; for( unsigned int n = 0; n < compositeTransform->GetNumberOfTransforms(); n++ ) { typename TransformType::TransformCategoryType transformCategory = compositeTransform->GetNthTransform( n )->GetTransformCategory(); if( transformCategory == TransformType::Linear || transformCategory == TransformType::DisplacementField ) { currentTransformCategory = transformCategory; startIndex = n; break; } else { collapsedCompositeTransform->AddTransform( compositeTransform->GetNthTransform( n ) ); } } // If a linear or displacement field transform is found then we can break down the // composite transform into neighboring sets of like transform types. if( currentTransformCategory != TransformType::UnknownTransformCategory ) { CompositeTransformPointer currentCompositeTransform = CompositeTransformType::New(); currentCompositeTransform->AddTransform( compositeTransform->GetNthTransform( startIndex ) ); for( unsigned int n = startIndex + 1; n < compositeTransform->GetNumberOfTransforms(); n++ ) { typename TransformType::TransformCategoryType transformCategory = compositeTransform->GetNthTransform( n )->GetTransformCategory(); if( transformCategory == currentTransformCategory ) { currentCompositeTransform->AddTransform( compositeTransform->GetNthTransform( n ) ); if( n == compositeTransform->GetNumberOfTransforms() - 1 ) { if( currentTransformCategory == TransformType::Linear ) { collapsedCompositeTransform->AddTransform( this->CollapseLinearTransforms( currentCompositeTransform ) ); } else if( currentTransformCategory == TransformType::DisplacementField ) { collapsedCompositeTransform->AddTransform( this->CollapseDisplacementFieldTransforms( currentCompositeTransform ) ); } } } else { if( currentTransformCategory == TransformType::Linear ) { collapsedCompositeTransform->AddTransform( this->CollapseLinearTransforms( currentCompositeTransform ) ); currentCompositeTransform->ClearTransformQueue(); } else if( currentTransformCategory == TransformType::DisplacementField ) { collapsedCompositeTransform->AddTransform( this->CollapseDisplacementFieldTransforms( currentCompositeTransform ) ); currentCompositeTransform->ClearTransformQueue(); } currentTransformCategory = transformCategory; if( ( transformCategory == TransformType::Linear || transformCategory == TransformType::DisplacementField ) && n < compositeTransform->GetNumberOfTransforms() - 1 ) { currentCompositeTransform->AddTransform( compositeTransform->GetNthTransform( n ) ); } else { collapsedCompositeTransform->AddTransform( compositeTransform->GetNthTransform( n ) ); } } } } collapsedCompositeTransform->FlattenTransformQueue(); return collapsedCompositeTransform; } template void RegistrationHelper ::ApplyCompositeLinearTransformToImageHeader( const CompositeTransformType * compositeTransform, ImageBaseType * const image, const bool applyInverse ) { if( !compositeTransform->IsLinear() ) { itkExceptionMacro( "The composite transform is not linear. Cannot collapse it to the image header." ); } typename AffineTransformType::Pointer totalTransform = this->CollapseLinearTransforms( compositeTransform ); typename ImageType::PointType origin = image->GetOrigin(); typename ImageType::DirectionType direction = image->GetDirection(); // Image direction matrix is type of RealType. // It should be converted to the current InternalComputationType before it is used to set transfrom parameters. vnl_matrix DoubleLocalDirection( VImageDimension, VImageDimension ); vnl_matrix localDirection( VImageDimension, VImageDimension ); DoubleLocalDirection = direction.GetVnlMatrix(); vnl_copy( DoubleLocalDirection, localDirection ); // Image origin is an itk point of type RealType. // It should be converted to the current InternalComputationType before it is used to set the offset parameters of transform. typename itk::Point localOrigin; localOrigin.CastFrom(origin); typename AffineTransformType::Pointer imageTransform = AffineTransformType::New(); imageTransform->SetMatrix( localDirection ); imageTransform->SetOffset( localOrigin.GetVectorFromOrigin() ); if( applyInverse ) { typename AffineTransformType::Pointer inverseImageTransform = AffineTransformType::New(); inverseImageTransform->SetMatrix( dynamic_cast( imageTransform-> GetInverseTransform().GetPointer() ) ->GetMatrix() ); inverseImageTransform->SetOffset( -( inverseImageTransform->GetMatrix() * imageTransform->GetOffset() ) ); totalTransform->Compose( inverseImageTransform.GetPointer(), false ); typename AffineTransformType::MatrixType inverseMatrix = dynamic_cast( totalTransform->GetInverseTransform().GetPointer() )->GetMatrix(); typename AffineTransformType::OffsetType inverseOffset = -( inverseMatrix * totalTransform->GetOffset() ); for( unsigned int d = 0; d < VImageDimension; d++ ) { origin[d] = inverseOffset[d]; } //direction = inverseMatrix; // Does not work because they probably have different types! vnl_matrix localInverseMatrix( VImageDimension, VImageDimension ); localInverseMatrix = inverseMatrix.GetVnlMatrix(); vnl_copy(localInverseMatrix, DoubleLocalDirection); direction = DoubleLocalDirection; } else { totalTransform->Compose( imageTransform, true ); typename AffineTransformType::MatrixType matrix = totalTransform->GetMatrix(); typename AffineTransformType::OffsetType offset = totalTransform->GetOffset(); for( unsigned int d = 0; d < VImageDimension; d++ ) { origin[d] = offset[d]; } //direction = matrix; // Does not work because they probably have different types! vnl_matrix localMatrix( VImageDimension, VImageDimension ); localMatrix = matrix.GetVnlMatrix(); vnl_copy(localMatrix, DoubleLocalDirection); direction = DoubleLocalDirection; } image->SetDirection( direction ); image->SetOrigin( origin ); } template template bool RegistrationHelper ::InitializeWithPreviousLinearTransform( const CompositeTransformType * compositeTransform, const std::string transformTypeName, typename TTransformType::Pointer & resultTransform ) { typedef itk::TranslationTransform TranslationTransformType; typedef typename RigidTransformTraits::TransformType RigidTransformType; std::string previousTxFileType = ""; const typename TransformType::ConstPointer preTransform = compositeTransform->GetBackTransform(); if( preTransform.IsNotNull() ) { previousTxFileType = preTransform->GetNameOfClass(); } else { this->Logger() << "ERROR: INITIALIZATION RETURNS FALSE. Previous Linear Transform is Null" << std::endl; return false; } this->Logger() << "Try to initialize the current " << transformTypeName << " from previous " << previousTxFileType << "." << std::endl; ///// if( transformTypeName == "Translation" ) { typename TranslationTransformType::Pointer initialTransform = dynamic_cast(resultTransform.GetPointer()); initialTransform->SetIdentity(); if( previousTxFileType == "TranslationTransform" ) { typename TranslationTransformType::ConstPointer tempInitializerTransform = dynamic_cast( preTransform.GetPointer() ); if( tempInitializerTransform.IsNull() ) { this->Logger() << "WARNING: Initialization Failed" << std::endl; return false; } //Translation to Translation initialTransform->SetFixedParameters( tempInitializerTransform->GetFixedParameters() ); initialTransform->SetParameters( tempInitializerTransform->GetParameters() ); } else { this->Logger() << "WARNING: Initialization Failed" << std::endl; return false; } } ///// else if( transformTypeName == "Euler2D" || transformTypeName == "Euler3D" ) { typename RigidTransformType::Pointer initialTransform = dynamic_cast(resultTransform.GetPointer()); initialTransform->SetIdentity(); if( previousTxFileType == "TranslationTransform" ) { typename TranslationTransformType::ConstPointer tempInitializerTransform = dynamic_cast( preTransform.GetPointer() ); if( tempInitializerTransform.IsNull() ) { this->Logger() << "WARNING: Initialization Failed" << std::endl; return false; } //Translation to Rigid initialTransform->SetOffset( tempInitializerTransform->GetOffset() ); } else if( previousTxFileType == "Euler3DTransform" || previousTxFileType == "Euler2DTransform" ) { typename RigidTransformType::ConstPointer tempInitializerTransform = dynamic_cast( preTransform.GetPointer() ); if( tempInitializerTransform.IsNull() ) { this->Logger() << "WARNING: Initialization Failed" << std::endl; return false; } //Rigid to Rigid initialTransform->SetFixedParameters( tempInitializerTransform->GetFixedParameters() ); initialTransform->SetParameters( tempInitializerTransform->GetParameters() ); } else { this->Logger() << "WARNING: Initialization Failed" << std::endl; return false; } } ///// else if( transformTypeName == "Affine" ) { typename AffineTransformType::Pointer initialTransform = dynamic_cast(resultTransform.GetPointer()); initialTransform->SetIdentity(); if( previousTxFileType == "TranslationTransform" ) { typename TranslationTransformType::ConstPointer tempInitializerTransform = dynamic_cast( preTransform.GetPointer() ); if( tempInitializerTransform.IsNull() ) { this->Logger() << "WARNING: Initialization Failed" << std::endl; return false; } //Translation to Affine initialTransform->SetOffset( tempInitializerTransform->GetOffset() ); } else if( previousTxFileType == "Euler3DTransform" || previousTxFileType == "Euler2DTransform" ) { typename RigidTransformType::ConstPointer tempInitializerTransform = dynamic_cast( preTransform.GetPointer() ); if( tempInitializerTransform.IsNull() ) { this->Logger() << "WARNING: Initialization Failed" << std::endl; return false; } //Rigid to Affine initialTransform->SetCenter( tempInitializerTransform->GetCenter() ); initialTransform->SetMatrix( tempInitializerTransform->GetMatrix() ); initialTransform->SetTranslation( tempInitializerTransform->GetTranslation() ); } else if( previousTxFileType == "AffineTransform" ) { typename AffineTransformType::ConstPointer tempInitializerTransform = dynamic_cast( preTransform.GetPointer() ); if( tempInitializerTransform.IsNull() ) { this->Logger() << "WARNING: Initialization Failed" << std::endl; return false; } //Affine to Affine initialTransform->SetFixedParameters( tempInitializerTransform->GetFixedParameters() ); initialTransform->SetParameters( tempInitializerTransform->GetParameters() ); } else { this->Logger() << "WARNING: Initialization Failed" << std::endl; return false; } } else { this->Logger() << "WARNING: Initialization Failed" << std::endl; return false; } ///// return true; // This function only returns flase or true (NOT FAILURE or SUCCESS). // If direct intialization fails, the program should NOT be stopped, // because the initial transform will be kept in the composite transform, // and the final results will be still correct. } template void RegistrationHelper ::PrintState() const { this->Logger() << "Dimension = " << Self::ImageDimension << std::endl << "Number of stages = " << this->m_NumberOfStages << std::endl << "Use Histogram Matching " << ( this->m_UseHistogramMatching ? "true" : "false" ) << std::endl << "Winsorize image intensities " << ( this->m_WinsorizeImageIntensities ? "true" : "false" ) << std::endl << "Lower quantile = " << this->m_LowerQuantile << std::endl << "Upper quantile = " << this->m_UpperQuantile << std::endl; for( unsigned i = 0; i < this->m_NumberOfStages; i++ ) { this->Logger() << "Stage " << i + 1 << " State" << std::endl; // NOTE: + 1 for consistency. const Metric & curMetric = this->m_Metrics[i]; const TransformMethod & curTransform = this->m_TransformMethods[i]; if( !this->IsPointSetMetric( curMetric.m_MetricType ) ) { this->Logger() << " Image metric = " << curMetric.GetMetricAsString() << std::endl << " Fixed image = " << curMetric.m_FixedImage << std::endl << " Moving image = " << curMetric.m_MovingImage << std::endl << " Weighting = " << curMetric.m_Weighting << std::endl << " Sampling strategy = " << (curMetric.m_SamplingStrategy == random ? "random" : (curMetric.m_SamplingStrategy == regular ) ? "regular" : (curMetric.m_SamplingStrategy == none ) ? "none" : "WARNING: UNKNOWN") << std::endl << " Number of bins = " << curMetric.m_NumberOfBins << std::endl << " Radius = " << curMetric.m_Radius << std::endl << " Sampling percentage = " << curMetric.m_SamplingPercentage << std::endl; } else { if( curMetric.m_MetricType == IGDM ) { this->Logger() << " Point Set Metric = " << curMetric.GetMetricAsString() << std::endl << " Fixed intensity point set = " << curMetric.m_FixedIntensityPointSet << std::endl << " Moving intensity point set = " << curMetric.m_MovingIntensityPointSet << std::endl << " Weighting = " << curMetric.m_Weighting << std::endl << " Intensity distance sigma = " << curMetric.m_IntensityDistanceSigma << std::endl << " Euclidean distance sigma = " << curMetric.m_EuclideanDistanceSigma << std::endl << " Evaluation K neighborhood = " << curMetric.m_EvaluationKNeighborhood << std::endl; } else { this->Logger() << " Point Set Metric = " << curMetric.GetMetricAsString() << std::endl << " Fixed labeled point set = " << curMetric.m_FixedLabeledPointSet << std::endl << " Moving labeled point set = " << curMetric.m_MovingLabeledPointSet << std::endl << " Weighting = " << curMetric.m_Weighting << std::endl << " Use only boundary points = " << ( curMetric.m_UseBoundaryPointsOnly ? "true" : "false" ) << std::endl << " Point set sigma = " << curMetric.m_PointSetSigma << std::endl << " Evaluation K neighborhood = " << curMetric.m_EvaluationKNeighborhood << std::endl << " Alpha = " << curMetric.m_Alpha << std::endl << " Use anisotropic covariances = " << ( curMetric.m_UseAnisotropicCovariances ? "true" : "false" ) << std::endl << " Sampling percentage = " << curMetric.m_SamplingPercentage << std::endl; } } this->Logger() << " Transform = " << curTransform.XfrmMethodAsString() << std::endl << " Gradient step = " << curTransform.m_GradientStep << std::endl << " Update field sigma (voxel space) = " << curTransform.m_UpdateFieldVarianceInVarianceSpace << std::endl << " Total field sigma (voxel space) = " << curTransform.m_TotalFieldVarianceInVarianceSpace << std::endl << " Update field time sigma = " << curTransform.m_UpdateFieldTimeSigma << std::endl << " Total field time sigma = " << curTransform.m_TotalFieldTimeSigma << std::endl << " Number of time indices = " << curTransform.m_NumberOfTimeIndices << std::endl << " Number of time point samples = " << curTransform.m_NumberOfTimeIndices << std::endl; } } } // namespace ants #endif // __itkantsRegistrationHelper_hxx ants-2.2.0/Examples/make_interpolator_snip.tmpl000066400000000000000000000162501311104306400217010ustar00rootroot00000000000000/* HACK: THIS IS COMMON CODE FOUND THAT WAS SHARED BETWEEN TWO PROGRAMS * IT SHOULD BE A TEMPLATED FUNCTION, but I don't have time to do that * right now. * In merging the two programs, it was found that there were missing * and inconsistent functions between the two sets of copied and pasted * code. */ //static int ConfigureInterpolatorFromString( const std::string & whichInterpolator, cache_spacing_for_smoothing_sigmas) //{ /** * Interpolation option */ typedef itk::InterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = ITK_NULLPTR; if( !std::strcmp( whichInterpolator.c_str(), "linear" ) ) { typedef itk::LinearInterpolateImageFunction LinearInterpolatorType; typename LinearInterpolatorType::Pointer linearInterpolator = LinearInterpolatorType::New(); interpolator = linearInterpolator; } else if( !std::strcmp( whichInterpolator.c_str(), "nearestneighbor" ) ) { typedef itk::NearestNeighborInterpolateImageFunction NearestNeighborInterpolatorType; typename NearestNeighborInterpolatorType::Pointer nearestNeighborInterpolator = NearestNeighborInterpolatorType::New(); interpolator = nearestNeighborInterpolator; } else if( !std::strcmp( whichInterpolator.c_str(), "bspline" ) ) { typedef itk::BSplineInterpolateImageFunction BSplineInterpolatorType; typename BSplineInterpolatorType::Pointer bSplineInterpolator = BSplineInterpolatorType::New(); if( interpolationOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { unsigned int bsplineOrder = parser->Convert( interpolationOption->GetFunction( 0 )->GetParameter( 0 ) ); bSplineInterpolator->SetSplineOrder( bsplineOrder ); } interpolator = bSplineInterpolator; } else if( !std::strcmp( whichInterpolator.c_str(), "gaussian" ) ) { typedef itk::GaussianInterpolateImageFunction GaussianInterpolatorType; typename GaussianInterpolatorType::Pointer gaussianInterpolator = GaussianInterpolatorType::New(); double sigma[VImageDimension]; for( unsigned int d = 0; d < VImageDimension; d++ ) { sigma[d] = cache_spacing_for_smoothing_sigmas[d]; } double alpha = 1.0; if( interpolationOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { std::vector s = parser->ConvertVector( interpolationOption->GetFunction( 0 )->GetParameter( 0 ) ); if( s.size() == VImageDimension ) { for( unsigned int d = 0; d < VImageDimension; d++ ) { sigma[d] = s[d]; } } else { for( unsigned int d = 0; d < VImageDimension; d++ ) { sigma[d] = s[0]; } } } if( interpolationOption->GetFunction( 0 )->GetNumberOfParameters() > 1 ) { alpha = parser->Convert( interpolationOption->GetFunction( 0 )->GetParameter( 1 ) ); } gaussianInterpolator->SetParameters( sigma, alpha ); interpolator = gaussianInterpolator; } else if( !std::strcmp( whichInterpolator.c_str(), "CosineWindowedSinc" ) ) { typedef itk::WindowedSincInterpolateImageFunction , itk::ConstantBoundaryCondition< ImageType >, RealType> CosineInterpolatorType; typename CosineInterpolatorType::Pointer cosineInterpolator = CosineInterpolatorType::New(); interpolator = cosineInterpolator; } else if( !std::strcmp( whichInterpolator.c_str(), "hammingwindowedsinc" ) ) { typedef itk::WindowedSincInterpolateImageFunction , itk::ConstantBoundaryCondition< ImageType >, RealType> HammingInterpolatorType; typename HammingInterpolatorType::Pointer hammingInterpolator = HammingInterpolatorType::New(); interpolator = hammingInterpolator; } else if( !std::strcmp( whichInterpolator.c_str(), "lanczoswindowedsinc" ) ) { typedef itk::WindowedSincInterpolateImageFunction , itk::ConstantBoundaryCondition< ImageType >, RealType > LanczosInterpolatorType; typename LanczosInterpolatorType::Pointer lanczosInterpolator = LanczosInterpolatorType::New(); interpolator = lanczosInterpolator; } else if( !std::strcmp( whichInterpolator.c_str(), "blackmanwindowedsinc" ) ) { typedef itk::WindowedSincInterpolateImageFunction , itk::ConstantBoundaryCondition< ImageType >, RealType > BlackmanInterpolatorType; typename BlackmanInterpolatorType::Pointer blackmanInterpolator = BlackmanInterpolatorType::New(); interpolator = blackmanInterpolator; } else if( !std::strcmp( whichInterpolator.c_str(), "welchwindowedsinc" ) ) { typedef itk::WindowedSincInterpolateImageFunction , itk::ConstantBoundaryCondition< ImageType >, RealType > WelchInterpolatorType; typename WelchInterpolatorType::Pointer welchInterpolator = WelchInterpolatorType::New(); interpolator = welchInterpolator; } else if( !std::strcmp( whichInterpolator.c_str(), "genericlabel" ) ) { typedef itk::LabelImageGenericInterpolateImageFunction GLInterpolatorType; typename GLInterpolatorType::Pointer GenericLabelInterpolator = GLInterpolatorType::New(); interpolator = GenericLabelInterpolator; } else if( !std::strcmp( whichInterpolator.c_str(), "multilabel" ) ) { const unsigned int NVectorComponents = 1; typedef VectorPixelCompare CompareType; typedef typename itk::LabelImageGaussianInterpolateImageFunction MultiLabelInterpolatorType; typename MultiLabelInterpolatorType::Pointer multiLabelInterpolator = MultiLabelInterpolatorType::New(); double sigma[VImageDimension]; for( unsigned int d = 0; d < VImageDimension; d++ ) { sigma[d] = cache_spacing_for_smoothing_sigmas[d]; } double alpha = 4.0; if( interpolationOption->GetFunction( 0 )->GetNumberOfParameters() > 0 ) { std::vector s = parser->ConvertVector( interpolationOption->GetFunction( 0 )->GetParameter( 0 ) ); if( s.size() == VImageDimension ) { for( unsigned int d = 0; d < VImageDimension; d++ ) { sigma[d] = s[d]; } } else { for( unsigned int d = 0; d < VImageDimension; d++ ) { sigma[d] = s[0]; } } } multiLabelInterpolator->SetParameters( sigma, alpha ); interpolator = multiLabelInterpolator; } //sanity check thtat this function MUST return a valid interpolator if ( interpolator.IsNull() ) { std::cout << "Error: Unrecognized interpolation option. " << whichInterpolator << std::endl; //return NULL; return EXIT_FAILURE; } //return interpolator; // } ants-2.2.0/Examples/sccan.cxx000066400000000000000000003354141311104306400160540ustar00rootroot00000000000000#include "antsUtilities.h" #include "antsAllocImage.h" #include #include "antsCommandLineOption.h" #include "antsCommandLineParser.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkRecursiveGaussianImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkResampleImageFilter.h" #include "itkBSplineInterpolateImageFunction.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "antsSCCANObject.h" #include "itkCSVNumericObjectFileWriter.h" #include "itkCSVArray2DDataObject.h" #include "itkCSVArray2DFileReader.h" #include "itkExtractImageFilter.h" #include "ReadWriteData.h" namespace ants { // namespace antssccan { template double vnl_pearson_corr( vnl_vector v1, vnl_vector v2 ) { double xysum = 0; for( unsigned int i = 0; i < v1.size(); i++ ) { xysum += v1(i) * v2(i); } double frac = 1.0 / (double)v1.size(); double xsum = v1.sum(), ysum = v2.sum(); double xsqr = v1.squared_magnitude(); double ysqr = v2.squared_magnitude(); double numer = xysum - frac * xsum * ysum; double denom = sqrt( ( xsqr - frac * xsum * xsum) * ( ysqr - frac * ysum * ysum) ); if( denom <= 0 ) { return 0; } return numer / denom; } template void WriteVectorToSpatialImage( std::string filename, std::string post, vnl_vector w_p, typename TImage::Pointer mask ) { std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); std::string extension; if( pos != std::string::npos ) { extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); extension = std::string( filepre, pos, filepre.length() - 1 ) + extension; filepre = std::string( filepre, 0, pos ); } } typedef itk::ants::antsSCCANObject SCCANType; typename SCCANType::Pointer sccanobj = SCCANType::New(); typename TImage::Pointer weights = sccanobj->ConvertVariateToSpatialImage( w_p, mask ); std::string fn1 = filepre + post + extension; WriteImage( weights, fn1.c_str() ); } template inline std::string sccan_to_string(const T& t) { std::stringstream ss; ss << t; std::string stringout = ss.str(); if( t < 100 ) { std::stringstream ss0; ss0 << 0; std::string extend = ss0.str(); stringout = std::string(extend + stringout); } if( t < 10 ) { std::stringstream ss0; ss0 << 0; std::string extend = ss0.str(); stringout = std::string(extend + stringout); } return stringout; } template void WriteSortedVariatesToSpatialImage( std::string filename, std::string post, vnl_matrix varmat, typename TImage::Pointer mask, vnl_matrix data_mat, bool have_mask, vnl_vector l_array, vnl_matrix prior_mat ) { vnl_matrix projections = data_mat * varmat; std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); std::string extension; if( pos != std::string::npos ) { extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); extension = std::string( filepre, pos, filepre.length() - 1 ) + extension; filepre = std::string( filepre, 0, pos ); } } std::string post2; std::ofstream myfile; std::string fnmp1 = filepre + std::string("projections") + post + std::string(".csv"); std::vector ColumnHeaders1; for( unsigned int nv = 0; nv < projections.cols(); nv++ ) { std::string colname = std::string("Variate") + sccan_to_string(nv); ColumnHeaders1.push_back( colname ); } typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer1 = WriterType::New(); writer1->SetFileName( fnmp1.c_str() ); writer1->SetColumnHeaders(ColumnHeaders1); writer1->SetInput( &projections ); try { writer1->Write(); } catch( itk::ExceptionObject& exp ) { // std::cerr << "Exception caught!" << std::endl; // std::cerr << exp << std::endl; return; } // std::cout << data_mat.cols() << prior_mat.cols() << data_mat.rows() << prior_mat.rows() << std::endl; vnl_matrix projectionsROI = data_mat * prior_mat.transpose(); std::string::size_type posROI = filename.rfind( "." ); std::string filepreROI = std::string( filename, 0, posROI ); std::string extensionROI; if( posROI != std::string::npos ) { extensionROI = std::string( filename, posROI, filename.length() - 1); if( extension == std::string(".gz") ) { posROI = filepreROI.rfind( "." ); extensionROI = std::string( filepreROI, posROI, filepreROI.length() - 1 ) + extensionROI; filepreROI = std::string( filepreROI, 0, posROI ); } } // std::string post2; // std::ofstream myfile; std::string fnmp_prior = filepreROI + std::string("projectionsROI") + post + std::string(".csv"); std::vector ColumnHeadersROI; for( unsigned int nv = 0; nv < projectionsROI.cols(); nv++ ) { std::string colnameROI = std::string("Variate") + sccan_to_string(nv); ColumnHeadersROI.push_back( colnameROI ); } typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writerROI = WriterType::New(); writerROI->SetFileName( fnmp_prior.c_str() ); writerROI->SetColumnHeaders(ColumnHeadersROI); writerROI->SetInput( &projectionsROI ); try { writerROI->Write(); } catch( itk::ExceptionObject& exp ) { // std::cerr << "Exception caught!" << std::endl; // std::cerr << exp << std::endl; return; } if( have_mask ) { // std::cout << " have_mask WriteSortedVariatesToSpatialImage " << have_mask << std::endl; for( unsigned int vars = 0; vars < varmat.columns(); vars++ ) { post2 = post + sccan_to_string(l_array.get(vars) ); vnl_vector temp = varmat.get_column(l_array.get(vars) ); // // std::cout<<" File Name "<( filename, post2, temp, mask); } } else { std::vector ColumnHeaders; // write out the array2D object std::string fnmp = filepre + std::string("ViewVecs") + std::string(".csv"); for( unsigned int nv = 0; nv < varmat.cols(); nv++ ) { std::string colname = std::string("Variate") + sccan_to_string(nv); ColumnHeaders.push_back( colname ); } WriterType::Pointer writer = WriterType::New(); writer->SetFileName( fnmp.c_str() ); writer->SetColumnHeaders(ColumnHeaders); writer->SetInput( &varmat ); try { writer->Write(); } catch( itk::ExceptionObject& exp ) { // std::cerr << "Exception caught!" << std::endl; // std::cerr << exp << std::endl; return; } } } template void WriteVariatesToSpatialImage( std::string filename, std::string post, vnl_matrix varmat, typename TImage::Pointer mask, vnl_matrix data_mat, bool have_mask, vnl_matrix u_mat ) { vnl_matrix projections = data_mat * varmat; std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); std::string extension; if( pos != std::string::npos ) { extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); extension = std::string( filepre, pos, filepre.length() - 1 ) + extension; filepre = std::string( filepre, 0, pos ); } } std::string post2; std::ofstream myfile; std::string fnmp = filepre + std::string("projections") + post + std::string(".csv"); std::vector ColumnHeaders; for( unsigned int nv = 0; nv < projections.cols(); nv++ ) { std::string colname = std::string("Variate") + sccan_to_string(nv); ColumnHeaders.push_back( colname ); } typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( fnmp.c_str() ); writer->SetColumnHeaders(ColumnHeaders); writer->SetInput( &projections ); try { writer->Write(); } catch( itk::ExceptionObject& exp ) { // std::cerr << "Exception caught!" << std::endl; // std::cerr << exp << std::endl; return; } if( have_mask ) { // std::cerr << " have_mask " << have_mask << std::endl; for( unsigned int vars = 0; vars < varmat.columns(); vars++ ) { post2 = post + sccan_to_string(vars); vnl_vector temp = varmat.get_column(vars); WriteVectorToSpatialImage( filename, post2, temp, mask); } } else { ColumnHeaders.clear(); // write out the array2D object fnmp = filepre + std::string("_Variate_") + post + std::string(".csv"); for( unsigned int nv = 0; nv < varmat.cols(); nv++ ) { std::string colname = std::string("Variate") + sccan_to_string(nv); ColumnHeaders.push_back( colname ); } writer = WriterType::New(); writer->SetFileName( fnmp.c_str() ); writer->SetColumnHeaders(ColumnHeaders); writer->SetInput( &varmat ); try { writer->Write(); } catch( itk::ExceptionObject& exp ) { // std::cerr << "Exception caught!" << std::endl; // std::cerr << exp << std::endl; return; } } if( u_mat.size() > 0 ) { // write out the array2D object for U matrix ColumnHeaders.clear(); fnmp = filepre + std::string("_Umatrix_") + post + std::string(".csv"); for( unsigned int nv = 0; nv < varmat.cols(); nv++ ) { std::string colname = std::string("U") + sccan_to_string(nv); ColumnHeaders.push_back( colname ); } writer = WriterType::New(); writer->SetFileName( fnmp.c_str() ); writer->SetColumnHeaders(ColumnHeaders); writer->SetInput( &u_mat ); try { writer->Write(); } catch( itk::ExceptionObject& exp ) { // std::cerr << "Exception caught!" << std::endl; // std::cerr << exp << std::endl; return; } } } template vnl_matrix CopyImageToVnlMatrix( typename TImage::Pointer p_img ) { typedef vnl_matrix vMatrix; typename TImage::SizeType pMatSize = p_img->GetLargestPossibleRegion().GetSize(); vMatrix p(pMatSize[0], pMatSize[1]); // a (size)x(size+1)-matrix of int's for( unsigned long j = 0; j < p.columns(); ++j ) // loop over columns { for( unsigned long i = 0; i < p.rows(); ++i ) // loop over rows { typename TImage::IndexType ind; ind[0] = i; ind[1] = j; TComp val = p_img->GetPixel(ind); p(i, j) = val; // to access matrix coefficients, } } return p; } template vnl_matrix DeleteRow(vnl_matrix p_in, unsigned int row) { typedef vnl_matrix vMatrix; unsigned int nrows = p_in.rows() - 1; if( row >= nrows ) { nrows = p_in.rows(); } vMatrix p(nrows, p_in.columns() ); unsigned int rowct = 0; for( long i = 0; i < static_cast(p.rows() ); ++i ) // loop over rows { if( i != static_cast(row) ) { p.set_row(rowct, p_in.get_row(i) ); rowct++; } } return p; } int sccanRandom(int n) { return rand() % n; } template vnl_matrix PermuteMatrix( vnl_matrix q, bool doperm = true) { typedef vnl_matrix vMatrix; std::vector permvec; for( unsigned long i = 0; i < q.rows(); i++ ) { permvec.push_back(i); } std::random_shuffle(permvec.begin(), permvec.end(), sccanRandom); // for (unsigned long i=0; i < q.rows(); i++) // // std::cout << " permv " << i << " is " << permvec[i] << std::endl; // for (unsigned long i=0; i < q.rows(); i++) // // std::cout << " permv " << i << " is " << permvec[i] << std::endl; // 1. permute q vMatrix q_perm(q.rows(), q.columns() ); for( unsigned long i = 0; i < q.rows(); i++ ) { unsigned long perm = permvec[i]; if( doperm ) { q_perm.set_row(i, q.get_row(perm) ); } else { q_perm.set_row(i, q.get_row(i) ); } } return q_perm; } template int matrixOperation( itk::ants::CommandLineParser::OptionType *option, itk::ants::CommandLineParser::OptionType * /* outputOption */ = ITK_NULLPTR ) { std::string funcName = std::string("matrixOperation"); typedef itk::Image ImageType; typename ImageType::Pointer outputImage = NULL; // option->SetUsageOption( 2, "multires_matrix_invert[list.txt,maskhighres.nii.gz,masklowres.nii.gz,matrix.mhd]" ); std::string value = option->GetFunction( 0 )->GetName(); if( strcmp( value.c_str(), "multires_matrix_invert" ) == 0 ) { std::string listfn = option->GetFunction( 0 )->GetParameter( 0 ); std::string maskhfn = option->GetFunction( 0 )->GetParameter( 1 ); std::string masklfn = option->GetFunction( 0 )->GetParameter( 2 ); // vnl_matrix matrixinv=MultiResMatrixInvert( listfn, maskhfn, // masklfn ); } return EXIT_SUCCESS; } template int CompareMatrixSizes( vnl_matrix & p, vnl_matrix & q ) { if( p.rows() != q.rows() ) { // std::cerr << " The number of rows must match !!" << std::endl; // std::cerr << " matrix-1 has " << p.rows() << " rows " << std::endl; // std::cerr << " matrix-2 has " << q.rows() << " rows " << std::endl; // std::cerr << " returning " << EXIT_FAILURE << std::endl; // throw std::exception(); return EXIT_FAILURE; } return 0; } template void ReadMatrixFromCSVorImageSet( std::string matname, vnl_matrix & p ) { typedef PixelType Scalar; typedef itk::Image MatrixImageType; typedef itk::ImageFileReader matReaderType; std::string ext = itksys::SystemTools::GetFilenameExtension( matname ); if( strcmp(ext.c_str(), ".csv") == 0 ) { typedef itk::CSVArray2DFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( matname.c_str() ); reader->SetFieldDelimiterCharacter( ',' ); reader->SetStringDelimiterCharacter( '"' ); reader->HasColumnHeadersOn(); reader->HasRowHeadersOff(); reader->UseStringDelimiterCharacterOff(); try { reader->Update(); } catch( itk::ExceptionObject& exp ) { // std::cerr << "Exception caught!" << std::endl; // std::cerr << exp << std::endl; } typedef itk::CSVArray2DDataObject DataFrameObjectType; DataFrameObjectType::Pointer dfo = reader->GetOutput(); p = dfo->GetMatrix(); return; } else { typename matReaderType::Pointer matreader1 = matReaderType::New(); matreader1->SetFileName( matname.c_str() ); matreader1->Update(); p = CopyImageToVnlMatrix( matreader1->GetOutput() ); } return; } template itk::Array2D ConvertImageListToMatrix( std::string imagelist, std::string maskfn, std::string outname ) { std::string ext = itksys::SystemTools::GetFilenameExtension( outname ); typedef itk::Array2D MatrixType; std::vector ColumnHeaders; MatrixType zmat(1, 1); typedef itk::Image ImageType; typedef itk::Image MatrixImageType; typedef itk::ImageFileReader ReaderType; typename ImageType::Pointer mask; ReadImage( mask, maskfn.c_str() ); unsigned long voxct = 0; typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator mIter( mask, mask->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { voxct++; } } std::vector image_fn_list; // first, count the number of files const unsigned int maxChar = 512; char lineBuffer[maxChar]; char filenm[maxChar]; unsigned int filecount = 0; { std::ifstream inputStreamA( imagelist.c_str(), std::ios::in ); if( !inputStreamA.is_open() ) { // std::cout << "Can't open image list file: " << imagelist << std::endl; return zmat; } while( !inputStreamA.eof() ) { inputStreamA.getline( lineBuffer, maxChar, '\n' ); if( sscanf( lineBuffer, "%s ", filenm) != 1 ) { continue; } else { image_fn_list.push_back(std::string(filenm) ); filecount++; } } inputStreamA.close(); } /** declare the output matrix image */ unsigned long xsize = image_fn_list.size(); unsigned long ysize = voxct; typename MatrixImageType::SizeType tilesize; tilesize[0] = xsize; tilesize[1] = ysize; // // std::cout <<" have voxct " << voxct << " and nsub " << filecount << " or " << image_fn_list.size()<< // std::endl; MatrixType matrix(xsize, ysize); matrix.Fill(0); for( unsigned int j = 0; j < image_fn_list.size(); j++ ) { typename ReaderType::Pointer reader2 = ReaderType::New(); reader2->SetFileName( image_fn_list[j] ); reader2->Update(); unsigned long xx = 0, yy = 0, tvoxct = 0; xx = j; for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { yy = tvoxct; matrix[xx][yy] = reader2->GetOutput()->GetPixel(mIter.GetIndex() ); if( j == 0 ) { std::string colname = std::string("V") + sccan_to_string(tvoxct); ColumnHeaders.push_back( colname ); } tvoxct++; } } } if( strcmp(ext.c_str(), ".csv") == 0 ) { // write out the array2D object typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( outname ); writer->SetInput( &matrix ); writer->SetColumnHeaders( ColumnHeaders ); try { writer->Write(); } catch( itk::ExceptionObject& exp ) { // std::cerr << "Exception caught!" << std::endl; // std::cerr << exp << std::endl; return matrix; } return matrix; } if( strcmp(ext.c_str(), ".mha") == 0 || strcmp(ext.c_str(), ".mhd") == 0 ) { typename MatrixImageType::RegionType region; region.SetSize( tilesize ); typename MatrixImageType::Pointer matimage = AllocImage(region); for( unsigned int j = 0; j < image_fn_list.size(); j++ ) { typename ReaderType::Pointer reader2 = ReaderType::New(); reader2->SetFileName( image_fn_list[j] ); reader2->Update(); unsigned long xx = 0, yy = 0, tvoxct = 0; xx = j; typename MatrixImageType::IndexType mind; for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { yy = tvoxct; mind[0] = xx; mind[1] = yy; matimage->SetPixel(mind, reader2->GetOutput()->GetPixel(mIter.GetIndex() ) ); tvoxct++; } } } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( outname ); writer->SetInput( matimage ); writer->Update(); } return matrix; } template int ConvertTimeSeriesImageToMatrix( std::string imagefn, std::string maskfn, std::string outname, double space_smoother, double time_smoother ) { const unsigned int ImageDimension = 4; typedef itk::Image ImageType; typedef itk::Image OutImageType; typedef typename OutImageType::IndexType OutIndexType; typedef typename ImageType::IndexType IndexType; typedef double Scalar; std::string ext = itksys::SystemTools::GetFilenameExtension( outname ); if( strcmp(ext.c_str(), ".csv") != 0 ) { // std::cerr << " must use .csv as output file extension " << std::endl; return EXIT_FAILURE; } typename ImageType::Pointer image1 = ITK_NULLPTR; typename OutImageType::Pointer mask = ITK_NULLPTR; // std::cerr << " imagefn " << imagefn << std::endl; if( imagefn.length() > 3 ) { ReadImage(image1, imagefn.c_str() ); } else { // std::cerr << " cannot read image " << imagefn << std::endl; return 1; } if( space_smoother > 0 ) { typename ImageType::SpacingType spacing = image1->GetSpacing(); typename ImageType::SpacingType spacing2 = image1->GetSpacing(); // basically, don't do any dim-4 smoothing spacing2[3] = sqrt(spacing[0] * spacing[0] + spacing[1] * spacing[1] + spacing[2] * spacing[2]) * 1.e6; image1->SetSpacing(spacing2); typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(space_smoother); filter->SetUseImageSpacingOn(); filter->SetMaximumError(.01f); filter->SetInput(image1); filter->Update(); image1 = filter->GetOutput(); image1->SetSpacing(spacing); } if( time_smoother > 0 ) { typename ImageType::SpacingType spacing = image1->GetSpacing(); typename ImageType::SpacingType spacing2 = image1->GetSpacing(); // basically, don't do any dim-4 smoothing double bigspace = sqrt(spacing[0] * spacing[0] + spacing[1] * spacing[1] + spacing[2] * spacing[2]) * 1.e6; // basically no spatial smoothing spacing2.Fill(bigspace); spacing2[3] = 1; image1->SetSpacing(spacing2); typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(time_smoother); filter->SetUseImageSpacingOn(); filter->SetMaximumError(.01f); filter->SetInput(image1); filter->Update(); image1 = filter->GetOutput(); image1->SetSpacing(spacing); } if( maskfn.length() > 3 ) { ReadImage(mask, maskfn.c_str() ); } else { // std::cerr << " cannot read mask " << maskfn << std::endl; return EXIT_FAILURE; } unsigned int timedims = image1->GetLargestPossibleRegion().GetSize()[ImageDimension - 1]; unsigned long voxct = 0; typedef itk::ExtractImageFilter ExtractFilterType; typedef itk::ImageRegionIteratorWithIndex SliceIt; SliceIt mIter( mask, mask->GetLargestPossibleRegion() ); typedef std::vector LabelSetType; unsigned int maxlabel = 0; LabelSetType myLabelSet1; { /** count the labels in the mask image */ for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { unsigned int label = static_cast( mIter.Get() + 0.5 ); if( label > 0 ) { if( find( myLabelSet1.begin(), myLabelSet1.end(), label ) == myLabelSet1.end() ) { myLabelSet1.push_back( label ); if( label > maxlabel ) { maxlabel = label; } } voxct++; } } } if( maxlabel == 0 ) { // std::cerr << "FAILURE: Max label in input mask " << maskfn << " is 0 " << std::endl; return EXIT_FAILURE; } // std::cout << " timedims " << timedims << " n-Labels " << myLabelSet1.size() << std::endl; typename ImageType::RegionType extractRegion = image1->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); unsigned int sub_vol = 0; extractRegion.SetIndex(ImageDimension - 1, sub_vol ); typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( image1 ); // extractFilter->SetDirectionCollapseToIdentity(); extractFilter->SetDirectionCollapseToSubmatrix(); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); typename OutImageType::Pointer outimage = extractFilter->GetOutput(); outimage->FillBuffer(0); typedef itk::ImageRegionIteratorWithIndex SliceIt; typedef vnl_vector timeVectorType; timeVectorType mSample(timedims, 0); typedef itk::Array2D MatrixType; std::vector ColumnHeaders; if( myLabelSet1.size() > 1 ) { /** sort the labels */ std::sort(myLabelSet1.begin(), myLabelSet1.end() ); /** create a map between the roi and its matrix index */ std::map vectorindexMap; for( unsigned int i = 0; i < myLabelSet1.size(); i++ ) { std::string colname = std::string("Label") + sccan_to_string( myLabelSet1[i] ); ColumnHeaders.push_back( colname ); vectorindexMap[myLabelSet1[i]] = i; } typedef vnl_vector countVectorType; countVectorType countVector( myLabelSet1.size(), 0 ); // std::cout << "Will map the image to its ROIs" << std::endl; MatrixType matrix(timedims, myLabelSet1.size() ); matrix.Fill(0); SliceIt vfIter2( outimage, outimage->GetLargestPossibleRegion() ); voxct = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); unsigned int label = static_cast( mask->GetPixel( ind ) + 0.5 ); if( label > 0 ) { unsigned int vind = vectorindexMap[label]; IndexType tind; // first collect all samples for that location for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); mSample(t) = pix; matrix[t][vind] += pix; countVector[vind] += 1; } } // check mask } for( unsigned int i = 0; i < myLabelSet1.size(); i++ ) { matrix.set_column( i, matrix.get_column( i ) / ( double ) countVector[i] ); } typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( outname ); writer->SetInput( &matrix ); writer->SetColumnHeaders( ColumnHeaders ); try { writer->Write(); } catch( itk::ExceptionObject& exp ) { // std::cerr << "Exception caught!" << std::endl; // std::cerr << exp << std::endl; return EXIT_FAILURE; } // std::cout << " done writing " << std::endl; return EXIT_SUCCESS; } MatrixType matrix(timedims, voxct); matrix.Fill(0); SliceIt vfIter2( outimage, outimage->GetLargestPossibleRegion() ); voxct = 0; for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { OutIndexType ind = vfIter2.GetIndex(); if( mask->GetPixel(ind) >= 0.5 ) { IndexType tind; // first collect all samples for that location for( unsigned int i = 0; i < ImageDimension - 1; i++ ) { tind[i] = ind[i]; } for( unsigned int t = 0; t < timedims; t++ ) { tind[ImageDimension - 1] = t; Scalar pix = image1->GetPixel(tind); mSample(t) = pix; matrix[t][voxct] = pix; } std::string colname = std::string("V") + sccan_to_string(voxct); ColumnHeaders.push_back( colname ); voxct++; } // check mask } typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( outname ); writer->SetInput( &matrix ); writer->SetColumnHeaders( ColumnHeaders ); try { writer->Write(); } catch( itk::ExceptionObject& exp ) { // std::cerr << "Exception caught!" << std::endl; // std::cerr << exp << std::endl; return EXIT_FAILURE; } // std::cout << " done writing " << std::endl; return EXIT_SUCCESS; } template int ConvertCSVVectorToImage( std::string csvfn, std::string maskfn, std::string outname, unsigned long rowOrCol ) { typedef PixelType Scalar; typedef vnl_matrix vMatrix; const unsigned int ImageDimension = 3; typedef itk::Image ImageType; /** read the images */ typename ImageType::Pointer mask = ITK_NULLPTR; ReadImage(mask, maskfn.c_str() ); typename ImageType::Pointer outimage = ITK_NULLPTR; ReadImage(outimage, maskfn.c_str() ); outimage->FillBuffer(0); typedef itk::ImageRegionIteratorWithIndex Iterator; unsigned long mct = 0; Iterator mIter( mask, mask->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { mct++; } } /** we refer to the two view matrices as P and Q */ vMatrix p; p.fill(0); ReadMatrixFromCSVorImageSet(csvfn, p); if( mct != p.rows() && mct != p.cols() ) { // std::cout << " csv-vec rows " << p.rows() << " cols " << p.cols() << " mask non zero elements " << mct << std::endl; throw std::exception(); } if( mct == p.rows() ) { if( rowOrCol > p.cols() - 1 ) { // std::cout << " You are trying to select the " << rowOrCol << "th column but there are only " << p.cols() << " columns " << std::endl; throw std::exception(); } mct = 0; for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { PixelType val = p(mct, rowOrCol); outimage->SetPixel(mIter.GetIndex(), val); mct++; } } } else if( mct == p.cols() ) // map the cols to the vector { if( rowOrCol > p.rows() - 1 ) { // std::cout << " You are trying to select the " << rowOrCol << "th row but there are only " << p.rows() << " rows " << std::endl; throw std::exception(); } mct = 0; for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { PixelType val = p(rowOrCol, mct); outimage->SetPixel(mIter.GetIndex(), val); mct++; } } } typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( outname ); writer->SetInput( outimage ); writer->Update(); return EXIT_SUCCESS; } // p.d. template void ConvertImageVecListToProjection( std::string veclist, std::string imagelist, std::string outname, bool average ) { // typedef itk::Image ImageType; typedef itk::Image ImageType; typedef itk::ImageFileReader ReaderType; std::vector image_fn_list; std::vector vec_fn_list; // first, count the number of files const unsigned int maxChar = 512; char lineBuffer[maxChar], lineBufferVec[maxChar]; char filenm[maxChar], filenmVec[maxChar]; unsigned int filecount = 0, filecountVec = 0; { std::ifstream inputStreamA( imagelist.c_str(), std::ios::in ); if( !inputStreamA.is_open() ) { // std::cout << "Can't open image list file: " << imagelist << std::endl; return; } while( !inputStreamA.eof() ) { inputStreamA.getline( lineBuffer, maxChar, '\n' ); if( sscanf( lineBuffer, "%s ", filenm) != 1 ) { continue; } else { image_fn_list.push_back(std::string(filenm) ); filecount++; } } inputStreamA.close(); } { std::ifstream inputStreamVec( veclist.c_str(), std::ios::in ); if( !inputStreamVec.is_open() ) { // std::cout << "Can't open Vec list file: " << veclist << std::endl; return; } while( !inputStreamVec.eof() ) { inputStreamVec.getline( lineBufferVec, maxChar, '\n' ); if( sscanf( lineBufferVec, "%s ", filenmVec) != 1 ) { continue; } else { vec_fn_list.push_back(std::string(filenmVec) ); filecountVec++; } } inputStreamVec.close(); } std::ofstream myfile; std::string fnmp = outname + std::string(".csv"); myfile.open(fnmp.c_str(), std::ios::out ); typedef itk::ImageRegionIteratorWithIndex Iterator; for( unsigned int j = 0; j < image_fn_list.size(); j++ ) { for( unsigned int k = 0; k < vec_fn_list.size(); k++ ) { double proj = 0, dotSum = 0, dotCounter = 0, dotTotal = 0; typename ReaderType::Pointer reader1 = ReaderType::New(); reader1->SetFileName( image_fn_list[j] ); reader1->Update(); typename ReaderType::Pointer reader2 = ReaderType::New(); reader2->SetFileName( vec_fn_list[k] ); reader2->Update(); Iterator mIter( reader1->GetOutput(), reader1->GetOutput()->GetLargestPossibleRegion() ); Iterator mIter2( reader2->GetOutput(), reader2->GetOutput()->GetLargestPossibleRegion() ); for( mIter.GoToBegin(), mIter2.GoToBegin(); !mIter.IsAtEnd() && !mIter2.IsAtEnd(); ++mIter, ++mIter2 ) { proj = mIter.Get() * mIter2.Get(); dotSum += proj; if( mIter2.Get() > 0 ) { dotCounter += mIter2.Get(); dotTotal += mIter.Get() * mIter2.Get(); } } if( average && dotCounter > 0 ) { dotSum = dotTotal / dotCounter; } if( k == vec_fn_list.size() - 1 ) { myfile << dotSum; } else { myfile << dotSum << " , "; } } myfile << std::endl; } myfile.close(); } template int SVD_One_View( itk::ants::CommandLineParser *sccanparser, unsigned int permct, unsigned int n_evec, unsigned int robustify, unsigned int p_cluster_thresh, unsigned int iterct, unsigned int svd_option, PixelType usel1, PixelType row_sparseness, PixelType smoother, unsigned int covering, bool verbosity, bool getSmall=0 ) { // std::cout << "SVD_One_View" << std::endl; typedef itk::Image ImageType; typedef double Scalar; typedef itk::ants::antsSCCANObject SCCANType; typedef typename SCCANType::MatrixType vMatrix; typedef typename SCCANType::VectorType vVector; typename SCCANType::Pointer sccanobj = SCCANType::New(); sccanobj->SetGetSmall( static_cast(getSmall) ); vMatrix priorROIMat; if( svd_option == 1 ) { // std::cout << " basic-svd " << std::endl; } else { // std::cout << " sparse-svd " << std::endl; // note: 2 (in options) is for svd implementation } itk::ants::CommandLineParser::OptionType::Pointer initOpt = sccanparser->GetOption( "initialization" ); itk::ants::CommandLineParser::OptionType::Pointer maskOpt = sccanparser->GetOption( "mask" ); if( !initOpt || initOpt->GetNumberOfFunctions() == 0 || !maskOpt || maskOpt->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no initialization set, will use data-driven approach." << std::endl; } else { std::string maskfn = maskOpt->GetFunction( 0 )->GetName(); std::string imagelistPrior = initOpt->GetFunction( 0 )->GetName(); // std::cout << "you will initialize with " << imagelistPrior << std::endl; std::string outname = "none"; priorROIMat = ConvertImageListToMatrix( imagelistPrior, maskfn, outname ); // std::cout << priorROIMat.rows() << " " << priorROIMat.cols() << std::endl; sccanobj->SetMatrixPriorROI( priorROIMat); } itk::ants::CommandLineParser::OptionType::Pointer init2Opt = sccanparser->GetOption( "initialization2" ); itk::ants::CommandLineParser::OptionType::Pointer mask2Opt = sccanparser->GetOption( "mask2" ); if( !init2Opt || init2Opt->GetNumberOfFunctions() == 0 || !mask2Opt || mask2Opt->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no initialization set, will use data-driven approach." << std::endl; } else { std::string maskfn = mask2Opt->GetFunction( 0 )->GetName(); std::string imagelistPrior = init2Opt->GetFunction( 0 )->GetName(); // std::cout << "you will initialize Q with " << imagelistPrior << std::endl; std::string outname = "none"; priorROIMat = ConvertImageListToMatrix( imagelistPrior, maskfn, outname ); // std::cout << priorROIMat.rows() << " " << priorROIMat.cols() << std::endl; sccanobj->SetMatrixPriorROI2( priorROIMat ); } itk::ants::CommandLineParser::OptionType::Pointer outputOption = sccanparser->GetOption( "output" ); if( !outputOption || outputOption->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no output option set." << std::endl; } itk::ants::CommandLineParser::OptionType::Pointer option = sccanparser->GetOption( "svd" ); PixelType gradstep = vnl_math_abs( usel1 ); sccanobj->SetCovering( covering ); sccanobj->SetSilent( ! verbosity ); if( usel1 > 0 ) { sccanobj->SetUseL1( true ); } else { sccanobj->SetUseL1( false ); } sccanobj->SetGradStep( gradstep ); sccanobj->SetMaximumNumberOfIterations(iterct); sccanobj->SetRowSparseness( row_sparseness ); sccanobj->SetSmoother( smoother ); /** read the matrix images */ /** we refer to the two view matrices as P and Q */ std::string pmatname = std::string(option->GetFunction( 0 )->GetParameter( 0 ) ); vMatrix p; ReadMatrixFromCSVorImageSet(pmatname, p); typename ImageType::Pointer mask1 = ITK_NULLPTR; bool have_p_mask = false; have_p_mask = ReadImage(mask1, option->GetFunction( 0 )->GetParameter( 1 ).c_str() ); double FracNonZero1 = sccanparser->Convert( option->GetFunction( 0 )->GetParameter( 2 ) ); vMatrix priorScaleMat; if( svd_option == 7 ) { FracNonZero1 = sccanparser->Convert( option->GetFunction( 0 )->GetParameter( 4 ) ); std::string imagelistPrior = option->GetFunction( 0 )->GetParameter( 2 ); // std::string priorScaleFile = option->GetFunction( 0 )->GetParameter( 3 ); std::string outname = "prior.mhd"; ConvertImageListToMatrix( imagelistPrior, option->GetFunction( 0 )->GetParameter( 1 ), outname ); ReadMatrixFromCSVorImageSet(outname, priorROIMat); // ReadMatrixFromCSVorImageSet(priorScaleFile, priorScaleMat); } // std::cout << " frac nonzero " << FracNonZero1 << std::endl; if( robustify > 0 ) { // std::cout << " make robust " << std::endl; p = sccanobj->RankifyMatrixColumns(p); } /** the penalties define the fraction of non-zero values for each view */ if( FracNonZero1 < 0 ) { FracNonZero1 = fabs(FracNonZero1); sccanobj->SetKeepPositiveP(false); } /** read the nuisance matrix image */ vMatrix r; if( option->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { std::string nuis_img = option->GetFunction( 0 )->GetParameter( 3 ); if( nuis_img.length() > 3 && svd_option != 7 ) { // std::cout << " nuis_img " << nuis_img << std::endl; ReadMatrixFromCSVorImageSet(nuis_img, r); if( CompareMatrixSizes( p, r ) == EXIT_FAILURE ) { return EXIT_FAILURE; } itk::ants::CommandLineParser::OptionType::Pointer partialccaOpt = sccanparser->GetOption( "partial-scca-option" ); std::string partialccaoption = std::string("PQ"); if( partialccaOpt ) { // enum SCCANFormulationType{ PQ , PminusRQ , PQminusR , PminusRQminusR , PQR }; if( partialccaOpt->GetNumberOfFunctions() > 0 ) { partialccaoption = sccanparser->Convert( partialccaOpt->GetFunction()->GetName() ); } // std::cout << " Partial SCCA option " << partialccaoption << std::endl; if( !partialccaoption.compare( std::string( "PQ" ) ) ) { sccanobj->SetSCCANFormulation( SCCANType::PQ ); } else if( !partialccaoption.compare( std::string( "PminusRQ" ) ) ) { sccanobj->SetSCCANFormulation( SCCANType::PminusRQ ); } else if( !partialccaoption.compare( std::string( "PQminusR" ) ) ) { sccanobj->SetSCCANFormulation( SCCANType::PQminusR ); } else if( !partialccaoption.compare( std::string( "PminusRQminusR" ) ) ) { sccanobj->SetSCCANFormulation( SCCANType::PminusRQminusR ); } } } } else { // std::cout << " No nuisance parameters." << std::endl; } sccanobj->SetFractionNonZeroP(FracNonZero1); sccanobj->SetMinClusterSizeP( p_cluster_thresh ); if( robustify > 0 ) { // std::cout << " make robust " << std::endl; p = sccanobj->RankifyMatrixColumns(p); } sccanobj->SetMatrixP( p ); sccanobj->SetMatrixR( r ); sccanobj->SetMaskImageP( mask1 ); double truecorr = 0; if( svd_option == 1 ) { truecorr = sccanobj->SparseReconHome(n_evec); } else if( svd_option == 3 ) { truecorr = sccanobj->SparseArnoldiSVD(n_evec); // cgsparse } else if( svd_option == 4 ) { truecorr = sccanobj->NetworkDecomposition( n_evec ); } else if( svd_option == 5 ) { truecorr = sccanobj->LASSO( n_evec ); } else if( svd_option == 2 ) { truecorr = sccanobj->CGSPCA(n_evec); // cgspca } else if( svd_option == 6 ) { truecorr = sccanobj->SparseRecon(n_evec); // sparse (default) } else if( svd_option == 7 ) { // sccanobj->SetPriorScaleMat( priorScaleMat); sccanobj->SetMatrixPriorROI( priorROIMat); sccanobj->SetFlagForSort(); sccanobj->SetLambda(sccanparser->Convert( option->GetFunction( 0 )->GetParameter( 3 ) ) ); truecorr = sccanobj->SparseReconPrior(n_evec, true); // Prior } else { truecorr = sccanobj->SparseArnoldiSVDGreedy(n_evec); // sparse (default) } vVector w_p = sccanobj->GetVariateP(0); // std::cout << " true-corr " << sccanobj->GetCanonicalCorrelations() << std::endl; if( outputOption ) { std::string filename = outputOption->GetFunction( 0 )->GetName(); // std::cout << " write " << filename << std::endl; std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); std::string extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); extension = std::string( filepre, pos, filepre.length() - 1 ) + extension; filepre = std::string( filepre, 0, pos ); } std::string post = std::string("View1vec"); WriteVariatesToSpatialImage( filename, post, sccanobj->GetVariatesP(), mask1, sccanobj->GetMatrixP(), have_p_mask, sccanobj->GetMatrixU() ); // WriteSortedVariatesToSpatialImage( filename, post, sccanobj->GetVariatesP() , mask1 , // sccanobj->GetMatrixP() , have_p_mask,sccanobj->GetSortFinalLocArray(),priorROIMat ); /** write the eigevalues to the csv file */ std::string fnmp = filepre + std::string("_eigenvalues.csv"); std::vector ColumnHeaders; std::string colname = std::string("Eigenvalue"); ColumnHeaders.push_back( colname ); typedef itk::CSVNumericObjectFileWriter CWriterType; CWriterType::Pointer cwriter = CWriterType::New(); cwriter->SetFileName( fnmp.c_str() ); cwriter->SetColumnHeaders(ColumnHeaders); vnl_matrix evals; evals.set_size(sccanobj->GetCanonicalCorrelations().size(), 1); for( unsigned int i = 0; i < sccanobj->GetCanonicalCorrelations().size(); i++ ) { double evil = sccanobj->GetCanonicalCorrelations() (i); evals(i, 0) = evil; } cwriter->SetInput( &evals ); cwriter->Write(); } // permutation test if( ( svd_option == 4 || svd_option == 5 ) && permct > 0 ) { // std::cout << "Begin" << permct << " permutations " << std::endl; unsigned long perm_exceed_ct = 0; for( unsigned long pct = 0; pct <= permct; pct++ ) { // 0. compute permutation for q ( switch around rows ) vMatrix p_perm = PermuteMatrix( sccanobj->GetOriginalMatrixP() ); vMatrix r_perm = PermuteMatrix( sccanobj->GetOriginalMatrixR() ); sccanobj->SetMatrixP( p_perm ); sccanobj->SetMatrixR( r_perm ); double permcorr = 1.e9; // if ( pct > 76 && pct < 79 ) if( svd_option == 4 ) { permcorr = sccanobj->NetworkDecomposition(n_evec); // cgsparse } if( svd_option == 5 ) { permcorr = sccanobj->LASSO( n_evec ); // cgsparse } if( permcorr < truecorr ) { perm_exceed_ct++; } // end solve cca permutation // std::cout << permcorr << " p-value " << (double)perm_exceed_ct // / (pct + 1) << " ct " << pct << " true " << truecorr << " vs " << permcorr << std::endl; } } return EXIT_SUCCESS; } template int SCCA_vnl( itk::ants::CommandLineParser *sccanparser, unsigned int permct, unsigned int n_evec, unsigned int newimp, unsigned int robustify, unsigned int p_cluster_thresh, unsigned int q_cluster_thresh, unsigned int iterct, PixelType usel1, PixelType uselong, PixelType row_sparseness, PixelType smoother, unsigned int covering, PixelType priorWeight = 0, unsigned int verbosity = 0 ) { itk::ants::CommandLineParser::OptionType::Pointer outputOption = sccanparser->GetOption( "output" ); bool writeoutput = true; if( !outputOption || outputOption->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no output option set." << std::endl; writeoutput = false; } if( newimp > 0 ) { // std::cout << "New imp irrelevant " << std::endl; } itk::ants::CommandLineParser::OptionType::Pointer option = sccanparser->GetOption( "scca" ); typedef itk::Image ImageType; typedef double Scalar; typedef itk::ants::antsSCCANObject SCCANType; typedef typename SCCANType::MatrixType vMatrix; typedef typename SCCANType::VectorType vVector; typename SCCANType::Pointer sccanobj = SCCANType::New(); sccanobj->SetCovering( covering ); sccanobj->SetSilent( ! verbosity ); sccanobj->SetPriorWeight( priorWeight ); sccanobj->SetMaximumNumberOfIterations(iterct); if( uselong > 0 ) { sccanobj->SetUseLongitudinalFormulation( uselong ); } PixelType gradstep = vnl_math_abs( usel1 ); if( usel1 > 0 ) { sccanobj->SetUseL1( true ); } else { sccanobj->SetUseL1( false ); } vMatrix priorROIMat; vMatrix priorROIMat2; itk::ants::CommandLineParser::OptionType::Pointer initOpt = sccanparser->GetOption( "initialization" ); itk::ants::CommandLineParser::OptionType::Pointer maskOpt = sccanparser->GetOption( "mask" ); if( !initOpt || initOpt->GetNumberOfFunctions() == 0 || !maskOpt || maskOpt->GetNumberOfFunctions() == 0 ) { } else { std::string maskfn = maskOpt->GetFunction( 0 )->GetName(); std::string imagelistPrior = initOpt->GetFunction( 0 )->GetName(); // std::cout << "you will initialize P with " << imagelistPrior << " and " << maskfn << std::endl; std::string outname = "none"; priorROIMat = ConvertImageListToMatrix( imagelistPrior, maskfn, outname ); // std::cout << priorROIMat.rows() << " " << priorROIMat.cols() << std::endl; sccanobj->SetMatrixPriorROI( priorROIMat); } itk::ants::CommandLineParser::OptionType::Pointer init2Opt = sccanparser->GetOption( "initialization2" ); itk::ants::CommandLineParser::OptionType::Pointer mask2Opt = sccanparser->GetOption( "mask2" ); if( !init2Opt || init2Opt->GetNumberOfFunctions() == 0 || !mask2Opt || mask2Opt->GetNumberOfFunctions() == 0 ) { // itkDebugStatement( std::cerr << "Warning: no Q initialization set, will use data-driven approach." << std::endl ); } else { std::string maskfn = mask2Opt->GetFunction( 0 )->GetName(); std::string imagelistPrior = init2Opt->GetFunction( 0 )->GetName(); // std::cout << "you will initialize Q with " << imagelistPrior << " and " << maskfn << std::endl; std::string outname = "none"; priorROIMat2 = ConvertImageListToMatrix( imagelistPrior, maskfn, outname ); // std::cout << priorROIMat2.rows() << " " << priorROIMat2.cols() << std::endl; sccanobj->SetMatrixPriorROI2( priorROIMat2 ); } sccanobj->SetGradStep( gradstep ); sccanobj->SetSmoother( smoother ); sccanobj->SetRowSparseness( row_sparseness ); Scalar pinvtoler = 1.e-6; /** read the matrix images */ /** we refer to the two view matrices as P and Q */ std::string pmatname = std::string(option->GetFunction( 0 )->GetParameter( 0 ) ); vMatrix p; // // std::cout <<" read-p "<< std::endl; ReadMatrixFromCSVorImageSet(pmatname, p); std::string qmatname = std::string(option->GetFunction( 0 )->GetParameter( 1 ) ); vMatrix q; // // std::cout <<" read-q "<< std::endl; ReadMatrixFromCSVorImageSet(qmatname, q); // // std::cout << q.get_row(0) << std::endl; // // std::cout << q.mean() << std::endl; if( CompareMatrixSizes( p, q ) == EXIT_FAILURE ) { return EXIT_FAILURE; } typename ImageType::Pointer mask1 = ITK_NULLPTR; std::string mask1fn = option->GetFunction( 0 )->GetParameter( 2 ); bool have_p_mask = false; if ( mask1fn.length() > 5 ) { have_p_mask = ReadImage(mask1, mask1fn.c_str() ); } typename ImageType::Pointer mask2 = ITK_NULLPTR; std::string mask2fn = option->GetFunction( 0 )->GetParameter( 3 ); bool have_q_mask = false; if ( mask2fn.length() > 5 ) { have_q_mask = ReadImage(mask2, mask2fn.c_str() ); } /** the penalties define the fraction of non-zero values for each view */ double FracNonZero1 = sccanparser->Convert( option->GetFunction( 0 )->GetParameter( 4 ) ); if( FracNonZero1 < 0 ) { FracNonZero1 = fabs(FracNonZero1); sccanobj->SetKeepPositiveP(false); // true if P sparsity > 0 } double FracNonZero2 = sccanparser->Convert( option->GetFunction( 0 )->GetParameter( 5 ) ); if( FracNonZero2 < 0 ) { FracNonZero2 = fabs(FracNonZero2); sccanobj->SetKeepPositiveQ(false); // true if Q sparsity > 0 } sccanobj->SetFractionNonZeroP(FracNonZero1); sccanobj->SetFractionNonZeroQ(FracNonZero2); sccanobj->SetMinClusterSizeP( p_cluster_thresh ); sccanobj->SetMinClusterSizeQ( q_cluster_thresh ); if( robustify > 0 ) { // std::cout << " make robust " << std::endl; p = sccanobj->RankifyMatrixColumns(p); q = sccanobj->RankifyMatrixColumns(q); } sccanobj->SetMatrixP( p ); sccanobj->SetMatrixQ( q ); sccanobj->SetMaskImageP( mask1 ); sccanobj->SetMaskImageQ( mask2 ); sccanobj->SparsePartialArnoldiCCA(n_evec ); vVector w_p = sccanobj->GetVariateP(0); vVector w_q = sccanobj->GetVariateQ(0); vVector sccancorrs = sccanobj->GetCanonicalCorrelations(); // std::cout << " true-corr " << sccancorrs << std::endl; std::string filename = outputOption->GetFunction( 0 )->GetName(); std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); std::string extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); extension = std::string( filepre, pos, filepre.length() - 1 ) + extension; filepre = std::string( filepre, 0, pos ); } std::string post = std::string("View1vec"); if( writeoutput ) { WriteVariatesToSpatialImage( filename, post, sccanobj->GetVariatesP(), mask1, sccanobj->GetMatrixP(), have_p_mask, sccanobj->GetMatrixU() ); post = std::string("View2vec"); WriteVariatesToSpatialImage( filename, post, sccanobj->GetVariatesQ(), mask2, sccanobj->GetMatrixQ(), have_q_mask, sccanobj->GetMatrixU() ); } /** begin permutation 1. q_pvMatrix CqqInv=vnl_svd_inverse(Cqq); q=q*CqqInv; sermuted ; 2. scca ; 3. test corrs and weights significance */ if( permct > 0 ) { vnl_vector perm_exceed_ct( sccancorrs.size(), 0 ); vVector w_p_signif_ct(w_p.size(), 0); vVector w_q_signif_ct(w_q.size(), 0); for( unsigned long pct = 0; pct <= permct; pct++ ) { // 0. compute permutation for q ( switch around rows ) sccanobj->SetFractionNonZeroP(FracNonZero1); sccanobj->SetFractionNonZeroQ(FracNonZero2); sccanobj->SetGradStep( gradstep ); sccanobj->SetMatrixP( p ); ReadMatrixFromCSVorImageSet(qmatname, q); vMatrix q_perm = PermuteMatrix( q ); sccanobj->SetMatrixQ( q_perm ); sccanobj->SparsePartialArnoldiCCA(n_evec ); vVector permcorrs = sccanobj->GetCanonicalCorrelations(); // std::cout << " perm-corr " << permcorrs << " ct " << pct << " p-values "; for( unsigned int kk = 0; kk < permcorrs.size(); kk++ ) { if( permcorrs[kk] > sccancorrs[kk] ) { perm_exceed_ct[kk]++; } // std::cout << ( double ) perm_exceed_ct[kk] / (pct + 1) << " "; } // std::cout << std::endl; vVector w_p_perm = sccanobj->GetVariateP(0); vVector w_q_perm = sccanobj->GetVariateQ(0); for( unsigned long j = 0; j < w_p.size(); j++ ) { if( w_p_perm(j) > w_p(j) ) { w_p_signif_ct(j) = w_p_signif_ct(j)++; } } for( unsigned long j = 0; j < w_q.size(); j++ ) { if( w_q_perm(j) > w_q(j) ) { w_q_signif_ct(j) = w_q_signif_ct(j)++; } } // end solve cca permutation if( pct == permct ) { // std::cout << "final_p_values" << ","; for( unsigned int kk = 0; kk < permcorrs.size(); kk++ ) { // std::cout << ( double ) perm_exceed_ct[kk] / (pct + 1) << ","; } // std::cout << "x" << std::endl; std::ofstream myfile; std::string fnmp = filepre + std::string("_summary.csv"); myfile.open(fnmp.c_str(), std::ios::out ); myfile << "TypeOfMeasure" << ","; for( unsigned int kk = 0; kk < permcorrs.size(); kk++ ) { std::string colname = std::string("Variate") + sccan_to_string(kk); myfile << colname << ","; } myfile << "x" << std::endl; myfile << "final_p_values" << ","; for( unsigned int kk = 0; kk < permcorrs.size(); kk++ ) { myfile << ( double ) perm_exceed_ct[kk] / (pct + 1) << ","; } myfile << "x" << std::endl; myfile << "corrs" << ","; for( unsigned int kk = 0; kk < permcorrs.size(); kk++ ) { myfile << sccancorrs[kk] << ","; } myfile << "x" << std::endl; myfile.close(); } } unsigned long psigct = 0, qsigct = 0; for( unsigned long j = 0; j < w_p.size(); j++ ) { if( w_p(j) > pinvtoler ) { w_p_signif_ct(j) = 1.0 - (double)w_p_signif_ct(j) / (double)(permct); if( w_p_signif_ct(j) > 0.949 ) { psigct++; } } else { w_p_signif_ct(j) = 0; } } for( unsigned long j = 0; j < w_q.size(); j++ ) { if( w_q(j) > pinvtoler ) { w_q_signif_ct(j) = 1.0 - (double)w_q_signif_ct(j) / (double)(permct); if( w_q_signif_ct(j) > 0.949 ) { qsigct++; } } else { w_q_signif_ct(j) = 0; } } if( writeoutput ) { post = std::string("View1pval"); if( have_p_mask ) { WriteVectorToSpatialImage( filename, post, w_p_signif_ct, mask1); } post = std::string("View2pval"); if( have_q_mask ) { WriteVectorToSpatialImage( filename, post, w_q_signif_ct, mask2); } } } return EXIT_SUCCESS; } template int mSCCA_vnl( itk::ants::CommandLineParser *sccanparser, unsigned int permct, bool run_partial_scca = false, unsigned int n_e_vecs = 3, unsigned int newimp = 0, unsigned int robustify = 0, unsigned int p_cluster_thresh = 100, unsigned int q_cluster_thresh = 1, unsigned int iterct = 20 ) { // std::cout << " Entering MSCCA --- computing " << n_e_vecs << " canonical variates by default. " << std::endl; itk::ants::CommandLineParser::OptionType::Pointer outputOption = sccanparser->GetOption( "output" ); if( !outputOption || outputOption->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no output option set." << std::endl; } // std::cout << " newimp " << newimp << std::endl; itk::ants::CommandLineParser::OptionType::Pointer option = sccanparser->GetOption( "scca" ); typedef itk::Image ImageType; typedef double Scalar; typedef itk::ants::antsSCCANObject SCCANType; typedef itk::Image MatrixImageType; typename SCCANType::Pointer sccanobj = SCCANType::New(); sccanobj->SetMaximumNumberOfIterations(iterct); typedef typename SCCANType::MatrixType vMatrix; typedef typename SCCANType::VectorType vVector; /** we refer to the two view matrices as P and Q */ typedef itk::Image ImageType; typedef double Scalar; typedef itk::Image MatrixImageType; /** read the matrix images */ std::string pmatname = std::string(option->GetFunction( 0 )->GetParameter( 0 ) ); vMatrix pin; ReadMatrixFromCSVorImageSet(pmatname, pin); std::string qmatname = std::string(option->GetFunction( 0 )->GetParameter( 1 ) ); vMatrix qin; ReadMatrixFromCSVorImageSet(qmatname, qin); std::string rmatname = std::string(option->GetFunction( 0 )->GetParameter( 2 ) ); vMatrix rin; ReadMatrixFromCSVorImageSet(rmatname, rin); if( CompareMatrixSizes( pin, qin ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( CompareMatrixSizes( qin, rin ) == EXIT_FAILURE ) { return EXIT_FAILURE; } if( CompareMatrixSizes( pin, rin ) == EXIT_FAILURE ) { return EXIT_FAILURE; } typename ImageType::Pointer mask1 = ITK_NULLPTR; bool have_p_mask = ReadImage(mask1, option->GetFunction( 0 )->GetParameter( 3 ).c_str() ); typename ImageType::Pointer mask2 = ITK_NULLPTR; bool have_q_mask = ReadImage(mask2, option->GetFunction( 0 )->GetParameter( 4 ).c_str() ); typename ImageType::Pointer mask3 = ITK_NULLPTR; /** the penalties define the fraction of non-zero values for each view */ double FracNonZero1 = sccanparser->Convert( option->GetFunction( 0 )->GetParameter( 6 ) ); if( FracNonZero1 < 0 ) { FracNonZero1 = fabs(FracNonZero1); sccanobj->SetKeepPositiveP(false); } double FracNonZero2 = sccanparser->Convert( option->GetFunction( 0 )->GetParameter( 7 ) ); if( FracNonZero2 < 0 ) { FracNonZero2 = fabs(FracNonZero2); sccanobj->SetKeepPositiveQ(false); } double FracNonZero3 = sccanparser->Convert( option->GetFunction( 0 )->GetParameter( 8 ) ); if( FracNonZero3 < 0 ) { FracNonZero3 = fabs(FracNonZero3); sccanobj->SetKeepPositiveR(false); } sccanobj->SetFractionNonZeroP(FracNonZero1); sccanobj->SetFractionNonZeroQ(FracNonZero2); sccanobj->SetFractionNonZeroR(FracNonZero3); for( unsigned int leave_out = pin.rows(); leave_out <= pin.rows(); leave_out++ ) { // std::cout << " Leaving Out " << leave_out << std::endl; vVector p_leave_out; vVector q_leave_out; if( leave_out < pin.rows() ) { p_leave_out = pin.get_row(leave_out); q_leave_out = qin.get_row(leave_out); } vMatrix p = DeleteRow( pin, leave_out ); vMatrix q = DeleteRow( qin, leave_out ); vMatrix r = DeleteRow( rin, leave_out ); sccanobj->SetMinClusterSizeP( p_cluster_thresh ); sccanobj->SetMinClusterSizeQ( q_cluster_thresh ); if( robustify > 0 ) { // std::cout << " make robust " << std::endl; p = sccanobj->RankifyMatrixColumns(p); q = sccanobj->RankifyMatrixColumns(q); r = sccanobj->RankifyMatrixColumns(r); } double truecorr = 0; if( run_partial_scca ) { // std::cout << " begin partial PQ " << std::endl; typename SCCANType::Pointer sccanobjCovar = SCCANType::New(); sccanobjCovar->SetMaximumNumberOfIterations(iterct); sccanobjCovar->SetMatrixP( p ); sccanobjCovar->SetMatrixQ( q ); sccanobjCovar->SetMatrixR( r ); sccanobjCovar->SetMinClusterSizeP( p_cluster_thresh ); sccanobjCovar->SetMinClusterSizeQ( q_cluster_thresh ); itk::ants::CommandLineParser::OptionType::Pointer partialccaOpt = sccanparser->GetOption( "partial-scca-option" ); std::string partialccaoption = std::string("PQ"); if( partialccaOpt ) { // enum SCCANFormulationType{ PQ , PminusRQ , PQminusR , PminusRQminusR , PQR }; if( partialccaOpt->GetNumberOfFunctions() > 0 ) { partialccaoption = sccanparser->Convert( partialccaOpt->GetFunction()->GetName() ); } // std::cout << " Partial SCCA option " << partialccaoption << std::endl; if( !partialccaoption.compare( std::string( "PQ" ) ) ) { sccanobjCovar->SetSCCANFormulation( SCCANType::PQ ); } else if( !partialccaoption.compare( std::string( "PminusRQ" ) ) ) { sccanobjCovar->SetSCCANFormulation( SCCANType::PminusRQ ); } else if( !partialccaoption.compare( std::string( "PQminusR" ) ) ) { sccanobjCovar->SetSCCANFormulation( SCCANType::PQminusR ); } else if( !partialccaoption.compare( std::string( "PminusRQminusR" ) ) ) { sccanobjCovar->SetSCCANFormulation( SCCANType::PminusRQminusR ); } } sccanobjCovar->SetFractionNonZeroP(FracNonZero1); sccanobjCovar->SetKeepPositiveP( sccanobj->GetKeepPositiveP() ); sccanobjCovar->SetFractionNonZeroQ(FracNonZero2); sccanobjCovar->SetKeepPositiveQ( sccanobj->GetKeepPositiveQ() ); sccanobjCovar->SetMaskImageP( mask1 ); sccanobjCovar->SetMaskImageQ( mask2 ); if( newimp == 1 ) { truecorr = sccanobjCovar->SparsePartialCCA(n_e_vecs); } else if( newimp == 0 ) { truecorr = sccanobjCovar->SparsePartialArnoldiCCA(n_e_vecs); } // truecorr=sccanobjCovar->RunSCCAN2multiple(n_e_vecs ); // std::cout << " partialed out corr "; for( unsigned int ff = 0; ff < sccanobjCovar->GetCanonicalCorrelations().size(); ff++ ) { // std::cout << " " << sccanobjCovar->GetCanonicalCorrelations()[ff]; } // std::cout << std::endl; if( outputOption ) { std::string filename = outputOption->GetFunction( 0 )->GetName(); std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); std::string extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); extension = std::string( filepre, pos, filepre.length() - 1 ) + extension; filepre = std::string( filepre, 0, pos ); } std::string post = std::string("View1vec"); WriteVariatesToSpatialImage( filename, post, sccanobjCovar->GetVariatesP(), mask1, sccanobjCovar->GetMatrixP( ), have_p_mask, sccanobj->GetMatrixU() ); post = std::string("View2vec"); WriteVariatesToSpatialImage( filename, post, sccanobjCovar->GetVariatesQ(), mask2, sccanobjCovar->GetMatrixQ( ), have_q_mask, sccanobj->GetMatrixU() ); } /** begin permutation 1. q_pvMatrix CqqInv=vnl_svd_inverse(Cqq); q=q*CqqInv; sermuted ; 2. scca ; 3. test corrs and weights significance */ if( permct > 0 ) { unsigned long perm_exceed_ct = 0; vVector w_p_signif_ct(p.cols(), 0); vVector w_q_signif_ct(q.cols(), 0); for( unsigned long pct = 0; pct <= permct; pct++ ) { /** both the new object and the copy object should produce the same results - verified in 1 example!*/ // typename SCCANType::Pointer sccanobjPerm=sccanobjCovar; typename SCCANType::Pointer sccanobjPerm = SCCANType::New(); sccanobjPerm->SetMaximumNumberOfIterations(iterct); sccanobjPerm->SetMinClusterSizeP( p_cluster_thresh ); sccanobjPerm->SetMinClusterSizeQ( q_cluster_thresh ); sccanobjPerm->SetFractionNonZeroP(FracNonZero1); sccanobjPerm->SetKeepPositiveP( sccanobj->GetKeepPositiveP() ); sccanobjPerm->SetKeepPositiveQ( sccanobj->GetKeepPositiveQ() ); sccanobjPerm->SetFractionNonZeroQ(FracNonZero2); sccanobjPerm->SetMaskImageP( mask1 ); sccanobjPerm->SetMaskImageQ( mask2 ); // 0. compute permutation for q ( switch around rows ) vMatrix p_perm = PermuteMatrix( sccanobjCovar->GetMatrixP() ); vMatrix q_perm = PermuteMatrix( sccanobjCovar->GetMatrixQ() ); vMatrix r_perm = PermuteMatrix( r ); sccanobjPerm->SetMatrixP( p_perm ); sccanobjPerm->SetMatrixQ( q_perm ); sccanobjPerm->SetMatrixR( r_perm ); // double permcorr=sccanobjPerm->RunSCCAN2(); sccanobjPerm->SetSCCANFormulation( sccanobjCovar->GetSCCANFormulation() ); sccanobjPerm->SetAlreadyWhitened( false ); double permcorr = 0; if( newimp == 0 ) { permcorr = sccanobjPerm->SparsePartialArnoldiCCA(n_e_vecs); } else if( newimp == 1 ) { permcorr = sccanobjPerm->SparsePartialCCA(n_e_vecs); } // permcorr=sccanobjPerm->RunSCCAN2multiple(n_e_vecs);// // else permcorr= // std::cout << " partialed out corr "; for( unsigned int ff = 0; ff < sccanobjPerm->GetCanonicalCorrelations().size(); ff++ ) { // std::cout << " " << sccanobjPerm->GetCanonicalCorrelations()[ff]; } // std::cout << std::endl; if( permcorr > truecorr ) { perm_exceed_ct++; } /* vVector w_p_perm=sccanobjPerm->GetVariateP(0); vVector w_q_perm=sccanobjPerm->GetVariateQ(0); for (unsigned long j=0; j sccanobjCovar->GetVariateP(0)(j)) { w_p_signif_ct(j)=w_p_signif_ct(j)++; } for (unsigned long j=0; j sccanobjCovar->GetVariateQ(0)(j) ) { w_q_signif_ct(j)=w_q_signif_ct(j)++; } // end solve cca permutation */ // std::cout << permcorr << " p-value " << (double)perm_exceed_ct // / (pct + 1) << " ct " << pct << " true " << truecorr << std::endl; } unsigned long psigct = 0, qsigct = 0; Scalar pinvtoler = 1.e-6; for( unsigned long j = 0; j < sccanobjCovar->GetVariateP(0).size(); j++ ) { if( sccanobjCovar->GetVariateP(0) (j) > pinvtoler ) { w_p_signif_ct(j) = 1.0 - (double)w_p_signif_ct(j) / (double)(permct); if( w_p_signif_ct(j) > 0.949 ) { psigct++; } } else { w_p_signif_ct(j) = 0; } } for( unsigned long j = 0; j < sccanobjCovar->GetVariateQ(0).size(); j++ ) { if( sccanobjCovar->GetVariateQ(0) (j) > pinvtoler ) { w_q_signif_ct(j) = 1.0 - (double)w_q_signif_ct(j) / (double)(permct); if( w_q_signif_ct(j) > 0.949 ) { qsigct++; } } else { w_q_signif_ct(j) = 0; } } // std::cout << " p-value " << (double)perm_exceed_ct / (permct) << " ct " << permct << std::endl; // std::cout << " p-vox " << (double)psigct / sccanobjCovar->GetVariateP(0).size() << " ct " << permct // << std::endl; // std::cout << " q-vox " << (double)qsigct / sccanobjCovar->GetVariateP(0).size() << " ct " << permct // << std::endl; // // std::cout << "Here in sccan after printing "<SetMatrixP( p ); sccanobj->SetMatrixQ( q ); sccanobj->SetMatrixR( r ); sccanobj->SetMaskImageP( mask1 ); sccanobj->SetMaskImageQ( mask2 ); sccanobj->SetMaskImageR( mask3 ); truecorr = sccanobj->RunSCCAN3(); vVector w_p = sccanobj->GetPWeights(); vVector w_q = sccanobj->GetQWeights(); vVector w_r = sccanobj->GetRWeights(); // std::cout << " final correlation " << truecorr << std::endl; // // std::cout << " Projection-P " << p*w_p << std::endl; // // std::cout << " Projection-Q " << q*w_q << std::endl; if( leave_out < pin.rows() ) { // std::cout << " Projection-leave-P " << dot_product(p_leave_out, w_p) << std::endl; // std::cout << " Projection-leave-Q " << dot_product(q_leave_out, w_q) << std::endl; } // // std::cout << " r weights " << w_r << std::endl; for( unsigned long j = 0; j < w_r.size(); j++ ) { if( w_r(j) > 0 ) { // std::cout << " r-weight " << j << "," << w_r(j) << std::endl; } } if( outputOption ) { std::string filename = outputOption->GetFunction( 0 )->GetName(); // std::cout << " write " << filename << std::endl; std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); std::string extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); extension = std::string( filepre, pos, filepre.length() - 1 ) + extension; filepre = std::string( filepre, 0, pos ); } std::string post = std::string("View1vec"); WriteVariatesToSpatialImage( filename, post, sccanobj->GetVariatesP(), mask1, sccanobj->GetMatrixP(), have_p_mask, sccanobj->GetMatrixU() ); post = std::string("View2vec"); WriteVariatesToSpatialImage( filename, post, sccanobj->GetVariatesQ(), mask2, sccanobj->GetMatrixQ(), have_q_mask, sccanobj->GetMatrixU() ); } /** begin permutation 1. q_pvMatrix CqqInv=vnl_svd_inverse(Cqq); q=q*CqqInv; sermuted ; 2. scca ; 3. test corrs and weights significance */ unsigned long perm_exceed_ct = 0; if( permct > 0 ) { vVector w_p_signif_ct(w_p.size(), 0); vVector w_q_signif_ct(w_q.size(), 0); vVector w_r_signif_ct(w_r.size(), 0); for( unsigned long pct = 0; pct <= permct; pct++ ) { // 0. compute permutation for q ( switch around rows ) // // std::cout << " dont permute q " << std::endl; vMatrix q_perm = PermuteMatrix( sccanobj->GetMatrixQ() ); vMatrix r_perm = PermuteMatrix( sccanobj->GetMatrixR() ); sccanobj->SetMatrixQ( q_perm ); sccanobj->SetMatrixR( r_perm ); double permcorr = sccanobj->RunSCCAN3(); if( permcorr > truecorr ) { perm_exceed_ct++; } vVector w_p_perm = sccanobj->GetPWeights(); vVector w_q_perm = sccanobj->GetQWeights(); vVector w_r_perm = sccanobj->GetRWeights(); for( unsigned long j = 0; j < w_r.size(); j++ ) { if( w_r_perm(j) > w_r(j) ) { w_r_signif_ct(j) = w_r_signif_ct(j)++; } } // // std::cout << " only testing correlation with biserial predictions " << std::endl; // end solve cca permutation // std::cout << permcorr << " p-value " << (double)perm_exceed_ct // / (pct + 1) << " ct " << pct << " true " << truecorr << std::endl; for( unsigned long j = 0; j < w_r.size(); j++ ) { if( w_r(j) > 0 ) { // std::cout << " r entry " << j << " signif " << (double)w_r_signif_ct(j) / (double)(pct + 1) << std::endl; } } } } // // std::cout << " p-value " << (double)perm_exceed_ct/(permct+1) << " ct " << permct << std::endl; } return EXIT_SUCCESS; } int sccan( itk::ants::CommandLineParser *sccanparser ) { // Define dimensionality const unsigned int ImageDimension = 3; typedef double matPixelType; itk::ants::CommandLineParser::OptionType::Pointer outputOption = sccanparser->GetOption( "output" ); if( !outputOption || outputOption->GetNumberOfFunctions() == 0 ) { // std::cout << "Warning: no output option set." << std::endl; } unsigned int permct = 0; itk::ants::CommandLineParser::OptionType::Pointer permoption = sccanparser->GetOption( "n_permutations" ); if( !permoption || permoption->GetNumberOfFunctions() == 0 ) { } else { permct = sccanparser->Convert( permoption->GetFunction()->GetName() ); } unsigned int iterct = 20; permoption = sccanparser->GetOption( "iterations" ); if( permoption && permoption->GetNumberOfFunctions() > 0 ) { iterct = sccanparser->Convert( permoption->GetFunction()->GetName() ); } unsigned int evec_ct = 1; itk::ants::CommandLineParser::OptionType::Pointer evec_option = sccanparser->GetOption( "n_eigenvectors" ); if( !evec_option || evec_option->GetNumberOfFunctions() == 0 ) { } else { evec_ct = sccanparser->Convert( evec_option->GetFunction()->GetName() ); } matPixelType uselong = 0; itk::ants::CommandLineParser::OptionType::Pointer long_option = sccanparser->GetOption( "uselong" ); if( !long_option || long_option->GetNumberOfFunctions() == 0 ) { } else { uselong = sccanparser->Convert( long_option->GetFunction()->GetName() ); } unsigned int covering = 1; itk::ants::CommandLineParser::OptionType::Pointer covering_option = sccanparser->GetOption( "covering" ); if( !covering_option || covering_option->GetNumberOfFunctions() == 0 ) { } else { covering = sccanparser->Convert( covering_option->GetFunction()->GetName() ); } matPixelType usel1 = 0.1; itk::ants::CommandLineParser::OptionType::Pointer l1_option = sccanparser->GetOption( "l1" ); if( !l1_option || l1_option->GetNumberOfFunctions() == 0 ) { } else { usel1 = sccanparser->Convert( l1_option->GetFunction()->GetName() ); } unsigned int robustify = 0; itk::ants::CommandLineParser::OptionType::Pointer robust_option = sccanparser->GetOption( "robustify" ); if( !robust_option || robust_option->GetNumberOfFunctions() == 0 ) { } else { robustify = sccanparser->Convert( robust_option->GetFunction()->GetName() ); } unsigned int verbosity = 0; itk::ants::CommandLineParser::OptionType::Pointer verbopt = sccanparser->GetOption( "verbose" ); if( !verbopt || verbopt->GetNumberOfFunctions() == 0 ) { } else { verbosity = sccanparser->Convert( verbopt->GetFunction()->GetName() ); } itk::ants::CommandLineParser::OptionType::Pointer evecg_option = sccanparser->GetOption( "EvecGradPenalty" ); if( !evecg_option || evecg_option->GetNumberOfFunctions() == 0 ) { } else { sccanparser->Convert( evecg_option->GetFunction()->GetName() ); } matPixelType smoother = 0; itk::ants::CommandLineParser::OptionType::Pointer smooth_option = sccanparser->GetOption( "smoother" ); if( !smooth_option || smooth_option->GetNumberOfFunctions() == 0 ) { } else { smoother = sccanparser->Convert( smooth_option->GetFunction()->GetName() ); } matPixelType priorWeight = 0; itk::ants::CommandLineParser::OptionType::Pointer pwoption = sccanparser->GetOption( "prior-weight" ); if( !pwoption || pwoption->GetNumberOfFunctions() == 0 ) { } else { priorWeight = sccanparser->Convert( pwoption->GetFunction()->GetName() ); } bool getSmall = 0; itk::ants::CommandLineParser::OptionType::Pointer getsmallopt = sccanparser->GetOption( "get-small" ); if( !getsmallopt || getsmallopt->GetNumberOfFunctions() == 0 ) { } else { getSmall = sccanparser->Convert( getsmallopt->GetFunction()->GetName() ); } matPixelType row_sparseness = 0; itk::ants::CommandLineParser::OptionType::Pointer row_option = sccanparser->GetOption( "row_sparseness" ); if( !row_option || row_option->GetNumberOfFunctions() == 0 ) { } else { row_sparseness = sccanparser->Convert( row_option->GetFunction()->GetName() ); } unsigned int p_cluster_thresh = 1; itk::ants::CommandLineParser::OptionType::Pointer clust_option = sccanparser->GetOption( "PClusterThresh" ); if( !clust_option || clust_option->GetNumberOfFunctions() == 0 ) { } else { p_cluster_thresh = sccanparser->Convert( clust_option->GetFunction()->GetName() ); } unsigned int q_cluster_thresh = 1; clust_option = sccanparser->GetOption( "QClusterThresh" ); if( !clust_option || clust_option->GetNumberOfFunctions() == 0 ) { } else { q_cluster_thresh = sccanparser->Convert( clust_option->GetFunction()->GetName() ); } itk::ants::CommandLineParser::OptionType::Pointer positivity_option = sccanparser->GetOption( "PositivityConstraint" ); if( !positivity_option || positivity_option->GetNumberOfFunctions() == 0 ) { } else { sccanparser->Convert( positivity_option->GetFunction()->GetName() ); } bool eigen_imp = false; itk::ants::CommandLineParser::OptionType::Pointer eigen_option = sccanparser->GetOption( "ridge_cca" ); if( !eigen_option || eigen_option->GetNumberOfFunctions() == 0 ) { } else { eigen_imp = sccanparser->Convert( eigen_option->GetFunction()->GetName() ); } // operations on individual matrices itk::ants::CommandLineParser::OptionType::Pointer matrixOption = sccanparser->GetOption( "imageset-to-matrix" ); if( matrixOption && matrixOption->GetNumberOfFunctions() > 0 ) { std::string outname = outputOption->GetFunction( 0 )->GetName(); std::string imagelist = matrixOption->GetFunction( 0 )->GetParameter( 0 ); std::string maskfn = matrixOption->GetFunction( 0 )->GetParameter( 1 ); ConvertImageListToMatrix( imagelist, maskfn, outname ); return EXIT_SUCCESS; } // operations on individual matrices itk::ants::CommandLineParser::OptionType::Pointer matrixOptionTimeSeries = sccanparser->GetOption( "timeseriesimage-to-matrix" ); if( matrixOptionTimeSeries && matrixOptionTimeSeries->GetNumberOfFunctions() > 0 ) { std::string outname = outputOption->GetFunction( 0 )->GetName(); std::string imagefn = matrixOptionTimeSeries->GetFunction( 0 )->GetParameter( 0 ); std::string maskfn = matrixOptionTimeSeries->GetFunction( 0 )->GetParameter( 1 ); double smoother_space = 0; if( matrixOptionTimeSeries->GetFunction( 0 )->GetNumberOfParameters() > 2 ) { smoother_space = sccanparser->Convert( matrixOptionTimeSeries->GetFunction( 0 )->GetParameter( 2 ) ); } double smoother_time = 0; if( matrixOptionTimeSeries->GetFunction( 0 )->GetNumberOfParameters() > 3 ) { smoother_time = sccanparser->Convert( matrixOptionTimeSeries->GetFunction( 0 )->GetParameter( 3 ) ); } ConvertTimeSeriesImageToMatrix( imagefn, maskfn, outname, smoother_space, smoother_time ); // std::cout << " outname done " << outname << std::endl; return EXIT_SUCCESS; } // operations on individual matrices itk::ants::CommandLineParser::OptionType::Pointer matrixOptionV2I = sccanparser->GetOption( "vector-to-image" ); if( matrixOptionV2I && matrixOptionV2I->GetNumberOfFunctions() > 0 ) { std::string outname = outputOption->GetFunction( 0 )->GetName(); std::string csvfn = matrixOptionV2I->GetFunction( 0 )->GetParameter( 0 ); std::string maskfn = matrixOptionV2I->GetFunction( 0 )->GetParameter( 1 ); unsigned long rowOrCol = sccanparser->Convert( matrixOptionV2I->GetFunction( 0 )->GetParameter( 2 ) ); ConvertCSVVectorToImage( csvfn, maskfn, outname, rowOrCol ); // std::cout << " V2I done " << outname << std::endl; return EXIT_SUCCESS; } // p.d. itk::ants::CommandLineParser::OptionType::Pointer matrixProjectionOption = sccanparser->GetOption( "imageset-to-projections" ); if( matrixProjectionOption && matrixProjectionOption->GetNumberOfFunctions() > 0 ) { std::string outFilename = outputOption->GetFunction( 0 )->GetName(); std::string vecList = matrixProjectionOption->GetFunction( 0 )->GetParameter( 0 ); std::string imageList = matrixProjectionOption->GetFunction( 0 )->GetParameter( 1 ); bool average = sccanparser->Convert( matrixProjectionOption->GetFunction( 0 )->GetParameter( 2 ) ); // // std::cout <<"here" << outFilename << " " << vecList << " " <(vecList, imageList, outFilename, average ); return EXIT_SUCCESS; } itk::ants::CommandLineParser::OptionType::Pointer svdOption = sccanparser->GetOption( "svd" ); if( svdOption && svdOption->GetNumberOfFunctions() > 0 ) { std::string initializationStrategy = svdOption->GetFunction()->GetName(); if( !initializationStrategy.compare( std::string( "sparse" ) ) ) { SVD_One_View( sccanparser, permct, evec_ct, robustify, p_cluster_thresh, iterct, 1, usel1, row_sparseness, smoother, covering, verbosity ); return EXIT_SUCCESS; } if( !initializationStrategy.compare( std::string( "cgspca" ) ) ) { SVD_One_View( sccanparser, permct, evec_ct, robustify, p_cluster_thresh, iterct, 2, usel1, row_sparseness, smoother, covering, verbosity ); return EXIT_SUCCESS; } if( !initializationStrategy.compare( std::string( "network" ) ) ) { SVD_One_View( sccanparser, permct, evec_ct, robustify, p_cluster_thresh, iterct, 4, usel1, row_sparseness, smoother, covering, verbosity ); return EXIT_SUCCESS; } if( !initializationStrategy.compare( std::string( "lasso" ) ) ) { SVD_One_View( sccanparser, permct, evec_ct, robustify, p_cluster_thresh, iterct, 5, usel1, row_sparseness, smoother, covering, verbosity ); return EXIT_SUCCESS; } if( !initializationStrategy.compare( std::string( "recon" ) ) ) { SVD_One_View( sccanparser, permct, evec_ct, robustify, p_cluster_thresh, iterct, 6, usel1, row_sparseness, smoother, covering, verbosity, getSmall ); return EXIT_SUCCESS; } if( !initializationStrategy.compare( std::string( "recon4d" ) ) ) { SVD_One_View( sccanparser, permct, evec_ct, robustify, p_cluster_thresh, iterct, 6, usel1, row_sparseness, smoother, covering, verbosity ); return EXIT_SUCCESS; } if( !initializationStrategy.compare( std::string( "prior" ) ) ) { SVD_One_View( sccanparser, permct, evec_ct, robustify, p_cluster_thresh, iterct, 7, usel1, row_sparseness, smoother, covering, verbosity ); return EXIT_SUCCESS; } SVD_One_View( sccanparser, permct, evec_ct, robustify, p_cluster_thresh, iterct, 1, usel1, row_sparseness, smoother, covering, verbosity ); return EXIT_SUCCESS; } // std::cout << " scca-max-iterations " << iterct << " you will assess significance with " << permct << " permutations." << std::endl; // operations on pairs of matrices itk::ants::CommandLineParser::OptionType::Pointer matrixPairOption = sccanparser->GetOption( "scca" ); if( matrixPairOption && matrixPairOption->GetNumberOfFunctions() > 0 ) { if( matrixPairOption && matrixPairOption->GetFunction( 0 )->GetNumberOfParameters() < 2 ) { std::cerr << " Incorrect number of parameters." << std::endl; return EXIT_FAILURE; } std::string initializationStrategy = matrixPairOption->GetFunction()->GetName(); // call RCCA_eigen or RCCA_vnl unsigned int exitvalue = EXIT_FAILURE; if( !initializationStrategy.compare( std::string( "two-view" ) ) ) { exitvalue = SCCA_vnl( sccanparser, permct, evec_ct, eigen_imp, robustify, p_cluster_thresh, q_cluster_thresh, iterct, usel1, uselong, row_sparseness, smoother, covering, priorWeight, verbosity ); } else if( !initializationStrategy.compare( std::string("three-view") ) ) { // std::cout << " mscca 3-view " << std::endl; exitvalue = mSCCA_vnl( sccanparser, permct, false, evec_ct, eigen_imp, robustify, p_cluster_thresh, q_cluster_thresh, iterct); } else if( !initializationStrategy.compare( std::string("partial") ) ) { // std::cout << " pscca " << std::endl; exitvalue = mSCCA_vnl( sccanparser, permct, true, evec_ct, eigen_imp, robustify, p_cluster_thresh, q_cluster_thresh, iterct); } else if( !initializationStrategy.compare( std::string("dynsccan") ) ) { // std::cout << " tscca " << std::endl; exitvalue = SCCA_vnl( sccanparser, permct, evec_ct, eigen_imp, robustify, p_cluster_thresh, q_cluster_thresh, iterct, usel1, uselong, row_sparseness, smoother, covering, verbosity ); } else { std::cerr << " unrecognized option in matrixPairOperation " << std::endl; return exitvalue; } // std::cout << " exit value " << exitvalue << std::endl; return exitvalue; } // matrixPairOption else { std::cerr << " no option specified " << std::endl; } return EXIT_FAILURE; } // entry point for the library; parameter 'args' is equivalent to 'argv' in (argc,argv) of commandline parameters to // 'main()' int sccan( std::vector args, std::ostream * /*out_stream = NULL */ ) { // put the arguments coming in as 'args' into standard (argc,argv) format; // 'args' doesn't have the command name as first, argument, so add it manually; // 'args' may have adjacent arguments concatenated into one argument, // which the sccanparser should handle args.insert( args.begin(), "sccan" ); std::remove( args.begin(), args.end(), std::string( "" ) ); int argc = args.size(); char* * argv = new char *[args.size() + 1]; for( unsigned int i = 0; i < args.size(); ++i ) { // allocate space for the string plus a null character argv[i] = new char[args[i].length() + 1]; std::strncpy( argv[i], args[i].c_str(), args[i].length() ); // place the null character in the end argv[i][args[i].length()] = '\0'; } argv[argc] = ITK_NULLPTR; // class to automatically cleanup argv upon destruction class Cleanup_argv { public: Cleanup_argv( char* * argv_, int argc_plus_one_ ) : argv( argv_ ), argc_plus_one( argc_plus_one_ ) { } ~Cleanup_argv() { for( unsigned int i = 0; i < argc_plus_one; ++i ) { delete[] argv[i]; } delete[] argv; } private: char* * argv; unsigned int argc_plus_one; }; Cleanup_argv cleanup_argv( argv, argc + 1 ); // antscout->set_stream( out_stream ); itk::ants::CommandLineParser::Pointer sccanparser = itk::ants::CommandLineParser::New(); sccanparser->SetCommand( argv[0] ); std::string commandDescription = std::string( "A tool for sparse statistical analysis on images : " ) + std::string( " scca, pscca (with options), mscca. Can also convert an imagelist/mask pair to a binary matrix image. " ); sccanparser->SetCommandDescription( commandDescription ); /** in this function, list all the operations you will perform */ typedef itk::ants::CommandLineParser::OptionType OptionType; { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Print the help menu (long version)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Output dependent on which option is called." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "output" ); option->SetShortName( 'o' ); option->SetUsageOption( 0, "outputImage" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Number of permutations to use in scca." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "n_permutations" ); option->SetShortName( 'p' ); option->SetUsageOption( 0, "500" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Smoothing function for variates" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "smoother" ); option->SetShortName( 's' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Row sparseness - if (+) then keep values (+) otherwise allow +/- values --- always L1" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "row_sparseness" ); option->SetShortName( 'z' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Max iterations for scca optimization (min 20)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "iterations" ); option->SetShortName( 'i' ); option->SetUsageOption( 0, "20" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Number of eigenvectors to compute in scca/spca." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "n_eigenvectors" ); option->SetShortName( 'n' ); option->SetUsageOption( 0, "2" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "rank-based scca" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "robustify" ); option->SetShortName( 'r' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "try to make the decomposition cover the whole domain, if possible " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "covering" ); option->SetShortName( 'c' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "use longitudinal formulation ( > 0 ) or not ( <= 0 ) " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "uselong" ); option->SetShortName( 'g' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "use l1 ( > 0 ) or l0 ( < 0 ) penalty, also sets gradient step size e.g. -l 0.5 ( L1 ) , -l -0.5 (L0) will set 0.5 grad descent step for either penalty" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "l1" ); option->SetShortName( 'l' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "cluster threshold on view P" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "PClusterThresh" ); option->SetUsageOption( 0, "1" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "cluster threshold on view Q" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "QClusterThresh" ); option->SetUsageOption( 0, "1" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Ridge cca." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "ridge_cca" ); option->SetShortName( 'e' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Initialization file list for Eigenanatomy - must also pass mask option" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "initialization" ); option->SetUsageOption( 0, "NA" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Initialization file list for SCCAN-Eigenanatomy - must also pass mask option" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "initialization2" ); option->SetUsageOption( 0, "NA" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Mask file for Eigenanatomy initialization" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask" ); option->SetUsageOption( 0, "NA" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Mask file for Eigenanatomy initialization 2" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask2" ); option->SetUsageOption( 0, "NA" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Choices for pscca: PQ, PminusRQ, PQminusR, PminusRQminusR " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "partial-scca-option" ); option->SetUsageOption( 0, "PminusRQ" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Scalar value weight on prior between 0 (prior is weak) and 1 (prior is strong). Only engaged if initialization is used." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "prior-weight" ); option->SetUsageOption( 0, "0.0" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Find smallest eigenvectors" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "get-small" ); option->SetUsageOption( 0, "0.0" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "set whether output is verbose" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "verbose" ); option->SetShortName( 'v' ); option->SetUsageOption( 0, "0" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "takes a list of image files names (one per line) " ) + std::string( "and converts it to a 2D matrix / image in binary or csv format depending on the filetype used to define the output." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "imageset-to-matrix" ); option->SetUsageOption( 0, "[list.txt,mask.nii.gz]" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "takes a timeseries (4D) image " ) + std::string( "and converts it to a 2D matrix csv format as output." ) + std::string( "If the mask has multiple labels ( more the one ) then the average time series in each label will be computed and put in the csv." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "timeseriesimage-to-matrix" ); option->SetUsageOption( 0, "[four_d_image.nii.gz,three_d_mask.nii.gz, optional-spatial-smoothing-param-in-spacing-units-default-zero, optional-temporal-smoothing-param-in-time-series-units-default-zero ]" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "converts the 1st column vector in a csv file back to an image --- currently needs the csv file to have > 1 columns. if the number of entries in the column does not equal the number of entries in the mask but the number of rows does equal the number of entries in the mask, then it will convert the row vector to an image. " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "vector-to-image" ); option->SetUsageOption( 0, "[vector.csv,three_d_mask.nii.gz, which-row-or-col ]" ); option->SetDescription( description ); sccanparser->AddOption( option ); } // p.d. { std::string description = std::string( "takes a list of image and projection files names (one per line) " ) + std::string( "and writes them to a csv file --- basically computing X*Y (matrices)." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "imageset-to-projections" ); option->SetUsageOption( 0, "[list_projections.txt,list_images.txt, bool do-average-not-real-projection ]" ); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "Matrix-based scca operations for 2 and 3 views." ) + std::string( "For all these options, the FracNonZero terms set the fraction of variables to use in the estimate. E.g. if one sets 0.5 then half of the variables will have non-zero values. If the FracNonZero is (+) then the weight vectors must be positive. If they are negative, weights can be (+) or (-). partial does partial scca for 2 views while partialing out the 3rd view. "); OptionType::Pointer option = OptionType::New(); option->SetLongName( "scca" ); option->SetUsageOption( 0, "two-view[matrix-view1.mhd,matrix-view2.mhd,mask1,mask2,FracNonZero1,FracNonZero2] "); option->SetUsageOption( 1, "three-view[matrix-view1.mhd,matrix-view2.mhd,matrix-view3.mhd,mask1,mask2,mask3,FracNonZero1,FracNonZero2,FracNonZero3]" ); option->SetUsageOption( 2, "partial[matrix-view1.mhd,matrix-view2.mhd,matrix-view3.mhd,mask1,mask2,mask3,FracNonZero1,FracNonZero2,FracNonZero3]" ); option->SetUsageOption( 3, "dynsccan[matrix-view1.mhd,matrix-view2.mhd,mask1,mask2,FracNonZero1,FracNonZero2] "); option->SetDescription( description ); sccanparser->AddOption( option ); } { std::string description = std::string( "a sparse svd implementation --- will report correlation of eigenvector with original data columns averaged over columns with non-zero weights." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "svd" ); option->SetUsageOption( 0, "sparse[matrix-view1.mhd,mask1,FracNonZero1,nuisance-matrix] --- will only use view1 ... unless nuisance matrix is specified." ); option->SetUsageOption( 1, "classic[matrix-view1.mhd,mask1,FracNonZero1,nuisance-matrix] --- will only use view1 ... unless nuisance matrix is specified." ); option->SetUsageOption( 2, "cgspca[matrix-view1.mhd,mask1,FracNonZero1,nuisance-matrix] --- will only use view1 ... unless nuisance matrix is specified, -i controls the number of sparse approximations per eigenvector, -n controls the number of eigenvectors. total output will then be i*n sparse eigenvectors." ); option->SetUsageOption( 3, "prior[ matrix.mha , mask.nii.gz , PriorList.txt , PriorScale.csv , PriorWeightIn0to1 , sparseness ] ... if sparseness is set to zero, we take sparseness from the priors." ); option->SetUsageOption( 4, "network[matrix-view1.mhd,mask1,FracNonZero1,guidance-matrix]" ); option->SetUsageOption( 5, "lasso[matrix-view1.mhd,mask1,Lambda,guidance-matrix]" ); option->SetUsageOption( 6, "recon[matrix-view1.mhd,mask1,FracNonZero1,nuisance-matrix]" ); option->SetUsageOption( 7, "recon4d[matrix-view1.mhd,mask1,FracNonZero1,nuisance-matrix]" ); option->SetDescription( description ); sccanparser->AddOption( option ); } if( sccanparser->Parse( argc, argv ) == EXIT_FAILURE ) { return EXIT_FAILURE; } // Print the entire help menu itk::ants::CommandLineParser::OptionType::Pointer shortHelpOption = sccanparser->GetOption( 'h' ); itk::ants::CommandLineParser::OptionType::Pointer longHelpOption = sccanparser->GetOption( "help" ); if( argc < 2 || ( shortHelpOption->GetFunction() && sccanparser->Convert( shortHelpOption->GetFunction()->GetName() ) == 1 ) ) { sccanparser->PrintMenu( std::cout, 5, true ); if( argc < 2 ) { return EXIT_FAILURE; } return EXIT_SUCCESS; } if( longHelpOption->GetFunction() && sccanparser->Convert( longHelpOption->GetFunction()->GetName() ) == 1 ) { sccanparser->PrintMenu( std::cout, 5, false ); return EXIT_SUCCESS; } // Print the long help menu for specific items if( longHelpOption && longHelpOption->GetNumberOfFunctions() > 0 && sccanparser->Convert( longHelpOption->GetFunction()->GetName() ) != 0 ) { itk::ants::CommandLineParser::OptionListType options = sccanparser->GetOptions(); for( unsigned int n = 0; n < longHelpOption->GetNumberOfFunctions(); n++ ) { std::string value = longHelpOption->GetFunction( n )->GetName(); itk::ants::CommandLineParser::OptionListType::const_iterator it; for( it = options.begin(); it != options.end(); ++it ) { const char *longName = ( ( *it )->GetLongName() ).c_str(); if( strstr( longName, value.c_str() ) == longName ) { sccanparser->PrintMenu( std::cout, 5, false ); } } } return EXIT_FAILURE; } // Call main routine return sccan( sccanparser ); } // now compute covariance matrices // covariance matrix --- Cov(X, Y) = E[XY] - E[X].E[Y] /* input matrix ta<-matrix(c(-1,1,2,2,-2,3,1,1,4,0,3,4),nrow=3,ncol=4) ta<-matrix(c(-1,1,2,-2,3,1,4,0,3),nrow=3,ncol=3) > ta [,1] [,2] [,3] [1,] -1 1 2 [2,] -2 3 1 [3,] 4 0 3 // cov(ta,ta) [,1] [,2] [,3] [1,] 10.333333 -4.166667 3.0 [2,] -4.166667 2.333333 -1.5 [3,] 3.000000 -1.500000 1.0 > cov(a[1,]-mean(a[1,]),a[1,]-mean(a[1,])) [1] 10.33333 v<-a[1,]-mean(a[1,]) > v*v [1] 1.777778 5.444444 13.444444 > sum(v*v) [1] 20.66667 > sum(v*v)/(2) <- sum( v*v ) / ( n - 1 ) = covariance of two vectors ... [1] 10.33333 */ /** try q.colwise.sum() to compute mean ... */ /** try q.rowwise.sum() to compute mean ... eMatrix testM(3,3); testM(0,0)=-1;testM(1,0)=1; testM(2,0)=2; testM(0,1)=-2;testM(1,1)=3; testM(2,1)=1; testM(0,2)= 4;testM(1,2)=0; testM(2,2)=3; p=testM; pMatSize[0]=3; pMatSize[1]=3; */ /* //1.0/(double)q.columns(); //randgen.drand32(); for (unsigned int it=0; it<4; it++) { // // std::cout << " 2norm(v0) " << v_0.two_norm() << std::endl; vVector v_1=(q)*v_0; double vnorm=v_1.two_norm(); // std::cout << " von " << vnorm << std::endl; v_0=v_1/(vnorm); // std::cout << " vo " << v_0 << std::endl; // check if power method works .... vVector Xv=q*v_0; Scalar vdotXv = dot_product(v_0,Xv); // std::cout << " vdotXv " << vdotXv << std::endl; vVector Xv2=Xv-v_0*vdotXv; // this value should be small -- i.e. v_0 is an eigenvector of X // std::cout << " init eigenvector result " << Xv2.squared_magnitude() << std::endl;} */ // // std::cout << v_0 << std::endl; /* function [Up,Sp,Vp] = rank_one_svd_update( U, S, V, a, b, force_orth ) % function [Up,Sp,Vp] = rank_one_svd_update( U, S, V, a, b, force_orth ) % % Given the SVD of % % X = U*S*V' % % update it to be the SVD of % % X + ab' = Up*Sp*Vp' % % that is, implement a rank-one update to the SVD of X. % % Depending on a,b there may be considerable structure that could % be exploited, but which this code does not. % % The subspace rotations involved may not preserve orthogonality due % to numerical round-off errors. To compensate, you can set the % "force_orth" flag, which will force orthogonality via a QR plus % another SVD. In a long loop, you may want to force orthogonality % every so often. % % See Matthew Brand, "Fast low-rank modifications of the thin % singular value decomposition". % % D. Wingate 8/17/2007 % current_rank = size( U, 2 ); % P is an orthogonal basis of the column-space % of (I-UU')a, which is the component of "a" that is % orthogonal to U. m = U' * a; p = a - U*m; Ra = sqrt(p'*p); P = (1/Ra)*p; % XXX this has problems if a is already in the column space of U! % I don't know what to do in that case. if ( Ra < 1e-13 ) fprintf('------> Whoa! No orthogonal component of m!\n'); end; % Q is an orthogonal basis of the column-space % of (I-VV')b. n = V' * b; q = b - V*n; Rb = sqrt(q'*q); Q = (1/Rb)*q; if ( Rb < 1e-13 ) fprintf('------> Whoa! No orthogonal component of n!\n'); end; % % Diagonalize K, maintaining rank % % XXX note that this diagonal-plus-rank-one, so we should be able % to take advantage of the structure! z = zeros( size(m) ); K = [ S z ; z' 0 ] + [ m; Ra ]*[ n; Rb ]'; [tUp,tSp,tVp] = svds( K, current_rank ); % % Now update our matrices! % Sp = tSp; Up = [ U P ] * tUp; Vp = [ V Q ] * tVp; % The above rotations may not preserve orthogonality, so we explicitly % deal with that via a QR plus another SVD. In a long loop, you may % want to force orthogonality every so often. if ( force_orth ) [UQ,UR] = qr( Up, 0 ); [VQ,VR] = qr( Vp, 0 ); [tUp,tSp,tVp] = svds( UR * Sp * VR', current_rank ); Up = UQ * tUp; Vp = VQ * tVp; Sp = tSp; end; return; */ // } // namespace antssccan } // namespace ants ants-2.2.0/Examples/simpleSynRegistration.cxx000066400000000000000000000201321311104306400213270ustar00rootroot00000000000000// This program does SyN registration with hard-coded parameters. // The whole registration process is included in "simpleSynReg" function. // The Usage: // This program does not have any flag. You should just put the arguments after the program name. /* ~/simpleSynRegistration fixed image moving image initial transform output prefix file name */ #include "antsUtilities.h" #include "itkantsRegistrationHelper.h" namespace ants { typedef ants::RegistrationHelper RegistrationHelperType; typedef RegistrationHelperType::ImageType ImageType; typedef RegistrationHelperType::CompositeTransformType CompositeTransformType; CompositeTransformType::TransformTypePointer simpleSynReg( ImageType::Pointer & fixedImage, ImageType::Pointer & movingImage, const CompositeTransformType::Pointer compositeInitialTransform ) { RegistrationHelperType::Pointer regHelper = RegistrationHelperType::New(); const std::string whichMetric = "mattes"; RegistrationHelperType::MetricEnumeration curMetric = regHelper->StringToMetricType(whichMetric); const float lowerQuantile( 0.0F ); const float upperQuantile( 1.0F ); const bool doWinsorize(false); regHelper->SetWinsorizeImageIntensities(doWinsorize, lowerQuantile, upperQuantile); const bool doHistogramMatch(true); regHelper->SetUseHistogramMatching(doHistogramMatch); const bool doEstimateLearningRateAtEachIteration = true; regHelper->SetDoEstimateLearningRateAtEachIteration( doEstimateLearningRateAtEachIteration ); std::vector > iterationList; std::vector convergenceThresholdList; std::vector convergenceWindowSizeList; std::vector > shrinkFactorsList; std::vector > smoothingSigmasList; std::vector iterations(3); iterations[0] = 100; iterations[1] = 70; iterations[2] = 20; std::cout << " number of levels = 3 " << std::endl; iterationList.push_back(iterations); const double convergenceThreshold = 1e-6; convergenceThresholdList.push_back(convergenceThreshold); const unsigned int convergenceWindowSize = 10; convergenceWindowSizeList.push_back(convergenceWindowSize); std::vector factors(3); factors[0] = 3; factors[1] = 2; factors[2] = 1; shrinkFactorsList.push_back(factors); std::vector sigmas(3); sigmas[0] = 2; sigmas[1] = 1; sigmas[2] = 0; smoothingSigmasList.push_back(sigmas); std::vector smoothingSigmasAreInPhysicalUnitsList; smoothingSigmasAreInPhysicalUnitsList.push_back(true); // Historical behavior before 2012-10-07 const float samplingPercentage = 1.0; RegistrationHelperType::SamplingStrategy samplingStrategy = RegistrationHelperType::none; const unsigned int binOption = 200; regHelper->AddMetric(curMetric, fixedImage, movingImage, 0, 1.0, samplingStrategy, binOption, 1, samplingPercentage); const float learningRate(0.25F); const float varianceForUpdateField( 3.0F ); const float varianceForTotalField(0.0F); regHelper->AddSyNTransform(learningRate, varianceForUpdateField, varianceForTotalField); regHelper->SetMovingInitialTransform( compositeInitialTransform ); regHelper->SetIterations( iterationList ); regHelper->SetConvergenceWindowSizes( convergenceWindowSizeList ); regHelper->SetConvergenceThresholds( convergenceThresholdList ); regHelper->SetSmoothingSigmas( smoothingSigmasList ); regHelper->SetShrinkFactors( shrinkFactorsList ); regHelper->SetSmoothingSigmasAreInPhysicalUnits( smoothingSigmasAreInPhysicalUnitsList ); if( regHelper->DoRegistration() == EXIT_SUCCESS ) { // Get the output transform CompositeTransformType::Pointer outputCompositeTransform = regHelper->GetModifiableCompositeTransform(); // write out transform actually computed, so skip the initial transform CompositeTransformType::TransformTypePointer resultTransform = outputCompositeTransform->GetNthTransform( 1 ); return resultTransform; } std::cerr << "FATAL ERROR: REGISTRATION PROCESS WAS UNSUCCESSFUL" << std::endl; CompositeTransformType::TransformTypePointer invalidTransform = ITK_NULLPTR; return invalidTransform; // Return an empty registration type. } int simpleSynRegistration( std::vector args, std::ostream* /*out_stream = NULL */ ) { // the arguments coming in as 'args' is a replacement for the standard (argc,argv) format // Just notice that the argv[i] equals to args[i-1] // and the argc equals: int argc = args.size() + 1; if( argc != 5 ) { std::cerr << "Usage: simpleSynRegistration\n" << " , , , " << std::endl; return EXIT_FAILURE; } // antscout->set_stream( out_stream ); ImageType::Pointer fixedImage; ImageType::Pointer movingImage; // ========read the fixed image typedef itk::ImageFileReader ImageReaderType; ImageReaderType::Pointer fixedImageReader = ImageReaderType::New(); fixedImageReader->SetFileName( args[0] ); fixedImageReader->Update(); fixedImage = fixedImageReader->GetOutput(); try { fixedImage->Update(); } catch( itk::ExceptionObject & excp ) { std::cerr << excp << std::endl; return EXIT_FAILURE; } // ==========read the moving image ImageReaderType::Pointer movingImageReader = ImageReaderType::New(); movingImageReader->SetFileName( args[1] ); movingImageReader->Update(); movingImage = movingImageReader->GetOutput(); try { movingImage->Update(); } catch( itk::ExceptionObject & excp ) { std::cerr << excp << std::endl; return EXIT_FAILURE; } std::cout << " fixed image: " << args[0] << std::endl; std::cout << " moving image: " << args[1] << std::endl; // ===========Read the initial transform and write that in a composite transform typedef RegistrationHelperType::TransformType TransformType; TransformType::Pointer initialTransform = itk::ants::ReadTransform( args[2] ); if( initialTransform.IsNull() ) { std::cerr << "Can't read initial transform " << std::endl; return EXIT_FAILURE; } CompositeTransformType::Pointer compositeInitialTransform = CompositeTransformType::New(); compositeInitialTransform->AddTransform( initialTransform ); // =========write the output transform // compute the output transform by calling the "simpleSynReg" function CompositeTransformType::TransformTypePointer outputTransform = simpleSynReg( fixedImage, movingImage, compositeInitialTransform ); if( outputTransform.IsNull() ) { std::cerr << "ERROR: Registration FAILED TO PRODUCE VALID TRANSFORM ...\n" << std::endl; return EXIT_FAILURE; } std::cout << "***** Ready to write the results ...\n" << std::endl; std::stringstream outputFileName; outputFileName << args[3] << "Warp.nii.gz"; itk::ants::WriteTransform( outputTransform, outputFileName.str() ); // compute and write the inverse of the output transform const bool writeInverse(true); if( writeInverse ) { typedef RegistrationHelperType::DisplacementFieldTransformType DisplacementFieldTransformType; DisplacementFieldTransformType::Pointer dispTransform = dynamic_cast(outputTransform.GetPointer() ); typedef DisplacementFieldTransformType::DisplacementFieldType DisplacementFieldType; std::stringstream outputInverseFileName; outputInverseFileName << args[3] << "InverseWarp.nii.gz"; typedef itk::ImageFileWriter InverseWriterType; InverseWriterType::Pointer inverseWriter = InverseWriterType::New(); inverseWriter->SetInput( dispTransform->GetInverseDisplacementField() ); inverseWriter->SetFileName( outputInverseFileName.str().c_str() ); inverseWriter->Update(); } return EXIT_SUCCESS; } } // namespace ants ants-2.2.0/Examples/template_for_executables.cxx.in000066400000000000000000000005561311104306400224330ustar00rootroot00000000000000#include #include #include #include "include/ants.h" int main( int argc , char * argv[] ) { const char * const * const ptr = &argv[1] ; const std::vector< std::string > command_line_args( ptr , ptr+argc-1 ); std::ostream * command_stream = &std::cout; return ants::@ANTS_FUNCTION_NAME@( command_line_args , command_stream ) ; } ants-2.2.0/ImageRegistration/000077500000000000000000000000001311104306400160665ustar00rootroot00000000000000ants-2.2.0/ImageRegistration/ANTS_affine_registration.h000066400000000000000000001660131311104306400231150ustar00rootroot00000000000000#ifndef ANTS_AFFINE_REGISTRATION_H_ #define ANTS_AFFINE_REGISTRATION_H_ #include #include #include #include "itkImage.h" #include "itkPoint.h" #include "itkCastImageFilter.h" #include "itkImageMaskSpatialObject.h" #include "itkCenteredEuler3DTransform.h" #include "itkRegularStepGradientDescentOptimizer.h" #include "itkGradientDescentOptimizer.h" #include "itkCenteredTransformInitializer.h" #include "itkMattesMutualInformationImageToImageMetric.h" #include "itkMultiResolutionImageRegistrationMethod.h" #include "itkLinearInterpolateImageFunction.h" #include "itkANTSAffine3DTransform.h" #include "itkCenteredRigid2DTransform.h" #include "itkANTSCenteredAffine2DTransform.h" #include "itkTransformFactory.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkResampleImageFilter.h" #include "itkLinearInterpolateImageFunction.h" #include "itkWarpImageFilter.h" #include "itkWarpImageWAffineFilter.h" template void read_transform_file(StringType filename, CastTransformPointerType & transform); template void write_transform_file(TransformPointerType & transform, StringType str); template void compute_single_affine_transform_3d(ImagePointerType I_fixed, ImagePointerType I_moving, MaskImagePointerType mask_fixed, TransformPointerType & transform, TransformPointerType & transform_initial); template void compute_single_affine_transform_2d(ImagePointerType I_fixed, ImagePointerType I_moving, MaskImagePointerType mask_fixed, TransformPointerType & transform, TransformPointerType & transform_initial); template void compute_single_affine_transform(ImagePointerType fixedImage, ImagePointerType movingImage, MaskImagePointerType maskImage, TransformPointerType & transform, TransformPointerType & transform_initial); // void compute_single_affine_transform_2d(itk::Image::Pointer I_fixed, // itk::Image::Pointer I_moving, // itk::Image::Pointer mask_fixed, // itk::CenteredAffine2DTransform::Pointer &transform); template void create_deformation_field_byref(const DisplacementFieldPointerType & ref, DisplacementFieldPointerType & field); // this is obsolet, use itkWarpImageWAffineFilter template void compose_affine_with_field(const TransformPointerType & aff, const DisplacementFieldPointerType & field, DisplacementFieldPointerType & field_output); template void warp_image_field(const ImagePointerType & img_input, const DisplacementFieldPointerType & field, ImagePointerType & img_output); template void warp_image_field_waffine(const ImagePointerType & img_input, const TransformPointerType & aff, const DisplacementFieldPointerType & field, ImagePointerType & img_output); template void affine_image(const ImageTypePointer & input_image, const TransformTypePointer & transform, const RefImageTypePointer & ref_image, ImageTypePointer & img_aff ); template void write_transform_file(TransformPointerType & transform, StringType filename) { itk::TransformFileWriter::Pointer transform_writer; transform_writer = itk::TransformFileWriter::New(); transform_writer->SetFileName(filename); transform_writer->SetInput(transform); try { transform_writer->Update(); } catch( itk::ExceptionObject & err ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl << "Exception in writing tranform file: " << std::endl << filename << std::endl; return; } return; } template void read_transform_file(StringType filename, CastTransformPointerType & transform) { typedef typename CastTransformPointerType::ObjectType CastTransformType; // const unsigned int InputSpaceDimension = CastTransformType::InputSpaceDimension; // const unsigned int OutputSpaceDimension = CastTransformType::OutputSpaceDimension; itk::TransformFactory::RegisterTransform(); itk::TransformFactory >::RegisterTransform(); typedef typename itk::TransformFileReader TranReaderType; TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(filename); try { tran_reader->Update(); } catch( itk::ExceptionObject & err ) { std::cout << err << std::endl; std::cout << "Exception caught in reading tran para file: " << filename << std::endl; return; } transform = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); return; } template void convert_affine_para_to_deformation_field(TransformPointerType & transform, DisplacementFieldType & def) { return; } template class SEARCH_POINT_TYPE { public: ParaType para0; // seed parameter ParaType para1; // local optimal parameter itk::Point center; // transformation center double rval; // registered value unsigned int index; // the index in the list, //for sorting int number_of_iteration; // the number of iteration used for gradient descent }; template void generate_search_seed_3d(SEARCH_LIST & search_list, ParaType & ret); template void generate_search_seed_2d(SEARCH_LIST & search_list, ParaType & ret); template void get_best_search_point(SEARCH_LIST & search_list, SEARCH_POINT & spt); template void add_search_point(SEARCH_LIST & search_list, SEARCH_POINT spt); // use moment of the image initializer to get cx and cy only template bool register_image_cxyz(ImagePointerType fixed_image, ImagePointerType moving_image, ParaType & para1, double & rval); // use an optional mask for the fixed image template bool register_image_affine3d_mres_mask(ImagePointerType fixed_image, ImagePointerType moving_image, ImageMaskSpatialObjectPointerType mask_fixed_object, ParaType para0, itk::Point center, int number_of_iteration, int MI_bins, int MI_samples, ParaType & para1, double & rval); template bool register_image_affine2d_mres_mask(ImagePointerType fixed_image, ImagePointerType moving_image, ImageMaskSpatialObjectPointerType mask_fixed_object, ParaType para0, itk::Point center, int number_of_iteration, int MI_bins, int MI_samples, ParaType & para1, double & rval); template double get_cost_value_mmi(ImagePointerType fixedImage, ImagePointerType movingImage, ParaType para, PointType center, TransformTypePointer null_transform); template bool compare_search_point(const SEARCH_POINT& lhs, const SEARCH_POINT& rhs) { return lhs.rval < rhs.rval; } template void compute_single_affine_transform_3d(ImagePointerType I_fixed, ImagePointerType I_moving, MaskImagePointerType mask_fixed, TransformPointerType & transform, TransformPointerType & transform_initial) { typedef typename ImagePointerType::ObjectType ImageType; const int ImageDimension = ImageType::ImageDimension; typedef typename ImageType::IOPixelType PixelType; typedef typename MaskImagePointerType::ObjectType MaskImageType; typedef typename MaskImageType::IOPixelType MaskPixelType; typedef typename TransformPointerType::ObjectType TransformType; typedef typename TransformType::ParametersType ParaType; // option parameters int number_of_seeds = 0; int number_of_iteration = 10000; int MI_bins = 32; int MI_samples = 6000; unsigned int time_seed = (unsigned int) time(NULL); srand(time_seed); // TODO: need to fix here bool b_use_mask = 0; // (mask_fixed == NULL); std::cout << "number_of_seeds: " << number_of_seeds << std::endl; std::cout << "rand_time_seed: " << time_seed << std::endl; std::cout << "number_of_iteration: " << number_of_iteration << std::endl; std::cout << "MI_bins: " << MI_bins << std::endl; std::cout << "MI_samples: " << MI_samples << std::endl; std::cout << "use mask: " << b_use_mask << std::endl; // memory of searched results typedef SEARCH_POINT_TYPE SEARCH_POINT; typedef std::vector SEARCH_LIST; SEARCH_LIST search_list; // typedef itk::ImageMaskSpatialObject ImageMaskSpatialObject; typedef itk::ImageMaskSpatialObject ImageMaskSpatialObject; typename ImageMaskSpatialObject::Pointer mask_fixed_object = 0; if( b_use_mask ) { typedef itk::Image CharMaskImageType; typedef itk::CastImageFilter CastFilterType; typename CastFilterType::Pointer cast_filter = CastFilterType::New(); cast_filter->SetInput(mask_fixed); cast_filter->Update(); typename CharMaskImageType::Pointer mask_fixed_char = cast_filter->GetOutput(); mask_fixed_object = ImageMaskSpatialObject::New(); mask_fixed_object->SetImage(mask_fixed_char); } typename ImageType::PointType origin; origin.Fill(0); I_moving->SetOrigin(origin); I_fixed->SetOrigin(origin); typename TransformType::Pointer trans = TransformType::New(); ParaType para0(trans->GetNumberOfParameters() ), para1(trans->GetNumberOfParameters() ); double rval; SEARCH_POINT spt; typedef itk::CenteredEuler3DTransform TransformType_Euler3D; ParaType para_cxy(TransformType_Euler3D::New()->GetNumberOfParameters() ); // translated from pre registration // find initial center bool is_ok = false; itk::Point center; if( transform_initial.IsNull() ) { is_ok = register_image_cxyz(I_fixed, I_moving, para_cxy, rval); if( !is_ok ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl << "initial affine registeration falied" << std::endl; std::exception(); } center[0] = para_cxy[3]; center[1] = para_cxy[4]; center[2] = para_cxy[5]; } else { center[0] = transform_initial->GetCenter()[0]; center[1] = transform_initial->GetCenter()[1]; center[2] = transform_initial->GetCenter()[2]; } std::cout << std::endl; for( int n = 0; (number_of_seeds > 0) ? (n < number_of_seeds) : (n <= number_of_seeds); n++ ) { if( n == 0 ) { if( transform_initial.IsNull() ) { para0[0] = 0.0; para0[1] = 0.0; para0[2] = 0.0; para0[3] = 1.0; para0[4] = 1.0; para0[5] = 1.0; para0[6] = 1.0; para0[7] = 0.0; para0[8] = 0.0; para0[9] = 0.0; para0[10] = 0.0; para0[11] = 0.0; para0[12] = 0.0; } else // use input as intial { for( unsigned int i = 0; i < transform_initial->GetParameters().Size(); i++ ) { para0[i] = transform_initial->GetParameters()[i]; } } } else { generate_search_seed_3d(search_list, para0); } // main registration using affine transform is_ok = register_image_affine3d_mres_mask(I_fixed, I_moving, mask_fixed_object, para0, center, number_of_iteration, MI_bins, MI_samples, para1, rval); if( !is_ok ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl << "affine registration failed!" << std::endl << "use the initial parameters" << std::endl; // return -1; } spt.para0 = para0; spt.para1 = para1; spt.center = center; spt.rval = rval; spt.index = n; spt.number_of_iteration = number_of_iteration; std::cout << "para0: " << para0 << std::endl; std::cout << "para1: " << para1 << std::endl; std::cout << "center: " << center << std::endl; std::cout << "rval: " << rval << std::endl; std::cout << "add the search result to the list ... seed [" << n << "]" << std::endl << std::endl; add_search_point(search_list, spt); } get_best_search_point(search_list, spt); std::cout << std::endl << "History: " << std::endl; for( int ii = 0; ii < search_list.size(); ii++ ) { std::cout << "[" << ii << "]: " << search_list[ii].rval << std::endl; std::cout << "center: " << search_list[ii].center << std::endl; std::cout << "para0: " << search_list[ii].para0 << std::endl; std::cout << "para1: " << search_list[ii].para1 << std::endl; } typename TransformType::Pointer transform_final = TransformType::New(); transform_final->SetParameters(spt.para1); transform_final->SetCenter(center); std::cout << "final transform parameters = " << transform_final->GetParameters() << std::endl; transform = transform_final; } double myrand(double l, double u) { // std::cout<<"in myrand" << std::endl; double r = l + (u - l) * rand() / (RAND_MAX + 1.0); return r; // std::cout<<"out myrand" << std::endl; } template double dist2_search_point(ParaType para0, ParaType para1) { // use theta / scale / skew to compare two nodes double d2 = 0; // double scale[4] = {1.0, 1.0, 1.0, 1.0}; for( int ii = 0; ii < 12; ii++ ) { double a = para0[ii] - para1[ii]; d2 += a * a; // * scale[ii]; } return d2; } template void generate_search_seed_3d(SEARCH_LIST & search_list, ParaType & para) { bool b_found = 0; double s1, s2, s3, k1, k2, k3 = 0; double a = 0; // rotation alpha double u = 0, v = 0, w = 0; // rotation axis // ParaType para(13); const double scale_upper = 1.5; const double scale_lower = 1 / 1.5; const double skew_lower = -1.0; const double skew_upper = 1.0; const double dist2_thres = 0.3; const double mypi = 3.1415926536; const double rot_angle_upper = mypi * (0.5); const double rot_angle_lower = mypi * (-0.5); unsigned int iteration = 0; unsigned int maxiteration = 50; while( b_found == 0 && iteration < maxiteration ) { iteration++; s1 = myrand(scale_lower, scale_upper); s2 = myrand(scale_lower, scale_upper); s3 = myrand(scale_lower, scale_upper); k1 = myrand(skew_lower, skew_upper); k2 = myrand(skew_lower, skew_upper); k3 = myrand(skew_lower, skew_upper); a = myrand(rot_angle_lower, rot_angle_upper); u = myrand(-1, 1); v = myrand(-1, 1); w = myrand(-1, 1); double n1 = 1.0 / sqrt(u * u + v * v + w * w); double sin_half_a = sin(a * 0.5); para.Fill(0.0); para[0] = sin_half_a * u * n1; para[1] = sin_half_a * v * n1; para[2] = sin_half_a * w * n1; para[3] = cos(a * 0.5); para[4] = s1; para[5] = s2; para[6] = s3; para[7] = k1; para[8] = k2; para[9] = k3; // std::cout << "test rand: " << para << " iteration " << iteration << std::endl; // search nearby search points bool bfar = 1; for( int i = 0; (bfar) & (i < search_list.size() ); i++ ) { ParaType para0 = search_list[i].para0; ParaType para1 = search_list[i].para1; double d0 = dist2_search_point(para0, para); double d1 = dist2_search_point(para1, para); // std::cout << "compare with para0: " << d0 << para0 << std::endl; // std::cout << "compare with para1: " << d1 << para1 << std::endl; bfar = bfar & (d0 > dist2_thres) & (d1 > dist2_thres); } b_found = bfar; // std::cout << "b_found = " << b_found << " bfar = " << bfar << std::endl; } // ret = para; } template void generate_search_seed_2d(SEARCH_LIST & search_list, ParaType & para) { bool b_found = 0; double r1, s1, s2, k = 0; const double pi = 3.1415927; const double theta_upper = pi / 4; const double theta_lower = -pi / 4; const double scale_upper = 1.5; const double scale_lower = 1 / 1.5; const double skew_lower = -1.0; const double skew_upper = 1.0; const double dist2_thres = 0.1; unsigned int iteration = 0; unsigned int maxiteration = 50; while( b_found == 0 && iteration < maxiteration ) { // for(;~b_found; ){ // std::cout << "b_found = " << b_found << std::endl; iteration++; r1 = myrand(theta_lower, theta_upper); s1 = myrand(scale_lower, scale_upper); s2 = myrand(scale_lower, scale_upper); k = myrand(skew_lower, skew_upper); para.Fill(0.0); para[0] = r1; para[1] = s1; para[2] = s2; para[3] = k; // // std::cout << "test rand: " << para << " iteration " << iteration << std::endl; // search nearby search points bool bfar = 1; for( int i = 0; (bfar) & (i < search_list.size() ); i++ ) { ParaType para0 = search_list[i].para0; ParaType para1 = search_list[i].para1; double d0 = dist2_search_point(para0, para); double d1 = dist2_search_point(para1, para); // std::cout << "compare with para0: " << d0 << para0 << std::endl; // std::cout << "compare with para1: " << d1 << para1 << std::endl; bfar = bfar & (d0 > dist2_thres) & (d1 > dist2_thres); } b_found = bfar; // std::cout << "b_found = " << b_found << " bfar = " << bfar << std::endl; } } template void get_best_search_point(SEARCH_LIST & search_list, SEARCH_POINT & spt) { std::sort(search_list.begin(), search_list.end(), compare_search_point ); spt = search_list[0]; } template void add_search_point(SEARCH_LIST & search_list, SEARCH_POINT spt) { search_list.push_back(spt); } template double get_cost_value_mmi(ImagePointerType fixedImage, ImagePointerType movingImage, ParaType para, PointType center, TransformTypePointer null_transform) { typedef typename ImagePointerType::ObjectType ImageType; typedef typename TransformTypePointer::ObjectType TransformType; typename TransformType::Pointer transform = TransformType::New(); transform->SetCenter(center); // transform->SetParameters(para); typedef typename itk::MattesMutualInformationImageToImageMetric mattesMutualInfoMetricType; typename mattesMutualInfoMetricType::Pointer mattesMutualInfo = mattesMutualInfoMetricType::New(); typedef typename itk::LinearInterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); interpolator->SetInputImage(movingImage); mattesMutualInfo->SetFixedImage(fixedImage); mattesMutualInfo->SetMovingImage(movingImage); mattesMutualInfo->SetFixedImageRegion(fixedImage->GetBufferedRegion() ); // mattesMutualInfo->SetMovingImage(outputImage); mattesMutualInfo->SetTransform(transform); mattesMutualInfo->SetInterpolator(interpolator); mattesMutualInfo->SetNumberOfHistogramBins( 32 ); mattesMutualInfo->SetNumberOfSpatialSamples( 5000 ); mattesMutualInfo->SetTransformParameters(para); mattesMutualInfo->Initialize(); double rval = 0; try { rval = mattesMutualInfo->GetValue(para); } catch( itk::ExceptionObject & err ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl << "Exception caught in computing mattesMutualInfo after registration" << std::endl << "Maybe: Too many samples map outside moving image buffer" << std::endl << "Set the cost value = 0 (max for MutualInfo) " << std::endl; rval = 0; } // test the cost before registration transform->SetIdentity(); transform->SetCenter(center); ParaType para0 = transform->GetParameters(); mattesMutualInfo->SetTransformParameters(para0); mattesMutualInfo->Initialize(); double rval0 = 0; try { rval0 = mattesMutualInfo->GetValue(para0); } catch( itk::ExceptionObject & err ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl << "Exception caught in computing mattesMutualInfo before registration" << std::endl << "Maybe: Too many samples map outside moving image buffer" << std::endl << "Set the cost value = 0 (max for MutualInfo) " << std::endl; rval0 = 0; } std::cout << "in cost: before register: cost = " << rval0 << std::endl; std::cout << "in cost: after register: cost = " << rval << std::endl; return rval; } template bool register_image_cxy(ImagePointerType fixed_image, ImagePointerType moving_image, ParaType & para1, double & rval) { // for 3d image use CenteredRigid2DTransform typedef itk::RegularStepGradientDescentOptimizer OptimizerType; typedef itk::CenteredRigid2DTransform TransformType_Rigid2D; typedef typename ImagePointerType::ObjectType ImageType; typename TransformType_Rigid2D::Pointer transform = TransformType_Rigid2D::New(); typedef itk::CenteredTransformInitializer< TransformType_Rigid2D, ImageType, ImageType> TransformInitializerType; typename TransformInitializerType::Pointer initializer = TransformInitializerType::New(); try { transform->SetIdentity(); initializer->SetTransform( transform ); initializer->SetFixedImage( fixed_image ); initializer->SetMovingImage( moving_image ); initializer->MomentsOn(); initializer->InitializeTransform(); transform->SetAngle( 0.0 ); } catch( itk::ExceptionObject & err ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!1" << std::endl << "Exception in InitializeTransform" << std::endl; return false; } para1 = transform->GetParameters(); std::cout << "finish initialize cx/cy/cz ..." << std::endl; std::cout << "cx/cy parameters (Euler3D): " << para1 << std::endl; return true; } template bool register_image_cxyz(ImagePointerType fixed_image, ImagePointerType moving_image, ParaType & para1, double & rval) { // for 3d image use CenteredRigid2DTransform typedef itk::RegularStepGradientDescentOptimizer OptimizerType; typedef itk::CenteredEuler3DTransform TransformType_Euler3D; typedef typename ImagePointerType::ObjectType ImageType; TransformType_Euler3D::Pointer transform = TransformType_Euler3D::New(); typedef itk::CenteredTransformInitializer< TransformType_Euler3D, ImageType, ImageType> TransformInitializerType; typename TransformInitializerType::Pointer initializer = TransformInitializerType::New(); try { transform->SetIdentity(); initializer->SetTransform( transform ); initializer->SetFixedImage( fixed_image ); initializer->SetMovingImage( moving_image ); initializer->MomentsOn(); initializer->InitializeTransform(); } catch( itk::ExceptionObject & err ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!1" << std::endl << "Exception in InitializeTransform" << std::endl; return false; } para1 = transform->GetParameters(); std::cout << "finish initialize cx/cy/cz ..." << std::endl; std::cout << "cx/cy parameters (Euler3D): " << para1 << std::endl; return true; } template bool register_image_affine3d_mres_mask(ImagePointerType fixed_image, ImagePointerType moving_image, ImageMaskSpatialObjectPointerType mask_fixed_object, ParaType para0, itk::Point center, int number_of_iteration, int MI_bins, int MI_samples, ParaType & para1, double & rval) { typedef typename ImagePointerType::ObjectType ImageType; typedef itk::RegularStepGradientDescentOptimizer OptimizerType; typedef itk::MattesMutualInformationImageToImageMetric MetricType; typedef typename ImagePointerType::ObjectType ImageType; typename MetricType::Pointer metric = MetricType::New(); // for mattesMutualInfo metric->SetNumberOfHistogramBins( MI_bins ); /** 32 BA */ metric->SetNumberOfSpatialSamples( MI_samples ); /** 6000 BA */ if( mask_fixed_object ) { metric->SetFixedImageMask(mask_fixed_object); std::cout << mask_fixed_object << std::endl; std::cout << mask_fixed_object->GetImage() << std::endl; } // typedef TransformType_Rigid2D TransformType_Pre; typedef itk::LinearInterpolateImageFunction InterpolatorType; typedef itk::MultiResolutionImageRegistrationMethod RegistrationType; typedef itk::MultiResolutionPyramidImageFilter fPyramidType; typename fPyramidType::Pointer fixedImagePyramid = fPyramidType::New(); typename fPyramidType::Pointer movingImagePyramid = fPyramidType::New(); typename ImageType::PointType origin; origin.Fill(0); moving_image->SetOrigin(origin); fixed_image->SetOrigin(origin); typename OptimizerType::Pointer optimizer = OptimizerType::New(); typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); typename RegistrationType::Pointer registration = RegistrationType::New(); typedef OptimizerType::ScalesType OptimizerScalesType; /*******************************************/ /* translate to para0 here */ std::cout << "pre registration para :" << para0 << std::endl; typedef itk::ANTSAffine3DTransform TransformType_ANTSAffine3D; typename TransformType_ANTSAffine3D::Pointer transform_a = TransformType_ANTSAffine3D::New(); transform_a->SetCenter(center); std::cout << "initial center: " << transform_a->GetCenter() << std::endl; // typedef OptimizerType::ScalesType OptimizerScalesType; OptimizerScalesType optimizerScales_a( transform_a->GetNumberOfParameters() ); const double translationScale = 1.0 / 1.e4; /** BA */ // /1.e6; // 1.e6; //1.e6; //1.0 / 1000.0; optimizerScales_a[0] = 1.0; // quaternion optimizerScales_a[1] = 1.0; // quaternion optimizerScales_a[2] = 1.0; // quaternion optimizerScales_a[3] = 1.0; // quaternion optimizerScales_a[4] = 1.0; // s1 optimizerScales_a[5] = 1.0; // s2 optimizerScales_a[6] = 1.0; // s3 optimizerScales_a[7] = 1.0; // k1 optimizerScales_a[8] = 1.0; // k2 optimizerScales_a[9] = 1.0; // k3 optimizerScales_a[10] = translationScale; optimizerScales_a[11] = translationScale; optimizerScales_a[12] = translationScale; optimizer->SetScales( optimizerScales_a ); optimizer->SetMaximumStepLength(0.1); optimizer->SetMinimumStepLength( 1.e-5 ); // optimizer->SetNumberOfIterations( 1000 ); // optimizer->SetNumberOfIterations( 500 ); optimizer->SetNumberOfIterations(number_of_iteration); // (500); /** BA */ optimizer->MinimizeOn(); // Create the Command observer and register it with the optimizer. // // CommandIterationUpdate::Pointer observer = CommandIterationUpdate::New(); // optimizer->AddObserver( itk::IterationEvent(), observer ); registration = RegistrationType::New(); registration->SetNumberOfLevels(3); registration->SetFixedImagePyramid( fixedImagePyramid ); registration->SetMovingImagePyramid( movingImagePyramid ); registration->SetMetric( metric ); registration->SetOptimizer( optimizer ); registration->SetInterpolator( interpolator ); registration->SetFixedImage( fixed_image ); registration->SetMovingImage( moving_image ); registration->SetFixedImageRegion( fixed_image->GetLargestPossibleRegion() ); registration->SetTransform( transform_a ); // reset the parameter // registration->SetInitialTransformParameters(para_pre ); registration->SetInitialTransformParameters(para0 ); std::cout << "reset initial transform parameters" << std::endl; std::cout << "para_pre: " << para0 << std::endl; // std::cout << "transform_a: " << transform_a << std::endl; rval = get_cost_value_mmi(fixed_image, moving_image, para0, center, transform_a); std::cout << "init measure value: rval = " << rval << std::endl; // rval = optimizer->GetValue(para0); // rval = metric->GetValue(para0); bool bsuc = 1; try { registration->Update(); } catch( itk::ExceptionObject & err ) { std::cout << "ExceptionObject caught !" << std::endl; std::cout << err << std::endl; bsuc = 0; // std::exception(); } if( bsuc ) { // OptimizerType::ParametersType // finalParameters = registration->GetLastTransformParameters(); para1 = registration->GetLastTransformParameters(); // para1 = finalParameters; // rval = optimizer->GetValue(para1); // rval = metric->GetValue(para1); // rval = registration->GetMetric()->GetValue(para1); rval = get_cost_value_mmi(fixed_image, moving_image, para1, center, transform_a); // double rval2 = optimizer->GetValue(); // std::cout << "measure value: rval2 = " << rval2 << std::endl; } else { // register failed para1 = para0; // registration->GetLastTransformParameters(); rval = get_cost_value_mmi(fixed_image, moving_image, para1, center, transform_a); } std::cout << "final affine3d registration para :" << para1 << std::endl; std::cout << "use iteration: " << optimizer->GetNumberOfIterations() << std::endl; std::cout << "measure value: rval = " << rval << std::endl; std::cout << "finish register..." << std::endl; return bsuc; } template bool register_image_affine2d_mres_mask(ImagePointerType fixed_image, ImagePointerType moving_image, ImageMaskSpatialObjectPointerType mask_fixed_object, ParaType para0, itk::Point center, int number_of_iteration, int MI_bins, int MI_samples, ParaType & para1, double & rval) { typedef typename ImagePointerType::ObjectType ImageType; typedef itk::RegularStepGradientDescentOptimizer OptimizerType; typedef itk::MattesMutualInformationImageToImageMetric MetricType; typedef typename ImagePointerType::ObjectType ImageType; typename MetricType::Pointer metric = MetricType::New(); // for mattesMutualInfo metric->SetNumberOfHistogramBins( MI_bins ); /** 32 BA */ metric->SetNumberOfSpatialSamples( MI_samples ); /** 6000 BA */ if( mask_fixed_object ) { metric->SetFixedImageMask(mask_fixed_object); std::cout << mask_fixed_object << std::endl; std::cout << mask_fixed_object->GetImage() << std::endl; } // typedef TransformType_Rigid2D TransformType_Pre; typedef itk::LinearInterpolateImageFunction InterpolatorType; typedef itk::MultiResolutionImageRegistrationMethod RegistrationType; typedef itk::MultiResolutionPyramidImageFilter fPyramidType; typename fPyramidType::Pointer fixedImagePyramid = fPyramidType::New(); typename fPyramidType::Pointer movingImagePyramid = fPyramidType::New(); typename ImageType::PointType origin; origin.Fill(0); moving_image->SetOrigin(origin); fixed_image->SetOrigin(origin); typename OptimizerType::Pointer optimizer = OptimizerType::New(); typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); typename RegistrationType::Pointer registration = RegistrationType::New(); typedef OptimizerType::ScalesType OptimizerScalesType; /*******************************************/ /* translate to para0 here */ std::cout << "pre registration para :" << para0 << std::endl; // typedef itk::CenteredAffine2DTransform TransformType; // typedef itk::CenteredAffine2DTransform TransformType_GSAffine2D; // typename TransformType_GSAffine2D::Pointer transform_a = TransformType_GSAffine2D::New(); typedef itk::ANTSCenteredAffine2DTransform TransformType_ANTSAffine2D; typename TransformType_ANTSAffine2D::Pointer transform_a = TransformType_ANTSAffine2D::New(); // transform_a->SetCenter(center); // std::cout<<"initial center: " << transform_a->GetCenter() << std::endl; OptimizerScalesType optimizerScales_a( transform_a->GetNumberOfParameters() ); const double translationScale = 1.0 / 1000.0; optimizerScales_a[0] = 1.0; optimizerScales_a[1] = 1.0; optimizerScales_a[2] = 1.0; optimizerScales_a[3] = 1.0; optimizerScales_a[4] = translationScale; optimizerScales_a[5] = translationScale; optimizerScales_a[6] = translationScale; optimizerScales_a[7] = translationScale; optimizer->SetScales( optimizerScales_a ); optimizer->SetMaximumStepLength( 0.1 ); optimizer->SetMinimumStepLength( 0.01 ); optimizer->SetNumberOfIterations( number_of_iteration ); optimizer->MinimizeOn(); // Create the Command observer and register it with the optimizer. // // CommandIterationUpdate::Pointer observer = CommandIterationUpdate::New(); // optimizer->AddObserver( itk::IterationEvent(), observer ); registration = RegistrationType::New(); registration->SetNumberOfLevels(3); registration->SetFixedImagePyramid( fixedImagePyramid ); registration->SetMovingImagePyramid( movingImagePyramid ); registration->SetMetric( metric ); registration->SetOptimizer( optimizer ); registration->SetInterpolator( interpolator ); registration->SetFixedImage( fixed_image ); registration->SetMovingImage( moving_image ); registration->SetFixedImageRegion( fixed_image->GetLargestPossibleRegion() ); registration->SetTransform( transform_a ); // reset the parameter // registration->SetInitialTransformParameters(para_pre ); registration->SetInitialTransformParameters(para0 ); std::cout << "reset initial transform parameters" << std::endl; std::cout << "para_pre: " << para0 << std::endl; // std::cout << "transform_a: " << transform_a << std::endl; rval = get_cost_value_mmi(fixed_image, moving_image, para0, center, transform_a); std::cout << "init measure value: rval = " << rval << std::endl; // rval = optimizer->GetValue(para0); // rval = metric->GetValue(para0); bool bsuc = 1; try { registration->Update(); } catch( itk::ExceptionObject & err ) { std::cout << "ExceptionObject caught !" << std::endl; std::cout << err << std::endl; bsuc = 0; // std::exception(); } if( bsuc ) { // OptimizerType::ParametersType // finalParameters = registration->GetLastTransformParameters(); para1 = registration->GetLastTransformParameters(); // para1 = finalParameters; // rval = optimizer->GetValue(para1); // rval = metric->GetValue(para1); // rval = registration->GetMetric()->GetValue(para1); rval = get_cost_value_mmi(fixed_image, moving_image, para1, center, transform_a); // double rval2 = optimizer->GetValue(); // std::cout << "measure value: rval2 = " << rval2 << std::endl; } else { // register failed para1 = para0; // registration->GetLastTransformParameters(); rval = get_cost_value_mmi(fixed_image, moving_image, para1, center, transform_a); } std::cout << "final affine2d registration para :" << para1 << std::endl; std::cout << "use iteration: " << optimizer->GetNumberOfIterations() << std::endl; std::cout << "measure value: rval = " << rval << std::endl; std::cout << "finish register..." << std::endl; return bsuc; } // template // void compute_single_affine_transform_2d(ImagePointerType I_fixed, ImagePointerType I_moving, MaskImagePointerType // mask_fixed, TransformPointerType &transform){ template void compute_single_affine_transform_2d(ImagePointerType I_fixed, ImagePointerType I_moving, MaskImagePointerType mask_fixed, TransformPointerType & transform, TransformPointerType & transform_initial) { typedef typename ImagePointerType::ObjectType ImageType; const int ImageDimension = ImageType::ImageDimension; typedef typename ImageType::IOPixelType PixelType; typedef typename MaskImagePointerType::ObjectType MaskImageType; typedef typename MaskImageType::IOPixelType MaskPixelType; typedef typename TransformPointerType::ObjectType TransformType; typedef typename TransformType::ParametersType ParaType; // option parameters int number_of_seeds = 0; int number_of_iteration = 500; int MI_bins = 32; int MI_samples = 6000; unsigned int time_seed = (unsigned int) time(NULL); srand(time_seed); // TODO: need to fix here bool b_use_mask = 0; // (mask_fixed == NULL); std::cout << "number_of_seeds: " << number_of_seeds << std::endl; std::cout << "rand_time_seed: " << time_seed << std::endl; std::cout << "number_of_iteration: " << number_of_iteration << std::endl; std::cout << "MI_bins: " << MI_bins << std::endl; std::cout << "MI_samples: " << MI_samples << std::endl; std::cout << "use mask: " << b_use_mask << std::endl; // memory of searched results typedef SEARCH_POINT_TYPE SEARCH_POINT; typedef std::vector SEARCH_LIST; SEARCH_LIST search_list; // typedef itk::ImageMaskSpatialObject ImageMaskSpatialObject; typedef typename itk::ImageMaskSpatialObject ImageMaskSpatialObject; typename ImageMaskSpatialObject::Pointer mask_fixed_object = 0; if( b_use_mask ) { typedef typename itk::Image CharMaskImageType; typedef typename itk::CastImageFilter CastFilterType; typename CastFilterType::Pointer cast_filter = CastFilterType::New(); cast_filter->SetInput(mask_fixed); cast_filter->Update(); typename CharMaskImageType::Pointer mask_fixed_char = cast_filter->GetOutput(); mask_fixed_object = ImageMaskSpatialObject::New(); mask_fixed_object->SetImage(mask_fixed_char); } typename ImageType::PointType origin; origin.Fill(0); I_moving->SetOrigin(origin); I_fixed->SetOrigin(origin); typename TransformType::Pointer trans = TransformType::New(); ParaType para0(trans->GetNumberOfParameters() ), para1(trans->GetNumberOfParameters() ); double rval; SEARCH_POINT spt; typedef typename itk::CenteredRigid2DTransform TransformType_Rigid2D; ParaType para_cxy(TransformType_Rigid2D::New()->GetNumberOfParameters() ); // translated from pre registration // find initial center bool is_ok = false; itk::Point center; if( transform_initial.IsNull() ) { // if (1) { is_ok = register_image_cxy(I_fixed, I_moving, para_cxy, rval); if( !is_ok ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl << "initial affine registeration falied" << std::endl; std::exception(); } center[0] = para_cxy[1]; center[1] = para_cxy[2]; std::cout << "is_ok=" << is_ok << "para_cxy:" << para_cxy << std::endl; } else { center[0] = transform_initial->GetCenter()[0]; center[1] = transform_initial->GetCenter()[1]; std::cout << "input transform: " << transform_initial << std::endl; } std::cout << "initial center: (" << center[0] << "," << center[1] << ")" << std::endl; for( int n = 0; (number_of_seeds > 0) ? (n < number_of_seeds) : (n <= number_of_seeds); n++ ) { if( n == 0 ) { // if (1) { if( transform_initial.IsNull() ) { para0[0] = 0; // para1[0]; // theta para0[1] = 1.0; // s1 para0[2] = 1.0; // s2 para0[3] = 0.0; // k para0[4] = center[0]; // para1[1]; //c1 para0[5] = center[1]; // para1[2]; //c2 para0[6] = para_cxy[3]; // 0;//para1[3]; //t1 para0[7] = para_cxy[4]; // 0; //para1[4]; //t2 std::cout << "ABC: " << std::endl; } else { for( unsigned int i = 0; i < transform_initial->GetParameters().Size(); i++ ) { para0[i] = transform_initial->GetParameters()[i]; } std::cout << "DEF: " << std::endl; } } else { generate_search_seed_2d(search_list, para0); } // main registration using affine transform is_ok = register_image_affine2d_mres_mask(I_fixed, I_moving, mask_fixed_object, para0, center, number_of_iteration, MI_bins, MI_samples, para1, rval); if( !is_ok ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl << "affine registration failed!" << std::endl << "use the initial parameters" << std::endl; // return -1; } spt.para0 = para0; spt.para1 = para1; spt.center = center; spt.rval = rval; spt.index = n; spt.number_of_iteration = number_of_iteration; std::cout << "para0: " << para0 << std::endl; std::cout << "para1: " << para1 << std::endl; std::cout << "center: " << center << std::endl; std::cout << "rval: " << rval << std::endl; std::cout << "add the search result to the list ... seed [" << n << "]" << std::endl << std::endl; add_search_point(search_list, spt); } get_best_search_point(search_list, spt); std::cout << std::endl << "History: " << std::endl; for( int ii = 0; ii < search_list.size(); ii++ ) { std::cout << "[" << ii << "]: " << search_list[ii].rval << std::endl; std::cout << "center: " << search_list[ii].center << std::endl; std::cout << "para0: " << search_list[ii].para0 << std::endl; std::cout << "para1: " << search_list[ii].para1 << std::endl; } typename TransformType::Pointer transform_final = TransformType::New(); transform_final->SetParameters(spt.para1); transform_final->SetCenter(center); std::cout << "final transform parameters = " << transform_final->GetParameters() << std::endl; transform = transform_final; } template void compute_single_affine_transform(ImagePointerType fixedImage, ImagePointerType movingImage, MaskImagePointerType maskImage, TransformPointerType & transform, TransformPointerType & transform_initial) { typedef typename ImagePointerType::ObjectType ImageType; const int ImageDimension = ImageType::ImageDimension; typedef typename ImageType::IOPixelType PixelType; typedef typename MaskImagePointerType::ObjectType MaskImageType; typedef typename MaskImageType::IOPixelType MaskPixelType; typedef typename TransformPointerType::ObjectType TransformType; std::cout << "transform_initial: IsNotNull():" << transform_initial.IsNotNull() << std::endl; if( ImageDimension == 2 ) { typedef itk::ANTSCenteredAffine2DTransform RunningAffineTransformType; RunningAffineTransformType::Pointer transform_running = RunningAffineTransformType::New(); RunningAffineTransformType::Pointer transform_running_initial; // = RunningAffineTransformType::New(); std::cout << "1: transform_running_initial: IsNotNull():" << transform_running_initial.IsNotNull() << std::endl; if( transform_initial.IsNotNull() ) { transform_running_initial->SetCenter(*(reinterpret_cast (const_cast(&(transform_initial ->GetCenter() ) ) ) ) ); transform_running_initial->SetMatrix(*(reinterpret_cast (const_cast(&(transform_initial-> GetMatrix() ) ) ) ) ); transform_running_initial->SetTranslation(*(reinterpret_cast (const_cast(&( transform_initial -> GetTranslation() ) ) ) ) ); } // Use type casting typedef typename itk::Image::Pointer R_ImagePointerType; R_ImagePointerType & R_fixedImage = reinterpret_cast(fixedImage); R_ImagePointerType & R_movingImage = reinterpret_cast(movingImage); R_ImagePointerType & R_maskImage = reinterpret_cast(maskImage); std::cout << "2: transform_running_initial: IsNotNull():" << transform_running_initial.IsNotNull() << std::endl; compute_single_affine_transform_2d(R_fixedImage, R_movingImage, R_maskImage, transform_running, transform_running_initial); // TODO: transform->SetCenter(*(reinterpret_cast (const_cast(&(transform_running-> GetCenter() ) ) ) ) ); transform->SetTranslation(*(reinterpret_cast (const_cast(&( transform_running ->GetTranslation() ) ) ) ) ); transform->SetMatrix(*(reinterpret_cast (const_cast(&(transform_running-> GetMatrix() ) ) ) ) ); // transform->SetFixedParameters(transform_running->GetFixedParameters()); // transform->SetParameters(transform_running->GetParameters()); } else if( ImageDimension == 3 ) { typedef itk::ANTSAffine3DTransform RunningAffineTransformType; RunningAffineTransformType::Pointer transform_running = RunningAffineTransformType::New(); RunningAffineTransformType::Pointer transform_running_initial = RunningAffineTransformType::New();; // compute_single_affine_transform_3d(fixedImage, movingImage, maskImage, transform_running); if( transform_initial.IsNotNull() ) { transform_running_initial->SetCenter(*(reinterpret_cast (const_cast(&(transform_initial ->GetCenter() ) ) ) ) ); transform_running_initial->SetMatrix(*(reinterpret_cast (const_cast(&(transform_initial-> GetMatrix() ) ) ) ) ); transform_running_initial->SetTranslation(*(reinterpret_cast (const_cast(&( transform_initial -> GetTranslation() ) ) ) ) ); } // Use type casting typedef typename itk::Image::Pointer R_ImagePointerType; R_ImagePointerType & R_fixedImage = reinterpret_cast(fixedImage); R_ImagePointerType & R_movingImage = reinterpret_cast(movingImage); R_ImagePointerType & R_maskImage = reinterpret_cast(maskImage); compute_single_affine_transform_3d(R_fixedImage, R_movingImage, R_maskImage, transform_running, transform_running_initial); // TODO: transform->SetCenter(*(reinterpret_cast (const_cast(&(transform_running-> GetCenter() ) ) ) ) ); transform->SetTranslation(*(reinterpret_cast (const_cast(&( transform_running ->GetTranslation() ) ) ) ) ); transform->SetMatrix(*(reinterpret_cast (const_cast(&(transform_running-> GetMatrix() ) ) ) ) ); // transform->SetFixedParameters(transform_running->GetFixedParameters()); // transform->SetParameters(transform_running->GetParameters()); } else { std::cout << "Unsupported, not 2D/ 3D" << std::endl; return; } } template void create_deformation_field_byref(const DisplacementFieldPointerType & ref, DisplacementFieldPointerType & field) { typedef typename DisplacementFieldPointerType::ObjectType DisplacementFieldType; // field = DisplacementFieldType::New(); typename DisplacementFieldType::RegionType region; region.SetSize(ref->GetLargestPossibleRegion().GetSize() ); region.SetIndex(ref->GetLargestPossibleRegion().GetIndex() ); field->SetRegions( region ); field->SetSpacing(ref->GetSpacing() ); field->SetOrigin(ref->GetOrigin() ); field->Allocate(); } // compose affine transform (in a matrix format A: (Ax+b)) with a deformation field F: // the new field is: F_new (x) = F ( A (x) ) // output should be allocated outside template void compose_affine_with_field(const TransformPointerType & aff, const DisplacementFieldPointerType & field, DisplacementFieldPointerType & field_output) { typedef typename DisplacementFieldPointerType::ObjectType DisplacementFieldType; typedef itk::ImageRegionIteratorWithIndex FieldIterator; typedef typename DisplacementFieldType::IndexType IndexType; typedef typename DisplacementFieldType::PointType PointType; typedef typename DisplacementFieldType::PixelType VectorType; const unsigned int ImageDimension = DisplacementFieldType::ImageDimension; // PointType zeroorigin; // zeroorigin.Fill(0); // field->SetOrigin(zeroorigin); // field_output->SetOrigin(zeroorigin); PointType pointIn1; PointType pointIn2; PointType pointIn3; // iterate through field_output finding the points that it maps to via field. // then take the difference from the original point and put it in the output field. // std::cout << " begin iteration " << std::endl; FieldIterator iter_field(field, field->GetLargestPossibleRegion() ); // std::cout << field_output->GetLargestPossibleRegion() << std::endl; int cnt = 0; for( iter_field.GoToBegin(); !iter_field.IsAtEnd(); ++iter_field ) { IndexType index = iter_field.GetIndex(); field_output->TransformIndexToPhysicalPoint( index, pointIn1 ); VectorType disp = iter_field.Get(); for( int jj = 0; jj < ImageDimension; jj++ ) { pointIn2[jj] = disp[jj] + pointIn1[jj]; } // use affine transform pointIn3 = aff->TransformPoint(pointIn2); VectorType out; for( int jj = 0; jj < ImageDimension; jj++ ) { out[jj] = pointIn3[jj] - pointIn1[jj]; } field_output->SetPixel(iter_field.GetIndex(), out); } // std::cout << " end iteration " << std::endl; } // this is obsolet, use itkWarpImageWAffineFilter template void warp_image_field(const ImagePointerType & img_input, const DisplacementFieldPointerType & field, ImagePointerType & img_output) { typedef typename ImagePointerType::ObjectType ImageType; typedef typename DisplacementFieldPointerType::ObjectType DisplacementFieldType; typedef typename itk::WarpImageFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); warper->SetInput(img_input); warper->SetDisplacementField(field); warper->SetEdgePaddingValue( 0); warper->SetOutputSpacing(field->GetSpacing() ); warper->SetOutputOrigin( field->GetOrigin() ); warper->Update(); img_output = warper->GetOutput(); } template void affine_image(const ImageTypePointer & input_image, const TransformPointerType & transform, const RefImageTypePointer & ref_image, ImageTypePointer & img_aff ) { typedef typename ImageTypePointer::ObjectType ImageType; typedef typename TransformPointerType::ObjectType TransformType; // apply the transform typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); typedef itk::LinearInterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); resampler->SetInterpolator( interpolator ); resampler->SetInput( input_image ); resampler->SetSize( ref_image->GetLargestPossibleRegion().GetSize() ); resampler->SetOutputOrigin( ref_image->GetOrigin() ); resampler->SetOutputSpacing( ref_image->GetSpacing() ); resampler->SetDefaultPixelValue( 0 ); resampler->SetTransform( transform ); resampler->Update(); img_aff = resampler->GetOutput(); } template void warp_image_field_waffine(const ImagePointerType & img_input, const TransformPointerType & aff, const DisplacementFieldPointerType & field, ImagePointerType & img_output) { // TODO: add a new WarpImageFilter to support affine as an input // temporary solution: typedef typename DisplacementFieldPointerType::ObjectType DisplacementFieldType; typename DisplacementFieldType::Pointer field_comp = DisplacementFieldType::New(); // create_deformation_field_byref(field, field_comp); // compose_affine_with_field(aff, field, field_comp); // warp_image_field(img_input, field_comp, img_output); typedef typename TransformPointerType::ObjectType TransformType; typedef typename ImagePointerType::ObjectType ImageType; typedef itk::WarpImageWAffineFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); warper->SetInput(img_input); warper->SetDisplacementField(field); warper->SetAffineTransform(aff); warper->SetEdgePaddingValue( 0); warper->SetOutputSpacing(field->GetSpacing() ); warper->SetOutputOrigin( field->GetOrigin() ); warper->SetOutputSize(field->GetLargestPossibleRegion().GetSize() ); warper->Update(); img_output = warper->GetOutput(); return; } // TODO: use my own code to implement all the optimization codes #endif /*ANTS_AFFINE_REGISTRATION_H_*/ ants-2.2.0/ImageRegistration/ANTS_affine_registration2.h000066400000000000000000001711121311104306400231730ustar00rootroot00000000000000#ifndef ANTS_AFFINE_REGISTRATION2_H_ #define ANTS_AFFINE_REGISTRATION2_H_ #include #include #include #include "itkImage.h" #include "itkPoint.h" #include "itkCastImageFilter.h" #include "itkImageMaskSpatialObject.h" #include "itkCenteredEuler3DTransform.h" #include "itkRegularStepGradientDescentOptimizer.h" #include "itkGradientDescentOptimizer.h" #include "itkCenteredTransformInitializer.h" #include "itkMattesMutualInformationImageToImageMetric.h" #include "itkCorrelationCoefficientHistogramImageToImageMetric.h" #include "itkMultiResolutionImageRegistrationMethod.h" #include "itkLinearInterpolateImageFunction.h" #include "itkANTSAffine3DTransform.h" #include "itkCenteredRigid2DTransform.h" #include "itkANTSCenteredAffine2DTransform.h" #include "itkTransformFactory.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkResampleImageFilter.h" #include "itkLinearInterpolateImageFunction.h" #include "itkWarpImageFilter.h" #include "itkWarpImageWAffineFilter.h" #include "itkImageMomentsCalculator.h" #include #include "ReadWriteData.h" #include "itkMeanSquaresImageToImageMetric.h" #include "itkGradientDifferenceImageToImageMetric.h" #include "itkNormalizedCorrelationImageToImageMetric.h" #include "itkImageRegionIterator.h" #include "itkRandomImageSource.h" #include "itkAddImageFilter.h" typedef enum { AffineWithMutualInformation = 1, AffineWithMeanSquareDifference, AffineWithHistogramCorrelation, AffineWithNormalizedCorrelation, AffineWithGradientDifference } AffineMetricType; template class OptAffine { public: typedef typename TAffineTransform::Pointer AffineTransformPointerType; typedef typename TMaskImage::Pointer MaskImagePointerType; typedef TAffineTransform AffineTransformType; typedef TMaskImage MaskImageType; typedef typename MaskImageType::Pointer MaskObjectPointerType; OptAffine(): metric_type (AffineWithMutualInformation) { MI_bins = 32; MI_samples = 6000; number_of_seeds = 0; time_seed = (unsigned int) time(ITK_NULLPTR); number_of_levels = 3; number_of_iteration_list.resize(number_of_levels, 10000); const int kParaDim = AffineTransformType::ParametersDimension; gradient_scales.resize(kParaDim, 1.0); is_rigid = false; maximum_step_length = 0.1; relaxation_factor = 0.5; minimum_step_length = 1.e-5; translation_scales = 1.e-4; use_rotation_header = false; ignore_void_orgin = true; }; ~OptAffine() { }; AffineTransformPointerType transform_initial; MaskImagePointerType mask_fixed; int MI_bins; int MI_samples; int number_of_seeds; unsigned int time_seed; int number_of_levels; std::vector number_of_iteration_list; std::vector gradient_scales; AffineMetricType metric_type; bool is_rigid; double maximum_step_length; double relaxation_factor; double minimum_step_length; double translation_scales; bool use_rotation_header; bool ignore_void_orgin; }; template std::ostream & operator<<(std::ostream& os, const OptAffine& p) { os << "OptAffine: "; os << "metric_type="; switch( p.metric_type ) { case AffineWithMutualInformation: os << "AffineWithMutualInformation" << std::endl; break; case AffineWithMeanSquareDifference: os << "AffineWithMeanSquareDifference" << std::endl; break; case AffineWithHistogramCorrelation: os << "AffineWithHistogramCorrelation" << std::endl; break; case AffineWithNormalizedCorrelation: os << "AffineWithNormalizedCorrelation" << std::endl; break; case AffineWithGradientDifference: os << "AffineWithGradientDifference" << std::endl; break; } os << "MI_bins=" << p.MI_bins << " " << "MI_samples=" << p.MI_samples << std::endl; os << "number_of_seeds=" << p.number_of_seeds << " " << "time_seed=" << p.time_seed << std::endl; os << "number_of_levels=" << p.number_of_levels << std::endl; os << "number_of_iteration_list=" << "["; for( unsigned int i = 0; i < p.number_of_iteration_list.size() - 1; i++ ) { os << p.number_of_iteration_list[i] << ","; } if( p.number_of_iteration_list.size() > 0 ) { os << p.number_of_iteration_list[p.number_of_iteration_list.size() - 1]; } os << "]" << std::endl; os << "graident_scales=" << "["; for( unsigned int i = 0; i < p.gradient_scales.size() - 1; i++ ) { os << p.gradient_scales[i] << ","; } if( p.gradient_scales.size() > 0 ) { os << p.gradient_scales[p.gradient_scales.size() - 1]; } os << "]" << std::endl; os << "is_rigid = " << p.is_rigid << std::endl; os << "mask null: " << p.mask_fixed.IsNull() << std::endl; os << "maximum_step_length=" << p.maximum_step_length << std::endl;; os << "relaxation_factor=" << p.relaxation_factor << std::endl; os << "minimum_step_length=" << p.minimum_step_length << std::endl; os << "translation_scales=" << p.translation_scales << std::endl; return os; }; template void WriteAffineTransformFile(typename TransformType::Pointer & transform, const std::string & filename) { itk::TransformFileWriter::Pointer transform_writer; transform_writer = itk::TransformFileWriter::New(); transform_writer->SetFileName(filename); transform_writer->SetInput(transform); try { transform_writer->Update(); } catch( itk::ExceptionObject & err ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << err << std::endl << "Exception in writing tranform file: " << std::endl << filename << std::endl; return; } return; } template void ReadAffineTransformFile(const std::string & filename, typename CastTransformType::Pointer & transform) { // const unsigned int InputSpaceDimension = CastTransformType::InputSpaceDimension; // const unsigned int OutputSpaceDimension = CastTransformType::OutputSpaceDimension; itk::TransformFactory::RegisterTransform(); itk::TransformFactory >::RegisterTransform(); typedef typename itk::TransformFileReader TranReaderType; TranReaderType::Pointer tran_reader = TranReaderType::New(); tran_reader->SetFileName(filename); try { tran_reader->Update(); } catch( itk::ExceptionObject & err ) { std::cout << err << std::endl; std::cout << "Exception caught in reading tran para file: " << filename << std::endl; return; } transform = dynamic_cast( (tran_reader->GetTransformList() )->front().GetPointer() ); return; } template void InitializeAffineOptmizationParameters(OptAffine & opt, double translationScale) { const int kImageDim = OptAffine::MaskImageType::ImageDimension; switch( kImageDim ) { case 2: { // const double translationScale = 1.0 / 1000.0; opt.gradient_scales[0] = 1.0; opt.gradient_scales[1] = 1.0; opt.gradient_scales[2] = 1.0; opt.gradient_scales[3] = 1.0; opt.gradient_scales[4] = translationScale; opt.gradient_scales[5] = translationScale; opt.gradient_scales[6] = translationScale; opt.gradient_scales[7] = translationScale; } break; case 3: { // const double translationScale = 1.0/1.e4; opt.gradient_scales[0] = 1.0; // quaternion opt.gradient_scales[1] = 1.0; // quaternion opt.gradient_scales[2] = 1.0; // quaternion opt.gradient_scales[3] = 1.0; // quaternion opt.gradient_scales[4] = 1.0; // s1 opt.gradient_scales[5] = 1.0; // s2 opt.gradient_scales[6] = 1.0; // s3 opt.gradient_scales[7] = 1.0; // k1 opt.gradient_scales[8] = 1.0; // k2 opt.gradient_scales[9] = 1.0; // k3 opt.gradient_scales[10] = translationScale; opt.gradient_scales[11] = translationScale; opt.gradient_scales[12] = translationScale; } break; } std::cout << opt; } template class RunningAffineCache { public: typedef TImagePyramid ImagePyramidType; typedef typename ImagePyramidType::value_type ImagePointerType; typedef typename ImagePointerType::ObjectType ImageType; typedef TMetricType MetricType; typedef typename MetricType::Pointer MetricPointerType; typedef TInterpolatorType InterpolatorType; typedef typename TInterpolatorType::Pointer InterpolatorPointerType; typedef TMaskObjectType MaskObjectType; typedef typename MaskObjectType::Pointer MaskObjectPointerType; RunningAffineCache() { }; ~RunningAffineCache() { }; MaskObjectPointerType mask_fixed_object; ImagePyramidType fixed_image_pyramid; ImagePyramidType moving_image_pyramid; MetricPointerType metric; MetricPointerType invmetric; InterpolatorPointerType interpolator; }; template void GetAffineTransformFromImage(const ImageTypePointer& img, AffineTransformPointer & aff) { typedef typename ImageTypePointer::ObjectType ImageType; typedef typename ImageType::DirectionType DirectionType; typedef typename ImageType::PointType PointType; typedef typename AffineTransformPointer::ObjectType::TranslationType VectorType; DirectionType direction = img->GetDirection(); PointType pt = img->GetOrigin(); VectorType translation; translation.Fill(0); aff->SetMatrix(direction); aff->SetCenter(pt); aff->SetTranslation(translation); } // ////////////////////////////////////////////////////////////////////// template inline void PreConversionInAffine(ImagePointerType & fixedImage, RunningImagePointerType& R_fixedImage, ImagePointerType & movingImage, RunningImagePointerType& R_movingImage, OptAffineType & opt, RunningOptAffineType & R_opt) { typedef typename OptAffineType::AffineTransformType AffineTransformType; typedef typename RunningOptAffineType::AffineTransformType RunningAffineTransformType; if( opt.use_rotation_header ) { std::cout << "===================>initialize from rotation header ... " << std::endl; // use the rotation header to initialize the affine: inv(Tm) * Tf typename AffineTransformType::Pointer aff_Im = AffineTransformType::New(); GetAffineTransformFromImage(movingImage, aff_Im); typename AffineTransformType::Pointer aff_If = AffineTransformType::New(); GetAffineTransformFromImage(fixedImage, aff_If); typename AffineTransformType::Pointer aff_combined = AffineTransformType::New(); aff_combined->SetFixedParameters(aff_If->GetFixedParameters() ); aff_combined->SetParameters(aff_If->GetParameters() ); typename AffineTransformType::Pointer aff_Im_inv = AffineTransformType::New(); aff_Im->GetInverse(aff_Im_inv); aff_combined->Compose(aff_Im_inv, 0); opt.transform_initial = aff_combined; // std::cout << "aff_If: " << aff_If << std::endl; // std::cout << "aff_Im: " << aff_Im << std::endl; // std::cout << "aff_combined: " << aff_combined << std::endl; } if( !opt.use_rotation_header && opt.ignore_void_orgin ) { std::cout << "===================> ignore void origins which are too far away to be possible alignments: use 0 instead." << std::endl; typename AffineTransformType::Pointer aff_Im = AffineTransformType::New(); GetAffineTransformFromImage(movingImage, aff_Im); typename AffineTransformType::Pointer aff_If = AffineTransformType::New(); GetAffineTransformFromImage(fixedImage, aff_If); bool b_far_origin_without_rotation = false; // bool b_far_origin_without_rotation = HaveFarOriginWithoutRotation(aff_If, aff_Im); if( b_far_origin_without_rotation ) { typename AffineTransformType::Pointer aff_combined = AffineTransformType::New(); aff_combined->SetFixedParameters(aff_If->GetFixedParameters() ); aff_combined->SetParameters(aff_If->GetParameters() ); typename AffineTransformType::Pointer aff_Im_inv = AffineTransformType::New(); aff_Im->GetInverse(aff_Im_inv); aff_combined->Compose(aff_Im_inv, 0); opt.transform_initial = aff_combined; } } if( opt.transform_initial.IsNotNull() ) { R_opt.transform_initial = RunningAffineTransformType::New(); R_opt.transform_initial->SetCenter(*(reinterpret_cast (const_cast(&(opt. transform_initial ->GetCenter() ) ) ) ) ); R_opt.transform_initial->SetMatrix(*(reinterpret_cast (const_cast(&(opt. transform_initial-> GetMatrix() ) ) ) ) ); R_opt.transform_initial->SetTranslation(*(reinterpret_cast (const_cast(&(opt. transform_initial -> GetTranslation() ) ) ) ) ); } // std::cout << "R_opt.transform_initial" << R_opt.transform_initial << std::endl; if( opt.mask_fixed.IsNotNull() ) { R_opt.mask_fixed = dynamic_cast (opt.mask_fixed.GetPointer() ); if( R_opt.mask_fixed.IsNull() ) { itkGenericExceptionMacro(<< "Can't convert optimizer mask to proper mask type."); } // have to set " -fno-strict-aliasing " in gcc to remove the following compilation warning: // warning: dereferencing type-punned pointer will break strict-aliasing rules } R_fixedImage = reinterpret_cast(fixedImage); R_movingImage = reinterpret_cast(movingImage); R_opt.MI_bins = opt.MI_bins; R_opt.MI_samples = opt.MI_samples; R_opt.number_of_seeds = opt.number_of_seeds; R_opt.time_seed = opt.time_seed; R_opt.number_of_levels = opt.number_of_levels; R_opt.number_of_iteration_list = opt.number_of_iteration_list; // R_opt.gradient_scales = opt.gradient_scales; // does not need, will assign value later. R_opt.metric_type = opt.metric_type; R_opt.is_rigid = opt.is_rigid; R_opt.maximum_step_length = opt.maximum_step_length; R_opt.relaxation_factor = opt.relaxation_factor; R_opt.minimum_step_length = opt.minimum_step_length; R_opt.translation_scales = opt.translation_scales; R_opt.use_rotation_header = opt.use_rotation_header; R_opt.ignore_void_orgin = opt.ignore_void_orgin; } // ////////////////////////////////////////////////////////////////////// template inline void PostConversionInAffine(RunningAffineTransformPointerType& transform_running, AffineTransformPointerType & transform) { typedef typename RunningAffineTransformPointerType::ObjectType RunningAffineTransformType; typedef typename AffineTransformPointerType::ObjectType AffineTransformType; transform->SetCenter(*(reinterpret_cast (const_cast(&(transform_running-> GetCenter() ) ) ) ) ); transform->SetTranslation(*(reinterpret_cast (const_cast(&(transform_running ->GetTranslation() ) ) ) ) ); transform->SetMatrix(*(reinterpret_cast (const_cast(&(transform_running->GetMatrix() ) ) ) ) ); // std::cout << "transform_running" << transform_running << std::endl; // std::cout << "transform" << transform << std::endl; } // ///////////////////////////////////////////////////////////////////////////// template void ComputeSingleAffineTransform2D3D(typename ImageType::Pointer & fixed_image, typename ImageType::Pointer & moving_image, OptAffineType & opt, typename TransformType::Pointer & transform) { const int ImageDimension = ImageType::ImageDimension; typedef std::vector ImagePyramidType; typedef itk::ImageMaskSpatialObject ImageMaskSpatialObjectType; typedef itk::LinearInterpolateImageFunction InterpolatorType; typedef typename TransformType::ParametersType ParaType; InitializeAffineOptmizationParameters(opt, opt.translation_scales); // std::cout << "DEBUG: opt.gradient_scales.size() = " << opt.gradient_scales.size() << std::endl; InitializeAffineTransform(fixed_image, moving_image, opt); std::cout << "input affine center: " << opt.transform_initial->GetCenter() << std::endl; std::cout << "input affine para: " << opt.transform_initial->GetParameters() << std::endl; transform = TransformType::New(); ParaType para_final(TransformType::ParametersDimension); switch( opt.metric_type ) { case AffineWithMeanSquareDifference: { typedef itk::MeanSquaresImageToImageMetric MetricType; typedef RunningAffineCache RunningAffineCacheType; RunningAffineCacheType running_cache; InitializeRunningAffineCache(fixed_image, moving_image, opt, running_cache); RegisterImageAffineMutualInformationMultiResolution(running_cache, opt, para_final); } break; case AffineWithHistogramCorrelation: { typedef itk::CorrelationCoefficientHistogramImageToImageMetric MetricType; typedef RunningAffineCache RunningAffineCacheType; RunningAffineCacheType running_cache; InitializeRunningAffineCache(fixed_image, moving_image, opt, running_cache); unsigned int nBins = 32; typename MetricType::HistogramType::SizeType histSize; histSize[0] = nBins; histSize[1] = nBins; running_cache.metric->SetHistogramSize(histSize); RegisterImageAffineMutualInformationMultiResolution(running_cache, opt, para_final); } break; case AffineWithNormalizedCorrelation: { typedef itk::NormalizedCorrelationImageToImageMetric MetricType; typedef RunningAffineCache RunningAffineCacheType; RunningAffineCacheType running_cache; InitializeRunningAffineCache(fixed_image, moving_image, opt, running_cache); RegisterImageAffineMutualInformationMultiResolution(running_cache, opt, para_final); } break; case AffineWithGradientDifference: { typedef itk::GradientDifferenceImageToImageMetric MetricType; typedef RunningAffineCache RunningAffineCacheType; RunningAffineCacheType running_cache; InitializeRunningAffineCache(fixed_image, moving_image, opt, running_cache); RegisterImageAffineMutualInformationMultiResolution(running_cache, opt, para_final); } break; case AffineWithMutualInformation: { typedef itk::MattesMutualInformationImageToImageMetric MetricType; typedef RunningAffineCache RunningAffineCacheType; RunningAffineCacheType running_cache; InitializeRunningAffineCache(fixed_image, moving_image, opt, running_cache); running_cache.metric->SetNumberOfHistogramBins( opt.MI_bins ); running_cache.metric->SetNumberOfSpatialSamples( opt.MI_samples ); RegisterImageAffineMutualInformationMultiResolution(running_cache, opt, para_final); } break; default: break; } bool noaffine = true; for( int i = 0; i < opt.number_of_levels; i++ ) { if( opt.number_of_iteration_list[i] > 0 ) { noaffine = false; } } if( noaffine ) { for( unsigned int i = TransformType::ParametersDimension - ImageDimension; i < TransformType::ParametersDimension; i++ ) { para_final[i] = 0; } } transform->SetParameters(para_final); transform->SetCenter(opt.transform_initial->GetCenter() ); double rval_init = TestCostValueMMI(fixed_image, moving_image, opt.transform_initial->GetParameters(), opt.transform_initial->GetCenter(), transform); // std::cout << "ABCDABCD: " << transform << std::endl; double rval_final = TestCostValueMMI(fixed_image, moving_image, para_final, opt.transform_initial->GetCenter(), transform); std::cout << "outputput affine center: " << transform->GetCenter() << std::endl; std::cout << "output affine para: " << transform->GetParameters() << std::endl; std::cout << "initial measure value (MMI): rval = " << rval_init << std::endl; std::cout << "final measure value (MMI): rval = " << rval_final << std::endl; std::cout << "finish affine registeration..." << std::endl; } // ///////////////////////////////////////////////////////////////////////// // the initial transform maybe any derivative class type from MatrixOffsetTransformBase, // it will be automatically converted to the my 2D/3D affine type template void ComputeSingleAffineTransform(typename ImageType::Pointer & fixedImage, typename ImageType::Pointer & movingImage, OptAffineType & opt, typename TransformType::Pointer & transform) { const int ImageDimension = ImageType::ImageDimension; typedef typename ImageType::IOPixelType PixelType; std::cout << "transform_initial: IsNotNull():" << opt.transform_initial.IsNotNull() << std::endl; if( ImageDimension == 2 ) { typedef itk::ANTSCenteredAffine2DTransform RunningAffineTransformType; typedef typename RunningAffineTransformType::Pointer RunningAffineTransformPointerType; const unsigned int RunningImageDimension = 2; typedef typename itk::Image RunningImageType; typedef typename RunningImageType::Pointer RunningImagePointerType; typedef OptAffine RunningOptAffineType; RunningImagePointerType R_fixedImage, R_movingImage; RunningOptAffineType R_opt; PreConversionInAffine(fixedImage, R_fixedImage, movingImage, R_movingImage, opt, R_opt); RunningAffineTransformPointerType transform_running = ITK_NULLPTR; ComputeSingleAffineTransform2D3D (R_fixedImage, R_movingImage, R_opt, transform_running); PostConversionInAffine(transform_running, transform); } else if( ImageDimension == 3 ) { typedef itk::ANTSAffine3DTransform RunningAffineTransformType; typedef typename RunningAffineTransformType::Pointer RunningAffineTransformPointerType; const unsigned int RunningImageDimension = 3; typedef typename itk::Image RunningImageType; typedef typename RunningImageType::Pointer RunningImagePointerType; typedef OptAffine RunningOptAffineType; RunningImagePointerType R_fixedImage, R_movingImage; RunningOptAffineType R_opt; PreConversionInAffine(fixedImage, R_fixedImage, movingImage, R_movingImage, opt, R_opt); RunningAffineTransformPointerType transform_running = ITK_NULLPTR; ComputeSingleAffineTransform2D3D (R_fixedImage, R_movingImage, R_opt, transform_running); PostConversionInAffine(transform_running, transform); } else { std::cout << "Unsupported, not 2D/ 3D" << std::endl; return; } } // ///////////////////////////////////////////////////////////////////////////// template void InitialzeImageMask(MaskImagePointerType & mask_fixed, ImageMaskSpatialObjectPointerType & mask_fixed_object) { if( mask_fixed.IsNull() ) { return; } const unsigned int ImageDimension = MaskImagePointerType::ObjectType::ImageDimension; typedef typename MaskImagePointerType::ObjectType MaskImageType; typedef typename ImageMaskSpatialObjectPointerType::ObjectType ImageMaskSpatialObjectType; typedef itk::Image CharMaskImageType; typedef itk::CastImageFilter CastFilterType; typename CastFilterType::Pointer cast_filter = CastFilterType::New(); cast_filter->SetInput(mask_fixed); cast_filter->Update(); typename CharMaskImageType::Pointer mask_fixed_char = cast_filter->GetOutput(); mask_fixed_object = ImageMaskSpatialObjectType::New(); mask_fixed_object->SetImage(mask_fixed_char); } // ///////////////////////////////////////////////////////////////////////////// template ImagePointerType AddRandomNoise(ImagePointerType & I) { typedef typename ImagePointerType::ObjectType ImageType; typename itk::RandomImageSource::Pointer randomImageSource = itk::RandomImageSource::New(); randomImageSource->SetOrigin(I->GetOrigin() ); randomImageSource->SetSpacing(I->GetSpacing() ); randomImageSource->SetSize(I->GetBufferedRegion().GetSize() ); randomImageSource->SetMin(0); randomImageSource->SetMax(0.001); randomImageSource->Update(); randomImageSource->GetOutput()->SetDirection(I->GetDirection() ); typedef itk::AddImageFilter AddImageFilterType; typename AddImageFilterType::Pointer addFilter = AddImageFilterType::New(); addFilter->SetInput1(I); addFilter->SetInput2(randomImageSource->GetOutput() ); addFilter->Update(); return addFilter->GetOutput(); } // ///////////////////////////////////////////////////////////////////////////// template void ComputeInitialPosition(ImagePointerType & I_fixed, ImagePointerType & I_moving, PointType & center, VectorType & translation_vec) { typedef typename ImagePointerType::ObjectType ImageType; typedef typename itk::ImageMomentsCalculator ImageCalculatorType; const unsigned int ImageDimension = ImageType::ImageDimension; typename ImageCalculatorType::Pointer calculator = ImageCalculatorType::New(); typename ImageCalculatorType::VectorType fixed_center; typename ImageCalculatorType::VectorType moving_center; // a dirty fix to handle the constant/blank images after preprocessing try { calculator->SetImage( I_fixed ); calculator->Compute(); fixed_center = calculator->GetCenterOfGravity(); calculator->SetImage( I_moving ); calculator->Compute(); moving_center = calculator->GetCenterOfGravity(); } catch( ... ) { // try to add a small amount of noise to avoid exception from computing moments std::cout << "try to add a small amount of noise to avoid exception" " from computing moments" << std::endl; ImagePointerType If1 = AddRandomNoise(I_fixed); ImagePointerType Im1 = AddRandomNoise(I_moving); // calculator->SetImage( I_fixed ); calculator->SetImage( If1 ); calculator->Compute(); fixed_center = calculator->GetCenterOfGravity(); // calculator->SetImage( I_moving ); calculator->SetImage( Im1 ); calculator->Compute(); moving_center = calculator->GetCenterOfGravity(); } for( unsigned int i = 0; i < ImageDimension; i++ ) { center[i] = fixed_center[i]; translation_vec[i] = moving_center[i] - fixed_center[i]; } } // fake a all-zero vector template void ComputeInitialPosition_tmp(ImagePointerType & I_fixed, ImagePointerType & I_moving, PointType & center, VectorType & translation_vec) { typedef typename ImagePointerType::ObjectType ImageType; typedef typename itk::ImageMomentsCalculator ImageCalculatorType; const unsigned int ImageDimension = ImageType::ImageDimension; typename ImageCalculatorType::Pointer calculator = ImageCalculatorType::New(); calculator->SetImage( I_fixed ); calculator->Compute(); typename ImageCalculatorType::VectorType fixed_center = calculator->GetCenterOfGravity(); calculator->SetImage( I_moving ); calculator->Compute(); typename ImageCalculatorType::VectorType moving_center = calculator->GetCenterOfGravity(); for( unsigned int i = 0; i < ImageDimension; i++ ) { center[i] = fixed_center[i]; translation_vec[i] = fixed_center[i] - fixed_center[i]; } } // ///////////////////////////////////////////////////////////////////////////// template void InjectInitialPara(PointType & center, VectorType & translation_vec, TransformPointerType & transform) { typedef typename TransformPointerType::ObjectType::ParametersType ParaType; ParaType para0(TransformPointerType::ObjectType::ParametersDimension); switch( (unsigned int) PointType::PointDimension ) { case 2: para0[0] = 0; // para1[0]; // theta para0[1] = 1.0; // s1 para0[2] = 1.0; // s2 para0[3] = 0.0; // k para0[4] = center[0]; // para1[1]; //c1 para0[5] = center[1]; // para1[2]; //c2 para0[6] = translation_vec[0]; // 0;//para1[3]; //t1 para0[7] = translation_vec[1]; // 0; //para1[4]; //t2 transform->SetParameters(para0); transform->SetCenter(center); break; case 3: para0[0] = 0.0; para0[1] = 0.0; para0[2] = 0.0; para0[3] = 1.0; para0[4] = 1.0; para0[5] = 1.0; para0[6] = 1.0; para0[7] = 0.0; para0[8] = 0.0; para0[9] = 0.0; para0[10] = translation_vec[0]; para0[11] = translation_vec[1]; para0[12] = translation_vec[2]; // para0[10] = 0.0; para0[11] = 0.0; para0[12] = 0.0; transform->SetParameters(para0); transform->SetCenter(center); break; } } // //////////////////////////////////////////////////////////////////////////////////////// template double TestCostValueMMI(ImagePointerType fixedImage, ImagePointerType movingImage, ParaType para, PointType center, TransformTypePointer /* null_transform */) { typedef typename ImagePointerType::ObjectType ImageType; typedef typename TransformTypePointer::ObjectType TransformType; typename TransformType::Pointer transform = TransformType::New(); transform->SetCenter(center); // transform->SetParameters(para); typedef typename itk::MattesMutualInformationImageToImageMetric mattesMutualInfoMetricType; typename mattesMutualInfoMetricType::Pointer mattesMutualInfo = mattesMutualInfoMetricType::New(); typedef typename itk::LinearInterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); interpolator->SetInputImage(movingImage); mattesMutualInfo->SetFixedImage(fixedImage); mattesMutualInfo->SetMovingImage(movingImage); mattesMutualInfo->SetFixedImageRegion(fixedImage->GetBufferedRegion() ); mattesMutualInfo->SetTransform(transform); mattesMutualInfo->SetInterpolator(interpolator); mattesMutualInfo->SetNumberOfHistogramBins( 32 ); mattesMutualInfo->SetNumberOfSpatialSamples( 5000 ); mattesMutualInfo->SetTransformParameters(para); mattesMutualInfo->Initialize(); double rval = 0; try { rval = mattesMutualInfo->GetValue(para); } catch( itk::ExceptionObject & err ) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << err << std::endl << "Exception caught in computing mattesMutualInfo after registration" << std::endl << "Maybe: Too many samples map outside moving image buffer" << std::endl << "Set the cost value = 0 (max for MutualInfo) " << std::endl; rval = 0; } return rval; } template ImagePointer ShrinkImageToScale(ImagePointer image, float scalingFactor ) { typedef typename ImagePointer::ObjectType ImageType; typedef typename ImageType::PixelType RealType; typedef typename ImagePointer::ObjectType ImageType; typename ImageType::SpacingType inputSpacing = image->GetSpacing(); typename ImageType::RegionType::SizeType inputSize = image->GetRequestedRegion().GetSize(); typename ImageType::SpacingType outputSpacing; typename ImageType::RegionType::SizeType outputSize; typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); RealType minimumSpacing = inputSpacing.GetVnlVector().min_value(); // RealType maximumSpacing = inputSpacing.GetVnlVector().max_value(); ImagePointer current_image = image; for( unsigned int d = 0; d < ImageType::ImageDimension; d++ ) { RealType scaling = vnl_math_min( scalingFactor * minimumSpacing / inputSpacing[d], static_cast( inputSize[d] ) / 32.0 ); outputSpacing[d] = inputSpacing[d] * scaling; outputSize[d] = static_cast( inputSpacing[d] * static_cast( inputSize[d] ) / outputSpacing[d] + 0.5 ); typedef itk::RecursiveGaussianImageFilter GaussianFilterType; typename GaussianFilterType::Pointer smoother = GaussianFilterType::New(); smoother->SetInputImage( current_image ); smoother->SetDirection( d ); smoother->SetNormalizeAcrossScale( false ); smoother->SetSigma( 0.25 * ( outputSpacing[d] / inputSpacing[d] ) ); if( smoother->GetSigma() > 0.0 ) { smoother->Update(); current_image = smoother->GetOutput(); } } resampler->SetInput(current_image ); resampler->SetSize(outputSize); resampler->SetOutputSpacing(outputSpacing); resampler->SetOutputOrigin(image->GetOrigin() ); resampler->SetOutputDirection(image->GetDirection() ); resampler->Update(); image = resampler->GetOutput(); // std::cout << "DEBUG: " << outputSize << std::endl; return image; } template void BuildImagePyramid(const ImagePointerType & image, int number_of_levels, ImagePyramidType & image_pyramid) { image_pyramid.resize(number_of_levels); image_pyramid[number_of_levels - 1] = image; double scale_factor = 2; for( int i = 0; i < number_of_levels - 1; i++ ) { image_pyramid[number_of_levels - 2 - i] = ShrinkImageToScale(image, scale_factor); scale_factor *= 2; } // for(int i=0; i < number_of_levels; i++) // std::cout << "level " << i << ": size: " << image_pyramid[i]->GetLargestPossibleRegion().GetSize() << // std::endl; } template void InitializeRunningAffineCache(ImagePointerType & fixed_image, ImagePointerType & moving_image, OptAffineType & opt, RunningAffineCacheType & running_cache) { typedef typename RunningAffineCacheType::InterpolatorType InterpolatorType; typedef typename RunningAffineCacheType::MetricType MetricType; BuildImagePyramid(fixed_image, opt.number_of_levels, running_cache.fixed_image_pyramid); BuildImagePyramid(moving_image, opt.number_of_levels, running_cache.moving_image_pyramid); InitialzeImageMask(opt.mask_fixed, running_cache.mask_fixed_object); running_cache.interpolator = InterpolatorType::New(); running_cache.metric = MetricType::New(); running_cache.invmetric = MetricType::New(); } template void InitializeAffineTransform(ImagePointerType & fixed_image, ImagePointerType & moving_image, OptAffineType& opt) { typedef typename OptAffineType::AffineTransformType TransformType; typedef typename TransformType::InputPointType PointType; typedef typename TransformType::OutputVectorType VectorType; std::cout << "opt.transform_initial.IsNull(): " << opt.transform_initial.IsNull() << std::endl; std::cout << " opt.use_rotation_header: " << opt.use_rotation_header << std::endl; std::cout << " opt.ignore_void_orgin: " << opt.ignore_void_orgin << std::endl; if( opt.transform_initial.IsNull() ) { PointType center; VectorType translation_vec; // std::cout << "GS: debug: fake a all zero translation_vec" << std::endl; // ComputeInitialPosition_tmp(fixed_image, moving_image, center, translation_vec); ComputeInitialPosition(fixed_image, moving_image, center, translation_vec); opt.transform_initial = TransformType::New(); InjectInitialPara(center, translation_vec, opt.transform_initial); } } template ParaType NormalizeGradientForRigidTransform(ParaType & original_gradient, int kImageDim) { ParaType new_gradient(original_gradient.Size() ); new_gradient = original_gradient; switch( kImageDim ) { case 2: // theta, s1, s2, k for( int j = 1; j <= 3; j++ ) { new_gradient[j] = 0.; } break; case 3: // q1,q2,q3,q4,s1,s2,s3,k1,k2,k3 for( int j = 4; j <= 9; j++ ) { new_gradient[j] = 0.; } break; } return new_gradient; } // ///////////////////////////////////////////////////////////////////////////// // template template bool SymmRegisterImageAffineMutualInformationMultiResolution(RunningAffineCacheType & running_cache, OptAffine & opt, ParaType & para_final) { typedef typename RunningAffineCacheType::ImagePyramidType ImagePyramidType; typedef typename RunningAffineCacheType::ImageType ImageType; typedef typename RunningAffineCacheType::ImagePointerType ImagePointerType; typedef typename RunningAffineCacheType::MetricType MetricType; typedef typename RunningAffineCacheType::InterpolatorType InterpolatorType; typedef typename OptAffine::AffineTransformType TransformType; typedef typename RunningAffineCacheType::MaskObjectPointerType MaskObjectPointerType; const unsigned int kImageDim = ImageType::ImageDimension; ImagePyramidType& fixed_image_pyramid = running_cache.fixed_image_pyramid; ImagePyramidType& moving_image_pyramid = running_cache.moving_image_pyramid; MaskObjectPointerType& mask_fixed_object = running_cache.mask_fixed_object; int number_of_levels = opt.number_of_levels; std::vector& number_of_iteration_list = opt.number_of_iteration_list; std::vector& gradient_scales = opt.gradient_scales; bool is_rigid = opt.is_rigid; // use my own's registration routine of image pyramid and gradient descent , only use ITK's implementation of mutual // information // try to use my own transform class together with image pyramid when transform are required (not much for MI though) typename TransformType::Pointer transform = TransformType::New(); typename TransformType::Pointer invtransform = TransformType::New(); typename InterpolatorType::Pointer& interpolator = running_cache.interpolator; typename InterpolatorType::Pointer invinterpolator = InterpolatorType::New(); typename MetricType::Pointer& metric = running_cache.metric; typename MetricType::Pointer& invmetric = running_cache.invmetric; const int kParaDim = TransformType::ParametersDimension; ParaType current_para(kParaDim); current_para = opt.transform_initial->GetParameters(); double maximum_step_length = opt.maximum_step_length; double relaxation_factor = opt.relaxation_factor; double minimum_step_length = opt.minimum_step_length; double current_step_length; for( int i = 0; i < number_of_levels; i++ ) { transform->SetParameters(current_para); transform->SetCenter(opt.transform_initial->GetCenter() ); ImagePointerType fixed_image = fixed_image_pyramid[i]; ImagePointerType moving_image = moving_image_pyramid[i]; int number_of_iteration_current_level = number_of_iteration_list[i]; interpolator->SetInputImage( moving_image ); metric->SetMovingImage( moving_image ); metric->SetFixedImage( fixed_image ); metric->SetTransform( transform ); metric->SetInterpolator( interpolator ); metric->SetFixedImageRegion(fixed_image->GetLargestPossibleRegion() ); if( mask_fixed_object.IsNotNull() ) { metric->SetFixedImageMask(mask_fixed_object); } metric->Initialize(); ParaType last_gradient(kParaDim); ParaType invlast_gradient(kParaDim); for( int j = 0; j < kParaDim; j++ ) { last_gradient[j] = 0; invlast_gradient[j] = 0; } current_step_length = maximum_step_length; bool is_converged = false; int used_iterations = 0; for( used_iterations = 0; used_iterations < number_of_iteration_current_level; used_iterations++ ) { transform->GetInverse(invtransform); invinterpolator->SetInputImage( fixed_image ); invmetric->SetMovingImage( fixed_image ); invmetric->SetFixedImage( moving_image ); invmetric->SetTransform( invtransform ); invmetric->SetInterpolator( invinterpolator ); invmetric->SetFixedImageRegion(moving_image->GetLargestPossibleRegion() ); invmetric->Initialize(); ParaType original_gradient(kParaDim); ParaType current_gradient(kParaDim); ParaType invoriginal_gradient(kParaDim); ParaType invcurrent_gradient(kParaDim); double value, invvalue; try { metric->GetValueAndDerivative(current_para, value, original_gradient); invmetric->GetValueAndDerivative( invtransform->GetParameters(), invvalue, invoriginal_gradient); } catch( itk::ExceptionObject & err ) { std::cout << "ExceptionObject caught !" << std::endl; std::cout << err << std::endl; return false; } // use the similar routine as RegularStepGradientDescentBaseOptimizer::AdvanceOneStep // to use oscillation as the minimization convergence // notice this is always a minimization procedure if( is_rigid ) { original_gradient = NormalizeGradientForRigidTransform(original_gradient, kImageDim); invoriginal_gradient = NormalizeGradientForRigidTransform(invoriginal_gradient, kImageDim); } for( int j = 0; j < kParaDim; j++ ) { current_gradient[j] = original_gradient[j] / gradient_scales[j]; } double gradient_magnitude = 0.0; for( int j = 0; j < kParaDim; j++ ) { gradient_magnitude += current_gradient[j] * current_gradient[j]; } gradient_magnitude = sqrt(gradient_magnitude); double inner_product_last_current_gradient = 0.0; for( int j = 0; j < kParaDim; j++ ) { inner_product_last_current_gradient += current_gradient[j] * last_gradient[j]; } if( inner_product_last_current_gradient < 0 ) { current_step_length *= relaxation_factor; } if( current_step_length < minimum_step_length || gradient_magnitude == 0.0 ) { is_converged = true; break; } // for inverse for( int j = 0; j < kParaDim; j++ ) { invcurrent_gradient[j] = invoriginal_gradient[j] / gradient_scales[j]; } double invgradient_magnitude = 0.0; for( int j = 0; j < kParaDim; j++ ) { invgradient_magnitude += invcurrent_gradient[j] * invcurrent_gradient[j]; } invgradient_magnitude = sqrt(invgradient_magnitude); inner_product_last_current_gradient = 0.0; for( int j = 0; j < kParaDim; j++ ) { inner_product_last_current_gradient += invcurrent_gradient[j] * invlast_gradient[j]; } if( inner_product_last_current_gradient < 0 ) { current_step_length *= relaxation_factor; } if( current_step_length < minimum_step_length || gradient_magnitude == 0.0 ) { is_converged = true; break; } for( int j = 0; j < kParaDim; j++ ) { current_para[j] += (-1.0) * current_gradient[j] * current_step_length / gradient_magnitude; current_para[j] += (1.0) * invcurrent_gradient[j] * current_step_length / invgradient_magnitude; } if( kImageDim == 3 ) // normalize quaternion { double quat_mag = 0.0; for( int j = 0; j < 4; j++ ) { quat_mag += current_para[j] * current_para[j]; } quat_mag = sqrt(quat_mag); for( int j = 0; j < 4; j++ ) { current_para[j] /= quat_mag; } if( !is_rigid ) { for( int j = 4; j < 7; j++ ) { current_para[j] *= quat_mag; } } } last_gradient = current_gradient; invlast_gradient = invcurrent_gradient; } std::cout << "level " << i << ", iter " << used_iterations << ", size: fix" << fixed_image->GetRequestedRegion().GetSize() << "-mov" << moving_image->GetRequestedRegion().GetSize(); std::cout << ", affine para: " << current_para << std::endl; if( is_converged ) { std::cout << " reach oscillation, current step: " << current_step_length << "<" << minimum_step_length << std::endl; } else { std::cout << " does not reach oscillation, current step: " << current_step_length << ">" << minimum_step_length << std::endl; } } para_final = current_para; return true; } // ///////////////////////////////////////////////////////////////////////////// // template template bool RegisterImageAffineMutualInformationMultiResolution(RunningAffineCacheType & running_cache, OptAffine & opt, ParaType & para_final) { typedef typename RunningAffineCacheType::ImagePyramidType ImagePyramidType; typedef typename RunningAffineCacheType::ImagePointerType ImagePointerType; typedef typename RunningAffineCacheType::ImageType ImageType; typedef typename RunningAffineCacheType::MetricType MetricType; typedef typename RunningAffineCacheType::InterpolatorType InterpolatorType; typedef typename OptAffine::AffineTransformType TransformType; typedef typename RunningAffineCacheType::MaskObjectPointerType MaskObjectPointerType; const unsigned int kImageDim = ImageType::ImageDimension; ImagePyramidType& fixed_image_pyramid = running_cache.fixed_image_pyramid; ImagePyramidType& moving_image_pyramid = running_cache.moving_image_pyramid; MaskObjectPointerType& mask_fixed_object = running_cache.mask_fixed_object; int number_of_levels = opt.number_of_levels; std::vector& number_of_iteration_list = opt.number_of_iteration_list; std::vector& gradient_scales = opt.gradient_scales; bool is_rigid = opt.is_rigid; // use my own's registration routine of image pyramid and gradient descent , only use ITK's implementation of mutual // information // try to use my own transform class together with image pyramid when transform are required (not much for MI though) typename TransformType::Pointer transform = TransformType::New(); typename InterpolatorType::Pointer& interpolator = running_cache.interpolator; typename MetricType::Pointer& metric = running_cache.metric; const int kParaDim = TransformType::ParametersDimension; ParaType current_para(kParaDim); current_para = opt.transform_initial->GetParameters(); double maximum_step_length = opt.maximum_step_length; double relaxation_factor = opt.relaxation_factor; double minimum_step_length = opt.minimum_step_length; double current_step_length; double value = 0; for( int i = 0; i < number_of_levels; i++ ) { transform->SetParameters(current_para); transform->SetCenter(opt.transform_initial->GetCenter() ); ImagePointerType fixed_image = fixed_image_pyramid[i]; ImagePointerType moving_image = moving_image_pyramid[i]; int number_of_iteration_current_level = number_of_iteration_list[i]; interpolator->SetInputImage( moving_image ); metric->SetMovingImage( moving_image ); metric->SetFixedImage( fixed_image ); metric->SetTransform( transform ); metric->SetInterpolator( interpolator ); metric->SetFixedImageRegion(fixed_image->GetLargestPossibleRegion() ); if( mask_fixed_object.IsNotNull() ) { metric->SetFixedImageMask(mask_fixed_object); } metric->Initialize(); ParaType last_gradient(kParaDim); ParaType invlast_gradient(kParaDim); for( int j = 0; j < kParaDim; j++ ) { last_gradient[j] = 0; invlast_gradient[j] = 0; } current_step_length = maximum_step_length; bool is_converged = false; int used_iterations = 0; for( used_iterations = 0; used_iterations < number_of_iteration_current_level; used_iterations++ ) { ParaType original_gradient(kParaDim); ParaType current_gradient(kParaDim); value = 0; try { metric->GetValueAndDerivative(current_para, value, original_gradient); } catch( itk::ExceptionObject & err ) { std::cout << "ExceptionObject caught !" << std::endl; std::cout << err << std::endl; return false; } // use the similar routine as RegularStepGradientDescentBaseOptimizer::AdvanceOneStep // to use oscillation as the minimization convergence // notice this is always a minimization procedure if( is_rigid ) { original_gradient = NormalizeGradientForRigidTransform(original_gradient, kImageDim); } for( int j = 0; j < kParaDim; j++ ) { current_gradient[j] = original_gradient[j] / gradient_scales[j]; } double gradient_magnitude = 0.0; for( int j = 0; j < kParaDim; j++ ) { gradient_magnitude += current_gradient[j] * current_gradient[j]; } gradient_magnitude = sqrt(gradient_magnitude); double inner_product_last_current_gradient = 0.0; for( int j = 0; j < kParaDim; j++ ) { inner_product_last_current_gradient += current_gradient[j] * last_gradient[j]; } if( inner_product_last_current_gradient < 0 ) { current_step_length *= relaxation_factor; } if( current_step_length < minimum_step_length || gradient_magnitude == 0.0 ) { is_converged = true; break; } if( current_step_length < minimum_step_length || gradient_magnitude == 0.0 ) { is_converged = true; break; } for( int j = 0; j < kParaDim; j++ ) { current_para[j] += (-1.0) * current_gradient[j] * current_step_length / gradient_magnitude; } if( kImageDim == 3 ) // normalize quaternion { double quat_mag = 0.0; for( int j = 0; j < 4; j++ ) { quat_mag += current_para[j] * current_para[j]; } quat_mag = sqrt(quat_mag); for( int j = 0; j < 4; j++ ) { current_para[j] /= quat_mag; } if( !is_rigid ) { for( int j = 4; j < 7; j++ ) { current_para[j] *= quat_mag; } } } last_gradient = current_gradient; } std::cout << "level " << i << ", iter " << used_iterations << ", size: fix" << fixed_image->GetRequestedRegion().GetSize() << "-mov" << moving_image->GetRequestedRegion().GetSize(); std::cout << ", affine para: " << current_para << std::endl; if( is_converged ) { std::cout << " reach oscillation, current step: " << current_step_length << "<" << minimum_step_length << std::endl; } else { std::cout << " does not reach oscillation, current step: " << current_step_length << ">" << minimum_step_length << std::endl; } } double value1 = value; if( !mask_fixed_object.IsNotNull() ) { typename TransformType::Pointer transform2 = TransformType::New(); ParaType current_para2(kParaDim); // GS: should use the inverse of initial transform here, removed the next line: // current_para2 = opt.transform_initial->GetParameters(); typename TransformType::Pointer transform2_initial = TransformType::New(); opt.transform_initial->GetInverse(transform2_initial); current_para2 = transform2_initial->GetParameters(); maximum_step_length = opt.maximum_step_length; relaxation_factor = opt.relaxation_factor; minimum_step_length = opt.minimum_step_length; value = 0; for( int i = 0; i < number_of_levels; i++ ) { transform2->SetParameters(current_para2); transform2->SetCenter(transform2_initial->GetCenter() ); /** see below -- we switch fixed and moving!! */ ImagePointerType fixed_image = moving_image_pyramid[i]; ImagePointerType moving_image = fixed_image_pyramid[i]; int number_of_iteration_current_level = number_of_iteration_list[i]; interpolator->SetInputImage( moving_image ); metric->SetMovingImage( moving_image ); metric->SetFixedImage( fixed_image ); metric->SetTransform( transform2 ); metric->SetInterpolator( interpolator ); metric->SetFixedImageRegion(fixed_image->GetLargestPossibleRegion() ); /** FIXME --- need a moving mask ... */ // if (mask_fixed_object.IsNotNull()) metric->SetFixedImageMask(mask_fixed_object); metric->Initialize(); ParaType last_gradient(kParaDim); for( int j = 0; j < kParaDim; j++ ) { last_gradient[j] = 0; } current_step_length = maximum_step_length; bool is_converged = false; int used_iterations = 0; for( used_iterations = 0; used_iterations < number_of_iteration_current_level; used_iterations++ ) { ParaType original_gradient(kParaDim); ParaType current_gradient(kParaDim); value = 0; try { metric->GetValueAndDerivative(current_para2, value, original_gradient); } catch( itk::ExceptionObject & err ) { std::cout << "ExceptionObject caught !" << std::endl; std::cout << err << std::endl; // don't have to return here if got anything from the previous forward direction // return false; break; } // use the similar routine as RegularStepGradientDescentBaseOptimizer::AdvanceOneStep // to use oscillation as the minimization convergence // notice this is always a minimization procedure if( is_rigid ) { original_gradient = NormalizeGradientForRigidTransform(original_gradient, kImageDim); } for( int j = 0; j < kParaDim; j++ ) { current_gradient[j] = original_gradient[j] / gradient_scales[j]; } double gradient_magnitude = 0.0; for( int j = 0; j < kParaDim; j++ ) { gradient_magnitude += current_gradient[j] * current_gradient[j]; } gradient_magnitude = sqrt(gradient_magnitude); double inner_product_last_current_gradient = 0.0; for( int j = 0; j < kParaDim; j++ ) { inner_product_last_current_gradient += current_gradient[j] * last_gradient[j]; } if( inner_product_last_current_gradient < 0 ) { current_step_length *= relaxation_factor; } if( current_step_length < minimum_step_length || gradient_magnitude == 0.0 ) { is_converged = true; break; } if( current_step_length < minimum_step_length || gradient_magnitude == 0.0 ) { is_converged = true; break; } for( int j = 0; j < kParaDim; j++ ) { current_para2[j] += (-1.0) * current_gradient[j] * current_step_length / gradient_magnitude; } if( kImageDim == 3 ) // normalize quaternion { double quat_mag = 0.0; for( int j = 0; j < 4; j++ ) { quat_mag += current_para2[j] * current_para2[j]; } quat_mag = sqrt(quat_mag); for( int j = 0; j < 4; j++ ) { current_para2[j] /= quat_mag; } if( !is_rigid ) { for( int j = 4; j < 7; j++ ) { current_para2[j] *= quat_mag; } } } last_gradient = current_gradient; } std::cout << "level " << i << ", iter " << used_iterations << ", size: fix" << fixed_image->GetRequestedRegion().GetSize() << "-mov" << moving_image->GetRequestedRegion().GetSize(); std::cout << ", affine para: " << current_para2 << std::endl; if( is_converged ) { std::cout << " reach oscillation, current step: " << current_step_length << "<" << minimum_step_length << std::endl; } else { std::cout << " does not reach oscillation, current step: " << current_step_length << ">" << minimum_step_length << std::endl; } } std::cout << " v1 " << value1 << " v2 " << value << std::endl; if( value < value1 ) { std::cout << " last params " << transform->GetParameters() << std::endl; std::cout << " my params " << transform2->GetParameters() << std::endl; transform2->GetInverse(transform); std::cout << " new params " << transform->GetParameters() << std::endl; para_final = transform->GetParameters(); return true; } } para_final = current_para; std::cout << "final " << para_final << std::endl; return true; } #endif /*ANTS_AFFINE_REGISTRATION2_H_*/ ants-2.2.0/ImageRegistration/itkANTSAffine3DTransform.h000066400000000000000000000141241311104306400227120ustar00rootroot00000000000000#ifndef __itkANTSAffine3DTransform_h #define __itkANTSAffine3DTransform_h #include #include "itkRigid3DTransform.h" #include "vnl/vnl_quaternion.h" namespace itk { /** \brief ANTSAffine3DTransform of a vector space (e.g. space coordinates). * * This transform applies a rotation and translation to the space * * \ingroup Transforms */ template // Data type for scalars (float or double) class ANTSAffine3DTransform : public MatrixOffsetTransformBase // public Rigid3DTransform< TScalarType > { public: /** Standard class typedefs. */ typedef ANTSAffine3DTransform Self; // typedef Rigid3DTransform< TScalarType > Superclass; typedef MatrixOffsetTransformBase Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** New macro for creation of through a Smart Pointer */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ // itkTypeMacro( ANTSAffine3DTransform, Rigid3DTransform ); itkTypeMacro( ANTSAffine3DTransform, MatrixOffsetTransformBase ); /** Dimension of parameters */ itkStaticConstMacro(InputSpaceDimension, unsigned int, 3); itkStaticConstMacro(OutputSpaceDimension, unsigned int, 3); itkStaticConstMacro(SpaceDimension, unsigned int, 3); itkStaticConstMacro(ParametersDimension, unsigned int, 13); /** Parameters Type */ typedef typename Superclass::ParametersType ParametersType; typedef typename Superclass::JacobianType JacobianType; typedef typename Superclass::ScalarType ScalarType; typedef typename Superclass::InputPointType InputPointType; typedef typename Superclass::OutputPointType OutputPointType; typedef typename Superclass::InputVectorType InputVectorType; typedef typename Superclass::OutputVectorType OutputVectorType; typedef typename Superclass::InputVnlVectorType InputVnlVectorType; typedef typename Superclass::OutputVnlVectorType OutputVnlVectorType; typedef typename Superclass::InputCovariantVectorType InputCovariantVectorType; typedef typename Superclass::OutputCovariantVectorType OutputCovariantVectorType; typedef typename Superclass::MatrixType MatrixType; typedef typename Superclass::InverseMatrixType InverseMatrixType; typedef typename Superclass::CenterType CenterType; typedef typename Superclass::OffsetType OffsetType; typedef typename Superclass::TranslationType TranslationType; /** VnlQuaternion type. */ typedef vnl_quaternion VnlQuaternionType; /** Compute the Jacobian Matrix of the transformation at one point */ /** Set the rotation of the rigid transform. * This method sets the rotation of a ANTSAffine3DTransform to a * value specified by the user. */ void SetRotation(const VnlQuaternionType & rotation); void SetS1(const TScalarType S1); void SetS2(const TScalarType S2); void SetS3(const TScalarType S3); void SetK1(const TScalarType K1); void SetK2(const TScalarType K2); void SetK3(const TScalarType K3); /** Get the rotation from an ANTSAffine3DTransform. * This method returns the value of the rotation of the * ANTSAffine3DTransform. **/ const VnlQuaternionType & GetRotation() const { return m_Rotation; } MatrixType ComputeMyRotationMatrix(); itkGetConstReferenceMacro( S1, TScalarType ); itkGetConstReferenceMacro( S2, TScalarType ); itkGetConstReferenceMacro( S3, TScalarType ); itkGetConstReferenceMacro( K1, TScalarType ); itkGetConstReferenceMacro( K2, TScalarType ); itkGetConstReferenceMacro( K3, TScalarType ); /** Set the parameters to the IdentityTransform */ virtual void SetIdentity() ITK_OVERRIDE; /** Set the transformation from a container of parameters. * This is typically used by optimizers. * There are 7 parameters. The first four represents the * quaternion and the last three represents the * offset. */ void SetParameters( const ParametersType & parameters ) ITK_OVERRIDE; const ParametersType & GetParameters() const ITK_OVERRIDE; // /** Compute the Jacobian of the transformation. // * This method computes the Jacobian matrix of the transformation. // * given point or vector, returning the transformed point or // * vector. The rank of the Jacobian will also indicate if the transform // * is invertible at this point. */ // const JacobianType & GetJacobian(const InputPointType &point ) const; /** Compute the Jacobian of the transformation * * This method computes the Jacobian matrix of the transformation. * given point or vector, returning the transformed point or * vector. The rank of the Jacobian will also indicate if the transform * is invertible at this point. * Get local Jacobian for the given point * \c j will sized properly as needed. */ virtual void ComputeJacobianWithRespectToParameters(const InputPointType & p, JacobianType & j) const ITK_OVERRIDE; protected: /* ANTSAffine3DTransform(const MatrixType &matrix, */ /* const OutputVectorType &offset); */ ANTSAffine3DTransform(unsigned int outputDims, unsigned int paramDims); ANTSAffine3DTransform(); ~ANTSAffine3DTransform() { }; virtual void ComputeMatrix() ITK_OVERRIDE; virtual void ComputeMatrixParameters() ITK_OVERRIDE; void SetVarRotation(const VnlQuaternionType & rotation) { m_Rotation = rotation; }; // const InverseMatrixType & GetInverseMatrix() const; void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; private: ANTSAffine3DTransform(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** Rotation of the transformation. */ VnlQuaternionType m_Rotation; /** added affine parameters **/ TScalarType m_S1; TScalarType m_S2; TScalarType m_S3; TScalarType m_K1; TScalarType m_K2; TScalarType m_K3; }; // class ANTSAffine3DTransform } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkANTSAffine3DTransform.hxx" #endif #endif /* __itkANTSAffine3DTransform_h */ ants-2.2.0/ImageRegistration/itkANTSAffine3DTransform.hxx000066400000000000000000000534021311104306400232740ustar00rootroot00000000000000#ifndef _itkANTSAffine3DTransform_hxx #define _itkANTSAffine3DTransform_hxx #include "itkANTSAffine3DTransform.h" #include "vnl/algo/vnl_qr.h" namespace itk { // Constructor with default arguments template ANTSAffine3DTransform::ANTSAffine3DTransform() : Superclass(ParametersDimension) { m_Rotation = VnlQuaternionType(0, 0, 0, 1); // axis * std::sin(t/2), std::cos(t/2) m_S1 = NumericTraits::OneValue(); m_S2 = NumericTraits::OneValue(); m_S3 = NumericTraits::OneValue(); m_K1 = NumericTraits::ZeroValue(); m_K2 = NumericTraits::ZeroValue(); m_K3 = NumericTraits::ZeroValue(); } // Constructor with default arguments template ANTSAffine3DTransform::ANTSAffine3DTransform(unsigned int outputSpaceDimension, unsigned int parametersDimension) : Superclass(outputSpaceDimension, parametersDimension) { m_Rotation = VnlQuaternionType(0, 0, 0, 1); // axis * std::sin(t/2), std::cos(t/2) m_S1 = NumericTraits::OneValue(); m_S2 = NumericTraits::OneValue(); m_S3 = NumericTraits::OneValue(); m_K1 = NumericTraits::ZeroValue(); m_K2 = NumericTraits::ZeroValue(); m_K3 = NumericTraits::ZeroValue(); } // // Constructor with explicit arguments // template // ANTSAffine3DTransform:: // ANTSAffine3DTransform( const MatrixType & matrix, // const OutputVectorType & offset ) : // Superclass(matrix, offset) // { // this->ComputeMatrixParameters(); // } // Print self template void ANTSAffine3DTransform::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Rotation: " << m_Rotation << std::endl; os << indent << "S1, S2, S3: " << m_S1 << ", " << m_S2 << ", " << m_S3 << std::endl; os << indent << "K1, K2, K3: " << m_K1 << ", " << m_K2 << ", " << m_K3 << std::endl; } // Set rotation template void ANTSAffine3DTransform::SetRotation( const VnlQuaternionType & rotation) { m_Rotation = rotation; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); return; } template void ANTSAffine3DTransform::SetS1(const TScalarType S1) { m_S1 = S1; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); return; } template void ANTSAffine3DTransform::SetS2(const TScalarType S2) { m_S2 = S2; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); return; } template void ANTSAffine3DTransform::SetS3(const TScalarType S3) { m_S3 = S3; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); return; } template void ANTSAffine3DTransform::SetK1(const TScalarType K1) { m_K1 = K1; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); return; } template void ANTSAffine3DTransform::SetK2(const TScalarType K2) { m_K2 = K2; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); return; } template void ANTSAffine3DTransform::SetK3(const TScalarType K3) { m_K3 = K3; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); return; } // Set the parameters in order to fit an Identity transform template void ANTSAffine3DTransform::SetIdentity(void) { m_Rotation = VnlQuaternionType(0, 0, 0, 1); m_S1 = NumericTraits::OneValue(); m_S2 = NumericTraits::OneValue(); m_S3 = NumericTraits::OneValue(); m_K1 = NumericTraits::ZeroValue(); m_K2 = NumericTraits::ZeroValue(); m_K3 = NumericTraits::ZeroValue(); this->Superclass::SetIdentity(); } // Set Parameters template void ANTSAffine3DTransform::SetParameters( const ParametersType & parameters) { OutputVectorType translation; // Transfer the quaternion part unsigned int par = 0; for( unsigned int j = 0; j < 4; j++ ) { m_Rotation[j] = parameters[par]; ++par; } m_S1 = parameters[par++]; m_S2 = parameters[par++]; m_S3 = parameters[par++]; m_K1 = parameters[par++]; m_K2 = parameters[par++]; m_K3 = parameters[par++]; this->ComputeMatrix(); // Transfer the constant part for( unsigned int i = 0; i < SpaceDimension; i++ ) { translation[i] = parameters[par]; ++par; } this->SetVarTranslation(translation); this->ComputeOffset(); // Modified is always called since we just have a pointer to the // parameters and cannot know if the parameters have changed. this->Modified(); } // Set Parameters template const typename ANTSAffine3DTransform::ParametersType & ANTSAffine3DTransform::GetParameters() const { VnlQuaternionType quaternion = this->GetRotation(); OutputVectorType translation = this->GetTranslation(); // Transfer the quaternion part unsigned int par = 0; for( unsigned int j = 0; j < 4; j++ ) { this->m_Parameters[par] = quaternion[j]; ++par; } this->m_Parameters[par++] = m_S1; this->m_Parameters[par++] = m_S2; this->m_Parameters[par++] = m_S3; this->m_Parameters[par++] = m_K1; this->m_Parameters[par++] = m_K2; this->m_Parameters[par++] = m_K3; // Transfer the constant part for( unsigned int i = 0; i < SpaceDimension; i++ ) { this->m_Parameters[par] = translation[i]; ++par; } return this->m_Parameters; } // // Get parameters // template // const typename ANTSAffine3DTransform::JacobianType & // ANTSAffine3DTransform::GetJacobian( // const InputPointType & p) const { // // this->m_Jacobian.Fill(0.0); // // TScalarType c1 = this->GetCenter()[0]; // TScalarType c2 = this->GetCenter()[1]; // TScalarType c3 = this->GetCenter()[2]; // TScalarType s1 = this->m_S1; // TScalarType s2 = this->m_S2; // TScalarType s3 = this->m_S3; // TScalarType k1 = this->m_K1; // TScalarType k2 = this->m_K2; // TScalarType k3 = this->m_K3; // TScalarType x1 = p[0]; // TScalarType x2 = p[1]; // TScalarType x3 = p[2]; // // // z1,z2,z3 is the S*K*point p // TScalarType w1 = (x1 - c1) + k1 * (x2 - c2) + k2 * (x3 - c3); // TScalarType w2 = (x2 - c2) + k3 * (x3 - c2); // TScalarType w3 = (x3 - c3); // // TScalarType z1 = s1 * w1; // TScalarType z2 = s2 * w2; // TScalarType z3 = s3 * w3; // // // compute Jacobian with respect to quaternion parameters // this->m_Jacobian[0][0] = 2.0 // * (m_Rotation.x() * z1 + m_Rotation.y() * z2 + m_Rotation.z() * z3); // this->m_Jacobian[0][1] = // 2.0 // * (-m_Rotation.y() * z1 + m_Rotation.x() * z2 // + m_Rotation.r() * z3); // this->m_Jacobian[0][2] = // 2.0 // * (-m_Rotation.z() * z1 - m_Rotation.r() * z2 // + m_Rotation.x() * z3); // this->m_Jacobian[0][3] = // -2.0 // * (-m_Rotation.r() * z1 + m_Rotation.z() * z2 // - m_Rotation.y() * z3); // // this->m_Jacobian[1][0] = -this->m_Jacobian[0][1]; // this->m_Jacobian[1][1] = this->m_Jacobian[0][0]; // this->m_Jacobian[1][2] = this->m_Jacobian[0][3]; // this->m_Jacobian[1][3] = -this->m_Jacobian[0][2]; // // this->m_Jacobian[2][0] = -this->m_Jacobian[0][2]; // this->m_Jacobian[2][1] = -this->m_Jacobian[0][3]; // this->m_Jacobian[2][2] = this->m_Jacobian[0][0]; // this->m_Jacobian[2][3] = this->m_Jacobian[0][1]; // // // get rotation matrix first // // this is done to compensate for the transposed representation // // between VNL and ITK // VnlQuaternionType conjugateRotation = m_Rotation.conjugate(); // MatrixType newMatrix; // newMatrix = conjugateRotation.rotation_matrix_transpose(); // // TScalarType r11 = newMatrix[0][0]; // TScalarType r12 = newMatrix[0][1]; // TScalarType r13 = newMatrix[0][2]; // TScalarType r21 = newMatrix[1][0]; // TScalarType r22 = newMatrix[1][1]; // TScalarType r23 = newMatrix[1][2]; // TScalarType r31 = newMatrix[2][0]; // TScalarType r32 = newMatrix[2][1]; // TScalarType r33 = newMatrix[2][2]; // // // compute Jacobian wrt S1/S2/S3 // this->m_Jacobian[0][4] = r11 * w1; // this->m_Jacobian[0][5] = r12 * w2; // this->m_Jacobian[0][6] = r13 * w3; // this->m_Jacobian[1][4] = r21 * w1; // this->m_Jacobian[1][5] = r22 * w2; // this->m_Jacobian[1][6] = r23 * w3; // this->m_Jacobian[2][4] = r31 * w1; // this->m_Jacobian[2][5] = r32 * w2; // this->m_Jacobian[2][6] = r33 * w3; // // // compute Jacobian wrt K1/K2/K3 // this->m_Jacobian[0][7] = r11 * s1 * (x2 - c2); // this->m_Jacobian[0][8] = r11 * s1 * (x3 - c3); // this->m_Jacobian[0][9] = r12 * s2 * (x3 - c3); // this->m_Jacobian[1][7] = r21 * s1 * (x2 - c2); // this->m_Jacobian[1][8] = r21 * s1 * (x3 - c3); // this->m_Jacobian[1][9] = r22 * s2 * (x3 - c3); // this->m_Jacobian[2][7] = r31 * s1 * (x2 - c2); // this->m_Jacobian[2][8] = r31 * s1 * (x3 - c3); // this->m_Jacobian[2][9] = r32 * s2 * (x3 - c3); // // // for test purpose // // FORCE ONLY DO DERIVATIVE ON S // // for(int ii=0; ii <3; ii++){ // // for(int jj=0; jj<=9;jj++){ // // this->m_Jacobian[ii][jj] = 0.0; // // } // // } // // this->m_Jacobian[0][4] = r11 * w1; // // this->m_Jacobian[1][4] = r21 * w1; // // this->m_Jacobian[2][4] = r31 * w1; // // // compute derivatives for the translation part // unsigned int blockOffset = 10; // for (unsigned int dim = 0; dim < SpaceDimension; dim++) { // this->m_Jacobian[dim][blockOffset + dim] = 1.0; // } // // return this->m_Jacobian; // // // // compute derivatives with respect to rotation // // this->m_Jacobian.Fill(0.0); // // // const TScalarType x = p[0] - this->GetCenter()[0]; // // const TScalarType y = p[1] - this->GetCenter()[1]; // // const TScalarType z = p[2] - this->GetCenter()[2]; // // // // compute Jacobian with respect to quaternion parameters // // this->m_Jacobian[0][0] = 2.0 * ( m_Rotation.x() * x + m_Rotation.y() * y // // + m_Rotation.z() * z ); // // this->m_Jacobian[0][1] = 2.0 * (- m_Rotation.y() * x + m_Rotation.x() * y // // + m_Rotation.r() * z ); // // this->m_Jacobian[0][2] = 2.0 * (- m_Rotation.z() * x - m_Rotation.r() * y // // + m_Rotation.x() * z ); // // this->m_Jacobian[0][3] = - 2.0 * (- m_Rotation.r() * x + m_Rotation.z() * y // // - m_Rotation.y() * z ); // // // this->m_Jacobian[1][0] = - this->m_Jacobian[0][1]; // // this->m_Jacobian[1][1] = this->m_Jacobian[0][0]; // // this->m_Jacobian[1][2] = this->m_Jacobian[0][3]; // // this->m_Jacobian[1][3] = - this->m_Jacobian[0][2]; // // // this->m_Jacobian[2][0] = - this->m_Jacobian[0][2]; // // this->m_Jacobian[2][1] = - this->m_Jacobian[0][3]; // // this->m_Jacobian[2][2] = this->m_Jacobian[0][0]; // // this->m_Jacobian[2][3] = this->m_Jacobian[0][1]; // // // // compute derivatives for the translation part // // unsigned int blockOffset = 4; // // for(unsigned int dim=0; dim < SpaceDimension; dim++ ) // // { // // this->m_Jacobian[ dim ][ blockOffset + dim ] = 1.0; // // } // // // return this->m_Jacobian; // // } template void ANTSAffine3DTransform ::ComputeJacobianWithRespectToParameters( const InputPointType & p, JacobianType & j) const { j.SetSize( this->GetOutputSpaceDimension(), this->GetNumberOfLocalParameters() ); j.Fill(0.0); TScalarType c1 = this->GetCenter()[0]; TScalarType c2 = this->GetCenter()[1]; TScalarType c3 = this->GetCenter()[2]; TScalarType s1 = this->m_S1; TScalarType s2 = this->m_S2; TScalarType s3 = this->m_S3; TScalarType k1 = this->m_K1; TScalarType k2 = this->m_K2; TScalarType k3 = this->m_K3; TScalarType x1 = p[0]; TScalarType x2 = p[1]; TScalarType x3 = p[2]; // z1,z2,z3 is the S*K*point p TScalarType w1 = (x1 - c1) + k1 * (x2 - c2) + k2 * (x3 - c3); TScalarType w2 = (x2 - c2) + k3 * (x3 - c3); TScalarType w3 = (x3 - c3); TScalarType z1 = s1 * w1; TScalarType z2 = s2 * w2; TScalarType z3 = s3 * w3; // compute Jacobian with respect to quaternion parameters j[0][0] = 2.0 * (m_Rotation.x() * z1 + m_Rotation.y() * z2 + m_Rotation.z() * z3); j[0][1] = 2.0 * (-m_Rotation.y() * z1 + m_Rotation.x() * z2 + m_Rotation.r() * z3); j[0][2] = 2.0 * (-m_Rotation.z() * z1 - m_Rotation.r() * z2 + m_Rotation.x() * z3); j[0][3] = -2.0 * (-m_Rotation.r() * z1 + m_Rotation.z() * z2 - m_Rotation.y() * z3); j[1][0] = -j[0][1]; j[1][1] = j[0][0]; j[1][2] = j[0][3]; j[1][3] = -j[0][2]; j[2][0] = -j[0][2]; j[2][1] = -j[0][3]; j[2][2] = j[0][0]; j[2][3] = j[0][1]; // get rotation matrix first // this is done to compensate for the transposed representation // between VNL and ITK VnlQuaternionType conjugateRotation = m_Rotation.conjugate(); MatrixType newMatrix; newMatrix = conjugateRotation.rotation_matrix_transpose(); TScalarType r11 = newMatrix[0][0]; TScalarType r12 = newMatrix[0][1]; TScalarType r13 = newMatrix[0][2]; TScalarType r21 = newMatrix[1][0]; TScalarType r22 = newMatrix[1][1]; TScalarType r23 = newMatrix[1][2]; TScalarType r31 = newMatrix[2][0]; TScalarType r32 = newMatrix[2][1]; TScalarType r33 = newMatrix[2][2]; // compute Jacobian wrt S1/S2/S3 j[0][4] = r11 * w1; j[0][5] = r12 * w2; j[0][6] = r13 * w3; j[1][4] = r21 * w1; j[1][5] = r22 * w2; j[1][6] = r23 * w3; j[2][4] = r31 * w1; j[2][5] = r32 * w2; j[2][6] = r33 * w3; // compute Jacobian wrt K1/K2/K3 j[0][7] = r11 * s1 * (x2 - c2); j[0][8] = r11 * s1 * (x3 - c3); j[0][9] = r12 * s2 * (x3 - c3); j[1][7] = r21 * s1 * (x2 - c2); j[1][8] = r21 * s1 * (x3 - c3); j[1][9] = r22 * s2 * (x3 - c3); j[2][7] = r31 * s1 * (x2 - c2); j[2][8] = r31 * s1 * (x3 - c3); j[2][9] = r32 * s2 * (x3 - c3); // for test purpose // FORCE ONLY DO DERIVATIVE ON S // for(int ii=0; ii <3; ii++){ // for(int jj=0; jj<=9;jj++){ // j[ii][jj] = 0.0; // } // } // j[0][4] = r11 * w1; // j[1][4] = r21 * w1; // j[2][4] = r31 * w1; // compute derivatives for the translation part unsigned int blockOffset = 10; for( unsigned int dim = 0; dim < SpaceDimension; dim++ ) { j[dim][blockOffset + dim] = 1.0; } // // compute derivatives with respect to rotation // j.Fill(0.0); // const TScalarType x = p[0] - this->GetCenter()[0]; // const TScalarType y = p[1] - this->GetCenter()[1]; // const TScalarType z = p[2] - this->GetCenter()[2]; // // compute Jacobian with respect to quaternion parameters // j[0][0] = 2.0 * ( m_Rotation.x() * x + m_Rotation.y() * y // + m_Rotation.z() * z ); // j[0][1] = 2.0 * (- m_Rotation.y() * x + m_Rotation.x() * y // + m_Rotation.r() * z ); // j[0][2] = 2.0 * (- m_Rotation.z() * x - m_Rotation.r() * y // + m_Rotation.x() * z ); // j[0][3] = - 2.0 * (- m_Rotation.r() * x + m_Rotation.z() * y // - m_Rotation.y() * z ); // j[1][0] = - j[0][1]; // j[1][1] = j[0][0]; // j[1][2] = j[0][3]; // j[1][3] = - j[0][2]; // j[2][0] = - j[0][2]; // j[2][1] = - j[0][3]; // j[2][2] = j[0][0]; // j[2][3] = j[0][1]; // // compute derivatives for the translation part // unsigned int blockOffset = 4; // for(unsigned int dim=0; dim < SpaceDimension; dim++ ) // { // j[ dim ][ blockOffset + dim ] = 1.0; // } // return j; } // template // const typename ANTSAffine3DTransform< TScalarType >::InverseMatrixType & // ANTSAffine3DTransform:: // GetInverseMatrix() const // { // // If the transform has been modified we recompute the inverse // if(this->InverseMatrixIsOld()) // { // InverseMatrixType newMatrix; // VnlQuaternionType conjugateRotation = m_Rotation.conjugate(); // VnlQuaternionType inverseRotation = conjugateRotation.inverse(); // newMatrix = inverseRotation.rotation_matrix_transpose(); // this->SetVarInverseMatrix(newMatrix); // } // return this->GetVarInverseMatrix(); // } template typename ANTSAffine3DTransform::MatrixType ANTSAffine3DTransform< TScalarType>::ComputeMyRotationMatrix() { VnlQuaternionType conjugateRotation = m_Rotation.conjugate(); // this is done to compensate for the transposed representation // between VNL and ITK MatrixType R; R = conjugateRotation.rotation_matrix_transpose(); return R; } template void ANTSAffine3DTransform::ComputeMatrix() { VnlQuaternionType conjugateRotation = m_Rotation.conjugate(); // this is done to compensate for the transposed representation // between VNL and ITK MatrixType R; R = conjugateRotation.rotation_matrix_transpose(); MatrixType S; S.Fill(NumericTraits::ZeroValue()); S[0][0] = this->m_S1; S[1][1] = this->m_S2; S[2][2] = this->m_S3; MatrixType K; K.Fill(NumericTraits::ZeroValue()); K[0][0] = NumericTraits::OneValue(); K[0][1] = this->m_K1; K[0][2] = this->m_K2; K[1][1] = NumericTraits::OneValue(); K[1][2] = this->m_K3; K[2][2] = NumericTraits::OneValue(); MatrixType newMatrix; newMatrix = R * S * K; this->SetVarMatrix(newMatrix); } template void ANTSAffine3DTransform::ComputeMatrixParameters() { // VnlQuaternionType quat(this->GetMatrix().GetVnlMatrix()); // m_Rotation = quat; // std::cout << "compute para: to be done!" << std::endl; // InternalMatrixType A, Q, R; typedef vnl_matrix TMatrix; TMatrix A, R, Q; A = this->GetMatrix().GetVnlMatrix(); vnl_qr myqr(A); Q = myqr.Q(); // Q() is the rotation R = myqr.R(); // R() is the upper triangluar // songgang: anyone added this??? // this is not necessary, for the mirror case // the scale factor could not negative // normalize R // need to run this, otherwise, identity matrix have negative scale!!! // // force diagnoal of rotation matrix to be positive // TMatrix dq(3,3,0); // for(unsigned i=0;i<3;i++){ // dq(i,i) = (R(i,i)>=0)? 1 : -1; // } // Q = Q * dq; // R = dq * R; // force trace of rotation matrix be maximum possible by multiplying // a diagonal (+/-1 +/-1 +/-1) whose determinant is always positive +1. double trA = Q(0, 0) + Q(1, 1) + Q(2, 2); // 1, 1, 1 double trB = Q(0, 0) - Q(1, 1) - Q(2, 2); // 1, -1, -1 double trC = -Q(0, 0) + Q(1, 1) - Q(2, 2); // -1, 1, -1 double trD = -Q(0, 0) - Q(1, 1) + Q(2, 2); // -1, -1, 1 double maxTr = trA; // find the maximum of all terms; if( trB > maxTr ) { maxTr = trB; // dividing by the maximum makes } if( trC > maxTr ) { maxTr = trC; // the computations more stable } if( trD > maxTr ) { maxTr = trD; // and avoid division by zero } if( maxTr == trB ) { TMatrix dq(3, 3, 0); dq(0, 0) = 1; dq(1, 1) = -1; dq(2, 2) = -1; Q = Q * dq; R = dq * R; } if( maxTr == trC ) { TMatrix dq(3, 3, 0); dq(0, 0) = -1; dq(1, 1) = 1; dq(2, 2) = -1; Q = Q * dq; R = dq * R; } if( maxTr == trD ) { TMatrix dq(3, 3, 0); dq(0, 0) = -1; dq(1, 1) = -1; dq(2, 2) = 1; Q = Q * dq; R = dq * R; } double tr = 1 + Q(0, 0) + Q(1, 1) + Q(2, 2); double s, r, u, v, w; if( tr > 0 ) { s = 0.5 / sqrt(tr); r = 0.25 / s; u = (Q(2, 1) - Q(1, 2) ) * s; v = (Q(0, 2) - Q(2, 0) ) * s; w = (Q(1, 0) - Q(0, 1) ) * s; } else if( Q(0, 0) > Q(1, 1) && Q(0, 0) > Q(2, 2) ) { s = 2 * sqrt(1 + Q(0, 0) - Q(1, 1) - Q(2, 2) ); u = 0.25 * s; v = (Q(0, 1) + Q(1, 0) ) / s; w = (Q(0, 2) + Q(2, 0) ) / s; r = (Q(1, 2) - Q(2, 1) ) / s; } else if( Q(0, 0) > Q(1, 1) ) { s = 2 * sqrt(1 + Q(1, 1) - Q(0, 0) - Q(2, 2) ); u = (Q(0, 1) + Q(1, 0) ) / s; v = 0.25 * s; w = (Q(1, 2) + Q(2, 1) ) / s; r = (Q(0, 2) - Q(2, 0) ) / s; } else { s = 2 * sqrt(1 + Q(2, 2) - Q(0, 0) - Q(1, 1) ); u = (Q(0, 2) + Q(2, 0) ) / s; v = (Q(1, 2) + Q(2, 1) ) / s; w = 0.25 * s; r = (Q(0, 1) - Q(1, 0) ) / s; } std::cout << "A=" << A << std::endl; std::cout << "rotation R" << Q << std::endl; std::cout << "upper R" << R << std::endl; std::cout << "s=" << s << " u=" << u << " v=" << v << " w" << w << " r=" << r << std::endl; m_Rotation = VnlQuaternionType(u, v, w, r); std::cout << "m_Rotation from vnl" << VnlQuaternionType(u, v, w, r) << std::endl; m_S1 = R(0, 0); m_S2 = R(1, 1); m_S3 = R(2, 2); m_K1 = R(0, 1) / R(0, 0); m_K2 = R(0, 2) / R(0, 0); m_K3 = R(1, 2) / R(1, 1); // std::cout << "before: this->GetMatrix(): " << this->GetMatrix(); this->ComputeMatrix(); // std::cout << "after: this->GetMatrix(): " << this->GetMatrix(); // std::cout << "A=" << A << std::endl; // std::cout << "R=" << R << std::endl; // std::cout << "R=" << R << std::endl; // std::cout << "dq=" << dq << std::endl; } } // namespace #endif ants-2.2.0/ImageRegistration/itkANTSCenteredAffine2DTransform.h000066400000000000000000000212771311104306400243720ustar00rootroot00000000000000#ifndef __itkANTSCenteredAffine2DTransform_h #define __itkANTSCenteredAffine2DTransform_h #include #include "itkMatrixOffsetTransformBase.h" #include "itkExceptionObject.h" namespace itk { template // Data type for scalars (float or double) // class Rigid2DTransform : class ANTSCenteredAffine2DTransform : public MatrixOffsetTransformBase // Dimensions of input and output spaces { public: /** Standard class typedefs. */ // typedef Rigid2DTransform Self; typedef ANTSCenteredAffine2DTransform Self; typedef MatrixOffsetTransformBase Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ // itkTypeMacro( Rigid2DTransform, MatrixOffsetTransformBase ); itkTypeMacro( ANTSCenteredAffine2DTransform, MatrixOffsetTransformBase ); /** New macro for creation of through a Smart Pointer */ itkNewMacro( Self ); /** Dimension of the space. */ itkStaticConstMacro(InputSpaceDimension, unsigned int, 2); itkStaticConstMacro(OutputSpaceDimension, unsigned int, 2); // itkStaticConstMacro(ParametersDimension, unsigned int, 3); itkStaticConstMacro(ParametersDimension, unsigned int, 8); /** Scalar type. */ typedef typename Superclass::ScalarType ScalarType; /** Parameters type. */ typedef typename Superclass::ParametersType ParametersType; /** Jacobian type. */ typedef typename Superclass::JacobianType JacobianType; // / Standard matrix type for this class typedef typename Superclass::MatrixType MatrixType; // / Standard vector type for this class typedef typename Superclass::OffsetType OffsetType; // / Standard vector type for this class typedef typename Superclass::InputVectorType InputVectorType; typedef typename Superclass::OutputVectorType OutputVectorType; // / Standard covariant vector type for this class typedef typename Superclass::InputCovariantVectorType InputCovariantVectorType; typedef typename Superclass::OutputCovariantVectorType OutputCovariantVectorType; // / Standard vnl_vector type for this class typedef typename Superclass::InputVnlVectorType InputVnlVectorType; typedef typename Superclass::OutputVnlVectorType OutputVnlVectorType; // / Standard coordinate point type for this class typedef typename Superclass::InputPointType InputPointType; typedef typename Superclass::OutputPointType OutputPointType; /** * Set the rotation Matrix of a Rigid2D Transform * * This method sets the 2x2 matrix representing the rotation * in the transform. The Matrix is expected to be orthogonal * with a certain tolerance. * * \warning This method will throw an exception is the matrix * provided as argument is not orthogonal. * * \sa MatrixOffsetTransformBase::SetMatrix() **/ // virtual void SetMatrix( const MatrixType & matrix ); /** * Set/Get the rotation matrix. These methods are old and are * retained for backward compatibility. Instead, use SetMatrix() * GetMatrix(). **/ // virtual void SetRotationMatrix(const MatrixType &matrix) // { this->SetMatrix( matrix ); } // const MatrixType & GetRotationMatrix() const // { return this->GetMatrix(); } /** * Compose the transformation with a translation * * This method modifies self to include a translation of the * origin. The translation is precomposed with self if pre is * true, and postcomposed otherwise. **/ // void Translate(const OffsetType &offset, bool pre=false); /** * Back transform by an rigid transformation. * * The BackTransform() methods are slated to be removed from ITK. * Instead, please use GetInverse() or CloneInverseTo() to generate * an inverse transform and then perform the transform using that * inverted transform. **/ inline InputPointType BackTransform(const OutputPointType & point ) const; inline InputVectorType BackTransform(const OutputVectorType & vector) const; inline InputVnlVectorType BackTransform(const OutputVnlVectorType & vector) const; inline InputCovariantVectorType BackTransform(const OutputCovariantVectorType & vector) const; /** Set/Get the angle of rotation in radians */ void SetAngle(TScalarType angle); void SetS1(TScalarType S1); void SetS2(TScalarType S2); void SetK(TScalarType K); itkGetConstReferenceMacro( Angle, TScalarType ); itkGetConstReferenceMacro( S1, TScalarType ); itkGetConstReferenceMacro( S2, TScalarType ); itkGetConstReferenceMacro( K, TScalarType ); /** Set the angle of rotation in degrees. */ void SetAngleInDegrees(TScalarType angle); /** Set/Get the angle of rotation in radians. These methods * are old and are retained for backward compatibility. * Instead, use SetAngle() and GetAngle(). */ // void SetRotation(TScalarType angle) // { this->SetAngle(angle); } // virtual const TScalarType & GetRotation() const // { return m_Angle; } /** Set the transformation from a container of parameters * This is typically used by optimizers. * There are 3 parameters. The first one represents the * angle of rotation in radians and the last two represents the translation. * The center of rotation is fixed. * * \sa Transform::SetParameters() * \sa Transform::SetFixedParameters() */ void SetParameters( const ParametersType & parameters ) ITK_OVERRIDE; /** Get the parameters that uniquely define the transform * This is typically used by optimizers. * There are 3 parameters. The first one represents the * angle or rotation in radians and the last two represents the translation. * The center of rotation is fixed. * * \sa Transform::GetParameters() * \sa Transform::GetFixedParameters() */ const ParametersType & GetParameters() const ITK_OVERRIDE; /** This method computes the Jacobian matrix of the transformation * at a given input point. * * \sa Transform::GetJacobian() */ // const JacobianType & GetJacobian(const InputPointType &point ) const; /** Compute the Jacobian of the transformation * * This method computes the Jacobian matrix of the transformation. * given point or vector, returning the transformed point or * vector. The rank of the Jacobian will also indicate if the transform * is invertible at this point. * Get local Jacobian for the given point * \c j will sized properly as needed. */ virtual void ComputeJacobianWithRespectToParameters(const InputPointType & p, JacobianType & j) const ITK_OVERRIDE; /** * This method creates and returns a new ANTSCenteredAffine2DTransform object * which is the inverse of self. **/ // void CloneInverseTo( Pointer & newinverse ) const; /** * This method creates and returns a new ANTSCenteredAffine2DTransform object * which has the same parameters. **/ void CloneTo( Pointer & clone ) const; /** Reset the parameters to create and identity transform. */ virtual void SetIdentity() ITK_OVERRIDE; protected: // Rigid2DTransform(); ANTSCenteredAffine2DTransform(); // Rigid2DTransform( unsigned int outputSpaceDimension, // unsigned int parametersDimension); ANTSCenteredAffine2DTransform( unsigned int outputSpaceDimension, unsigned int parametersDimension); // ~Rigid2DTransform(); ~ANTSCenteredAffine2DTransform(); /** * Print contents of an ANTSCenteredAffine2DTransform **/ void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; /** Compute the matrix from angle. This is used in Set methods * to update the underlying matrix whenever a transform parameter * is changed. */ virtual void ComputeMatrix() ITK_OVERRIDE; /** Compute the angle from the matrix. This is used to compute * transform parameters from a given matrix. This is used in * MatrixOffsetTransformBase::Compose() and * MatrixOffsetTransformBase::GetInverse(). */ virtual void ComputeMatrixParameters() ITK_OVERRIDE; /** Update angle without recomputation of other internal variables. */ void SetVarAngle( TScalarType angle ) { m_Angle = angle; } void SetVarS1( TScalarType S1) { m_S1 = S1; } void SetVarS2( TScalarType S2) { m_S2 = S2; } void SetVarK( TScalarType K) { m_K = K; } private: ANTSCenteredAffine2DTransform(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented TScalarType m_Angle; TScalarType m_S1; TScalarType m_S2; TScalarType m_K; }; // class ANTSCenteredAffine2DTransform } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkANTSCenteredAffine2DTransform.hxx" #endif #endif /* __itkANTSCenteredAffine2DTransform_h */ ants-2.2.0/ImageRegistration/itkANTSCenteredAffine2DTransform.hxx000066400000000000000000000352521311104306400247500ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkANTSCenteredAffine2DTransform_hxx #define _itkANTSCenteredAffine2DTransform_hxx // #include "itkRigid2DTransform.h" #include "itkANTSCenteredAffine2DTransform.h" #include "vnl/algo/vnl_qr.h" namespace itk { // Constructor with default arguments // template // ANTSCenteredAffine2DTransform:: // ANTSCenteredAffine2DTransform(): // Superclass(OutputSpaceDimension, ParametersDimension) // { // m_Angle = NumericTraits< TScalarType >::ZeroValue(); // } template ANTSCenteredAffine2DTransform::ANTSCenteredAffine2DTransform() : Superclass(ParametersDimension) { m_Angle = NumericTraits::ZeroValue(); m_S1 = NumericTraits::OneValue(); m_S2 = NumericTraits::OneValue(); m_K = NumericTraits::ZeroValue(); } // Constructor with arguments template ANTSCenteredAffine2DTransform::ANTSCenteredAffine2DTransform( unsigned int spaceDimension, unsigned int parametersDimension) : Superclass(spaceDimension, parametersDimension) { m_Angle = NumericTraits::ZeroValue(); m_S1 = NumericTraits::OneValue(); m_S2 = NumericTraits::OneValue(); m_K = NumericTraits::ZeroValue(); } // Destructor template ANTSCenteredAffine2DTransform:: ~ANTSCenteredAffine2DTransform() { } // Print self template void ANTSCenteredAffine2DTransform::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Angle = " << m_Angle << std::endl; os << indent << "S1 = " << m_S1 << std::endl; os << indent << "S2 = " << m_S2 << std::endl; os << indent << "K = " << m_K << std::endl; // os << indent << "S2 = " << m_ << std::endl; } // Set the rotation matrix // template // void // ANTSCenteredAffine2DTransform:: // SetMatrix(const MatrixType & matrix ) // { // itkDebugMacro("setting m_Matrix to " << matrix ); // // The matrix must be orthogonal otherwise it is not // // representing a valid rotaion in 2D space // typename MatrixType::InternalMatrixType test = // matrix.GetVnlMatrix() * matrix.GetTranspose(); // const double tolerance = 1e-10; // if( !test.is_identity( tolerance ) ) // { // itk::ExceptionObject ex(__FILE__,__LINE__,"Attempt to set a Non-Orthogonal matrix",ITK_LOCATION); // throw ex; // } // this->SetVarMatrix( matrix ); // this->ComputeOffset(); // this->ComputeMatrixParameters(); // this->Modified(); // } /** Compute the Angle from the Rotation Matrix */ template void ANTSCenteredAffine2DTransform ::ComputeMatrixParameters( void ) { typedef vnl_matrix TMatrix; TMatrix A, Q, R; A = this->GetMatrix().GetVnlMatrix(); vnl_qr myqr(A); R = myqr.Q(); // Q() is the rotation Q = myqr.R(); // R() is the upper triangluar TMatrix dq(2, 2, 0); for( unsigned i = 0; i < 2; i++ ) { dq(i, i) = (Q(i, i) >= 0) ? 1 : -1; } R = R * dq; Q = dq * Q; // std::cout << "A=" << A << std::endl; // std::cout << "Q=" << Q << std::endl; // std::cout << "R=" << R << std::endl; // std::cout << "dq=" << dq << std::endl; m_Angle = std::acos(R[0][0]); if( this->GetMatrix()[1][0] < 0.0 ) { m_Angle = -m_Angle; } m_S1 = Q[0][0]; m_S2 = Q[1][1]; m_K = Q[0][1] / Q[0][0]; this->ComputeMatrix(); if( this->GetMatrix()[1][0] - sin(m_Angle) > 0.000001 ) { itkWarningMacro("Bad Rotation Matrix " << this->GetMatrix() ); } } // // Compose with a translation // template // void // ANTSCenteredAffine2DTransform:: // Translate(const OffsetType &offset, bool) // { // OutputVectorType newOffset = this->GetOffset(); // newOffset += offset; // this->SetOffset(newOffset); // } // Create and return an inverse transformation // template // void // ANTSCenteredAffine2DTransform:: // CloneInverseTo( Pointer & result ) const // { // result = New(); // result->SetCenter( this->GetCenter() ); // inverse have the same center // result->SetAngle( -this->GetAngle() ); // result->SetTranslation( -( this->GetInverseMatrix() * this->GetTranslation() ) ); // } // Create and return a clone of the transformation template void ANTSCenteredAffine2DTransform::CloneTo( Pointer & result ) const { result = New(); result->SetCenter( this->GetCenter() ); result->SetAngle( this->GetAngle() ); result->SetS1( this->GetS1() ); result->SetS2( this->GetS2() ); result->SetK( this->GetK() ); result->SetTranslation( this->GetTranslation() ); } // Reset the transform to an identity transform template void ANTSCenteredAffine2DTransform::SetIdentity( void ) { this->Superclass::SetIdentity(); m_Angle = NumericTraits::ZeroValue(); m_S1 = NumericTraits::OneValue(); m_S2 = NumericTraits::OneValue(); m_K = NumericTraits::ZeroValue(); } // Set the angle of rotation template void ANTSCenteredAffine2DTransform ::SetAngle(TScalarType angle) { m_Angle = angle; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); } template void ANTSCenteredAffine2DTransform ::SetS1(TScalarType S1) { m_S1 = S1; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); } template void ANTSCenteredAffine2DTransform ::SetS2(TScalarType S2) { m_S2 = S2; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); } template void ANTSCenteredAffine2DTransform ::SetK(TScalarType K) { m_K = K; this->ComputeMatrix(); this->ComputeOffset(); this->Modified(); } // Set the angle of rotation template void ANTSCenteredAffine2DTransform ::SetAngleInDegrees(TScalarType angle) { const TScalarType angleInRadians = angle * std::atan(1.0) / 45.0; this->SetAngle( angleInRadians ); } // Compute the matrix from the angle template void ANTSCenteredAffine2DTransform ::ComputeMatrix( void ) { const double ca = std::cos(m_Angle ); const double sa = std::sin(m_Angle ); const double s1 = m_S1; const double s2 = m_S2; const double k = m_K; MatrixType rotationMatrix; rotationMatrix[0][0] = ca; rotationMatrix[0][1] = -sa; rotationMatrix[1][0] = sa; rotationMatrix[1][1] = ca; MatrixType scaleMatrix; scaleMatrix[0][0] = s1; scaleMatrix[0][1] = 0; scaleMatrix[1][0] = 0; scaleMatrix[1][1] = s2; MatrixType shearMatrix; shearMatrix[0][0] = 1; shearMatrix[0][1] = k; shearMatrix[1][0] = 0; shearMatrix[1][1] = 1; MatrixType varMatrix; varMatrix = rotationMatrix * scaleMatrix * shearMatrix; this->SetVarMatrix( varMatrix ); } // Set Parameters template void ANTSCenteredAffine2DTransform::SetParameters( const ParametersType & parameters ) { itkDebugMacro( << "Setting parameters " << parameters ); // Set angle/s1/s2/k this->SetVarAngle( parameters[0] ); this->SetVarS1( parameters[1] ); this->SetVarS2( parameters[2] ); this->SetVarK( parameters[3] ); // Set the center InputPointType center; for( unsigned int i = 0; i < OutputSpaceDimension; i++ ) { center[i] = parameters[i + 4]; } this->SetVarCenter( center ); // Set translation OutputVectorType translation; for( unsigned int i = 0; i < OutputSpaceDimension; i++ ) { translation[i] = parameters[i + 6]; } this->SetVarTranslation( translation ); // Update matrix and offset this->ComputeMatrix(); this->ComputeOffset(); // Modified is always called since we just have a pointer to the // parameters and cannot know if the parameters have changed. this->Modified(); itkDebugMacro(<< "After setting parameters "); } // Get Parameters template const typename ANTSCenteredAffine2DTransform::ParametersType & ANTSCenteredAffine2DTransform:: GetParameters( void ) const { itkDebugMacro( << "Getting parameters "); // Get the angle/s1/s2/k this->m_Parameters[0] = this->GetAngle(); this->m_Parameters[1] = this->GetS1(); this->m_Parameters[2] = this->GetS2(); this->m_Parameters[3] = this->GetK(); // Get the center for( unsigned int i = 0; i < OutputSpaceDimension; i++ ) { this->m_Parameters[i + 4] = this->GetCenter()[i]; } // Get the translation for( unsigned int i = 0; i < OutputSpaceDimension; i++ ) { this->m_Parameters[i + 6] = this->GetTranslation()[i]; } itkDebugMacro(<< "After getting parameters " << this->m_Parameters ); return this->m_Parameters; } // // Compute transformation Jacobian // template // const typename ANTSCenteredAffine2DTransform::JacobianType & // ANTSCenteredAffine2DTransform:: // GetJacobian( const InputPointType & p ) const // { // // const double ca = std::cos(this->GetAngle() ); // const double sa = std::sin(this->GetAngle() ); // const double s1 = m_S1; // const double s2 = m_S2; // const double k = m_K; // // this->m_Jacobian.Fill(0.0); // // const double cx = this->GetCenter()[0]; // const double cy = this->GetCenter()[1]; // // // derivatives with respect to the angle // // this->m_Jacobian[0][0] = -sa * ( p[0] - cx ) - ca * ( p[1] - cy ); // // this->m_Jacobian[1][0] = ca * ( p[0] - cx ) - sa * ( p[1] - cy ); // // double pxoff = (p[0]-cx)+k*(p[1]-cy); // double pyoff = p[1]-cy; // // // wrt. theta // this->m_Jacobian[0][0] = s1*( pxoff )*(-sa) + s2*( pyoff )*(-ca); // this->m_Jacobian[1][0] = s1*( pxoff )*(ca) + s2*( pyoff )*(-sa); // // // wrt. s1/s2 // this->m_Jacobian[0][1] = ca * pxoff; // this->m_Jacobian[0][2] = -sa * pyoff; // // this->m_Jacobian[1][1] = sa * pxoff; // this->m_Jacobian[1][2] = ca * pyoff; // // // wrt. k // this->m_Jacobian[0][3] = ca * s1 * pyoff; // this->m_Jacobian[1][3] = sa * s1 * pyoff; // // // wrt. cx/cy // this->m_Jacobian[0][4] = - s1*ca + 1.0; // this->m_Jacobian[0][5] = -(k*s1*ca - s2*sa); // this->m_Jacobian[1][4] = - s1*sa; // this->m_Jacobian[1][5] = -(k*s1*sa + s2*ca) + 1.0; // // // // wrt. t1/t2 // this->m_Jacobian[0][6] = 1.0; // this->m_Jacobian[1][7] = 1.0; // // // compute derivatives for the translation part // // unsigned int blockOffset = 1; // // for(unsigned int dim=0; dim < OutputSpaceDimension; dim++ ) // // { // // this->m_Jacobian[ dim ][ blockOffset + dim ] = 1.0; // // } // // return this->m_Jacobian; // // } template void ANTSCenteredAffine2DTransform::ComputeJacobianWithRespectToParameters(const InputPointType & p, JacobianType & j) const { const double ca = std::cos(this->GetAngle() ); const double sa = std::sin(this->GetAngle() ); const double s1 = m_S1; const double s2 = m_S2; const double k = m_K; j.SetSize( this->GetOutputSpaceDimension(), this->GetNumberOfLocalParameters() ); j.Fill(0.0); const double cx = this->GetCenter()[0]; const double cy = this->GetCenter()[1]; // derivatives with respect to the angle // j[0][0] = -sa * ( p[0] - cx ) - ca * ( p[1] - cy ); // j[1][0] = ca * ( p[0] - cx ) - sa * ( p[1] - cy ); double pxoff = (p[0] - cx) + k * (p[1] - cy); double pyoff = p[1] - cy; // wrt. theta j[0][0] = s1 * ( pxoff ) * (-sa) + s2 * ( pyoff ) * (-ca); j[1][0] = s1 * ( pxoff ) * (ca) + s2 * ( pyoff ) * (-sa); // wrt. s1/s2 j[0][1] = ca * pxoff; j[0][2] = -sa * pyoff; j[1][1] = sa * pxoff; j[1][2] = ca * pyoff; // wrt. k j[0][3] = ca * s1 * pyoff; j[1][3] = sa * s1 * pyoff; // wrt. cx/cy j[0][4] = -s1 * ca + 1.0; j[0][5] = -(k * s1 * ca - s2 * sa); j[1][4] = -s1 * sa; j[1][5] = -(k * s1 * sa + s2 * ca) + 1.0; // wrt. t1/t2 j[0][6] = 1.0; j[1][7] = 1.0; } // Back transform a point template typename ANTSCenteredAffine2DTransform::InputPointType ANTSCenteredAffine2DTransform::BackTransform(const OutputPointType & point) const { itkWarningMacro( << "BackTransform(): This method is slated to be removed from ITK. Instead, please use GetInverse() to generate an inverse transform and then perform the transform using that inverted transform."); return this->GetInverseMatrix() * (point - this->GetOffset() ); } // Back transform a vector template typename ANTSCenteredAffine2DTransform::InputVectorType ANTSCenteredAffine2DTransform::BackTransform(const OutputVectorType & vect ) const { itkWarningMacro( << "BackTransform(): This method is slated to be removed from ITK. Instead, please use GetInverse() to generate an inverse transform and then perform the transform using that inverted transform."); return this->GetInverseMatrix() * vect; } // Back transform a vnl_vector template typename ANTSCenteredAffine2DTransform::InputVnlVectorType ANTSCenteredAffine2DTransform::BackTransform(const OutputVnlVectorType & vect ) const { itkWarningMacro( << "BackTransform(): This method is slated to be removed from ITK. Instead, please use GetInverse() to generate an inverse transform and then perform the transform using that inverted transform."); return this->GetInverseMatrix() * vect; } // Back Transform a CovariantVector template typename ANTSCenteredAffine2DTransform::InputCovariantVectorType ANTSCenteredAffine2DTransform::BackTransform(const OutputCovariantVectorType & vect) const { itkWarningMacro( << "BackTransform(): This method is slated to be removed from ITK. Instead, please use GetInverse() to generate an inverse transform and then perform the transform using that inverted transform."); return this->GetMatrix() * vect; } } // namespace #endif ants-2.2.0/ImageRegistration/itkANTSImageRegistrationOptimizer.cxx000066400000000000000000003113661311104306400253420ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkANTSImageRegistrationOptimizer_hxx_ #define _itkANTSImageRegistrationOptimizer_hxx_ // disable debug warnings in MS compiler #ifdef _MSC_VER #pragma warning(disable: 4786) #endif #include "antsAllocImage.h" #include "itkVectorParameterizedNeighborhoodOperatorImageFilter.h" #include "itkANTSImageRegistrationOptimizer.h" #include "itkIdentityTransform.h" #include "itkLinearInterpolateImageFunction.h" #include "itkRecursiveGaussianImageFilter.h" #include "itkVectorGaussianInterpolateImageFunction.h" #include "itkResampleImageFilter.h" #include "itkVectorNeighborhoodOperatorImageFilter.h" #include "vnl/vnl_math.h" #include "ANTS_affine_registration2.h" #include "itkWarpImageMultiTransformFilter.h" // #include "itkVectorImageFileWriter.h" namespace itk { template ANTSImageRegistrationOptimizer ::ANTSImageRegistrationOptimizer() { this->m_DisplacementField = ITK_NULLPTR; this->m_InverseDisplacementField = ITK_NULLPTR; this->m_AffineTransform = ITK_NULLPTR; itk::TransformFactory::RegisterTransform(); itk::TransformFactory >::RegisterTransform(); itk::TransformFactory >::RegisterTransform(); this->m_FixedPointSet = ITK_NULLPTR; this->m_MovingPointSet = ITK_NULLPTR; this->m_UseMulti = true; this->m_UseROI = false; this->m_MaskImage = ITK_NULLPTR; this->m_ReferenceSpaceImage = ITK_NULLPTR; this->m_Debug = false; this->m_ScaleFactor = 1.0; this->m_SubsamplingFactors.SetSize( 0 ); this->m_GaussianSmoothingSigmas.SetSize( 0 ); this->m_SyNF = ITK_NULLPTR; this->m_SyNFInv = ITK_NULLPTR; this->m_SyNM = ITK_NULLPTR; this->m_SyNMInv = ITK_NULLPTR; this->m_Parser = ITK_NULLPTR; this->m_GaussianTruncation = 256; this->m_TimeVaryingVelocity = ITK_NULLPTR; this->m_LastTimeVaryingVelocity = ITK_NULLPTR; this->m_LastTimeVaryingUpdate = ITK_NULLPTR; this->m_DeltaTime = 0.1; this->m_SyNType = 0; this->m_UseNN = false; this->m_UseBSplineInterpolation = false; this->m_VelocityFieldInterpolator = VelocityFieldInterpolatorType::New(); this->m_HitImage = ITK_NULLPTR; this->m_ThickImage = ITK_NULLPTR; this->m_SyNFullTime = 0; } template typename ANTSImageRegistrationOptimizer::ImagePointer ANTSImageRegistrationOptimizer ::SubsampleImage( ImagePointer image, RealType /* scalingFactor */, typename ImageType::PointType outputOrigin, typename ImageType::DirectionType outputDirection, AffineTransformPointer aff ) { typename ImageType::SpacingType outputSpacing = this->m_CurrentDomainSpacing; typename ImageType::RegionType::SizeType outputSize = this->m_CurrentDomainSize; // RealType minimumSpacing = inputSpacing.GetVnlVector().min_value(); // RealType maximumSpacing = inputSpacing.GetVnlVector().max_value(); typedef ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); typedef LinearInterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); interpolator->SetInputImage( image ); resampler->SetInterpolator( interpolator ); typedef itk::IdentityTransform IdentityTransformType; typename IdentityTransformType::Pointer transform = IdentityTransformType::New(); transform->SetIdentity(); resampler->SetTransform( transform ); if( aff ) { // std::cout << " Setting Aff to " << this->m_AffineTransform << std::endl; resampler->SetTransform( aff ); } resampler->SetInput( image ); resampler->SetOutputSpacing( outputSpacing ); resampler->SetOutputOrigin( outputOrigin ); resampler->SetOutputDirection( outputDirection ); resampler->SetSize( outputSize ); resampler->Update(); ImagePointer outimage = resampler->GetOutput(); if( this->m_UseROI ) { outimage = this->MakeSubImage(outimage); // WriteImage(outimage,"temps.hdr"); // warp with affine & deformable } return outimage; } template typename ANTSImageRegistrationOptimizer::DisplacementFieldPointer ANTSImageRegistrationOptimizer ::CopyDisplacementField( DisplacementFieldPointer input ) { DisplacementFieldPointer output = AllocImage(input); typedef ImageRegionIterator Iterator; Iterator inIter( input, input->GetBufferedRegion() ); Iterator outIter( output, output->GetBufferedRegion() ); inIter.GoToBegin(); outIter.GoToBegin(); for( ; !inIter.IsAtEnd(); ++inIter, ++outIter ) { outIter.Set( inIter.Get() ); } return output; } template void ANTSImageRegistrationOptimizer ::SmoothDisplacementFieldGauss(DisplacementFieldPointer field, TReal sig, bool /* useparamimage */, unsigned int lodim) { if( this->m_Debug ) { std::cout << " enter gauss smooth " << sig << std::endl; } if( sig <= 0 ) { return; } if( !field ) { std::cout << " No Field in gauss Smoother " << std::endl; return; } DisplacementFieldPointer tempField = AllocImage(field); typedef typename DisplacementFieldType::PixelType DispVectorType; typedef typename DispVectorType::ValueType ScalarType; typedef GaussianOperator OperatorType; // typedef VectorNeighborhoodOperatorImageFilter< DisplacementFieldType, DisplacementFieldType> SmootherType; typedef VectorParameterizedNeighborhoodOperatorImageFilter< DisplacementFieldType, DisplacementFieldType, ImageType> SmootherType; OperatorType * oper = new OperatorType; typename SmootherType::Pointer smoother = SmootherType::New(); typedef typename DisplacementFieldType::PixelContainerPointer PixelContainerPointer; PixelContainerPointer swapPtr; // graft the output field onto the mini-pipeline smoother->GraftOutput( tempField ); for( unsigned int j = 0; j < lodim; j++ ) { // smooth along this dimension oper->SetDirection( j ); TReal sigt = sig; oper->SetVariance( sigt ); oper->SetMaximumError(0.001 ); oper->SetMaximumKernelWidth( (unsigned int) this->m_GaussianTruncation ); oper->CreateDirectional(); // todo: make sure we only smooth within the buffered region smoother->SetOperator( *oper ); smoother->SetInput( field ); smoother->Update(); if( j < lodim - 1 ) { // swap the containers swapPtr = smoother->GetOutput()->GetPixelContainer(); smoother->GraftOutput( field ); field->SetPixelContainer( swapPtr ); smoother->Modified(); } } // graft the output back to this filter tempField->SetPixelContainer( field->GetPixelContainer() ); // make sure boundary does not move TReal weight = 1.0; if( sig < 0.5 ) { weight = 1.0 - 1.0 * (sig / 0.5); } TReal weight2 = 1.0 - weight; typedef itk::ImageRegionIteratorWithIndex Iterator; typename DisplacementFieldType::SizeType size = field->GetLargestPossibleRegion().GetSize(); Iterator outIter( field, field->GetLargestPossibleRegion() ); for( outIter.GoToBegin(); !outIter.IsAtEnd(); ++outIter ) { bool onboundary = false; typename DisplacementFieldType::IndexType index = outIter.GetIndex(); for( unsigned int i = 0; i < ImageDimension; i++ ) { if( index[i] < 1 || index[i] >= static_cast( size[i] ) - 1 ) { onboundary = true; } } if( onboundary ) { DispVectorType vec; vec.Fill(0.0); outIter.Set(vec); } else { // field=this->CopyDisplacementField( DispVectorType svec = smoother->GetOutput()->GetPixel(index); outIter.Set( svec * weight + outIter.Get() * weight2); } } if( this->m_Debug ) { std::cout << " done gauss smooth " << std::endl; } delete oper; } template void ANTSImageRegistrationOptimizer ::SmoothVelocityGauss(TimeVaryingVelocityFieldPointer field, TReal sig, unsigned int lodim) { if( sig <= 0 ) { return; } if( !field ) { std::cout << " No Field in gauss Smoother " << std::endl; return; } TimeVaryingVelocityFieldPointer tempField = AllocImage(field); typedef typename TimeVaryingVelocityFieldType::PixelType TVVFVectorType; typedef typename TVVFVectorType::ValueType ScalarType; typedef GaussianOperator OperatorType; typedef VectorNeighborhoodOperatorImageFilter SmootherType; OperatorType * oper = new OperatorType; typename SmootherType::Pointer smoother = SmootherType::New(); typedef typename TimeVaryingVelocityFieldType::PixelContainerPointer PixelContainerPointer; PixelContainerPointer swapPtr; // graft the output field onto the mini-pipeline smoother->GraftOutput( tempField ); for( unsigned int j = 0; j < lodim; j++ ) { // smooth along this dimension oper->SetDirection( j ); oper->SetVariance( sig ); oper->SetMaximumError(0.001 ); oper->SetMaximumKernelWidth( (unsigned int) this->m_GaussianTruncation ); oper->CreateDirectional(); // todo: make sure we only smooth within the buffered region smoother->SetOperator( *oper ); smoother->SetInput( field ); smoother->Update(); if( j < lodim - 1 ) { // swap the containers swapPtr = smoother->GetOutput()->GetPixelContainer(); smoother->GraftOutput( field ); field->SetPixelContainer( swapPtr ); smoother->Modified(); } } // graft the output back to this filter tempField->SetPixelContainer( field->GetPixelContainer() ); // make sure boundary does not move TReal weight = 1.0; if( sig < 0.5 ) { weight = 1.0 - 1.0 * (sig / 0.5); } TReal weight2 = 1.0 - weight; typedef itk::ImageRegionIteratorWithIndex Iterator; typename TimeVaryingVelocityFieldType::SizeType size = field->GetLargestPossibleRegion().GetSize(); Iterator outIter( field, field->GetLargestPossibleRegion() ); for( outIter.GoToBegin(); !outIter.IsAtEnd(); ++outIter ) { bool onboundary = false; typename TimeVaryingVelocityFieldType::IndexType index = outIter.GetIndex(); for( unsigned int i = 0; i < ImageDimension; i++ ) { if( index[i] < 1 || index[i] >= static_cast( size[i] ) - 1 ) { onboundary = true; } } if( onboundary ) { TVVFVectorType vec; vec.Fill(0.0); outIter.Set(vec); } else { // field=this->CopyDisplacementField( TVVFVectorType svec = smoother->GetOutput()->GetPixel(index); outIter.Set( svec * weight + outIter.Get() * weight2); } } if( this->m_Debug ) { std::cout << " done gauss smooth " << std::endl; } delete oper; } template void ANTSImageRegistrationOptimizer ::SmoothDisplacementFieldBSpline( DisplacementFieldPointer field, ArrayType meshsize, unsigned int splineorder, unsigned int numberoflevels ) { if( this->m_Debug ) { std::cout << " enter bspline smooth " << std::endl; } if( !field ) { std::cout << " No Field in bspline Smoother " << std::endl; return; } if( splineorder <= 0 ) { return; } typename BSplineFilterType::ArrayType numberofcontrolpoints; for( unsigned int d = 0; d < ImageDimension; d++ ) { if( meshsize[d] <= 0 ) { return; } numberofcontrolpoints[d] = static_cast( meshsize[d] ) + splineorder; } VectorType zeroVector; zeroVector.Fill( 0.0 ); // typedef VectorImageFileWriter // DisplacementFieldWriterType; // typename DisplacementFieldWriterType::Pointer writer = DisplacementFieldWriterType::New(); // writer->SetInput( field ); // writer->SetFileName( "field.nii.gz" ); // writer->Update(); // std::exception(); typename ImageType::DirectionType originalDirection = field->GetDirection(); typename ImageType::DirectionType identityDirection; identityDirection.SetIdentity(); field->SetDirection( identityDirection ); typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); bspliner->SetInput( field ); bspliner->SetNumberOfLevels( numberoflevels ); bspliner->SetSplineOrder( splineorder ); bspliner->SetNumberOfControlPoints( numberofcontrolpoints ); bspliner->SetIgnorePixelValue( zeroVector ); bspliner->Update(); field->SetDirection( originalDirection ); // make sure boundary does not move typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator bIter( bspliner->GetOutput(), bspliner->GetOutput()->GetLargestPossibleRegion() ); Iterator outIter( field, field->GetLargestPossibleRegion() ); for( outIter.GoToBegin(), bIter.GoToBegin(); !outIter.IsAtEnd(); ++outIter, ++bIter ) { // bool onboundary=false; // typename DisplacementFieldType::IndexType index = outIter.GetIndex(); // for( int i = 0; i < ImageDimension; i++ ) // { // if ( index[i] < 1 || index[i] >= static_cast( size[i] )-1 ) // onboundary = true; // } // if (onboundary) // { // VectorType vec; // vec.Fill(0.0); // outIter.Set(vec); // } // else // { outIter.Set( bIter.Get() ); // } } if( this->m_Debug ) { std::cout << " done bspline smooth " << std::endl; } } template void ANTSImageRegistrationOptimizer ::ComposeDiffs(DisplacementFieldPointer fieldtowarpby, DisplacementFieldPointer field, DisplacementFieldPointer fieldout, TReal timesign) { typedef Point VPointType; // field->SetSpacing( fieldtowarpby->GetSpacing() ); // field->SetOrigin( fieldtowarpby->GetOrigin() ); // field->SetDirection( fieldtowarpby->GetDirection() ); if( !fieldout ) { VectorType zero; zero.Fill(0); fieldout = AllocImage(fieldtowarpby); } typedef typename DisplacementFieldType::PixelType DispVectorType; typedef itk::ImageRegionIteratorWithIndex FieldIterator; typedef typename DisplacementFieldType::IndexType DispIndexType; typedef itk::VectorLinearInterpolateImageFunction DefaultInterpolatorType; typename DefaultInterpolatorType::Pointer vinterp = DefaultInterpolatorType::New(); vinterp->SetInputImage(field); // vinterp->SetParameters(NULL,1); VPointType pointIn1; VPointType pointIn2; typename DefaultInterpolatorType::ContinuousIndexType contind; // married to pointIn2 VPointType pointIn3; unsigned int ct = 0; // iterate through fieldtowarpby finding the points that it maps to via field. // then take the difference from the original point and put it in the output field. // std::cout << " begin iteration " << std::endl; FieldIterator m_FieldIter( fieldtowarpby, fieldtowarpby->GetLargestPossibleRegion() ); for( m_FieldIter.GoToBegin(); !m_FieldIter.IsAtEnd(); ++m_FieldIter ) { DispIndexType index = m_FieldIter.GetIndex(); bool dosample = true; // if (sub && m_TRealImage->GetPixel(index) < 0.5) dosample=false; if( dosample ) { fieldtowarpby->TransformIndexToPhysicalPoint( index, pointIn1 ); DispVectorType disp = m_FieldIter.Get(); for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { pointIn2[jj] = disp[jj] + pointIn1[jj]; } typename DefaultInterpolatorType::OutputType disp2; if( vinterp->IsInsideBuffer(pointIn2) ) { disp2 = vinterp->Evaluate( pointIn2 ); } else { disp2.Fill(0); } for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { pointIn3[jj] = disp2[jj] * timesign + pointIn2[jj]; } DispVectorType out; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { out[jj] = pointIn3[jj] - pointIn1[jj]; } fieldout->SetPixel(m_FieldIter.GetIndex(), out); ct++; } // endif } // end iteration } template typename ANTSImageRegistrationOptimizer::DisplacementFieldPointer ANTSImageRegistrationOptimizer ::IntegrateConstantVelocity(DisplacementFieldPointer totalField, unsigned int ntimesteps, TReal timestep) { VectorType zero; zero.Fill(0); DisplacementFieldPointer diffmap = AllocImage(totalField, zero); for( unsigned int nts = 0; nts < ntimesteps; nts++ ) { this->ComposeDiffs(diffmap, totalField, diffmap, timestep); } return diffmap; } template typename ANTSImageRegistrationOptimizer::DisplacementFieldPointer ANTSImageRegistrationOptimizer ::ComputeUpdateField(DisplacementFieldPointer fixedwarp, DisplacementFieldPointer movingwarp, PointSetPointer fpoints, PointSetPointer wpoints, DisplacementFieldPointer totalUpdateInvField, bool updateenergy) { ImagePointer mask = ITK_NULLPTR; if( movingwarp && this->m_MaskImage && !this->m_ComputeThickness ) { mask = this->WarpMultiTransform( this->m_ReferenceSpaceImage, this->m_MaskImage, ITK_NULLPTR, movingwarp, false, this->m_FixedImageAffineTransform ); } else if( this->m_MaskImage && !this->m_ComputeThickness ) { mask = this->SubsampleImage( this->m_MaskImage, this->m_ScaleFactor, this->m_MaskImage->GetOrigin(), this->m_MaskImage->GetDirection(), ITK_NULLPTR); } if( !fixedwarp ) { std::cout << " NO F WARP " << std::endl; fixedwarp = this->m_DisplacementField; } // if ( !movingwarp) std::cout<< " NO M WARP " << std::endl; // / std::cout << " get upd field " << std::endl; typename ImageType::SpacingType spacing = fixedwarp->GetSpacing(); VectorType zero; zero.Fill(0); DisplacementFieldPointer updateField; DisplacementFieldPointer updateFieldInv; DisplacementFieldPointer totalUpdateField = AllocImage(fixedwarp, zero); // bool hadpointsetmetric=false; RealType sumWeights = 0.0; for( unsigned int n = 0; n < this->m_SimilarityMetrics.size(); n++ ) { sumWeights += this->m_SimilarityMetrics[n]->GetWeightScalar(); } sumWeights = 1; for( unsigned int metricCount = 0; metricCount < this->m_SimilarityMetrics.size(); metricCount++ ) { bool ispointsetmetric = false; /** build an update field */ if( this->m_SimilarityMetrics.size() == 1 ) { updateField = totalUpdateField; if( totalUpdateInvField ) { updateFieldInv = totalUpdateInvField; } } else { updateField = AllocImage(fixedwarp, zero); if( totalUpdateInvField ) { updateFieldInv = AllocImage(fixedwarp, zero); } } /** get the update */ typedef typename FiniteDifferenceFunctionType::NeighborhoodType NeighborhoodIteratorType; typedef ImageRegionIterator UpdateIteratorType; // TimeStepType timeStep; void *globalData; // std::cout << " B " << std::endl; AffineTransformPointer faffinverse = ITK_NULLPTR; if( this->m_FixedImageAffineTransform ) { faffinverse = AffineTransformType::New(); this->m_FixedImageAffineTransform->GetInverse(faffinverse); } AffineTransformPointer affinverse = ITK_NULLPTR; if( this->m_AffineTransform ) { affinverse = AffineTransformType::New(); this->m_AffineTransform->GetInverse(affinverse); } // for each metric, warp the assoc. Images /** We loop Over This To Do MultiVariate */ /** FIXME really should pass an image list and then warp each one in turn then expand the update field to fit size of total deformation */ ImagePointer wmimage = ITK_NULLPTR; if( fixedwarp ) { wmimage = this->WarpMultiTransform( this->m_ReferenceSpaceImage, this->m_SmoothMovingImages[metricCount], this->m_AffineTransform, fixedwarp, false, ITK_NULLPTR ); } else { wmimage = this->SubsampleImage( this->m_SmoothMovingImages[metricCount], this->m_ScaleFactor, this->m_SmoothMovingImages[metricCount]->GetOrigin(), this->m_SmoothMovingImages[metricCount]->GetDirection(), ITK_NULLPTR); } // std::cout << " C " << std::endl; ImagePointer wfimage = ITK_NULLPTR; if( movingwarp ) { wfimage = this->WarpMultiTransform( this->m_ReferenceSpaceImage, this->m_SmoothFixedImages[metricCount], ITK_NULLPTR, movingwarp, false, this->m_FixedImageAffineTransform ); } else { wfimage = this->SubsampleImage( this->m_SmoothFixedImages[metricCount], this->m_ScaleFactor, this->m_SmoothFixedImages[metricCount]->GetOrigin(), this->m_SmoothFixedImages[metricCount]->GetDirection(), ITK_NULLPTR); } /* if (this->m_TimeVaryingVelocity && ! this->m_MaskImage ) { std::string outname=this->localANTSGetFilePrefix(this->m_OutputNamingConvention.c_str())+std::string("thick.nii.gz"); /// WriteImage(wmimage,outname.c_str()); outname=this->localANTSGetFilePrefix(this->m_OutputNamingConvention.c_str())+std::string("thick2.nii.gz"); WriteImage(wfimage,outname.c_str()); } */ // std::string // outname=this->localANTSGetFilePrefix(this->m_OutputNamingConvention.c_str())+std::string("temp.nii.gz"); // WriteImage(wmimage,outname.c_str()); // std::string // outname2=this->localANTSGetFilePrefix(this->m_OutputNamingConvention.c_str())+std::string("temp2.nii.gz"); // WriteImage(wfimage,outname2.c_str()); /** MV Loop END -- Would have to collect update fields then add them * together somehow -- Would also have to eliminate the similarity * metric loop within ComputeUpdateField */ // Get the FiniteDifferenceFunction to use in calculations. MetricBaseTypePointer df = this->m_SimilarityMetrics[metricCount]->GetModifiableMetric(); df->SetFixedImage(wfimage); df->SetMovingImage(wmimage); if( df->ThisIsAPointSetMetric() ) { ispointsetmetric = true; } if( fpoints && ispointsetmetric ) { df->SetFixedPointSet(fpoints); } else if( ispointsetmetric ) { std::cout << "NO POINTS!! " << std::endl; } if( wpoints && ispointsetmetric ) { df->SetMovingPointSet(wpoints); } else if( ispointsetmetric ) { std::cout << "NO POINTS!! " << std::endl; } typename ImageType::SizeType radius = df->GetRadius(); df->InitializeIteration(); typename DisplacementFieldType::Pointer output = updateField; typedef NeighborhoodAlgorithm::ImageBoundaryFacesCalculator FaceCalculatorType; typedef typename FaceCalculatorType::FaceListType FaceListType; FaceCalculatorType faceCalculator; FaceListType faceList = faceCalculator(updateField, updateField->GetLargestPossibleRegion(), radius); typename FaceListType::iterator fIt = faceList.begin(); globalData = df->GetGlobalDataPointer(); // Process the non-boundary region. NeighborhoodIteratorType nD(radius, updateField, *fIt); UpdateIteratorType nU(updateField, *fIt); nD.GoToBegin(); nU.GoToBegin(); while( !nD.IsAtEnd() ) { bool oktosample = true; TReal maskprob = 1.0; if( mask ) { maskprob = mask->GetPixel( nD.GetIndex() ); if( maskprob > 1.0 ) { maskprob = 1.0; } if( maskprob < 0.1 ) { oktosample = false; } } if( oktosample ) { VectorType temp = df->ComputeUpdate(nD, globalData) * maskprob; nU.Value() += temp; if( totalUpdateInvField ) { typename ImageType::IndexType index = nD.GetIndex(); temp = df->ComputeUpdateInv(nD, globalData) * maskprob + updateFieldInv->GetPixel(index); updateFieldInv->SetPixel(index, temp); } // else nU.Value() -= df->ComputeUpdateInv(nD, globalData)*maskprob; ++nD; ++nU; } else { ++nD; ++nU; } } // begin restriction of deformation field bool restrict = false; for( unsigned int jj = 0; jj < this->m_RestrictDeformation.size(); jj++ ) { const TReal temp = this->m_RestrictDeformation[jj]; if( fabs( temp - 1 ) > 1.e-5 ) { restrict = true; } } if( restrict && this->m_RestrictDeformation.size() == ImageDimension ) { nU.GoToBegin(); while( !nU.IsAtEnd() ) { const typename ImageType::IndexType & index = nU.GetIndex(); VectorType temp = updateField->GetPixel(index); for( unsigned int jj = 0; jj < this->m_RestrictDeformation.size(); jj++ ) { temp[jj] *= this->m_RestrictDeformation[jj]; updateField->SetPixel(index, temp); } if( updateFieldInv ) { VectorType tempInv = updateFieldInv->GetPixel(index); for( unsigned int jj = 0; jj < this->m_RestrictDeformation.size(); jj++ ) { tempInv[jj] *= this->m_RestrictDeformation[jj]; updateFieldInv->SetPixel(index, temp); } } ++nU; } } // end restrict deformation field if( updateenergy ) { this->m_LastEnergy[metricCount] = this->m_Energy[metricCount]; this->m_Energy[metricCount] = df->GetEnergy(); // *this->m_SimilarityMetrics[metricCount]->GetWeightScalar()/sumWeights; } // smooth the fields // if (!ispointsetmetric || ImageDimension == 2 ){ this->SmoothDisplacementField(updateField, true); if( updateFieldInv ) { this->SmoothDisplacementField(updateFieldInv, true); } // /} /* else // use another strategy -- exact lm? / something like Laplacian { TReal tmag=0; for (unsigned int ff=0; ff<5; ff++) { tmag=0; this->SmoothDisplacementField(updateField,true); if (updateFieldInv) this->SmoothDisplacementField(updateFieldInv,true); nD.GoToBegin(); nU.GoToBegin(); while( !nD.IsAtEnd() ) { typename ImageType::IndexType index=nD.GetIndex(); bool oktosample=true; TReal maskprob=1.0; if (mask) { maskprob=mask->GetPixel( nD.GetIndex() ); if (maskprob > 1.0) maskprob=1.0; if ( maskprob < 0.1) oktosample=false; } VectorType F1; F1.Fill(0); VectorType F2; F2.Fill(0); if ( oktosample ) { F1 = df->ComputeUpdate(nD, globalData)*maskprob; if (totalUpdateInvField) { F2 = df->ComputeUpdateInv(nD, globalData)*maskprob; } ++nD; ++nU; } else { ++nD; ++nU; } // compute mags of F1 and F2 -- if large enough, reset them TReal f1mag=0,f2mag=0,umag=0; for (unsigned int dim=0; dimGetPixel(index)[dim]/spacing[dim]*updateField->GetPixel(index)[dim]/spacing[dim]; } f1mag=sqrt(f1mag); f2mag=sqrt(f2mag); umag=sqrt(umag); if ( f1mag > 0.05 ) updateField->SetPixel(index,F1); if ( f2mag > 0.05 ) updateFieldInv->SetPixel(index,F2); tmag+=umag; } // std::cout << " total mag " << tmag << std::endl; } //smooth the total field this->SmoothDisplacementField(updateField,true); if (updateFieldInv) this->SmoothDisplacementField(updateFieldInv,true); } */ // normalize update field then add to total field typedef ImageRegionIteratorWithIndex Iterator; Iterator dIter(totalUpdateField, totalUpdateField->GetLargestPossibleRegion() ); TReal mag = 0.0; TReal max = 0.0; unsigned long ct = 0; TReal total = 0; for( dIter.GoToBegin(); !dIter.IsAtEnd(); ++dIter ) { typename ImageType::IndexType index = dIter.GetIndex(); VectorType vec = updateField->GetPixel(index); mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += vec[jj] / spacing[jj] * vec[jj] / spacing[jj]; } mag = sqrt(mag); // if (mag > 0. ) std::cout << " mag " << mag << " max " << max << " vec " << vec << std::endl; if( mag > max ) { max = mag; } ct++; total += mag; // std::cout << " mag " << mag << std::endl; } if( this->m_Debug ) { std::cout << "PRE MAX " << max << std::endl; } TReal max2 = 0; if( max <= 0 ) { max = 1; } for( dIter.GoToBegin(); !dIter.IsAtEnd(); ++dIter ) { typename ImageType::IndexType index = dIter.GetIndex(); VectorType vec = updateField->GetPixel(index); vec = vec / max; mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += vec[jj] / spacing[jj] * vec[jj] / spacing[jj]; } mag = sqrt(mag); if( mag > max2 ) { max2 = mag; } // if (mag > 0.95) std::cout << " mag " << mag << " max " << max << " vec " << vec << " ind " << index // << // std::endl; /** FIXME need weights between metrics */ RealType normalizedWeight = this->m_SimilarityMetrics[metricCount]->GetWeightScalar() / sumWeights; // RealType weight = this->m_SimilarityMetrics[metricCount]->GetWeightImage()->GetPixel( diter.GetIndex() ); if( ispointsetmetric ) { VectorType intensityupdate = dIter.Get(); VectorType lmupdate = vec; TReal lmag = 0; for( unsigned int li = 0; li < ImageDimension; li++ ) { lmag += (lmupdate[li] / spacing[li]) * (lmupdate[li] / spacing[li]); } lmag = sqrt(lmag); TReal modi = 1; if( lmag > 1 ) { modi = 0; } else { modi = 1.0 - lmag; } TReal iwt = 1 * modi; TReal lmwt = normalizedWeight; VectorType totalv = intensityupdate * iwt + lmupdate * lmwt; dIter.Set(totalv); } else { dIter.Set(dIter.Get() + vec * normalizedWeight); } } if( totalUpdateInvField ) { Iterator invDIter(totalUpdateInvField, totalUpdateInvField->GetLargestPossibleRegion() ); mag = 0.0; max = 0.0; ct = 0; total = 0; for( invDIter.GoToBegin(); !invDIter.IsAtEnd(); ++invDIter ) { typename ImageType::IndexType index = invDIter.GetIndex(); VectorType vec = updateFieldInv->GetPixel(index); mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += vec[jj] / spacing[jj] * vec[jj] / spacing[jj]; } mag = sqrt(mag); // if (mag > 0. ) std::cout << " mag " << mag << " max " << max << " vec " << vec << std::endl; if( mag > max ) { max = mag; } ct++; total += mag; // std::cout << " mag " << mag << std::endl; } if( this->m_Debug ) { std::cout << "PRE MAX " << max << std::endl; } max2 = 0; if( max <= 0 ) { max = 1; } for( invDIter.GoToBegin(); !invDIter.IsAtEnd(); ++invDIter ) { typename ImageType::IndexType index = invDIter.GetIndex(); VectorType vec = updateFieldInv->GetPixel(index); vec = vec / max; mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += vec[jj] / spacing[jj] * vec[jj] / spacing[jj]; } mag = sqrt(mag); if( mag > max2 ) { max2 = mag; } // if (mag > 0.95) std::cout << " mag " << mag << " max " << max << " vec " << vec << " ind " << index // << // std::endl; /** FIXME need weights between metrics */ RealType normalizedWeight = this->m_SimilarityMetrics[metricCount]->GetWeightScalar() / sumWeights; // RealType weight = this->m_SimilarityMetrics[metricCount]->GetWeightImage()->GetPixel( diter.GetIndex() ); if( ispointsetmetric ) { VectorType intensityupdate = invDIter.Get(); VectorType lmupdate = vec; TReal lmag = 0; for( unsigned int li = 0; li < ImageDimension; li++ ) { lmag += (lmupdate[li] / spacing[li]) * (lmupdate[li] / spacing[li]); } lmag = sqrt(lmag); TReal modi = 1; if( lmag > 1 ) { modi = 0; } else { modi = 1.0 - lmag; } TReal iwt = 1 * modi; TReal lmwt = normalizedWeight; VectorType totalv = intensityupdate * iwt + lmupdate * lmwt; invDIter.Set(totalv); } else { invDIter.Set(invDIter.Get() + vec * normalizedWeight); } } } if( this->m_Debug ) { std::cout << "PO MAX " << max2 << " sz" << totalUpdateField->GetLargestPossibleRegion().GetSize() << std::endl; } } // this->SmoothDisplacementField( totalUpdateField,true); // if (totalUpdateInvField) this->SmoothDisplacementField( totalUpdateInvField,true); return totalUpdateField; } template typename ANTSImageRegistrationOptimizer::DisplacementFieldPointer ANTSImageRegistrationOptimizer ::ComputeUpdateFieldAlternatingMin(DisplacementFieldPointer fixedwarp, DisplacementFieldPointer movingwarp, PointSetPointer fpoints, PointSetPointer wpoints, DisplacementFieldPointer totalUpdateInvField, bool updateenergy) { ImagePointer mask = NULL; if( movingwarp && this->m_MaskImage ) { mask = this->WarpMultiTransform( this->m_ReferenceSpaceImage, this->m_MaskImage, NULL, movingwarp, false, this->m_FixedImageAffineTransform ); } else if( this->m_MaskImage ) { mask = this->SubsampleImage( this->m_MaskImage, this->m_ScaleFactor, this->m_MaskImage->GetOrigin(), this->m_MaskImage->GetDirection(), NULL); } if( !fixedwarp ) { std::cout << " NO F WARP " << std::endl; fixedwarp = this->m_DisplacementField; } // if ( !movingwarp) std::cout<< " NO M WARP " << std::endl; // / std::cout << " get upd field " << std::endl; typename ImageType::SpacingType spacing = fixedwarp->GetSpacing(); VectorType zero; zero.Fill(0); DisplacementFieldPointer updateField; DisplacementFieldPointer updateFieldInv; DisplacementFieldPointer totalUpdateField = AllocImage(fixedwarp, zero); RealType sumWeights = 0.0; for( unsigned int n = 0; n < this->m_SimilarityMetrics.size(); n++ ) { sumWeights += this->m_SimilarityMetrics[n]->GetWeightScalar(); } sumWeights = 1; // for ( unsigned int metricCount = 0; metricCount < this->m_SimilarityMetrics.size(); metricCount++ ) // { // MetricBaseTypePointer df = this->m_SimilarityMetrics[metricCount]->GetMetric(); // if (df->ThisIsAPointSetMetric()) hadpointsetmetric=true; // } // for ( unsigned int metricCount = 0; metricCount < this->m_SimilarityMetrics.size(); metricCount++ ) unsigned int metricCount = this->m_CurrentIteration % this->m_SimilarityMetrics.size(); { bool ispointsetmetric = false; /** build an update field */ if( true ) // this->m_SimilarityMetrics.size() == 1 ) { updateField = totalUpdateField; if( totalUpdateInvField ) { updateFieldInv = totalUpdateInvField; } } else { updateField = AllocImage(fixedwarp, zero); if( totalUpdateInvField ) { updateFieldInv = AllocImage(fixedwarp, zero); } } /** get the update */ typedef typename FiniteDifferenceFunctionType::NeighborhoodType NeighborhoodIteratorType; typedef ImageRegionIterator UpdateIteratorType; // TimeStepType timeStep; void *globalData; // std::cout << " B " << std::endl; // for each metric, warp the assoc. Images /** We loop Over This To Do MultiVariate */ /** FIXME really should pass an image list and then warp each one in turn then expand the update field to fit size of total deformation */ ImagePointer wmimage = NULL; if( fixedwarp ) { wmimage = this->WarpMultiTransform( this->m_ReferenceSpaceImage, this->m_SmoothMovingImages[metricCount], this->m_AffineTransform, fixedwarp, false, this->m_FixedImageAffineTransform ); } else { wmimage = this->SubsampleImage( this->m_SmoothMovingImages[metricCount], this->m_ScaleFactor, this->m_SmoothMovingImages[metricCount]->GetOrigin(), this->m_SmoothMovingImages[metricCount]->GetDirection(), NULL); } // std::cout << " C " << std::endl; ImagePointer wfimage = NULL; if( movingwarp ) { wfimage = this->WarpMultiTransform( this->m_ReferenceSpaceImage, this->m_SmoothFixedImages[metricCount], NULL, movingwarp, false, this->m_FixedImageAffineTransform ); } else { wfimage = this->SubsampleImage( this->m_SmoothFixedImages[metricCount], this->m_ScaleFactor, this->m_SmoothFixedImages[metricCount]->GetOrigin(), this->m_SmoothFixedImages[metricCount]->GetDirection(), NULL); } // std::cout << " D " << std::endl; /** MV Loop END -- Would have to collect update fields then add them * together somehow -- Would also have to eliminate the similarity * metric loop within ComputeUpdateField */ // Get the FiniteDifferenceFunction to use in calculations. MetricBaseTypePointer df = this->m_SimilarityMetrics[metricCount]->GetMetric(); df->SetFixedImage(wfimage); df->SetMovingImage(wmimage); if( df->ThisIsAPointSetMetric() ) { ispointsetmetric = true; } if( fpoints && ispointsetmetric ) { df->SetFixedPointSet(fpoints); } else if( ispointsetmetric ) { std::cout << "NO POINTS!! " << std::endl; } if( wpoints && ispointsetmetric ) { df->SetMovingPointSet(wpoints); } else if( ispointsetmetric ) { std::cout << "NO POINTS!! " << std::endl; } typename ImageType::SizeType radius = df->GetRadius(); df->InitializeIteration(); typename DisplacementFieldType::Pointer output = updateField; typedef NeighborhoodAlgorithm::ImageBoundaryFacesCalculator FaceCalculatorType; typedef typename FaceCalculatorType::FaceListType FaceListType; FaceCalculatorType faceCalculator; FaceListType faceList = faceCalculator(updateField, updateField->GetLargestPossibleRegion(), radius); typename FaceListType::iterator fIt = faceList.begin(); globalData = df->GetGlobalDataPointer(); // Process the non-boundary region. NeighborhoodIteratorType nD(radius, updateField, *fIt); UpdateIteratorType nU(updateField, *fIt); nD.GoToBegin(); nU.GoToBegin(); while( !nD.IsAtEnd() ) { bool oktosample = true; TReal maskprob = 1.0; if( mask ) { maskprob = mask->GetPixel( nD.GetIndex() ); if( maskprob > 1.0 ) { maskprob = 1.0; } if( maskprob < 0.1 ) { oktosample = false; } } if( oktosample ) { nU.Value() += df->ComputeUpdate(nD, globalData) * maskprob; if( totalUpdateInvField ) { typename ImageType::IndexType index = nD.GetIndex(); VectorType temp = df->ComputeUpdateInv(nD, globalData) * maskprob; updateFieldInv->SetPixel(index, temp); } // else nU.Value() -= df->ComputeUpdateInv(nD, globalData)*maskprob; ++nD; ++nU; } else { ++nD; ++nU; } } if( updateenergy ) { this->m_LastEnergy[metricCount] = this->m_Energy[metricCount]; this->m_Energy[metricCount] = df->GetEnergy(); // *this->m_SimilarityMetrics[metricCount]->GetWeightScalar()/sumWeights; } // smooth the fields // if (!ispointsetmetric || ImageDimension == 2 ){ this->SmoothDisplacementField(updateField, true); if( updateFieldInv ) { this->SmoothDisplacementField(updateFieldInv, true); } // } /* else // use another strategy -- exact lm? / something like Laplacian { TReal tmag=0; for (unsigned int ff=0; ff<5; ff++) { tmag=0; this->SmoothDisplacementField(updateField,true); if (updateFieldInv) this->SmoothDisplacementField(updateFieldInv,true); nD.GoToBegin(); nU.GoToBegin(); while( !nD.IsAtEnd() ) { typename ImageType::IndexType index=nD.GetIndex(); bool oktosample=true; TReal maskprob=1.0; if (mask) { maskprob=mask->GetPixel( nD.GetIndex() ); if (maskprob > 1.0) maskprob=1.0; if ( maskprob < 0.1) oktosample=false; } VectorType F1; F1.Fill(0); VectorType F2; F2.Fill(0); if ( oktosample ) { F1 = df->ComputeUpdate(nD, globalData)*maskprob; if (totalUpdateInvField) { F2 = df->ComputeUpdateInv(nD, globalData)*maskprob; } ++nD; ++nU; } else { ++nD; ++nU; } // compute mags of F1 and F2 -- if large enough, reset them TReal f1mag=0,f2mag=0,umag=0; for (unsigned int dim=0; dimGetPixel(index)[dim]/spacing[dim]*updateField->GetPixel(index)[dim]/spacing[dim]; } f1mag=sqrt(f1mag); f2mag=sqrt(f2mag); umag=sqrt(umag); if ( f1mag > 0.05 ) updateField->SetPixel(index,F1); if ( f2mag > 0.05 ) updateFieldInv->SetPixel(index,F2); tmag+=umag; } // std::cout << " total mag " << tmag << std::endl; } //smooth the total field this->SmoothDisplacementField(updateField,true); if (updateFieldInv) this->SmoothDisplacementField(updateFieldInv,true); } */ // normalize update field then add to total field typedef ImageRegionIteratorWithIndex Iterator; Iterator dIter(totalUpdateField, totalUpdateField->GetLargestPossibleRegion() ); TReal mag = 0.0; TReal max = 0.0; unsigned long ct = 0; TReal total = 0; for( dIter.GoToBegin(); !dIter.IsAtEnd(); ++dIter ) { typename ImageType::IndexType index = dIter.GetIndex(); VectorType vec = updateField->GetPixel(index); mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += vec[jj] / spacing[jj] * vec[jj] / spacing[jj]; } mag = sqrt(mag); // if (mag > 0. ) std::cout << " mag " << mag << " max " << max << " vec " << vec << std::endl; if( mag > max ) { max = mag; } ct++; total += mag; // std::cout << " mag " << mag << std::endl; } if( this->m_Debug ) { std::cout << "PRE MAX " << max << std::endl; } TReal max2 = 0; if( max <= 0 ) { max = 1; } for( dIter.GoToBegin(); !dIter.IsAtEnd(); ++dIter ) { typename ImageType::IndexType index = dIter.GetIndex(); VectorType vec = updateField->GetPixel(index); vec = vec / max; mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += vec[jj] / spacing[jj] * vec[jj] / spacing[jj]; } mag = sqrt(mag); if( mag > max2 ) { max2 = mag; } // if (mag > 0.95) std::cout << " mag " << mag << " max " << max << " vec " << vec << " ind " << index // << // std::endl; /** FIXME need weights between metrics */ RealType normalizedWeight = this->m_SimilarityMetrics[metricCount]->GetWeightScalar() / sumWeights; /* RealType weight = this->m_SimilarityMetrics[metricCount]->GetWeightImage()->GetPixel( diter.GetIndex() ); if (ispointsetmetric ) { VectorType intensityupdate=dIter.Get(); VectorType lmupdate=vec; TReal lmag=0; for (unsigned int li=0; li 1) modi=0; else modi=1.0-lmag; TReal iwt=1*modi; TReal lmwt=normalizedWeight; VectorType totalv=intensityupdate*iwt+lmupdate*lmwt; dIter.Set(totalv); } else */ dIter.Set(dIter.Get() + vec * normalizedWeight); } if( totalUpdateInvField ) { Iterator invDIter(totalUpdateInvField, totalUpdateInvField->GetLargestPossibleRegion() ); mag = 0.0; max = 0.0; ct = 0; total = 0; for( invDIter.GoToBegin(); !invDIter.IsAtEnd(); ++invDIter ) { typename ImageType::IndexType index = invDIter.GetIndex(); VectorType vec = updateFieldInv->GetPixel(index); mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += vec[jj] / spacing[jj] * vec[jj] / spacing[jj]; } mag = sqrt(mag); // if (mag > 0. ) std::cout << " mag " << mag << " max " << max << " vec " << vec << std::endl; if( mag > max ) { max = mag; } ct++; total += mag; // std::cout << " mag " << mag << std::endl; } if( this->m_Debug ) { std::cout << "PRE MAX " << max << std::endl; } max2 = 0; if( max <= 0 ) { max = 1; } for( invDIter.GoToBegin(); !invDIter.IsAtEnd(); ++invDIter ) { typename ImageType::IndexType index = invDIter.GetIndex(); VectorType vec = updateFieldInv->GetPixel(index); vec = vec / max; mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += vec[jj] / spacing[jj] * vec[jj] / spacing[jj]; } mag = sqrt(mag); if( mag > max2 ) { max2 = mag; } // if (mag > 0.95) std::cout << " mag " << mag << " max " << max << " vec " << vec << " ind " << index // << // std::endl; /** FIXME need weights between metrics */ RealType normalizedWeight = this->m_SimilarityMetrics[metricCount]->GetWeightScalar() / sumWeights; // RealType weight = this->m_SimilarityMetrics[metricCount]->GetWeightImage()->GetPixel( diter.GetIndex() ); /* if (ispointsetmetric ) { VectorType intensityupdate=invDIter.Get(); VectorType lmupdate=vec; TReal lmag=0; for (unsigned int li=0; li 1) modi=0; else modi=1.0-lmag; TReal iwt=1*modi; TReal lmwt=normalizedWeight; VectorType totalv=intensityupdate*iwt+lmupdate*lmwt; invDIter.Set(totalv); } else */ invDIter.Set(invDIter.Get() + vec * normalizedWeight); } } if( this->m_Debug ) { std::cout << "PO MAX " << max2 << " sz" << totalUpdateField->GetLargestPossibleRegion().GetSize() << std::endl; } } // this->SmoothDisplacementField( totalUpdateField,true); // if (totalUpdateInvField) this->SmoothDisplacementField( totalUpdateInvField,true); return totalUpdateField; } template void ANTSImageRegistrationOptimizer ::DiffeomorphicExpRegistrationUpdate(ImagePointer /* fixedImage */, ImagePointer movingImage, PointSetPointer fpoints, PointSetPointer mpoints) { // this function computes a velocity field that --- when composed with itself --- optimizes the registration // solution. // it's very different than the Exp approach used by DiffeomorphicDemons which just composes small deformation over // time. // DiffDem cannot reconstruct the path between images without recomputing the registration. // DiffDem also cannot create an inverse mapping. /** FIXME really should pass an image list and then warp each one in turn then expand the update field to fit size of total deformation */ VectorType zero; zero.Fill(0); DisplacementFieldPointer totalUpdateField = ITK_NULLPTR; DisplacementFieldPointer totalField = this->m_DisplacementField; /** generate phi and phi gradient */ // TReal timestep=1.0/(TReal)this->m_NTimeSteps; // for (unsigned int nts=0; nts<=this->m_NTimeSteps; nts+=this->m_NTimeSteps) unsigned int nts = (unsigned int)this->m_NTimeSteps; { DisplacementFieldPointer diffmap = this->IntegrateConstantVelocity(totalField, nts, 1); // DisplacementFieldPointer invdiffmap = this->IntegrateConstantVelocity(totalField,(unsigned int)( // this->m_NTimeSteps)-nts, (-1.)); ImagePointer wfimage, wmimage; PointSetPointer wfpoints = ITK_NULLPTR, wmpoints = ITK_NULLPTR; AffineTransformPointer aff = this->m_AffineTransform; if( mpoints ) { // need full inverse map DisplacementFieldPointer tinvdiffmap = this->IntegrateConstantVelocity(totalField, nts, (-1.) ); wmpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, movingImage, mpoints, aff, tinvdiffmap, true, this->m_FixedImageAffineTransform ); } DisplacementFieldPointer updateField = this->ComputeUpdateField( diffmap, ITK_NULLPTR, fpoints, wmpoints); // updateField = this->IntegrateConstantVelocity( updateField, nts, timestep); TReal maxl = this->MeasureDeformation(updateField); if( maxl <= 0 ) { maxl = 1; } typedef ImageRegionIteratorWithIndex Iterator; Iterator dIter(updateField, updateField->GetLargestPossibleRegion() ); for( dIter.GoToBegin(); !dIter.IsAtEnd(); ++dIter ) { dIter.Set( dIter.Get() * this->m_GradstepAltered / maxl); } this->ComposeDiffs(updateField, totalField, totalField, 1); /* TReal maxl= this->MeasureDeformation(updateField); if (maxl <= 0) maxl=1; typedef ImageRegionIteratorWithIndex Iterator; Iterator dIter(updateField,updateField->GetLargestPossibleRegion() ); for( dIter.GoToBegin(); !dIter.IsAtEnd(); ++dIter ) { dIter.Set( dIter.Get()*this->m_GradstepAltered/this->m_NTimeSteps ); totalField->SetPixel(dIter.GetIndex(), dIter.Get() + totalField->GetPixel(dIter.GetIndex()) ); } */ } this->SmoothDisplacementField(totalField, false); return; } template void ANTSImageRegistrationOptimizer ::GreedyExpRegistrationUpdate(ImagePointer /* fixedImage */, ImagePointer /* movingImage */, PointSetPointer fpoints, PointSetPointer /* mpoints */) { // similar approach to christensen 96 and diffeomorphic demons VectorType zero; zero.Fill(0); DisplacementFieldPointer totalUpdateField = ITK_NULLPTR; // we compose the update with this field. DisplacementFieldPointer totalField = this->m_DisplacementField; TReal timestep = 1.0 / (TReal) this->m_NTimeSteps; unsigned int nts = (unsigned int)this->m_NTimeSteps; ImagePointer wfimage, wmimage; PointSetPointer wfpoints = ITK_NULLPTR, wmpoints = ITK_NULLPTR; AffineTransformPointer aff = this->m_AffineTransform; DisplacementFieldPointer updateField = this->ComputeUpdateField( totalField, ITK_NULLPTR, fpoints, wmpoints); updateField = this->IntegrateConstantVelocity( updateField, nts, timestep); TReal maxl = this->MeasureDeformation(updateField); if( maxl <= 0 ) { maxl = 1; } typedef ImageRegionIteratorWithIndex Iterator; Iterator dIter(updateField, updateField->GetLargestPossibleRegion() ); for( dIter.GoToBegin(); !dIter.IsAtEnd(); ++dIter ) { dIter.Set( dIter.Get() * this->m_GradstepAltered / maxl ); } this->ComposeDiffs(updateField, totalField, totalField, 1); // maxl= this->MeasureDeformation(totalField); // std::cout << " maxl " << maxl << " gsa " << this->m_GradstepAltered << std::endl; // totalField=this->CopyDisplacementField(totalUpdateField); this->SmoothDisplacementField(totalField, false); return; } // added by songgang template typename ANTSImageRegistrationOptimizer::AffineTransformPointer ANTSImageRegistrationOptimizer::AffineOptimization(OptAffineType & affine_opt) { // typedef itk::Image TempImageType; // typename TempImageType::Pointer fixedImage = TempImageType::New(); // typename TempImageType::Pointer movingImage = TempImageType::New(); ImagePointer fixedImage; ImagePointer movingImage; /** FIXME -- here we assume the metrics all have the same image */ fixedImage = this->m_SimilarityMetrics[0]->GetFixedImage(); movingImage = this->m_SimilarityMetrics[0]->GetMovingImage(); // TODO: get mask image pointer / type for mask image if( this->m_MaskImage ) { affine_opt.mask_fixed = this->m_MaskImage; } // AffineTransformPointer &transform_init = affine_opt.transform_initial; // ImagePointer &maskImage = affine_opt.mask_fixed; AffineTransformPointer transform = AffineTransformType::New(); // std::cout << "In AffineOptimization: transform_init.IsNotNull()=" << transform_init.IsNotNull() << // std::endl; // compute_single_affine_transform(fixedImage, movingImage, maskImage, transform, transform_init); // OptAffine opt; ComputeSingleAffineTransform(fixedImage, movingImage, affine_opt, transform); return transform; } template void ANTSImageRegistrationOptimizer ::SyNRegistrationUpdate(ImagePointer fixedImage, ImagePointer movingImage, PointSetPointer fpoints, PointSetPointer mpoints) { VectorType zero; zero.Fill(0); DisplacementFieldPointer totalUpdateField, totalUpdateInvField = AllocImage(this->m_DisplacementField, zero); if( !this->m_SyNF ) { std::cout << " Allocating " << std::endl; this->m_SyNF = this->CopyDisplacementField(totalUpdateInvField); this->m_SyNFInv = this->CopyDisplacementField(this->m_SyNF); this->m_SyNM = this->CopyDisplacementField(totalUpdateInvField); this->m_SyNMInv = this->CopyDisplacementField(this->m_SyNF); // this->m_Debug=true; if( this->m_Debug ) { std::cout << " SyNFInv" << this->m_SyNFInv->GetLargestPossibleRegion().GetSize() << std::endl; } if( this->m_Debug ) { std::cout << " t updIf " << totalUpdateInvField->GetLargestPossibleRegion().GetSize() << std::endl; } if( this->m_Debug ) { std::cout << " synf " << this->m_SyNF->GetLargestPossibleRegion().GetSize() << std::endl; } // this->m_Debug=false; std::cout << " Allocating Done " << std::endl; } if( !this->m_SyNF ) { std::cout << " F'D UP " << std::endl; } PointSetPointer wfpoints = ITK_NULLPTR, wmpoints = ITK_NULLPTR; AffineTransformPointer aff = this->m_AffineTransform; AffineTransformPointer affinverse = ITK_NULLPTR; if( aff ) { affinverse = AffineTransformType::New(); aff->GetInverse(affinverse); } if( mpoints ) { wmpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, movingImage, mpoints, aff, this->m_SyNM, true, this->m_FixedImageAffineTransform ); } if( fpoints ) { // need full inverse map wfpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, fixedImage, fpoints, ITK_NULLPTR, this->m_SyNF, false, this->m_FixedImageAffineTransform ); } // syncom totalUpdateField = this->ComputeUpdateField(this->m_SyNMInv, this->m_SyNFInv, wfpoints, wmpoints, totalUpdateInvField); this->ComposeDiffs(this->m_SyNF, totalUpdateField, this->m_SyNF, this->m_GradstepAltered); this->ComposeDiffs(this->m_SyNM, totalUpdateInvField, this->m_SyNM, this->m_GradstepAltered); if( this->m_TotalSmoothingparam > 0 || this->m_TotalSmoothingMeshSize[0] > 0 ) { this->SmoothDisplacementField( this->m_SyNF, false); this->SmoothDisplacementField( this->m_SyNM, false); } this->InvertField(this->m_SyNF, this->m_SyNFInv); this->InvertField(this->m_SyNM, this->m_SyNMInv); this->InvertField(this->m_SyNFInv, this->m_SyNF); this->InvertField(this->m_SyNMInv, this->m_SyNM); // std::cout << " F " << this->MeasureDeformation(this->m_SyNF) << " F1 " << // this->MeasureDeformation(this->m_SyNFInv) << std::endl; // std::cout << " M " << this->MeasureDeformation(this->m_SyNM) << " M1 " << // this->MeasureDeformation(this->m_SyNMInv) << std::endl; return; } template void ANTSImageRegistrationOptimizer ::SyNExpRegistrationUpdate(ImagePointer fixedImage, ImagePointer movingImage, PointSetPointer fpoints, PointSetPointer mpoints) { std::cout << " SyNEX"; typename ImageType::SpacingType spacing = fixedImage->GetSpacing(); VectorType zero; zero.Fill(0); DisplacementFieldPointer totalUpdateField, totalUpdateInvField = AllocImage(this->m_DisplacementField, zero); if( !this->m_SyNF ) { std::cout << " Allocating " << std::endl; this->m_SyNF = this->CopyDisplacementField(totalUpdateInvField); this->m_SyNFInv = this->CopyDisplacementField(this->m_SyNF); this->m_SyNM = this->CopyDisplacementField(totalUpdateInvField); this->m_SyNMInv = this->CopyDisplacementField(this->m_SyNF); std::cout << " Allocating Done " << std::endl; } if( !this->m_SyNF ) { std::cout << " F'D UP " << std::endl; } ImagePointer wfimage, wmimage; PointSetPointer wfpoints = NULL, wmpoints = NULL; AffineTransformPointer aff = this->m_AffineTransform; AffineTransformPointer affinverse = NULL; // here, SyNF holds the moving velocity field, SyNM holds the fixed // velocity field and we integrate both to generate the inv/fwd fields TReal timestep = 1.0 / (TReal) this->m_NTimeSteps; unsigned int nts = this->m_NTimeSteps; DisplacementFieldPointer fdiffmap = this->IntegrateConstantVelocity(this->m_SyNF, nts, 1); this->m_SyNFInv = this->IntegrateConstantVelocity(this->m_SyNF, nts, (-1.) ); DisplacementFieldPointer mdiffmap = this->IntegrateConstantVelocity(this->m_SyNM, nts, 1); this->m_SyNMInv = this->IntegrateConstantVelocity(this->m_SyNM, nts, (-1.) ); if( aff ) { affinverse = AffineTransformType::New(); aff->GetInverse(affinverse); } if( mpoints ) { wmpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, movingImage, mpoints, aff, this->m_SyNM, true, this->m_FixedImageAffineTransform ); } if( fpoints ) { // need full inverse map wfpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, fixedImage, fpoints, NULL, this->m_SyNF, false, this->m_FixedImageAffineTransform ); } totalUpdateField = this->ComputeUpdateField( this->m_SyNMInv, this->m_SyNFInv, wfpoints, wmpoints, totalUpdateInvField); // then addd typedef ImageRegionIteratorWithIndex Iterator; Iterator dIter(this->m_SyNF, this->m_SyNF->GetLargestPossibleRegion() ); TReal max = 0, max2 = 0; for( dIter.GoToBegin(); !dIter.IsAtEnd(); ++dIter ) { typename ImageType::IndexType index = dIter.GetIndex(); VectorType vecf = totalUpdateField->GetPixel(index); VectorType vecm = totalUpdateInvField->GetPixel(index); dIter.Set(dIter.Get() + vecf * this->m_GradstepAltered); this->m_SyNM->SetPixel( index, this->m_SyNM->GetPixel( index ) + vecm * this->m_GradstepAltered); // min field difference => geodesic => DV/dt=0 TReal geowt1 = 0.99; TReal geowt2 = 1.0 - geowt1; VectorType synmv = this->m_SyNM->GetPixel( index ); VectorType synfv = this->m_SyNF->GetPixel( index ); this->m_SyNM->SetPixel( index, synmv * geowt1 - synfv * geowt2); this->m_SyNF->SetPixel( index, synfv * geowt1 - synmv * geowt2); } if( this->m_TotalSmoothingparam > 0 || this->m_TotalSmoothingMeshSize[0] > 0 ) { this->SmoothDisplacementField( this->m_SyNF, false); this->SmoothDisplacementField( this->m_SyNM, false); } // std::cout << " TUF " << this->MeasureDeformation(this->m_SyNF) << " TUM " << // this->MeasureDeformation(this->m_SyNM) << std::endl; return; } template void ANTSImageRegistrationOptimizer ::UpdateTimeVaryingVelocityFieldWithSyNFandSyNM() { typedef TimeVaryingVelocityFieldType tvt; bool generatetvfield = false; if( !this->m_TimeVaryingVelocity ) { generatetvfield = true; } else { for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { if( this->m_CurrentDomainSize[jj] != this->m_TimeVaryingVelocity->GetLargestPossibleRegion().GetSize()[jj] ) { generatetvfield = true; } } } VectorType zero; zero.Fill(0); if( generatetvfield ) { typename tvt::RegionType gregion; typename tvt::SizeType gsize; typename tvt::SpacingType gspace; typename tvt::PointType gorigin; gorigin.Fill(0); for( unsigned int dim = 0; dim < TDimension; dim++ ) { gsize[dim] = this->m_CurrentDomainSize[dim]; gspace[dim] = this->m_CurrentDomainSpacing[dim]; gorigin[dim] = this->m_CurrentDomainOrigin[dim]; } gsize[TDimension] = (long unsigned int) this->m_NTimeSteps; gspace[TDimension] = 1; gregion.SetSize(gsize); /** The TV Field has the direction of the sub-image -- the time domain has identity transform */ typename tvt::DirectionType iddir; iddir.Fill(0); iddir[ImageDimension][ImageDimension] = 1; for( unsigned int i = 0; i < ImageDimension + 1; i++ ) { for( unsigned int j = 0; j < ImageDimension + 1; j++ ) { // if (i == j) iddir[i][j]=1; if( i < ImageDimension && j < ImageDimension ) { iddir[i][j] = this->GetDisplacementField()->GetDirection()[i][j]; } } } this->m_TimeVaryingVelocity = AllocImage(gregion, gspace, gorigin, iddir, zero); } typedef itk::ImageRegionIteratorWithIndex TVFieldIterator; TVFieldIterator m_FieldIter( this->m_TimeVaryingVelocity, this->m_TimeVaryingVelocity->GetLargestPossibleRegion() ); for( m_FieldIter.GoToBegin(); !m_FieldIter.IsAtEnd(); ++m_FieldIter ) { typename tvt::IndexType velind = m_FieldIter.GetIndex(); IndexType ind; for( unsigned int j = 0; j < ImageDimension; j++ ) { ind[j] = velind[j]; } if( velind[ImageDimension] == 0 ) { VectorType vel = this->m_SyNF->GetPixel(ind); m_FieldIter.Set(vel); } else if( velind[ImageDimension] == 1 ) { VectorType vel = this->m_SyNM->GetPixel(ind) * (-1.0); m_FieldIter.Set(vel); } } // std::cout <<" ALlocated TV F "<< std::endl; } template void ANTSImageRegistrationOptimizer ::CopyOrAddToVelocityField( TimeVaryingVelocityFieldPointer velocity, DisplacementFieldPointer update1, DisplacementFieldPointer update2, TReal timept) { typedef TimeVaryingVelocityFieldType tvt; VectorType zero; typedef itk::ImageRegionIteratorWithIndex TVFieldIterator; int tpupdate = (unsigned int) ( ( (TReal) this->m_NTimeSteps - 1.0) * timept + 0.5); // std::cout <<" add to " << tpupdate << std::endl; TReal tmag = 0; TVFieldIterator m_FieldIter(velocity, velocity->GetLargestPossibleRegion() ); for( m_FieldIter.GoToBegin(); !m_FieldIter.IsAtEnd(); ++m_FieldIter ) { typename tvt::IndexType velind = m_FieldIter.GetIndex(); typename ImageType::IndexType ind; for( unsigned int j = 0; j < ImageDimension; j++ ) { ind[j] = velind[j]; } if( velind[ImageDimension] == tpupdate && update1 ) { VectorType vel = update1->GetPixel(ind); TReal mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += vel[jj] * vel[jj]; } tmag += sqrt(mag); m_FieldIter.Set(vel + m_FieldIter.Get() ); } if( velind[ImageDimension] == tpupdate && update2 ) { VectorType vel = update2->GetPixel(ind) * (-1); m_FieldIter.Set(vel + m_FieldIter.Get() ); } } // std::cout << " tmag " << tmag << std::endl; } template void ANTSImageRegistrationOptimizer ::SyNTVRegistrationUpdate(ImagePointer fixedImage, ImagePointer movingImage, PointSetPointer fpoints, PointSetPointer mpoints) { VectorType zero; zero.Fill(0); DisplacementFieldPointer totalUpdateField; DisplacementFieldPointer totalUpdateInvField = AllocImage(this->m_DisplacementField, zero); if( !this->m_SyNF ) { std::cout << " Allocating " << std::endl; this->m_SyNF = this->CopyDisplacementField(totalUpdateInvField); this->m_SyNFInv = this->CopyDisplacementField(this->m_SyNF); this->m_SyNM = this->CopyDisplacementField(totalUpdateInvField); this->m_SyNMInv = this->CopyDisplacementField(this->m_SyNF); std::cout << " Allocating Done " << std::endl; } if( !this->m_SyNF ) { std::cout << " F'D UP " << std::endl; } ImagePointer wfimage, wmimage; PointSetPointer wfpoints = ITK_NULLPTR, wmpoints = ITK_NULLPTR; AffineTransformPointer aff = this->m_AffineTransform; AffineTransformPointer affinverse = ITK_NULLPTR; typedef ImageRegionIteratorWithIndex Iterator; Iterator dIter(this->m_SyNF, this->m_SyNF->GetLargestPossibleRegion() ); // here, SyNF holds the moving velocity field, SyNM holds the fixed // velocity field and we integrate both to generate the inv/fwd fields typename JacobianFunctionType::Pointer jfunction = JacobianFunctionType::New(); TReal lot = 0, hit = 0.5; TReal lot2 = 1.0; this->UpdateTimeVaryingVelocityFieldWithSyNFandSyNM(); // sets tvt to SyNF and SyNM -- works only for 2 time points! this->m_SyNFInv = this->IntegrateVelocity(hit, lot); this->m_SyNMInv = this->IntegrateVelocity(hit, lot2); if( aff ) { affinverse = AffineTransformType::New(); aff->GetInverse(affinverse); } if( mpoints ) { /**FIXME -- NEED INTEGRATION FOR POINTS ONLY -- warp landmarks for * tv-field */ // std::cout <<" aff " << std::endl; /** NOte, totalUpdateInvField is filled with zeroes! -- we only want affine mapping */ wmpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, movingImage, mpoints, aff, totalUpdateInvField, true, ITK_NULLPTR ); DisplacementFieldPointer mdiffmap = this->IntegrateLandmarkSetVelocity(lot2, hit, wmpoints, movingImage); wmpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, movingImage, wmpoints, ITK_NULLPTR, mdiffmap, true, ITK_NULLPTR ); } if( fpoints ) { // need full inverse map wfpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, movingImage, fpoints, ITK_NULLPTR, totalUpdateInvField, true, this->m_FixedImageAffineTransform ); DisplacementFieldPointer fdiffmap = this->IntegrateLandmarkSetVelocity(lot, hit, wfpoints, fixedImage); wfpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, fixedImage, wfpoints, ITK_NULLPTR, fdiffmap, false, ITK_NULLPTR ); } totalUpdateField = this->ComputeUpdateField( this->m_SyNMInv, this->m_SyNFInv, wfpoints, wmpoints, totalUpdateInvField, true); for( dIter.GoToBegin(); !dIter.IsAtEnd(); ++dIter ) { typename ImageType::IndexType index = dIter.GetIndex(); VectorType vecf = totalUpdateField->GetPixel(index) * 1; VectorType vecm = totalUpdateInvField->GetPixel(index); // update time components 1 & 2 this->m_SyNF->SetPixel( index, this->m_SyNF->GetPixel( index ) + vecf * this->m_GradstepAltered); this->m_SyNM->SetPixel( index, this->m_SyNM->GetPixel( index ) + vecm * this->m_GradstepAltered); // min field difference => geodesic => DV/dt=0 TReal geowt1 = 0.95; TReal geowt2 = 1.0 - geowt1; VectorType synmv = this->m_SyNM->GetPixel( index ); VectorType synfv = this->m_SyNF->GetPixel( index ); this->m_SyNM->SetPixel( index, synmv * geowt1 - synfv * geowt2); this->m_SyNF->SetPixel( index, synfv * geowt1 - synmv * geowt2); } if( this->m_TotalSmoothingparam > 0 || this->m_TotalSmoothingMeshSize[0] > 0 ) { // smooth time components separately this->SmoothDisplacementField( this->m_SyNF, false); this->SmoothDisplacementField( this->m_SyNM, false); } return; } template void ANTSImageRegistrationOptimizer ::DiReCTUpdate(ImagePointer fixedImage, ImagePointer movingImage, PointSetPointer fpoints, PointSetPointer mpoints) { typedef TimeVaryingVelocityFieldType tvt; TimeVaryingVelocityFieldPointer velocityUpdate = ITK_NULLPTR; VectorType zero; zero.Fill(0); DisplacementFieldPointer totalUpdateInvField = AllocImage(this->m_DisplacementField, zero); unsigned long numpx = this->m_DisplacementField->GetBufferedRegion().GetNumberOfPixels(); bool generatetvfield = false; bool enlargefield = false; if( !this->m_TimeVaryingVelocity ) { generatetvfield = true; } else { for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { if( this->m_CurrentDomainSize[jj] != this->m_TimeVaryingVelocity->GetLargestPossibleRegion().GetSize()[jj] ) { enlargefield = true; } } } velocityUpdate = tvt::New(); typename tvt::RegionType gregion; typename tvt::SizeType gsize; typename tvt::SpacingType gspace; typename tvt::PointType gorigin; gorigin.Fill(0); for( unsigned int dim = 0; dim < TDimension; dim++ ) { gsize[dim] = this->m_CurrentDomainSize[dim]; gspace[dim] = this->m_CurrentDomainSpacing[dim]; gorigin[dim] = this->m_CurrentDomainOrigin[dim]; } if( this->m_NTimeSteps < 2 ) { this->m_NTimeSteps = 2; } gsize[TDimension] = (unsigned long) this->m_NTimeSteps; TReal hitstep = 1.0 / ( (TReal) this->m_NTimeSteps - 1); gspace[TDimension] = 1; gregion.SetSize(gsize); velocityUpdate->SetSpacing( gspace ); velocityUpdate->SetOrigin( gorigin ); /** The TV Field has the direction of the sub-image -- the time domain has identity transform */ typename tvt::DirectionType iddir; iddir.Fill(0); iddir[ImageDimension][ImageDimension] = 1; for( unsigned int i = 0; i < ImageDimension + 1; i++ ) { for( unsigned int j = 0; j < ImageDimension + 1; j++ ) { // if (i == j) iddir[i][j]=1; if( i < ImageDimension && j < ImageDimension ) { iddir[i][j] = this->GetDisplacementField()->GetDirection()[i][j]; } } } velocityUpdate->SetDirection( iddir ); velocityUpdate->SetLargestPossibleRegion(gregion); velocityUpdate->SetRequestedRegion( gregion); velocityUpdate->SetBufferedRegion( gregion ); velocityUpdate->Allocate(); velocityUpdate->FillBuffer(zero); if( generatetvfield ) { this->m_TimeVaryingVelocity = tvt::New(); this->m_TimeVaryingVelocity->SetSpacing( gspace ); this->m_TimeVaryingVelocity->SetOrigin( gorigin ); this->m_TimeVaryingVelocity->SetDirection( iddir ); this->m_TimeVaryingVelocity->SetLargestPossibleRegion(gregion); this->m_TimeVaryingVelocity->SetRequestedRegion( gregion); this->m_TimeVaryingVelocity->SetBufferedRegion( gregion ); this->m_TimeVaryingVelocity->Allocate(); this->m_TimeVaryingVelocity->FillBuffer(zero); /* this->m_LastTimeVaryingVelocity=tvt::New(); this->m_LastTimeVaryingVelocity->SetSpacing( gspace ); this->m_LastTimeVaryingVelocity->SetOrigin( gorigin ); this->m_LastTimeVaryingVelocity->SetDirection( iddir ); this->m_LastTimeVaryingVelocity->SetLargestPossibleRegion(gregion); this->m_LastTimeVaryingVelocity->SetRequestedRegion( gregion); this->m_LastTimeVaryingVelocity->SetBufferedRegion( gregion ); this->m_LastTimeVaryingVelocity->Allocate(); this->m_LastTimeVaryingVelocity->FillBuffer(zero); */ this->m_LastTimeVaryingUpdate = tvt::New(); this->m_LastTimeVaryingUpdate->SetSpacing( gspace ); this->m_LastTimeVaryingUpdate->SetOrigin( gorigin ); this->m_LastTimeVaryingUpdate->SetDirection( iddir ); this->m_LastTimeVaryingUpdate->SetLargestPossibleRegion(gregion); this->m_LastTimeVaryingUpdate->SetRequestedRegion( gregion); this->m_LastTimeVaryingUpdate->SetBufferedRegion( gregion ); this->m_LastTimeVaryingUpdate->Allocate(); this->m_LastTimeVaryingUpdate->FillBuffer(zero); } else if( enlargefield ) { this->m_TimeVaryingVelocity = this->ExpandVelocity(); this->m_TimeVaryingVelocity->SetSpacing(gspace); this->m_TimeVaryingVelocity->SetOrigin(gorigin); /* this->m_LastTimeVaryingVelocity=tvt::New(); this->m_LastTimeVaryingVelocity->SetSpacing( gspace ); this->m_LastTimeVaryingVelocity->SetOrigin( gorigin ); this->m_LastTimeVaryingVelocity->SetDirection( iddir ); this->m_LastTimeVaryingVelocity->SetLargestPossibleRegion(gregion); this->m_LastTimeVaryingVelocity->SetRequestedRegion( gregion); this->m_LastTimeVaryingVelocity->SetBufferedRegion( gregion ); this->m_LastTimeVaryingVelocity->Allocate(); this->m_LastTimeVaryingVelocity->FillBuffer(zero);*/ this->m_LastTimeVaryingUpdate = tvt::New(); this->m_LastTimeVaryingUpdate->SetSpacing( gspace ); this->m_LastTimeVaryingUpdate->SetOrigin( gorigin ); this->m_LastTimeVaryingUpdate->SetDirection( iddir ); this->m_LastTimeVaryingUpdate->SetLargestPossibleRegion(gregion); this->m_LastTimeVaryingUpdate->SetRequestedRegion( gregion); this->m_LastTimeVaryingUpdate->SetBufferedRegion( gregion ); this->m_LastTimeVaryingUpdate->Allocate(); this->m_LastTimeVaryingUpdate->FillBuffer(zero); } if( !this->m_SyNF ) { std::cout << " Allocating " << std::endl; this->m_SyNF = this->CopyDisplacementField(totalUpdateInvField); this->m_SyNFInv = this->CopyDisplacementField(this->m_SyNF); this->m_SyNM = this->CopyDisplacementField(totalUpdateInvField); this->m_SyNMInv = this->CopyDisplacementField(this->m_SyNF); std::cout << " Allocating Done " << std::endl; } if( !this->m_SyNF ) { std::cout << " F'D UP " << std::endl; } ImagePointer wfimage, wmimage; PointSetPointer wfpoints = ITK_NULLPTR, wmpoints = ITK_NULLPTR; AffineTransformPointer aff = this->m_AffineTransform; AffineTransformPointer affinverse = ITK_NULLPTR; typedef ImageRegionIteratorWithIndex Iterator; Iterator dIter(this->m_SyNF, this->m_SyNF->GetLargestPossibleRegion() ); // here, SyNF holds the moving velocity field, SyNM holds the fixed // velocity field and we integrate both to generate the inv/fwd fields typename JacobianFunctionType::Pointer jfunction = JacobianFunctionType::New(); TReal lot = 0, lot2 = 1.0; unsigned int fct = 100; for( TReal hit = 0; hit <= 1; hit = hit + hitstep ) { this->m_SyNFInv = this->IntegrateVelocity(hit, lot); this->m_SyNMInv = this->IntegrateVelocity(hit, lot2); if( false && this->m_CurrentIteration == 1 && this->m_SyNFInv ) { typedef itk::ImageFileWriter DisplacementFieldWriterType; typename DisplacementFieldWriterType::Pointer writer = DisplacementFieldWriterType::New(); std::ostringstream osstream; osstream << fct; fct++; std::string fnm = std::string("field1") + osstream.str() + std::string("warp.nii.gz"); writer->SetInput( this->m_SyNFInv ); writer->SetFileName( fnm.c_str() ); std::cout << " write " << fnm << std::endl; writer->Update(); // writer->SetInput( this->m_SyNMInv ); // writer->SetFileName( fnm2.c_str() ); // writer->Update(); } if( aff ) { affinverse = AffineTransformType::New(); aff->GetInverse(affinverse); } if( mpoints ) { /**FIXME -- NEED INTEGRATION FOR POINTS ONLY -- warp landmarks for * tv-field */ // std::cout <<" aff " << std::endl; /** NOte, totalUpdateInvField is filled with zeroes! -- we only want affine mapping */ wmpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, movingImage, mpoints, aff, totalUpdateInvField, true, ITK_NULLPTR ); DisplacementFieldPointer mdiffmap = this->IntegrateLandmarkSetVelocity(lot2, hit, wmpoints, movingImage); wmpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, movingImage, wmpoints, ITK_NULLPTR, mdiffmap, true, ITK_NULLPTR ); } if( fpoints ) { // need full inverse map wfpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, movingImage, fpoints, ITK_NULLPTR, totalUpdateInvField, true, this->m_FixedImageAffineTransform ); DisplacementFieldPointer fdiffmap = this->IntegrateLandmarkSetVelocity(lot, hit, wfpoints, fixedImage); wfpoints = this->WarpMultiTransform(this->m_ReferenceSpaceImage, fixedImage, wfpoints, ITK_NULLPTR, fdiffmap, false, ITK_NULLPTR ); } DisplacementFieldPointer totalUpdateField = this->ComputeUpdateField( this->m_SyNMInv, this->m_SyNFInv, wfpoints, wmpoints, totalUpdateInvField, true); if( this->m_SyNFullTime == 2 ) { totalUpdateInvField = ITK_NULLPTR; } this->CopyOrAddToVelocityField( velocityUpdate, totalUpdateField, totalUpdateInvField, hit ); } // http://en.wikipedia.org/wiki/Conjugate_gradient_method // below is r_k+1 this->SmoothVelocityGauss( velocityUpdate, this->m_GradSmoothingparam, ImageDimension ); // update total velocity with v-update TReal tmag = 0; typedef itk::ImageRegionIteratorWithIndex TVFieldIterator; TVFieldIterator m_FieldIter( this->m_TimeVaryingVelocity, this->m_TimeVaryingVelocity->GetLargestPossibleRegion() ); for( m_FieldIter.GoToBegin(); !m_FieldIter.IsAtEnd(); ++m_FieldIter ) { TReal A = 1; TReal alpha = 0, alpha1 = 0, alpha2 = 0, beta = 0, beta1 = 0, beta2 = 0; VectorType vec1 = velocityUpdate->GetPixel(m_FieldIter.GetIndex() ); // r_k+1 VectorType vec2 = vec1; // this->m_LastTimeVaryingVelocity->GetPixel(m_FieldIter.GetIndex()); // r_k VectorType upd = this->m_LastTimeVaryingUpdate->GetPixel(m_FieldIter.GetIndex() ); // p_k for( unsigned int ii = 0; ii < ImageDimension; ii++ ) { alpha1 = vec2[ii] * vec2[ii]; alpha2 = upd[ii] * upd[ii]; beta1 = vec1[ii] * vec1[ii]; beta2 = vec2[ii] * vec2[ii]; } if( alpha2 > 0 ) { alpha = alpha1 / (A * alpha2 + 0.001); } if( beta2 > 0 ) { beta = beta1 / (beta2 + 0.001); } if( beta > 1 ) { beta = 1; } if( alpha > 1 ) { alpha = 1; } // std::cout <<" beta " << beta << " alpha " << alpha << " it " << this->m_CurrentIteration << // std::endl; VectorType newupd = (vec1); if( this->m_CurrentIteration > 2 ) { newupd = (vec1 + upd) * 0.5; } VectorType newsoln = m_FieldIter.Get() + this->m_GradstepAltered * newupd; m_FieldIter.Set( newsoln ); // VectorType vec2u=vec2 - alpha*A*upd; // this->m_LastTimeVaryingVelocity->SetPixel(m_FieldIter.GetIndex(), vec1 ); this->m_LastTimeVaryingUpdate->SetPixel(m_FieldIter.GetIndex(), newupd); TReal mag = 0; VectorType vv = m_FieldIter.Get(); for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += vv[jj] * vv[jj]; } tmag += sqrt(mag); } tmag /= ( (TReal) this->m_NTimeSteps * (TReal)numpx); std::cout << " DiffLength " << tmag << std::endl; if( this->m_TotalSmoothingparam > 0 || this->m_TotalSmoothingMeshSize[0] > 0 ) { this->SmoothVelocityGauss( this->m_TimeVaryingVelocity, this->m_TotalSmoothingparam, ImageDimension ); // this->SmoothDisplacementField( this->m_SyNF,false); // this->SmoothDisplacementField( this->m_SyNM,false); } return; } template typename ANTSImageRegistrationOptimizer::DisplacementFieldPointer ANTSImageRegistrationOptimizer ::IntegrateVelocity(TReal starttimein, TReal finishtimein ) { ImagePointer mask = ITK_NULLPTR; if( this->m_SyNMInv && this->m_MaskImage ) { mask = this->WarpMultiTransform( this->m_ReferenceSpaceImage, this->m_MaskImage, ITK_NULLPTR, this->m_SyNMInv, false, this->m_FixedImageAffineTransform ); } else if( this->m_MaskImage ) { mask = this->SubsampleImage( this->m_MaskImage, this->m_ScaleFactor, this->m_MaskImage->GetOrigin(), this->m_MaskImage->GetDirection(), ITK_NULLPTR); } // std::cout << " st " << starttimein << " ft " << finishtimein << std::endl; bool dothick = false; if( finishtimein > starttimein && this->m_ComputeThickness ) { dothick = true; } if( dothick && this->m_CurrentIteration > 2 ) { this->m_ThickImage = AllocImage(this->m_SyNF, 0); this->m_HitImage = AllocImage(this->m_SyNF, 0); } else { this->m_HitImage = ITK_NULLPTR; this->m_ThickImage = ITK_NULLPTR; } VectorType zero; zero.Fill(0); DisplacementFieldPointer intfield = AllocImage (this->m_DisplacementField->GetLargestPossibleRegion(), zero); intfield->SetSpacing( this->m_CurrentDomainSpacing ); intfield->SetOrigin( this->m_DisplacementField->GetOrigin() ); intfield->SetDirection( this->m_DisplacementField->GetDirection() ); if( starttimein == finishtimein ) { return intfield; } if( !this->m_TimeVaryingVelocity ) { std::cout << " No TV Field " << std::endl; return intfield; } this->m_VelocityFieldInterpolator->SetInputImage(this->m_TimeVaryingVelocity); typedef itk::ImageRegionIteratorWithIndex FieldIterator; if( starttimein < 0 ) { starttimein = 0; } if( starttimein > 1 ) { starttimein = 1; } if( finishtimein < 0 ) { finishtimein = 0; } if( finishtimein > 1 ) { finishtimein = 1; } FieldIterator m_FieldIter(this->GetDisplacementField(), this->GetDisplacementField()->GetLargestPossibleRegion() ); // std::cout << " Start Int " << starttimein << std::endl; if( mask && !this->m_ComputeThickness ) { for( m_FieldIter.GoToBegin(); !m_FieldIter.IsAtEnd(); ++m_FieldIter ) { IndexType velind = m_FieldIter.GetIndex(); VectorType disp; if( mask->GetPixel(velind) > 0.05 ) { disp = this->IntegratePointVelocity(starttimein, finishtimein, velind) * mask->GetPixel(velind); } else { disp.Fill(0); } intfield->SetPixel(velind, disp); } } else { for( m_FieldIter.GoToBegin(); !m_FieldIter.IsAtEnd(); ++m_FieldIter ) { IndexType velind = m_FieldIter.GetIndex(); VectorType disp = this->IntegratePointVelocity(starttimein, finishtimein, velind); intfield->SetPixel(velind, disp); } } if( this->m_ThickImage && this->m_MaskImage ) { std::string outname = this->localANTSGetFilePrefix(this->m_OutputNamingConvention.c_str() ) + std::string( "thick.nii.gz"); std::cout << " write " << outname << std::endl; WriteImage(this->m_ThickImage, outname.c_str() ); } return intfield; } template typename ANTSImageRegistrationOptimizer::DisplacementFieldPointer ANTSImageRegistrationOptimizer ::IntegrateLandmarkSetVelocity(TReal starttimein, TReal finishtimein, typename ANTSImageRegistrationOptimizer::PointSetPointer mypoints, typename ANTSImageRegistrationOptimizer ::ImagePointer /* refimage */ ) { VectorType zero; zero.Fill(0); DisplacementFieldPointer intfield = AllocImage (this->m_DisplacementField->GetLargestPossibleRegion(), zero); intfield->SetSpacing( this->m_CurrentDomainSpacing ); intfield->SetOrigin( this->m_DisplacementField->GetOrigin() ); intfield->SetDirection( this->m_DisplacementField->GetDirection() ); if( starttimein == finishtimein ) { return intfield; } if( !this->m_TimeVaryingVelocity ) { std::cout << " No TV Field " << std::endl; return intfield; } this->m_VelocityFieldInterpolator->SetInputImage(this->m_TimeVaryingVelocity); if( starttimein < 0 ) { starttimein = 0; } if( starttimein > 1 ) { starttimein = 1; } if( finishtimein < 0 ) { finishtimein = 0; } if( finishtimein > 1 ) { finishtimein = 1; } unsigned long sz1 = mypoints->GetNumberOfPoints(); for( unsigned long ii = 0; ii < sz1; ii++ ) { PointType point; // std::cout <<" get point " << std::endl; const bool isValidPoint = mypoints->GetPoint(ii, &point); if( !isValidPoint ) { itkExceptionMacro( << "Invalid Point found at " << ii ); } else { // std::cout <<" get point index " << point << std::endl; ImagePointType pt, wpt; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { pt[jj] = point[jj]; } IndexType velind; const bool bisinside = intfield->TransformPhysicalPointToIndex( pt, velind ); // std::cout <<" inside? " << bisinside << std::endl; if( bisinside ) { // std::cout << "integrate " << std::endl; VectorType disp = this->IntegratePointVelocity(starttimein, finishtimein, velind); // std::cout << "put inside " << std::endl; intfield->SetPixel(velind, disp); } } } return intfield; } template typename ANTSImageRegistrationOptimizer::VectorType ANTSImageRegistrationOptimizer ::IntegratePointVelocity(TReal starttimein, TReal finishtimein, IndexType velind) { typedef Point xPointType; this->m_Debug = false; VectorType zero; zero.Fill(0); if( starttimein == finishtimein ) { return zero; } typedef typename TimeVaryingVelocityFieldType::IndexType VIndexType; this->m_VelocityFieldInterpolator->SetInputImage(this->m_TimeVaryingVelocity); TReal dT = this->m_DeltaTime; unsigned int m_NumberOfTimePoints = this->m_TimeVaryingVelocity->GetLargestPossibleRegion().GetSize()[TDimension]; if( starttimein < 0 ) { starttimein = 0; } if( starttimein > 1 ) { starttimein = 1; } if( finishtimein < 0 ) { finishtimein = 0; } if( finishtimein > 1 ) { finishtimein = 1; } TReal timesign = 1.0; if( starttimein > finishtimein ) { timesign = -1.0; } VectorType velo; velo.Fill(0); typename VelocityFieldInterpolatorType::ContinuousIndexType vcontind; TReal itime = starttimein; unsigned long ct = 0; TReal thislength = 0, euclideandist = 0; bool timedone = false; VectorType disp; TReal deltaTime = dT, vecsign = 1.0; typename ImageType::SpacingType spacing = this->m_DisplacementField->GetSpacing(); if( starttimein > finishtimein ) { vecsign = -1.0; } VIndexType vind; vind.Fill(0); xPointType pointIn1; pointIn1[TDimension] = 0; // Note: Set the highest value to zero; xPointType pointIn2; pointIn2[TDimension] = 0; // Note: Set the highest value to zero; xPointType pointIn3; pointIn3[TDimension] = 0; // Note: Set the highest value to zero; for( unsigned int jj = 0; jj < TDimension; jj++ ) { vind[jj] = velind[jj]; pointIn1[jj] = velind[jj] * spacing[jj]; } this->m_TimeVaryingVelocity->TransformIndexToPhysicalPoint( vind, pointIn1); // time is in [0,1] pointIn1[TDimension] = starttimein * (m_NumberOfTimePoints - 1); xPointType Y1x; xPointType Y2x; xPointType Y3x; xPointType Y4x; // set up parameters for start of integration disp.Fill(0.0); timedone = false; itime = starttimein; ct = 0; while( !timedone ) { TReal itimetn1 = itime - timesign * deltaTime; TReal itimetn1h = itime - timesign * deltaTime * 0.5; if( itimetn1h < 0 ) { itimetn1h = 0; } if( itimetn1h > 1 ) { itimetn1h = 1; } if( itimetn1 < 0 ) { itimetn1 = 0; } if( itimetn1 > 1 ) { itimetn1 = 1; } TReal totalmag = 0; // first get current position of particle typename VelocityFieldInterpolatorType::OutputType f1; f1.Fill(0); typename VelocityFieldInterpolatorType::OutputType f2; f2.Fill(0); typename VelocityFieldInterpolatorType::OutputType f3; f3.Fill(0); typename VelocityFieldInterpolatorType::OutputType f4; f4.Fill(0); for( unsigned int jj = 0; jj < TDimension; jj++ ) { pointIn2[jj] = disp[jj] + pointIn1[jj]; Y1x[jj] = pointIn2[jj]; Y2x[jj] = pointIn2[jj]; Y3x[jj] = pointIn2[jj]; Y4x[jj] = pointIn2[jj]; } if( this->m_Debug ) { std::cout << " p2 " << pointIn2 << std::endl; } Y1x[TDimension] = itimetn1 * (TReal)(m_NumberOfTimePoints - 1); Y2x[TDimension] = itimetn1h * (TReal)(m_NumberOfTimePoints - 1); Y3x[TDimension] = itimetn1h * (TReal)(m_NumberOfTimePoints - 1); Y4x[TDimension] = itime * (TReal)(m_NumberOfTimePoints - 1); if( this->m_Debug ) { std::cout << " p2 " << pointIn2 << " y1 " << Y1x[TDimension] << " y4 " << Y4x[TDimension] << std::endl; } if( this->m_VelocityFieldInterpolator->IsInsideBuffer(Y1x) ) { f1 = this->m_VelocityFieldInterpolator->Evaluate( Y1x ); for( unsigned int jj = 0; jj < TDimension; jj++ ) { Y2x[jj] += f1[jj] * deltaTime * 0.5; } } if( this->m_VelocityFieldInterpolator->IsInsideBuffer(Y2x) ) { f2 = this->m_VelocityFieldInterpolator->Evaluate( Y2x ); for( unsigned int jj = 0; jj < TDimension; jj++ ) { Y3x[jj] += f2[jj] * deltaTime * 0.5; } } if( this->m_VelocityFieldInterpolator->IsInsideBuffer(Y3x) ) { f3 = this->m_VelocityFieldInterpolator->Evaluate( Y3x ); for( unsigned int jj = 0; jj < TDimension; jj++ ) { Y4x[jj] += f3[jj] * deltaTime; } } if( this->m_VelocityFieldInterpolator->IsInsideBuffer(Y4x) ) { f4 = this->m_VelocityFieldInterpolator->Evaluate( Y4x ); } for( unsigned int jj = 0; jj < TDimension; jj++ ) { pointIn3[jj] = pointIn2[jj] + vecsign * deltaTime / 6.0 * ( f1[jj] + 2.0 * f2[jj] + 2.0 * f3[jj] + f4[jj] ); } pointIn3[TDimension] = itime * (TReal)(m_NumberOfTimePoints - 1); VectorType out; TReal mag = 0, dmag = 0; for( unsigned int jj = 0; jj < TDimension; jj++ ) { out[jj] = pointIn3[jj] - pointIn1[jj]; mag += (pointIn3[jj] - pointIn2[jj]) * (pointIn3[jj] - pointIn2[jj]); dmag += (pointIn3[jj] - pointIn1[jj]) * (pointIn3[jj] - pointIn1[jj]); disp[jj] = out[jj]; } // std::cout << " p3 " << pointIn3 << std::endl; dmag = sqrt(dmag); totalmag += sqrt(mag); ct++; thislength += totalmag; euclideandist = dmag; itime = itime + deltaTime * timesign; if( starttimein > finishtimein ) { if( itime <= finishtimein ) { timedone = true; } } else if( thislength == 0 ) { timedone = true; } else { if( itime >= finishtimein ) { timedone = true; } } } // now we have the thickness value stored in thislength if( this->m_ThickImage && this->m_HitImage ) { // set up parameters for start of integration velo.Fill(0); itime = starttimein; timedone = false; vind.Fill(0); for( unsigned int jj = 0; jj < TDimension; jj++ ) { vind[jj] = velind[jj]; pointIn1[jj] = velind[jj] * spacing[jj]; } this->m_TimeVaryingVelocity->TransformIndexToPhysicalPoint( vind, pointIn1); // time is in [0,1] pointIn1[TDimension] = starttimein * (m_NumberOfTimePoints - 1); // set up parameters for start of integration disp.Fill(0.0); timedone = false; itime = starttimein; ct = 0; while( !timedone ) { TReal itimetn1 = itime - timesign * deltaTime; TReal itimetn1h = itime - timesign * deltaTime * 0.5; if( itimetn1h < 0 ) { itimetn1h = 0; } if( itimetn1h > 1 ) { itimetn1h = 1; } if( itimetn1 < 0 ) { itimetn1 = 0; } if( itimetn1 > 1 ) { itimetn1 = 1; } // TReal totalmag=0; // first get current position of particle typename VelocityFieldInterpolatorType::OutputType f1; f1.Fill(0); typename VelocityFieldInterpolatorType::OutputType f2; f2.Fill(0); typename VelocityFieldInterpolatorType::OutputType f3; f3.Fill(0); typename VelocityFieldInterpolatorType::OutputType f4; f4.Fill(0); for( unsigned int jj = 0; jj < TDimension; jj++ ) { pointIn2[jj] = disp[jj] + pointIn1[jj]; Y1x[jj] = pointIn2[jj]; Y2x[jj] = pointIn2[jj]; Y3x[jj] = pointIn2[jj]; Y4x[jj] = pointIn2[jj]; } if( this->m_Debug ) { std::cout << " p2 " << pointIn2 << std::endl; } Y1x[TDimension] = itimetn1 * (TReal)(m_NumberOfTimePoints - 1); Y2x[TDimension] = itimetn1h * (TReal)(m_NumberOfTimePoints - 1); Y3x[TDimension] = itimetn1h * (TReal)(m_NumberOfTimePoints - 1); Y4x[TDimension] = itime * (TReal)(m_NumberOfTimePoints - 1); if( this->m_Debug ) { std::cout << " p2 " << pointIn2 << " y1 " << Y1x[TDimension] << " y4 " << Y4x[TDimension] << std::endl; } if( this->m_VelocityFieldInterpolator->IsInsideBuffer(Y1x) ) { f1 = this->m_VelocityFieldInterpolator->Evaluate( Y1x ); for( unsigned int jj = 0; jj < TDimension; jj++ ) { Y2x[jj] += f1[jj] * deltaTime * 0.5; } } if( this->m_VelocityFieldInterpolator->IsInsideBuffer(Y2x) ) { f2 = this->m_VelocityFieldInterpolator->Evaluate( Y2x ); for( unsigned int jj = 0; jj < TDimension; jj++ ) { Y3x[jj] += f2[jj] * deltaTime * 0.5; } } if( this->m_VelocityFieldInterpolator->IsInsideBuffer(Y3x) ) { f3 = this->m_VelocityFieldInterpolator->Evaluate( Y3x ); for( unsigned int jj = 0; jj < TDimension; jj++ ) { Y4x[jj] += f3[jj] * deltaTime; } } if( this->m_VelocityFieldInterpolator->IsInsideBuffer(Y4x) ) { f4 = this->m_VelocityFieldInterpolator->Evaluate( Y4x ); } for( unsigned int jj = 0; jj < TDimension; jj++ ) { pointIn3[jj] = pointIn2[jj] + vecsign * deltaTime / 6.0 * ( f1[jj] + 2.0 * f2[jj] + 2.0 * f3[jj] + f4[jj] ); } pointIn3[TDimension] = itime * (TReal)(m_NumberOfTimePoints - 1); VectorType out; TReal mag = 0, dmag = 0; for( unsigned int jj = 0; jj < TDimension; jj++ ) { out[jj] = pointIn3[jj] - pointIn1[jj]; mag += (pointIn3[jj] - pointIn2[jj]) * (pointIn3[jj] - pointIn2[jj]); dmag += (pointIn3[jj] - pointIn1[jj]) * (pointIn3[jj] - pointIn1[jj]); disp[jj] = out[jj]; } itime = itime + deltaTime * timesign; if( starttimein > finishtimein ) { if( itime <= finishtimein ) { timedone = true; } } else { if( itime >= finishtimein ) { timedone = true; } } // bool isingray=true; if( this->m_MaskImage ) { if( this->m_MaskImage->GetPixel(velind) ) { VIndexType thind2; IndexType thind; bool isin = this->m_TimeVaryingVelocity->TransformPhysicalPointToIndex( pointIn3, thind2 ); for( unsigned int ij = 0; ij < ImageDimension; ij++ ) { thind[ij] = thind2[ij]; } if( isin ) { unsigned long lastct = (unsigned long) this->m_HitImage->GetPixel(thind); unsigned long newct = lastct + 1; TReal oldthick = this->m_ThickImage->GetPixel(thind); TReal newthick = (TReal)lastct / (TReal)newct * oldthick + 1.0 / (TReal)newct * euclideandist; this->m_HitImage->SetPixel( thind, newct ); this->m_ThickImage->SetPixel(thind, newthick ); } else { std::cout << " thind " << thind << " edist " << euclideandist << " p3 " << pointIn3 << " p1 " << pointIn1 << std::endl; } // this->m_ThickImage->SetPixel(thind, thislength ); } } } } // if (!isinside) { std::cout << " velind " << velind << " not inside " << Y1 << std::endl; } if( this->m_Debug ) { std::cout << " Length " << thislength << std::endl; } this->m_Debug = false; return disp; } /** * Standard "PrintSelf" method */ template void ANTSImageRegistrationOptimizer ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkANTSImageRegistrationOptimizer.h000066400000000000000000002447211311104306400247670ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkANTSImageRegistrationOptimizer_h #define __itkANTSImageRegistrationOptimizer_h #include "antsAllocImage.h" #include "itkObject.h" #include "itkObjectFactory.h" #include "itkVectorGaussianInterpolateImageFunction.h" #include "antsCommandLineParser.h" #include "itkShiftScaleImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkMinimumMaximumImageFilter.h" #include "itkImage.h" #include "itkMacro.h" #include "ReadWriteData.h" #include "itkCenteredEuler3DTransform.h" #include "itkQuaternionRigidTransform.h" #include "itkANTSAffine3DTransform.h" #include "itkANTSCenteredAffine2DTransform.h" #include "itkCenteredTransformInitializer.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "itkFiniteDifferenceFunction.h" #include "itkFixedArray.h" #include "itkANTSSimilarityMetric.h" #include "itkVectorExpandImageFilter.h" #include "itkPDEDeformableRegistrationFilter.h" #include "itkWarpImageFilter.h" #include "itkWarpImageMultiTransformFilter.h" #include "itkDisplacementFieldFromMultiTransformFilter.h" #include "itkWarpImageWAffineFilter.h" #include "itkPointSet.h" #include "itkVector.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkGeneralToBSplineDisplacementFieldFilter.h" #include "itkBSplineControlPointImageFunction.h" #include "ANTS_affine_registration2.h" #include "itkVectorFieldGradientImageFunction.h" #include "itkBSplineInterpolateImageFunction.h" namespace itk { template class ANTSImageRegistrationOptimizer : public Object { public: /** Standard class typedefs. */ typedef ANTSImageRegistrationOptimizer Self; typedef Object Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( ANTSImageRegistrationOptimizer, Object ); itkStaticConstMacro( Dimension, unsigned int, TDimension ); itkStaticConstMacro( ImageDimension, unsigned int, TDimension ); typedef double TComp; typedef TReal RealType; typedef Image ImageType; typedef typename ImageType::Pointer ImagePointer; typedef itk::MatrixOffsetTransformBase TransformType; /** Point Types for landmarks and labeled point-sets */ typedef itk::ANTSLabeledPointSet LabeledPointSetType; typedef typename LabeledPointSetType::Pointer LabeledPointSetPointer; typedef typename LabeledPointSetType::PointSetType PointSetType; typedef typename PointSetType::Pointer PointSetPointer; typedef typename PointSetType::PointType PointType; typedef typename PointSetType::PixelType PointDataType; typedef typename ImageType::PointType ImagePointType; typedef TransformType AffineTransformType; typedef typename AffineTransformType::Pointer AffineTransformPointer; typedef OptAffine OptAffineType; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldPointer; typedef itk::Image TimeVaryingVelocityFieldType; typedef typename TimeVaryingVelocityFieldType::Pointer TimeVaryingVelocityFieldPointer; typedef itk::VectorLinearInterpolateImageFunction VelocityFieldInterpolatorType; typedef itk::VectorGaussianInterpolateImageFunction VelocityFieldInterpolatorType2; typedef typename DisplacementFieldType::IndexType IndexType; typedef ants::CommandLineParser ParserType; typedef typename ParserType::OptionType OptionType; typedef GeneralToBSplineDisplacementFieldFilter BSplineFilterType; typedef FixedArray ArrayType; /** Typedefs for similarity metrics */ typedef ANTSSimilarityMetric SimilarityMetricType; typedef typename SimilarityMetricType::Pointer SimilarityMetricPointer; typedef std::vector SimilarityMetricListType; /** FiniteDifferenceFunction type. */ typedef FiniteDifferenceFunction FiniteDifferenceFunctionType; typedef typename FiniteDifferenceFunctionType::TimeStepType TimeStepType; typedef typename FiniteDifferenceFunctionType::Pointer FiniteDifferenceFunctionPointer; typedef AvantsPDEDeformableRegistrationFunction MetricBaseType; typedef typename MetricBaseType::Pointer MetricBaseTypePointer; /* Jacobian and other calculations */ typedef itk::VectorFieldGradientImageFunction JacobianFunctionType; /** Set functions */ void SetAffineTransform(AffineTransformPointer A) { this->m_AffineTransform = A; } void SetDisplacementField(DisplacementFieldPointer A) { this->m_DisplacementField = A; } void SetTimeVaryingVelocityField(TimeVaryingVelocityFieldPointer A) { this->m_TimeVaryingVelocity = A; this->m_VelocityFieldInterpolator->SetInputImage(this->m_TimeVaryingVelocity); } void SetInverseDisplacementField(DisplacementFieldPointer A) { this->m_InverseDisplacementField = A; } void SetMaskImage( ImagePointer m) { this->m_MaskImage = m; } void SetReferenceSpaceImage( ImagePointer m) { this->m_ReferenceSpaceImage = m; } void SetFixedImageAffineTransform(AffineTransformPointer A) { this->m_FixedImageAffineTransform = A; } AffineTransformPointer GetFixedImageAffineTransform() { return this->m_FixedImageAffineTransform; } /** Get functions */ AffineTransformPointer GetAffineTransform() { return this->m_AffineTransform; } DisplacementFieldPointer GetDisplacementField() { return this->m_DisplacementField; } DisplacementFieldPointer GetInverseDisplacementField() { return this->m_InverseDisplacementField; } /** Initialize all parameters */ void SetNumberOfLevels(unsigned int i) { this->m_NumberOfLevels = i; } void SetParser( typename ParserType::Pointer P ) { this->m_Parser = P; } /** Basic operations */ DisplacementFieldPointer CopyDisplacementField( DisplacementFieldPointer input ); std::string localANTSGetFilePrefix(const char *str) { std::string filename = str; std::string::size_type pos = filename.rfind( "." ); std::string filepre = std::string( filename, 0, pos ); #if 0 //HACK: This looks like an error in logic. It has no effect //Perhaps this shoudl be put into a common function, or use ITK code to accomplish the desired task. //as is done in BRAINS. if( pos != std::string::npos ) { std::string extension = std::string( filename, pos, filename.length() - 1); if( extension == std::string(".gz") ) { pos = filepre.rfind( "." ); extension = std::string( filepre, pos, filepre.length() - 1 ); } } #endif return filepre; } void SmoothDisplacementField(DisplacementFieldPointer field, bool TrueEqualsGradElseTotal ) { typename ParserType::OptionType::Pointer regularizationOption = this->m_Parser->GetOption( "regularization" ); if( ( regularizationOption->GetFunction( 0 )->GetName() ).find( "DMFFD" ) != std::string::npos ) { if( ( !TrueEqualsGradElseTotal && this->m_TotalSmoothingparam == 0.0 ) || ( TrueEqualsGradElseTotal && this->m_GradSmoothingparam == 0.0 ) ) { return; } ArrayType meshSize; unsigned int splineOrder = this->m_BSplineFieldOrder; TReal bsplineKernelVariance = static_cast( splineOrder + 1 ) / 12.0; unsigned int numberOfLevels = 1; if( TrueEqualsGradElseTotal ) { if( this->m_GradSmoothingparam < 0.0 ) { meshSize = this->m_GradSmoothingMeshSize; for( unsigned int d = 0; d < ImageDimension; d++ ) { meshSize[d] *= static_cast( std::pow( 2.0, static_cast( this->m_CurrentLevel ) ) ); } } else { TReal spanLength = std::sqrt( this->m_GradSmoothingparam / bsplineKernelVariance ); for( unsigned int d = 0; d < ImageDimension; d++ ) { meshSize[d] = static_cast( field->GetLargestPossibleRegion().GetSize()[d] / spanLength + 0.5 ); } } this->SmoothDisplacementFieldBSpline( field, meshSize, splineOrder, numberOfLevels ); } else { if( this->m_TotalSmoothingparam < 0.0 ) { meshSize = this->m_TotalSmoothingMeshSize; for( unsigned int d = 0; d < ImageDimension; d++ ) { meshSize[d] *= static_cast( std::pow( 2.0, static_cast( this->m_CurrentLevel ) ) ); } } else { TReal spanLength = std::sqrt( this->m_TotalSmoothingparam / bsplineKernelVariance ); for( unsigned int d = 0; d < ImageDimension; d++ ) { meshSize[d] = static_cast( field->GetLargestPossibleRegion().GetSize()[d] / spanLength + 0.5 ); } } RealType maxMagnitude = 0.0; ImageRegionIterator It( field, field->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { RealType magnitude = ( It.Get() ).GetNorm(); if( magnitude > maxMagnitude ) { maxMagnitude = magnitude; } } this->SmoothDisplacementFieldBSpline( field, meshSize, splineOrder, numberOfLevels ); if( maxMagnitude > 0.0 ) { for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { It.Set( It.Get() / maxMagnitude ); } } } } else // Gaussian { TReal sig = 0; if( TrueEqualsGradElseTotal ) { sig = this->m_GradSmoothingparam; } else { sig = this->m_TotalSmoothingparam; } this->SmoothDisplacementFieldGauss(field, sig); } } void SmoothDisplacementFieldGauss(DisplacementFieldPointer field = NULL, TReal sig = 0.0, bool useparamimage = false, unsigned int lodim = ImageDimension); // TReal = smoothingparam, int = maxdim to smooth void SmoothVelocityGauss(TimeVaryingVelocityFieldPointer field, TReal, unsigned int); void SmoothDisplacementFieldBSpline(DisplacementFieldPointer field, ArrayType meshSize, unsigned int splineorder, unsigned int numberoflevels ); DisplacementFieldPointer ComputeUpdateFieldAlternatingMin(DisplacementFieldPointer fixedwarp, DisplacementFieldPointer movingwarp, PointSetPointer fpoints = NULL, PointSetPointer wpoints = NULL, DisplacementFieldPointer updateFieldInv = NULL, bool updateenergy = true); DisplacementFieldPointer ComputeUpdateField(DisplacementFieldPointer fixedwarp, DisplacementFieldPointer movingwarp, PointSetPointer fpoints = NULL, PointSetPointer wpoints = NULL, DisplacementFieldPointer updateFieldInv = NULL, bool updateenergy = true); TimeVaryingVelocityFieldPointer ExpandVelocity() { float expandFactors[ImageDimension + 1]; expandFactors[ImageDimension] = 1; // m_Debug = false; for( unsigned int idim = 0; idim < ImageDimension; idim++ ) { expandFactors[idim] = (float) this->m_CurrentDomainSize[idim] / (TReal) this->m_TimeVaryingVelocity->GetLargestPossibleRegion().GetSize()[idim]; if( expandFactors[idim] < 1 ) { expandFactors[idim] = 1; } if( this->m_Debug ) { std::cout << " ExpFac " << expandFactors[idim] << " curdsz " << this->m_CurrentDomainSize[idim] << std::endl; } } VectorType pad; pad.Fill(0); typedef VectorExpandImageFilter ExpanderType; typename ExpanderType::Pointer m_FieldExpander = ExpanderType::New(); m_FieldExpander->SetInput(this->m_TimeVaryingVelocity); m_FieldExpander->SetExpandFactors( expandFactors ); // m_FieldExpander->SetEdgePaddingValue( pad ); m_FieldExpander->UpdateLargestPossibleRegion(); return m_FieldExpander->GetOutput(); } DisplacementFieldPointer ExpandField(DisplacementFieldPointer field, typename ImageType::SpacingType targetSpacing) { // this->m_Debug=true; float expandFactors[ImageDimension]; for( unsigned int idim = 0; idim < ImageDimension; idim++ ) { expandFactors[idim] = (TReal) this->m_CurrentDomainSize[idim] / (TReal)field->GetLargestPossibleRegion().GetSize()[idim]; if( expandFactors[idim] < 1 ) { expandFactors[idim] = 1; } // if (this->m_Debug) std::cout << " ExpFac " << expandFactors[idim] << " curdsz " << // this->m_CurrentDomainSize[idim] << std::endl; } VectorType pad; pad.Fill(0); typedef VectorExpandImageFilter ExpanderType; typename ExpanderType::Pointer m_FieldExpander = ExpanderType::New(); m_FieldExpander->SetInput(field); m_FieldExpander->SetExpandFactors( expandFactors ); // use default // m_FieldExpander->SetEdgePaddingValue( pad ); m_FieldExpander->UpdateLargestPossibleRegion(); typename DisplacementFieldType::Pointer fieldout = m_FieldExpander->GetOutput(); fieldout->SetSpacing(targetSpacing); fieldout->SetOrigin(field->GetOrigin() ); if( this->m_Debug ) { std::cout << " Field size " << fieldout->GetLargestPossibleRegion().GetSize() << std::endl; } // this->m_Debug=false; return fieldout; } ImagePointer GetVectorComponent(DisplacementFieldPointer field, unsigned int index) { // Initialize the Moving to the displacement field typedef DisplacementFieldType FieldType; typename ImageType::Pointer sfield = AllocImage(field); typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( field, field->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { VectorType v1 = vfIter.Get(); sfield->SetPixel(vfIter.GetIndex(), v1[index]); } return sfield; } ImagePointer SubsampleImage( ImagePointer, RealType, typename ImageType::PointType outputOrigin, typename ImageType::DirectionType outputDirection, AffineTransformPointer aff = NULL); DisplacementFieldPointer SubsampleField( DisplacementFieldPointer field, typename ImageType::SizeType targetSize, typename ImageType::SpacingType targetSpacing ) { std::cout << "FIXME -- NOT DONE CORRECTLY " << std::endl; std::cout << "FIXME -- NOT DONE CORRECTLY " << std::endl; std::cout << "FIXME -- NOT DONE CORRECTLY " << std::endl; std::cout << "FIXME -- NOT DONE CORRECTLY " << std::endl; std::cout << " SUBSAM FIELD SUBSAM FIELD SUBSAM FIELD " << std::endl; typename DisplacementFieldType::Pointer sfield; for( unsigned int i = 0; i < ImageDimension; i++ ) { typename ImageType::Pointer precomp = this->GetVectorComponent(field, i); typename ImageType::Pointer comp = this->SubsampleImage(precomp, targetSize, targetSpacing); if( i == 0 ) { sfield = AllocImage(comp); } typedef itk::ImageRegionIteratorWithIndex Iterator; typedef typename DisplacementFieldType::PixelType DispVectorType; DispVectorType v1; DispVectorType zero; zero.Fill(0.0); Iterator vfIter( sfield, sfield->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { v1 = vfIter.Get(); v1[i] = comp->GetPixel(vfIter.GetIndex() ); vfIter.Set(v1); } } return sfield; } PointSetPointer WarpMultiTransform(ImagePointer referenceimage, ImagePointer movingImage, PointSetPointer movingpoints, AffineTransformPointer aff, DisplacementFieldPointer totalField, bool doinverse, AffineTransformPointer fixedaff ) { if( !movingpoints ) { std::cout << " NULL POINTS " << std::endl; return ITK_NULLPTR; } AffineTransformPointer affinverse = ITK_NULLPTR; if( aff ) { affinverse = AffineTransformType::New(); aff->GetInverse(affinverse); } AffineTransformPointer fixedaffinverse = ITK_NULLPTR; if( fixedaff ) { fixedaffinverse = AffineTransformType::New(); fixedaff->GetInverse(fixedaffinverse); } typedef itk::WarpImageMultiTransformFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); warper->SetInput(movingImage); warper->SetEdgePaddingValue( 0); warper->SetSmoothScale(1); if( !doinverse ) { if( totalField ) { warper->PushBackDisplacementFieldTransform(totalField); } if( fixedaff ) { warper->PushBackAffineTransform(fixedaff); } else if( aff ) { warper->PushBackAffineTransform(aff); } } else { if( aff ) { warper->PushBackAffineTransform( affinverse ); } else if( fixedaff ) { warper->PushBackAffineTransform(fixedaffinverse); } if( totalField ) { warper->PushBackDisplacementFieldTransform(totalField); } } warper->SetOutputOrigin(referenceimage->GetOrigin() ); typename ImageType::SizeType size = referenceimage->GetLargestPossibleRegion().GetSize(); if( totalField ) { size = totalField->GetLargestPossibleRegion().GetSize(); } warper->SetOutputSize(size); typename ImageType::SpacingType spacing = referenceimage->GetSpacing(); if( totalField ) { spacing = totalField->GetSpacing(); } warper->SetOutputSpacing(spacing); warper->SetOutputDirection(referenceimage->GetDirection() ); totalField->SetOrigin(referenceimage->GetOrigin() ); totalField->SetDirection(referenceimage->GetDirection() ); // warper->Update(); // std::cout << " updated in point warp " << std::endl; PointSetPointer outputMesh = PointSetType::New(); unsigned long count = 0; const unsigned long sz1 = movingpoints->GetNumberOfPoints(); if( this->m_Debug ) { std::cout << " BEFORE #points " << sz1 << std::endl; } for( unsigned long ii = 0; ii < sz1; ii++ ) { PointType point; const bool isValid = movingpoints->GetPoint(ii, &point); if( !isValid ) { itkExceptionMacro( "Invalid moving point at : " << ii ); } else { PointDataType label = 0; movingpoints->GetPointData(ii, &label); // convert pointtype to imagepointtype ImagePointType pt; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { pt[jj] = point[jj]; } ImagePointType wpt; const bool bisinside = warper->MultiTransformSinglePoint(pt, wpt); if( bisinside ) { PointType wpoint; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { wpoint[jj] = wpt[jj]; } outputMesh->SetPointData( count, label ); outputMesh->SetPoint( count, wpoint ); // if (ii % 100 == 0) std::cout << " pt " << pt << " wpt " << wpt << std::endl; count++; } } } if( this->m_Debug ) { std::cout << " AFTER #points " << count << std::endl; } // if (count != sz1 ) std::cout << " WARNING: POINTS ARE MAPPING OUT OF IMAGE DOMAIN " << 1.0 - (TReal) // count/(TReal)(sz1+1) << std::endl; return outputMesh; } ImagePointer WarpMultiTransform( ImagePointer referenceimage, ImagePointer movingImage, AffineTransformPointer aff, DisplacementFieldPointer totalField, bool doinverse, AffineTransformPointer fixedaff ) { typedef typename ImageType::DirectionType DirectionType; DirectionType rdirection = referenceimage->GetDirection(); DirectionType mdirection = movingImage->GetDirection(); AffineTransformPointer affinverse = ITK_NULLPTR; if( aff ) { affinverse = AffineTransformType::New(); aff->GetInverse(affinverse); } AffineTransformPointer fixedaffinverse = ITK_NULLPTR; if( fixedaff ) { fixedaffinverse = AffineTransformType::New(); fixedaff->GetInverse(fixedaffinverse); } DirectionType iddir; iddir.Fill(0); for( unsigned int i = 0; i < ImageDimension; i++ ) { iddir[i][i] = 1; } typedef itk::LinearInterpolateImageFunction InterpolatorType1; typedef itk::NearestNeighborInterpolateImageFunction InterpolatorType2; typedef itk::BSplineInterpolateImageFunction InterpolatorType3; typename InterpolatorType1::Pointer interp1 = InterpolatorType1::New(); typename InterpolatorType2::Pointer interpnn = InterpolatorType2::New(); typename InterpolatorType3::Pointer interpcu = InterpolatorType3::New(); typedef itk::WarpImageMultiTransformFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); warper->SetInput(movingImage); warper->SetEdgePaddingValue( 0); warper->SetSmoothScale(1); warper->SetInterpolator(interp1); if( this->m_UseNN ) { warper->SetInterpolator(interpnn); } if( !doinverse ) { if( totalField ) { warper->PushBackDisplacementFieldTransform(totalField); } if( fixedaff ) { warper->PushBackAffineTransform(fixedaff); } else if( aff ) { warper->PushBackAffineTransform(aff); } } else { if( aff ) { warper->PushBackAffineTransform( affinverse ); } else if( fixedaff ) { warper->PushBackAffineTransform(fixedaffinverse); } if( totalField ) { warper->PushBackDisplacementFieldTransform(totalField); } } warper->SetOutputOrigin(referenceimage->GetOrigin() ); typename ImageType::SizeType size = referenceimage->GetLargestPossibleRegion().GetSize(); if( totalField ) { size = totalField->GetLargestPossibleRegion().GetSize(); } warper->SetOutputSize(size); typename ImageType::SpacingType spacing = referenceimage->GetSpacing(); if( totalField ) { spacing = totalField->GetSpacing(); } warper->SetOutputSpacing(spacing); warper->SetOutputDirection(referenceimage->GetDirection() ); totalField->SetOrigin(referenceimage->GetOrigin() ); totalField->SetDirection(referenceimage->GetDirection() ); warper->Update(); if( this->m_Debug ) { std::cout << " updated ok -- warped image output size " << warper->GetOutput()->GetLargestPossibleRegion().GetSize() << " requested size " << totalField->GetLargestPossibleRegion().GetSize() << std::endl; } typename ImageType::Pointer outimg = warper->GetOutput(); return outimg; } ImagePointer SmoothImageToScale(ImagePointer image, TReal scalingFactor ) { typename ImageType::SpacingType inputSpacing = image->GetSpacing(); typename ImageType::RegionType::SizeType inputSize = image->GetRequestedRegion().GetSize(); typename ImageType::SpacingType outputSpacing; typename ImageType::RegionType::SizeType outputSize; RealType minimumSpacing = inputSpacing.GetVnlVector().min_value(); // RealType maximumSpacing = inputSpacing.GetVnlVector().max_value(); for( unsigned int d = 0; d < Dimension; d++ ) { RealType scaling = vnl_math_min( scalingFactor * minimumSpacing / inputSpacing[d], static_cast( inputSize[d] ) / 32.0 ); outputSpacing[d] = inputSpacing[d] * scaling; outputSize[d] = static_cast( inputSpacing[d] * static_cast( inputSize[d] ) / outputSpacing[d] + 0.5 ); typedef RecursiveGaussianImageFilter GaussianFilterType; typename GaussianFilterType::Pointer smoother = GaussianFilterType::New(); smoother->SetInputImage( image ); smoother->SetDirection( d ); smoother->SetNormalizeAcrossScale( false ); TReal sig = (outputSpacing[d] / inputSpacing[d] - 1.0) * 0.2; // /(TReal)ImageDimension; smoother->SetSigma(sig ); if( smoother->GetSigma() > 0.0 ) { smoother->Update(); image = smoother->GetOutput(); } } image = this->NormalizeImage(image); return image; } ImagePointer GaussianSmoothImage( ImagePointer image, TReal sigma ) { typedef DiscreteGaussianImageFilter SmootherType; typename SmootherType::Pointer smoother = SmootherType::New(); smoother->SetVariance( vnl_math_sqr( sigma ) ); smoother->SetMaximumError( 0.01 ); smoother->SetInput( image ); ImagePointer smoothImage = smoother->GetOutput(); smoothImage->Update(); smoothImage->DisconnectPipeline(); smoothImage = this->NormalizeImage( smoothImage ); return smoothImage; } typename ANTSImageRegistrationOptimizer::DisplacementFieldPointer IntegrateConstantVelocity( DisplacementFieldPointer totalField, unsigned int ntimesteps, TReal timeweight); /** Base optimization functions */ // AffineTransformPointer AffineOptimization(AffineTransformPointer &aff_init, OptAffine &affine_opt); // {return // NULL;} AffineTransformPointer AffineOptimization(OptAffineType & affine_opt); // {return NULL;} std::string GetTransformationModel() { return this->m_TransformationModel; } void SetTransformationModel( std::string s) { this->m_TransformationModel = s; std::cout << " Requested Transformation Model: " << this->m_TransformationModel << " : Using " << std::endl; if( this->m_TransformationModel == std::string("Elast") ) { std::cout << "Elastic model for transformation. " << std::endl; } else if( this->m_TransformationModel == std::string("SyN") ) { std::cout << "SyN diffeomorphic model for transformation. " << std::endl; } else if( this->m_TransformationModel == std::string("GreedyExp") ) { std::cout << "Greedy Exp Diff model for transformation. Similar to Diffeomorphic Demons. Params same as Exp model. " << std::endl; this->m_TransformationModel = std::string("GreedyExp"); } else { std::cout << "Exp Diff model for transformation. " << std::endl; this->m_TransformationModel = std::string("Exp"); } } void SetUpParameters() { /** Univariate Deformable Mapping */ // set up parameters for deformation restriction typename OptionType::Pointer restrictDeformationOption = this->m_Parser->GetOption( "Restrict-Deformation" ); if( restrictDeformationOption && restrictDeformationOption->GetNumberOfFunctions() ) { std::string temp = restrictDeformationOption->GetFunction()->GetName(); this->m_RestrictDeformation = this->m_Parser->template ConvertVector( temp ); if( this->m_RestrictDeformation.size() != ImageDimension ) { std::cout << " You input a vector of size : " << this->m_RestrictDeformation.size() << " for --Restrict-Deformation. The vector length does not match the image dimension. Ignoring. " << std::endl; for( unsigned int jj = 0; jj < this->m_RestrictDeformation.size(); jj++ ) { this->m_RestrictDeformation[jj] = 0; } } } // set up max iterations per level typename OptionType::Pointer numberOfIterationsOption = this->m_Parser->GetOption( "number-of-iterations" ); if( numberOfIterationsOption && numberOfIterationsOption->GetNumberOfFunctions() ) { std::string temp = this->m_Parser->GetOption( "number-of-iterations" )->GetFunction()->GetName(); this->m_Iterations = this->m_Parser->template ConvertVector(temp); this->SetNumberOfLevels(this->m_Iterations.size() ); } this->m_UseROI = false; typename OptionType::Pointer roiOption = this->m_Parser->GetOption( "roi" ); if( roiOption && roiOption->GetNumberOfFunctions() ) { std::string temp = roiOption->GetFunction()->GetName(); this->m_RoiNumbers = this->m_Parser->template ConvertVector(temp); if( temp.length() > 3 ) { this->m_UseROI = true; } } typename ParserType::OptionType::Pointer oOption = this->m_Parser->GetOption( "output-naming" ); if( oOption->GetNumberOfFunctions() ) { this->m_OutputNamingConvention = oOption->GetFunction( 0 )->GetName(); } typename ParserType::OptionType::Pointer thicknessOption = this->m_Parser->GetOption( "geodesic" ); if( thicknessOption->GetNumberOfFunctions() && ( thicknessOption->GetFunction( 0 )->GetName() == "true" || thicknessOption->GetFunction( 0 )->GetName() == "1" ) ) { // asymm forces this->m_ComputeThickness = 1; this->m_SyNFullTime = 2; } else if( thicknessOption->GetNumberOfFunctions() && thicknessOption->GetFunction( 0 )->GetName() == "2" ) { // symmetric forces this->m_ComputeThickness = 1; this->m_SyNFullTime = 1; } else { this->m_ComputeThickness = 0; // not full time varying stuff } /** * Get transformation model and associated parameters */ typename ParserType::OptionType::Pointer transformOption = this->m_Parser->GetOption( "transformation-model" ); this->SetTransformationModel( transformOption->GetFunction( 0 )->GetName() ); if( transformOption->GetFunction( 0 )->GetNumberOfParameters() >= 1 ) { std::string parameter = transformOption->GetFunction( 0 )->GetParameter( 0 ); TReal _temp = this->m_Parser->template Convert( parameter ); this->m_Gradstep = _temp; this->m_GradstepAltered = _temp; } else { this->m_Gradstep = 0.5; this->m_GradstepAltered = 0.5; } if( transformOption->GetFunction( 0 )->GetNumberOfParameters() >= 2 ) { std::string parameter = transformOption->GetFunction( 0 )->GetParameter( 1 ); this->m_NTimeSteps = this->m_Parser->template Convert( parameter ); } else { this->m_NTimeSteps = 1; } if( transformOption->GetFunction( 0 )->GetNumberOfParameters() >= 3 ) { std::string parameter = transformOption->GetFunction( 0 )->GetParameter( 2 ); this->m_DeltaTime = this->m_Parser->template Convert( parameter ); if( this->m_DeltaTime > 1 ) { this->m_DeltaTime = 1; } if( this->m_DeltaTime <= 0 ) { this->m_DeltaTime = 0.001; } std::cout << " set DT " << this->m_DeltaTime << std::endl; this->m_SyNType = 1; } else { this->m_DeltaTime = 0.1; } // if ( transformOption->GetFunction( 0 )->GetNumberOfParameters() >= 3 ) // { // std::string parameter = transformOption->GetFunction( 0 )->GetParameter( 2 ); // this->m_SymmetryType // = this->m_Parser->template Convert( parameter ); // } /** * Get regularization and associated parameters */ this->m_GradSmoothingparam = -1; this->m_TotalSmoothingparam = -1; this->m_GradSmoothingMeshSize.Fill( 0 ); this->m_TotalSmoothingMeshSize.Fill( 0 ); typename ParserType::OptionType::Pointer regularizationOption = this->m_Parser->GetOption( "regularization" ); if( regularizationOption->GetFunction( 0 )->GetName() == "Gauss" ) { if( regularizationOption->GetFunction( 0 )->GetNumberOfParameters() >= 1 ) { std::string parameter = regularizationOption->GetFunction( 0 )->GetParameter( 0 ); this->m_GradSmoothingparam = this->m_Parser->template Convert( parameter ); } else { this->m_GradSmoothingparam = 3; } if( regularizationOption->GetFunction( 0 )->GetNumberOfParameters() >= 2 ) { std::string parameter = regularizationOption->GetFunction( 0 )->GetParameter( 1 ); this->m_TotalSmoothingparam = this->m_Parser->template Convert( parameter ); } else { this->m_TotalSmoothingparam = 0.5; } if( regularizationOption->GetFunction( 0 )->GetNumberOfParameters() >= 3 ) { std::string parameter = regularizationOption->GetFunction( 0 )->GetParameter( 2 ); this->m_GaussianTruncation = this->m_Parser->template Convert( parameter ); } else { this->m_GaussianTruncation = 256; } std::cout << " Grad Step " << this->m_Gradstep << " total-smoothing " << this->m_TotalSmoothingparam << " gradient-smoothing " << this->m_GradSmoothingparam << std::endl; } else if( ( regularizationOption->GetFunction( 0 )->GetName() ).find( "DMFFD" ) != std::string::npos ) { if( regularizationOption->GetFunction( 0 )->GetNumberOfParameters() >= 1 ) { std::string parameter = regularizationOption->GetFunction( 0 )->GetParameter( 0 ); if( parameter.find( "x" ) != std::string::npos ) { std::vector gradMeshSize = this->m_Parser->template ConvertVector( parameter ); for( unsigned int d = 0; d < ImageDimension; d++ ) { this->m_GradSmoothingMeshSize[d] = gradMeshSize[d]; } } else { this->m_GradSmoothingparam = this->m_Parser->template Convert( parameter ); } } else { this->m_GradSmoothingparam = 3.0; } if( regularizationOption->GetFunction( 0 )->GetNumberOfParameters() >= 2 ) { std::string parameter = regularizationOption->GetFunction( 0 )->GetParameter( 1 ); if( parameter.find( "x" ) != std::string::npos ) { std::vector totalMeshSize = this->m_Parser->template ConvertVector( parameter ); for( unsigned int d = 0; d < ImageDimension; d++ ) { this->m_TotalSmoothingMeshSize[d] = totalMeshSize[d]; } } else { this->m_TotalSmoothingparam = this->m_Parser->template Convert( parameter ); } } else { this->m_TotalSmoothingparam = 0.5; } if( regularizationOption->GetFunction( 0 )->GetNumberOfParameters() >= 3 ) { std::string parameter = regularizationOption->GetFunction( 0 )->GetParameter( 2 ); this->m_BSplineFieldOrder = this->m_Parser->template Convert( parameter ); } else { this->m_BSplineFieldOrder = 3; } std::cout << " Grad Step " << this->m_Gradstep << " total-smoothing " << this->m_TotalSmoothingparam << " gradient-smoothing " << this->m_GradSmoothingparam << " bspline-field-order " << this->m_BSplineFieldOrder << std::endl; } else { this->m_GradSmoothingparam = 3; this->m_TotalSmoothingparam = 0.5; std::cout << " Default Regularization is Gaussian smoothing with : " << this->m_GradSmoothingparam << " & " << this->m_TotalSmoothingparam << std::endl; // itkExceptionMacro( "Invalid regularization: " << regularizationOption->GetFunction( 0 )->GetName() ); } } void ComputeMultiResolutionParameters(ImagePointer fixedImage ) { VectorType zero; zero.Fill(0); /** Compute scale factors */ this->m_FullDomainSpacing = fixedImage->GetSpacing(); this->m_FullDomainSize = fixedImage->GetRequestedRegion().GetSize(); this->m_CurrentDomainSpacing = fixedImage->GetSpacing(); this->m_CurrentDomainSize = fixedImage->GetRequestedRegion().GetSize(); this->m_CurrentDomainDirection = fixedImage->GetDirection(); this->m_FullDomainOrigin.Fill(0); this->m_CurrentDomainOrigin.Fill(0); /** alter the input size based on information gained from the ROI information - if available */ if( this->m_UseROI ) { for( unsigned int ii = 0; ii < ImageDimension; ii++ ) { this->m_FullDomainSize[ii] = (typename ImageType::SizeType::SizeValueType) this->m_RoiNumbers[ii + ImageDimension]; this->m_FullDomainOrigin[ii] = this->m_RoiNumbers[ii]; } std::cout << " ROI #s : size " << this->m_FullDomainSize << " orig " << this->m_FullDomainOrigin << std::endl; } RealType minimumSpacing = this->m_FullDomainSpacing.GetVnlVector().min_value(); // RealType maximumSpacing = this->m_FullDomainSpacing.GetVnlVector().max_value(); for( unsigned int d = 0; d < Dimension; d++ ) { RealType scaling = this->m_ScaleFactor; // If the user doesn't specify subsampling factors, we go to the // default behavior if( this->m_SubsamplingFactors.size() == 0 ) { scaling = vnl_math_min( this->m_ScaleFactor * minimumSpacing / this->m_FullDomainSpacing[d], static_cast( this->m_FullDomainSize[d] ) / 32.0 ); } if( scaling < 1.0 ) { scaling = 1.0; } this->m_CurrentDomainSpacing[d] = this->m_FullDomainSpacing[d] * scaling; this->m_CurrentDomainSize[d] = static_cast( this->m_FullDomainSpacing[d] * static_cast( this->m_FullDomainSize[d] ) / this->m_CurrentDomainSpacing[d] + 0.5 ); this->m_CurrentDomainOrigin[d] = static_cast( this->m_FullDomainSpacing[d] * static_cast( this->m_FullDomainOrigin[d] ) / this->m_CurrentDomainSpacing[d] + 0.5 ); } // this->m_Debug=true; if( this->m_Debug ) { std::cout << " outsize " << this->m_CurrentDomainSize << " curspc " << this->m_CurrentDomainSpacing << " fullspc " << this->m_FullDomainSpacing << " fullsz " << this->m_FullDomainSize << std::endl; } // this->m_Debug=false; if( !this->m_DisplacementField ) { /*FIXME -- need initial deformation strategy */ typename ImageType::RegionType region; region.SetSize( this->m_CurrentDomainSize); this->m_DisplacementField = AllocImage(region, this->m_CurrentDomainSpacing, fixedImage->GetOrigin(), fixedImage->GetDirection(), zero); std::cout << " allocated def field " << this->m_DisplacementField->GetDirection() << std::endl; // std::exception(); } else { this->m_DisplacementField = this->ExpandField(this->m_DisplacementField, this->m_CurrentDomainSpacing); if( this->m_TimeVaryingVelocity ) { this->ExpandVelocity(); } } } ImagePointer NormalizeImage( ImagePointer image) { typedef itk::MinimumMaximumImageFilter MinMaxFilterType; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetInput( image ); minMaxFilter->Update(); TReal min = minMaxFilter->GetMinimum(); TReal shift = -1.0 * static_cast( min ); TReal scale = static_cast( minMaxFilter->GetMaximum() ); scale += shift; scale = 1.0 / scale; typedef itk::ShiftScaleImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetShift( shift ); filter->SetScale( scale ); filter->Update(); return filter->GetOutput(); } void DeformableOptimization() { DisplacementFieldPointer updateField = ITK_NULLPTR; this->SetUpParameters(); typename ImageType::SpacingType spacing; VectorType zero; zero.Fill(0); std::cout << " setting N-TimeSteps = " << this->m_NTimeSteps << " trunc " << this->m_GaussianTruncation << std::endl; // Get subsample factors and gaussian smoothing sigmas if specified // by the user. typename OptionType::Pointer subsamplingOption = this->m_Parser->GetOption( "subsampling-factors" ); if( subsamplingOption && subsamplingOption->GetNumberOfFunctions() ) { std::string subsamplingfactors = subsamplingOption->GetFunction()->GetName(); if( subsamplingfactors.size() > 0 ) { std::vector factors = this->m_Parser->template ConvertVector( subsamplingfactors ); if( factors.size() == this->m_NumberOfLevels ) { this->m_SubsamplingFactors.SetSize( this->m_NumberOfLevels ); for( unsigned int d = 0; d < this->m_NumberOfLevels; d++ ) { this->m_SubsamplingFactors[d] = factors[d]; } } // else // { // itkWarningMacro( "The number of levels does not match the size of factors." // << " Using default settings." ); // } } } typename OptionType::Pointer gaussianSmoothingSigmasOption = this->m_Parser->GetOption( "gaussian-smoothing-sigmas" ); if( gaussianSmoothingSigmasOption && gaussianSmoothingSigmasOption->GetNumberOfFunctions() ) { std::string gaussiansmoothingsigmas = gaussianSmoothingSigmasOption->GetFunction()->GetName(); std::vector sigmas = this->m_Parser->template ConvertVector( gaussiansmoothingsigmas ); if( sigmas.size() == this->m_NumberOfLevels ) { this->m_GaussianSmoothingSigmas.SetSize( this->m_NumberOfLevels ); for( unsigned int d = 0; d < this->m_NumberOfLevels; d++ ) { this->m_GaussianSmoothingSigmas[d] = sigmas[d]; } } // else // { // itkWarningMacro( "The number of levels does not match the size of sigmas." // << " Using default settings." ); // } } unsigned int maxits = 0; for( unsigned int currentLevel = 0; currentLevel < this->m_NumberOfLevels; currentLevel++ ) { if( this->m_Iterations[currentLevel] > maxits ) { maxits = this->m_Iterations[currentLevel]; } } if( maxits == 0 ) { this->m_DisplacementField = ITK_NULLPTR; this->m_InverseDisplacementField = ITK_NULLPTR; // this->ComputeMultiResolutionParameters(this->m_SimilarityMetrics[0]->GetFixedImage()); return; } /* this is a hack to force univariate mappings in the future, we will re-cast this framework s.t. multivariate images can be used */ unsigned int numberOfMetrics = this->m_SimilarityMetrics.size(); for( unsigned int metricCount = 1; metricCount < numberOfMetrics; metricCount++ ) { this->m_SimilarityMetrics[metricCount]->GetFixedImage()->SetOrigin( this->m_SimilarityMetrics[0]->GetFixedImage()->GetOrigin() ); this->m_SimilarityMetrics[metricCount]->GetFixedImage()->SetDirection( this->m_SimilarityMetrics[0]->GetFixedImage()->GetDirection() ); this->m_SimilarityMetrics[metricCount]->GetMovingImage()->SetOrigin( this->m_SimilarityMetrics[0]->GetMovingImage( )->GetOrigin() ); this->m_SimilarityMetrics[metricCount]->GetMovingImage()->SetDirection( this->m_SimilarityMetrics[0]->GetMovingImage()->GetDirection() ); } /* here, we assign all point set pointers to any single non-null point-set pointer */ for( unsigned int metricCount = 0; metricCount < numberOfMetrics; metricCount++ ) { for( unsigned int metricCount2 = 0; metricCount2 < numberOfMetrics; metricCount2++ ) { if( this->m_SimilarityMetrics[metricCount]->GetFixedPointSet() ) { this->m_SimilarityMetrics[metricCount2]->SetFixedPointSet( this->m_SimilarityMetrics[metricCount]->GetFixedPointSet() ); } if( this->m_SimilarityMetrics[metricCount]->GetMovingPointSet() ) { this->m_SimilarityMetrics[metricCount2]->SetMovingPointSet( this->m_SimilarityMetrics[metricCount]->GetMovingPointSet() ); } } } this->m_SmoothFixedImages.resize(numberOfMetrics, ITK_NULLPTR); this->m_SmoothMovingImages.resize(numberOfMetrics, ITK_NULLPTR); for( unsigned int currentLevel = 0; currentLevel < this->m_NumberOfLevels; currentLevel++ ) { this->m_CurrentLevel = currentLevel; typedef Vector ProfilePointDataType; typedef Image CurveType; typedef PointSet EnergyProfileType; typedef typename EnergyProfileType::PointType ProfilePointType; typedef typename EnergyProfileType::Pointer EnergyProfilePointer; std::vector energyProfiles; energyProfiles.resize( numberOfMetrics ); for( unsigned int qq = 0; qq < numberOfMetrics; qq++ ) { energyProfiles[qq] = EnergyProfileType::New(); energyProfiles[qq]->Initialize(); } ImagePointer fixedImage; ImagePointer movingImage; this->m_GradstepAltered = this->m_Gradstep; if( this->m_SubsamplingFactors.Size() == 0 ) { this->m_ScaleFactor = std::pow( 2.0, (int)static_cast( this->m_NumberOfLevels - currentLevel - 1 ) ); } else { this->m_ScaleFactor = this->m_SubsamplingFactors[currentLevel]; } std::cout << " ScaleFactor " << this->m_ScaleFactor; if( this->m_GaussianSmoothingSigmas.size() > 0 ) { std::cout << " smoothing sigma " << this->m_GaussianSmoothingSigmas[currentLevel]; } std::cout << " nlev " << this->m_NumberOfLevels << " curl " << currentLevel << std::endl; /** FIXME -- here we assume the metrics all have the same image */ fixedImage = this->m_SimilarityMetrics[0]->GetFixedImage(); movingImage = this->m_SimilarityMetrics[0]->GetMovingImage(); if( !this->m_ReferenceSpaceImage ) { this->m_ReferenceSpaceImage = fixedImage; } this->ComputeMultiResolutionParameters(this->m_ReferenceSpaceImage); std::cout << " Its at this level " << this->m_Iterations[currentLevel] << std::endl; /* generate smoothed images for all metrics */ for( unsigned int metricCount = 0; metricCount < numberOfMetrics; metricCount++ ) { if( this->m_GaussianSmoothingSigmas.size() == 0 ) { this->m_SmoothFixedImages[metricCount] = this->SmoothImageToScale( this->m_SimilarityMetrics[metricCount]->GetFixedImage(), this->m_ScaleFactor ); this->m_SmoothMovingImages[metricCount] = this->SmoothImageToScale( this->m_SimilarityMetrics[metricCount]->GetMovingImage(), this->m_ScaleFactor ); } else { this->m_SmoothFixedImages[metricCount] = this->GaussianSmoothImage( this->m_SimilarityMetrics[metricCount]->GetFixedImage(), this->m_GaussianSmoothingSigmas[currentLevel] ); this->m_SmoothMovingImages[metricCount] = this->GaussianSmoothImage( this->m_SimilarityMetrics[metricCount]->GetMovingImage(), this->m_GaussianSmoothingSigmas[currentLevel] ); } } fixedImage = this->m_SmoothFixedImages[0]; movingImage = this->m_SmoothMovingImages[0]; unsigned int nmet = this->m_SimilarityMetrics.size(); this->m_LastEnergy.resize(nmet, 1.e12); this->m_Energy.resize(nmet, 1.e9); this->m_EnergyBad.resize(nmet, 0); bool converged = false; this->m_CurrentIteration = 0; if( this->GetTransformationModel() != std::string("SyN") ) { this->m_FixedImageAffineTransform = ITK_NULLPTR; } while( !converged ) { for( unsigned int metricCount = 0; metricCount < numberOfMetrics; metricCount++ ) { this->m_SimilarityMetrics[metricCount]->GetModifiableMetric()->SetIterations(this->m_CurrentIteration); } if( this->GetTransformationModel() == std::string("Elast") ) { if( this->m_Iterations[currentLevel] > 0 ) { this->ElasticRegistrationUpdate(fixedImage, movingImage); } } else if( this->GetTransformationModel() == std::string("SyN") ) { if( currentLevel > 0 ) { this->m_SyNF = this->ExpandField(this->m_SyNF, this->m_CurrentDomainSpacing); this->m_SyNFInv = this->ExpandField(this->m_SyNFInv, this->m_CurrentDomainSpacing); this->m_SyNM = this->ExpandField(this->m_SyNM, this->m_CurrentDomainSpacing); this->m_SyNMInv = this->ExpandField(this->m_SyNMInv, this->m_CurrentDomainSpacing); } if( this->m_Iterations[currentLevel] > 0 ) { if( this->m_SyNType && this->m_ComputeThickness ) { this->DiReCTUpdate(fixedImage, movingImage, this->m_SimilarityMetrics[0]->GetFixedPointSet(), this->m_SimilarityMetrics[0]->GetMovingPointSet() ); } else if( this->m_SyNType ) { this->SyNTVRegistrationUpdate(fixedImage, movingImage, this->m_SimilarityMetrics[0]->GetFixedPointSet(), this->m_SimilarityMetrics[0]->GetMovingPointSet() ); } else { this->SyNRegistrationUpdate(fixedImage, movingImage, this->m_SimilarityMetrics[0]->GetFixedPointSet(), this->m_SimilarityMetrics[0]->GetMovingPointSet() ); } } else if( this->m_SyNType ) { this->UpdateTimeVaryingVelocityFieldWithSyNFandSyNM(); } // this->CopyOrAddToVelocityField( this->m_SyNF, 0 , false); } else if( this->GetTransformationModel() == std::string("Exp") ) { if( this->m_Iterations[currentLevel] > 0 ) { this->DiffeomorphicExpRegistrationUpdate(fixedImage, movingImage, this->m_SimilarityMetrics[0]->GetFixedPointSet(), this->m_SimilarityMetrics[0]->GetMovingPointSet() ); } } else if( this->GetTransformationModel() == std::string("GreedyExp") ) { if( this->m_Iterations[currentLevel] > 0 ) { this->GreedyExpRegistrationUpdate(fixedImage, movingImage, this->m_SimilarityMetrics[0]->GetFixedPointSet(), this->m_SimilarityMetrics[0]->GetMovingPointSet() ); } } this->m_CurrentIteration++; /** * This is where we track the energy profile to check for convergence. */ for( unsigned int qq = 0; qq < numberOfMetrics; qq++ ) { ProfilePointType point; point[0] = this->m_CurrentIteration - 1; ProfilePointDataType energy; energy[0] = this->m_Energy[qq]; energyProfiles[qq]->SetPoint( this->m_CurrentIteration - 1, point ); energyProfiles[qq]->SetPointData( this->m_CurrentIteration - 1, energy ); } /** * If there are a sufficent number of iterations, fit a quadratic * single B-spline span to the number of energy profile points * in the first metric. To test convergence, evaluate the derivative * at the end of the profile to determine if >= 0. To change to a * window of the energy profile, simply change the origin (assuming that * the desired window will start at the user-specified origin and * end at the current iteration). */ // Added by Paul: allow weighted metric to be used here typename ParserType::OptionType::Pointer regularizationOption = this->m_Parser->GetOption( "use-all-metrics-for-convergence" ); bool use_all_metrics = (atoi(regularizationOption->GetFunction()->GetName().c_str() ) > 0); unsigned int domtar = 12; if( this->m_CurrentIteration > domtar ) { typedef BSplineScatteredDataPointSetToImageFilter BSplinerType; typename BSplinerType::Pointer bspliner = BSplinerType::New(); typename CurveType::PointType origin; unsigned int domainorigin = 0; unsigned int domainsize = this->m_CurrentIteration - domainorigin; if( this->m_CurrentIteration > domtar ) { domainsize = domtar; domainorigin = this->m_CurrentIteration - domainsize; } origin.Fill( domainorigin ); typename CurveType::SizeType size; size.Fill( domainsize ); typename CurveType::SpacingType _spacing; _spacing.Fill( 1 ); typename EnergyProfileType::Pointer energyProfileWindow = EnergyProfileType::New(); energyProfileWindow->Initialize(); unsigned int windowBegin = static_cast( origin[0] ); TReal totale = 0; for( unsigned int qq = windowBegin; qq < this->m_CurrentIteration; qq++ ) { ProfilePointType point; point[0] = qq; ProfilePointDataType energy; energy.Fill( 0 ); if( use_all_metrics ) { for( unsigned int im = 0; im < numberOfMetrics; im++ ) { ProfilePointType pm; pm[0] = qq; ProfilePointDataType em; em.Fill(0); energyProfiles[im]->GetPointData( qq, &em ); RealType weight = this->m_SimilarityMetrics[im]->GetWeightScalar(); energy[0] += weight * em[0]; } } else { energyProfiles[0]->GetPointData( qq, &energy ); } totale += energy[0]; energyProfileWindow->SetPoint( qq - windowBegin, point ); energyProfileWindow->SetPointData( qq - windowBegin, energy ); } // std::cout <<" totale " << totale << std::endl; if( totale > 0 ) { totale *= (-1.0); } for( unsigned int qq = windowBegin; qq < this->m_CurrentIteration; qq++ ) { ProfilePointDataType energy; energy.Fill(0); if( use_all_metrics ) { for( unsigned int im = 0; im < numberOfMetrics; im++ ) { ProfilePointType pm; pm[0] = qq; ProfilePointDataType em; em.Fill(0); energyProfiles[im]->GetPointData( qq, &em ); RealType weight = this->m_SimilarityMetrics[im]->GetWeightScalar(); energy[0] += weight * em[0]; } } else { energyProfiles[0]->GetPointData( qq, &energy ); } energyProfileWindow->SetPointData( qq - windowBegin, energy / totale); } bspliner->SetInput( energyProfileWindow ); bspliner->SetOrigin( origin ); bspliner->SetSpacing( _spacing ); bspliner->SetSize( size ); bspliner->SetNumberOfLevels( 1 ); unsigned int order = 1; bspliner->SetSplineOrder( order ); typename BSplinerType::ArrayType ncps; ncps.Fill( order + 1); // single span, order = 2 bspliner->SetNumberOfControlPoints( ncps ); bspliner->Update(); typedef BSplineControlPointImageFunction BSplinerType2; typename BSplinerType2::Pointer bspliner2 = BSplinerType2::New(); bspliner2->SetOrigin( origin ); bspliner2->SetSpacing( _spacing ); bspliner2->SetSize( size ); bspliner2->SetSplineOrder( 1 ); bspliner2->SetInputImage( bspliner->GetPhiLattice() ); ProfilePointType endPoint; endPoint[0] = static_cast( this->m_CurrentIteration - domainsize * 0.5 ); typename BSplinerType2::GradientType gradient = bspliner2->EvaluateGradientAtParametricPoint( endPoint ); this->m_ESlope = gradient[0][0]; if( this->m_ESlope < 0.0001 && this->m_CurrentIteration > domtar ) { converged = true; } std::cout << " E-Slope " << this->m_ESlope; // << std::endl; } for( unsigned int qq = 0; qq < this->m_Energy.size(); qq++ ) { if( qq == 0 ) { std::cout << " iteration " << this->m_CurrentIteration; } std::cout << " energy " << qq << " : " << this->m_Energy[qq]; // << " Last " << // this->m_LastEnergy[qq]; if( this->m_LastEnergy[qq] < this->m_Energy[qq] ) { this->m_EnergyBad[qq]++; } } for( unsigned int qq = 0; qq < this->m_Energy.size(); qq++ ) { if( this->m_CurrentIteration <= 1 ) { this->m_EnergyBad[qq] = 0; } } // if ( this->m_EnergyBad[0] > 2) // { // this->m_GradstepAltered*=0.8; // std::cout <<" reducing gradstep " << this->m_GradstepAltered; // this->m_EnergyBad[this->m_Energy.size()-1]=0; // } std::cout << std::endl; if( this->m_CurrentIteration >= this->m_Iterations[currentLevel] ) { converged = true; } // || this->m_EnergyBad[0] >= 6 ) // if( converged && this->m_CurrentIteration >= this->m_Iterations[currentLevel] ) { std::cout << " tired convergence: reached max iterations " << std::endl; } else if( converged ) { std::cout << " Converged due to oscillation in optimization "; for( unsigned int qq = 0; qq < this->m_Energy.size(); qq++ ) { std::cout << " metric " << qq << " bad " << this->m_EnergyBad[qq] << " "; } std::cout << std::endl; } } } if( this->GetTransformationModel() == std::string("SyN") ) { // TReal timestep=1.0/(TReal)this->m_NTimeSteps; // unsigned int nts=this->m_NTimeSteps; if( this->m_SyNType ) { // this->m_SyNFInv = this->IntegrateConstantVelocity(this->m_SyNF, nts, timestep*(-1.)); // this->m_SyNMInv = this->IntegrateConstantVelocity(this->m_SyNM, nts, timestep*(-1.)); // this->m_SyNF= this->IntegrateConstantVelocity(this->m_SyNF, nts, timestep); // this->m_SyNM= this->IntegrateConstantVelocity(this->m_SyNM, // nts, timestep); // DisplacementFieldPointer fdiffmap = this->IntegrateVelocity(0,0.5); // this->m_SyNFInv = this->IntegrateVelocity(0.5,0); // DisplacementFieldPointer mdiffmap = this->IntegrateVelocity(0.5,1); // this->m_SyNMInv = this->IntegrateVelocity(1,0.5); // this->m_SyNM=this->CopyDisplacementField(mdiffmap); // this->m_SyNF=this->CopyDisplacementField(fdiffmap); this->m_DisplacementField = this->IntegrateVelocity(0, 1); // ImagePointer wmimage= this->WarpMultiTransform( // this->m_SmoothFixedImages[0],this->m_SmoothMovingImages[0], this->m_AffineTransform, // this->m_DisplacementField, false , this->m_ScaleFactor ); this->m_InverseDisplacementField = this->IntegrateVelocity(1, 0); } else { this->m_InverseDisplacementField = this->CopyDisplacementField( this->m_SyNM); this->ComposeDiffs(this->m_SyNF, this->m_SyNMInv, this->m_DisplacementField, 1); this->ComposeDiffs(this->m_SyNM, this->m_SyNFInv, this->m_InverseDisplacementField, 1); } } else if( this->GetTransformationModel() == std::string("Exp") ) { DisplacementFieldPointer diffmap = this->IntegrateConstantVelocity( this->m_DisplacementField, (unsigned int)this->m_NTimeSteps, 1 ); // 1.0/ (TReal)this->m_NTimeSteps); DisplacementFieldPointer invdiffmap = this->IntegrateConstantVelocity(this->m_DisplacementField, (unsigned int) this->m_NTimeSteps, -1 ); // -1.0/(TReal)this->m_NTimeSteps); this->m_InverseDisplacementField = invdiffmap; this->m_DisplacementField = diffmap; AffineTransformPointer invaff = ITK_NULLPTR; if( this->m_AffineTransform ) { invaff = AffineTransformType::New(); this->m_AffineTransform->GetInverse(invaff); if( this->m_Debug ) { std::cout << " ??????invaff " << this->m_AffineTransform << std::endl << std::endl; } if( this->m_Debug ) { std::cout << " invaff?????? " << invaff << std::endl << std::endl; } } } else if( this->GetTransformationModel() == std::string("GreedyExp") ) { DisplacementFieldPointer diffmap = this->m_DisplacementField; this->m_InverseDisplacementField = ITK_NULLPTR; this->m_DisplacementField = diffmap; AffineTransformPointer invaff = ITK_NULLPTR; if( this->m_AffineTransform ) { invaff = AffineTransformType::New(); this->m_AffineTransform->GetInverse(invaff); if( this->m_Debug ) { std::cout << " ??????invaff " << this->m_AffineTransform << std::endl << std::endl; } if( this->m_Debug ) { std::cout << " invaff?????? " << invaff << std::endl << std::endl; } } } this->m_DisplacementField->SetOrigin( this->m_ReferenceSpaceImage->GetOrigin() ); this->m_DisplacementField->SetDirection( this->m_ReferenceSpaceImage->GetDirection() ); if( this->m_InverseDisplacementField ) { this->m_InverseDisplacementField->SetOrigin( this->m_ReferenceSpaceImage->GetOrigin() ); this->m_InverseDisplacementField->SetDirection( this->m_ReferenceSpaceImage->GetDirection() ); } if( this->m_TimeVaryingVelocity ) { std::string outname = localANTSGetFilePrefix(this->m_OutputNamingConvention.c_str() ) + std::string( "velocity.mhd"); typename itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); writer->SetFileName(outname.c_str() ); writer->SetInput( this->m_TimeVaryingVelocity); writer->UpdateLargestPossibleRegion(); // writer->Write(); std::cout << " write tv field " << outname << std::endl; // WriteImage( this->m_TimeVaryingVelocity , outname.c_str()); } } void DiffeomorphicExpRegistrationUpdate(ImagePointer fixedImage, ImagePointer movingImage, PointSetPointer fpoints = NULL, PointSetPointer mpoints = NULL); void GreedyExpRegistrationUpdate(ImagePointer fixedImage, ImagePointer movingImage, PointSetPointer fpoints = NULL, PointSetPointer mpoints = NULL); void SyNRegistrationUpdate(ImagePointer fixedImage, ImagePointer movingImage, PointSetPointer fpoints = NULL, PointSetPointer mpoints = NULL); void SyNExpRegistrationUpdate(ImagePointer fixedImage, ImagePointer movingImage, PointSetPointer fpoints = NULL, PointSetPointer mpoints = NULL); void SyNTVRegistrationUpdate(ImagePointer fixedImage, ImagePointer movingImage, PointSetPointer fpoints = NULL, PointSetPointer mpoints = NULL); void DiReCTUpdate(ImagePointer fixedImage, ImagePointer movingImage, PointSetPointer fpoints = NULL, PointSetPointer mpoints = NULL); /** allows one to copy or add a field to a time index within the velocity * field */ void UpdateTimeVaryingVelocityFieldWithSyNFandSyNM(); void CopyOrAddToVelocityField( TimeVaryingVelocityFieldPointer velocity, DisplacementFieldPointer update1, DisplacementFieldPointer update2, TReal timept); // void CopyOrAddToVelocityField( DisplacementFieldPointer update, unsigned int timeindex, bool // CopyIsTrueOtherwiseAdd); void ElasticRegistrationUpdate(ImagePointer /* fixedImage */, ImagePointer /* xxxxmovingImage */) { typename ImageType::SpacingType spacing; VectorType zero; zero.Fill(0); DisplacementFieldPointer updateField; updateField = this->ComputeUpdateField(this->m_DisplacementField, ITK_NULLPTR, ITK_NULLPTR, ITK_NULLPTR, ITK_NULLPTR); typedef ImageRegionIteratorWithIndex Iterator; Iterator dIter(this->m_DisplacementField, this->m_DisplacementField->GetLargestPossibleRegion() ); for( dIter.GoToBegin(); !dIter.IsAtEnd(); ++dIter ) { typename ImageType::IndexType index = dIter.GetIndex(); VectorType vec = updateField->GetPixel(index); dIter.Set(dIter.Get() + vec * this->m_Gradstep); } if( this->m_Debug ) { std::cout << " updated elast " << " up-sz " << updateField->GetLargestPossibleRegion() << std::endl; std::cout << " t-sz " << this->m_DisplacementField->GetLargestPossibleRegion() << std::endl; } this->SmoothDisplacementField(this->m_DisplacementField, false); return; } ImagePointer WarpImageBackward( ImagePointer image, DisplacementFieldPointer field ) { typedef WarpImageFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); warper->SetInput(image); warper->SetDisplacementField( field ); warper->SetEdgePaddingValue( 0); warper->SetOutputSpacing(field->GetSpacing() ); warper->SetOutputOrigin( field->GetOrigin() ); warper->Update(); return warper->GetOutput(); } void ComposeDiffs(DisplacementFieldPointer fieldtowarpby, DisplacementFieldPointer field, DisplacementFieldPointer fieldout, TReal sign); void SetSimilarityMetrics( SimilarityMetricListType S ) { this->m_SimilarityMetrics = S; } void SetFixedPointSet( PointSetPointer p ) { this->m_FixedPointSet = p; } void SetMovingPointSet( PointSetPointer p ) { this->m_MovingPointSet = p; } void SetDeltaTime( TReal t) { this->m_DeltaTime = t; } TReal InvertField(DisplacementFieldPointer field, DisplacementFieldPointer inverseField, TReal weight = 1.0, TReal toler = 0.1, int maxiter = 20, bool /* print */ = false) { TReal mytoler = toler; unsigned int mymaxiter = maxiter; typename ParserType::OptionType::Pointer thicknessOption = this->m_Parser->GetOption( "go-faster" ); if( thicknessOption->GetFunction( 0 )->GetName() == "true" || thicknessOption->GetFunction( 0 )->GetName() == "1" ) { mytoler = 0.5; maxiter = 12; } VectorType zero; zero.Fill(0); // if (this->GetElapsedIterations() < 2 ) maxiter=10; ImagePointer TRealImage = AllocImage(field); typedef typename DisplacementFieldType::PixelType DispVectorType; typedef typename DisplacementFieldType::IndexType DispIndexType; typedef ImageRegionIteratorWithIndex Iterator; DisplacementFieldPointer lagrangianInitCond = AllocImage(field); DisplacementFieldPointer eulerianInitCond = AllocImage(field); typedef typename DisplacementFieldType::SizeType SizeType; SizeType size = field->GetLargestPossibleRegion().GetSize(); typename ImageType::SpacingType spacing = field->GetSpacing(); unsigned long npix = 1; for( unsigned int j = 0; j < ImageDimension; j++ ) // only use in-plane spacing { npix *= field->GetLargestPossibleRegion().GetSize()[j]; } TReal max = 0; Iterator iter( field, field->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { DispIndexType index = iter.GetIndex(); DispVectorType vec1 = iter.Get(); DispVectorType newvec = vec1 * weight; lagrangianInitCond->SetPixel(index, newvec); TReal mag = 0; for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { mag += newvec[jj] * newvec[jj]; } mag = sqrt(mag); if( mag > max ) { max = mag; } } eulerianInitCond->FillBuffer(zero); TReal scale = (1.) / max; if( scale > 1. ) { scale = 1.0; } // TReal initscale=scale; Iterator vfIter( inverseField, inverseField->GetLargestPossibleRegion() ); // int num=10; // for (int its=0; its subpix && meandif > subpix*0.1 && badct < 2 )//&& ct < 20 && denergy > 0) // TReal length=0.0; TReal stepl = 2.; TReal epsilon = (TReal)size[0] / 256; if( epsilon > 1 ) { epsilon = 1; } while( difmag > mytoler && ct 0.001 ) { meandif = 0.0; // this field says what position the eulerian field should contain in the E domain this->ComposeDiffs(inverseField, lagrangianInitCond, eulerianInitCond, 1); difmag = 0.0; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { DispIndexType index = vfIter.GetIndex(); DispVectorType update = eulerianInitCond->GetPixel(index); TReal mag = 0; for( unsigned int j = 0; j < ImageDimension; j++ ) { update[j] *= (-1.0); mag += (update[j] / spacing[j]) * (update[j] / spacing[j]); } mag = sqrt(mag); meandif += mag; if( mag > difmag ) { difmag = mag; } // if (mag < 1.e-2) update.Fill(0); eulerianInitCond->SetPixel(index, update); TRealImage->SetPixel(index, mag); } meandif /= (TReal)npix; if( ct == 0 ) { epsilon = 0.75; } else { epsilon = 0.5; } stepl = difmag * epsilon; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { TReal val = TRealImage->GetPixel(vfIter.GetIndex() ); DispVectorType update = eulerianInitCond->GetPixel(vfIter.GetIndex() ); if( val > stepl ) { update = update * (stepl / val); } DispVectorType upd = vfIter.Get() + update * (epsilon); vfIter.Set(upd); } ct++; } // std::cout <<" difmag " << difmag << ": its " << ct << std::endl; return difmag; } void SetUseNearestNeighborInterpolation( bool useNN) { this->m_UseNN = useNN; } void SetUseBSplineInterpolation( bool useNN) { this->m_UseBSplineInterpolation = useNN; } VectorType IntegratePointVelocity(TReal starttimein, TReal finishtimein, IndexType startPoint); protected: DisplacementFieldPointer IntegrateVelocity(TReal, TReal); DisplacementFieldPointer IntegrateLandmarkSetVelocity(TReal, TReal, PointSetPointer movingpoints, ImagePointer referenceimage ); ImagePointer MakeSubImage( ImagePointer bigimage) { typedef itk::ImageRegionIteratorWithIndex Iterator; typename ImageType::RegionType region; region.SetSize( this->m_CurrentDomainSize); typename ImageType::IndexType index; index.Fill(0); region.SetIndex(index); ImagePointer varimage = AllocImage(region, this->m_CurrentDomainSpacing, bigimage->GetOrigin(), bigimage->GetDirection(), 0); typename ImageType::IndexType cornerind; cornerind.Fill(0); for( unsigned int ii = 0; ii < ImageDimension; ii++ ) { TReal diff = (TReal) this->m_CurrentDomainOrigin[ii] - (TReal) this->m_CurrentDomainSize[ii] / 2; if( diff < 0 ) { diff = 0; } cornerind[ii] = (unsigned long) diff; } // std::cout << " corner index " << cornerind << std::endl; Iterator vfIter2( bigimage, bigimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { typename ImageType::IndexType origindex = vfIter2.GetIndex(); typename ImageType::IndexType _index = vfIter2.GetIndex(); bool oktosample = true; for( unsigned int ii = 0; ii < ImageDimension; ii++ ) { TReal centerbasedcoord = (origindex[ii] - this->m_CurrentDomainOrigin[ii]); // TReal diff = // index[ii]=origindex[ii]-cornerind[ii]; if( fabs(centerbasedcoord) > (this->m_CurrentDomainSize[ii] / 2 - 1) ) { oktosample = false; } } if( oktosample ) { // std::cout << " index " << index << " origindex " << origindex << " ok? " << oktosample << // std::endl; varimage->SetPixel(_index, bigimage->GetPixel(origindex) ); } } // std::cout << " sizes " << varimage->GetLargestPossibleRegion().GetSize() << " bigimage " << // bigimage->GetLargestPossibleRegion().GetSize() << std::endl; return varimage; } TReal MeasureDeformation(DisplacementFieldPointer field, int /* option */ = 0) { typedef typename DisplacementFieldType::PixelType DispVectorType; typedef typename DisplacementFieldType::IndexType DispIndexType; typedef typename DisplacementFieldType::SizeType SizeType; typedef ImageRegionIteratorWithIndex Iterator; // all we have to do here is add the local field to the global field. Iterator vfIter( field, field->GetLargestPossibleRegion() ); SizeType size = field->GetLargestPossibleRegion().GetSize(); unsigned long ct = 1; TReal totalmag = 0; TReal maxstep = 0; // this->m_EuclideanNorm=0; typename ImageType::SpacingType myspacing = field->GetSpacing(); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { DispIndexType index = vfIter.GetIndex(); DispIndexType rindex = vfIter.GetIndex(); DispIndexType lindex = vfIter.GetIndex(); DispVectorType update = vfIter.Get(); TReal mag = 0.0; TReal stepl = 0.0; for( unsigned int i = 0; i < ImageDimension; i++ ) { rindex = index; lindex = index; if( (int)index[i] < (int)(size[i] - 2) ) { rindex[i] = rindex[i] + 1; } if( index[i] > 2 ) { lindex[i] = lindex[i] - 1; } DispVectorType rupdate = field->GetPixel(rindex); DispVectorType lupdate = field->GetPixel(lindex); DispVectorType dif = rupdate - lupdate; for( unsigned int tt = 0; tt < ImageDimension; tt++ ) { stepl += update[tt] * update[tt] / (myspacing[tt] * myspacing[tt]); mag += dif[tt] * dif[tt] / (myspacing[tt] * myspacing[tt]); } } stepl = sqrt(stepl); mag = sqrt(mag); if( stepl > maxstep ) { maxstep = stepl; } ct++; totalmag += mag; // this->m_EuclideanNorm+=stepl; } // this->m_EuclideanNorm/=ct; // this->m_ElasticPathLength = totalmag/ct; // this->m_LinftyNorm = maxstep; // std::cout << " Elast path length " << this->m_ElasticPathLength << " L inf norm " << this->m_LinftyNorm << // std::endl; // if (this->m_ElasticPathLength >= this->m_ArcLengthGoal) // if (maxstep >= this->m_ArcLengthGoal) { // this->StopRegistration(); // scale the field to the right length // TReal scale=this->m_ArcLengthGoal/this->m_ElasticPathLength; // for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter )vfIter.Set(vfIter.Get()*scale); } // if (this->m_LinftyNorm <= 0) this->m_LinftyNorm=1; // if (this->m_ElasticPathLength <= 0) this->m_ElasticPathLength=0; // if (this->m_EuclideanNorm <= 0) this->m_EuclideanNorm=0; // if (option==0) return this->m_ElasticPathLength; // else if (option==2) return this->m_EuclideanNorm; // else return maxstep; } ANTSImageRegistrationOptimizer(); virtual ~ANTSImageRegistrationOptimizer() { } void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; private: ANTSImageRegistrationOptimizer( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented typename VelocityFieldInterpolatorType::Pointer m_VelocityFieldInterpolator; typename ImageType::SizeType m_CurrentDomainSize; typename ImageType::PointType m_CurrentDomainOrigin; typename ImageType::SpacingType m_CurrentDomainSpacing; typename ImageType::DirectionType m_CurrentDomainDirection; typename ImageType::SizeType m_FullDomainSize; typename ImageType::PointType m_FullDomainOrigin; typename ImageType::SpacingType m_FullDomainSpacing; AffineTransformPointer m_AffineTransform; AffineTransformPointer m_FixedImageAffineTransform; DisplacementFieldPointer m_DisplacementField; DisplacementFieldPointer m_InverseDisplacementField; std::vector m_GradientDescentParameters; std::vector m_MetricScalarWeights; std::vector m_SmoothFixedImages; std::vector m_SmoothMovingImages; bool m_Debug; unsigned int m_NumberOfLevels; typename ParserType::Pointer m_Parser; SimilarityMetricListType m_SimilarityMetrics; ImagePointer m_MaskImage; ImagePointer m_ReferenceSpaceImage; TReal m_ScaleFactor; bool m_UseMulti; bool m_UseROI; bool m_UseNN; bool m_UseBSplineInterpolation; unsigned int m_CurrentIteration; unsigned int m_CurrentLevel; std::string m_TransformationModel; std::string m_OutputNamingConvention; PointSetPointer m_FixedPointSet; PointSetPointer m_MovingPointSet; std::vector m_Iterations; std::vector m_RestrictDeformation; std::vector m_RoiNumbers; TReal m_GradSmoothingparam; TReal m_TotalSmoothingparam; TReal m_Gradstep; TReal m_GradstepAltered; TReal m_NTimeSteps; TReal m_GaussianTruncation; TReal m_DeltaTime; TReal m_ESlope; /** energy stuff */ std::vector m_Energy; std::vector m_LastEnergy; std::vector m_EnergyBad; /** for SyN only */ DisplacementFieldPointer m_SyNF; DisplacementFieldPointer m_SyNFInv; DisplacementFieldPointer m_SyNM; DisplacementFieldPointer m_SyNMInv; TimeVaryingVelocityFieldPointer m_TimeVaryingVelocity; TimeVaryingVelocityFieldPointer m_LastTimeVaryingVelocity; TimeVaryingVelocityFieldPointer m_LastTimeVaryingUpdate; unsigned int m_SyNType; /** for BSpline stuff */ unsigned int m_BSplineFieldOrder; ArrayType m_GradSmoothingMeshSize; ArrayType m_TotalSmoothingMeshSize; /** For thickness calculation */ ImagePointer m_HitImage; ImagePointer m_ThickImage; unsigned int m_ComputeThickness; unsigned int m_SyNFullTime; /** Subsampling/Gaussian smoothing parameters */ Array m_GaussianSmoothingSigmas; Array m_SubsamplingFactors; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkANTSImageRegistrationOptimizer.cxx" #endif #endif ants-2.2.0/ImageRegistration/itkANTSImageTransformation.cxx000066400000000000000000000206741311104306400237720ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkANTSImageTransformation_hxx_ #define _itkANTSImageTransformation_hxx_ // disable debug warnings in MS compiler #ifdef _MSC_VER #pragma warning(disable: 4786) #endif #include "ANTS_affine_registration2.h" #include "itkANTSImageTransformation.h" #include "itkIdentityTransform.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkLinearInterpolateImageFunction.h" #include "itkRecursiveGaussianImageFilter.h" #include "itkResampleImageFilter.h" #include "itkTransformFileWriter.h" #include "itkDisplacementFieldTransform.h" #include "vnl/vnl_math.h" namespace itk { template ANTSImageTransformation ::ANTSImageTransformation() { this->m_DisplacementField = ITK_NULLPTR; this->m_InverseDisplacementField = ITK_NULLPTR; this->m_AffineTransform = ITK_NULLPTR; this->m_WriteComponentImages = false; m_DeformationRegionOfInterestSize.Fill(0); m_DeformationRegionSpacing.Fill(1); m_DeformationRegionOfInterestCenter.Fill(0); } template void ANTSImageTransformation ::Compose() { } template void ANTSImageTransformation ::Write() { std::cout << " begin writing " << m_NamingConvention << std::endl; std::string filePrefix = this->m_NamingConvention; std::string::size_type pos = filePrefix.rfind( "." ); std::string extension; std::string gzExtension( "" ); if( pos != std::string::npos && std::string( filePrefix, pos, pos + 2 ) != std::string( "./" ) ) { bool is_supported = false; filePrefix = std::string( filePrefix, 0, pos ); extension = std::string( this->m_NamingConvention, pos, this->m_NamingConvention.length() - 1 ); if( extension != std::string( ".xfm" ) ) { if( extension == std::string( ".gz" ) ) { gzExtension = std::string( ".gz" ); pos = filePrefix.rfind( "." ); extension = std::string( filePrefix, pos, filePrefix.length() - 1 ); filePrefix = std::string( filePrefix, 0, pos ); } // GetSupportedWriteExtensions typedef itk::ImageIOBase IOBaseType; typedef std::list ArrayOfImageIOType; typedef typename IOBaseType::ArrayOfExtensionsType ArrayOfExtensionsType; ArrayOfImageIOType allobjects = itk::ObjectFactoryBase::CreateAllInstance("itkImageIOBase"); ArrayOfImageIOType::iterator itr = allobjects.begin(); while( itr != allobjects.end() ) { IOBaseType * io = dynamic_cast( itr->GetPointer() ); if( !io ) { std::cout << "Got a null pointer in the array" << std::endl; } else { const ArrayOfExtensionsType & writeExtensions = io->GetSupportedWriteExtensions(); ArrayOfExtensionsType::const_iterator writeItr = writeExtensions.begin(); while( writeItr != writeExtensions.end() ) { std::string test_ext = *writeItr; // std::cout <<" compare " << extension << " to " << test_ext << std::endl; if( extension == test_ext ) { is_supported = true; } // else std::cout <<" not the same " << std::endl; ++writeItr; } } ++itr; } if( !is_supported ) { std::cout << " WARNING! we are guessing you want .nii.gz for your image output instead of: " << extension << std::endl; filePrefix = this->m_NamingConvention; extension = std::string(".nii.gz"); } } } else { extension = std::string( ".nii.gz" ); } if( extension == std::string( ".xfm") ) { typedef itk::DisplacementFieldTransform DisplacementFieldTransform; itk::TransformFactory< itk::DisplacementFieldTransform >::RegisterTransform (); itk::TransformFactory< itk::MatrixOffsetTransformBase >::RegisterTransform (); std::string fw_filename = filePrefix + extension; std::string bw_filename = filePrefix + "_inverse" + extension; //using ITKv4 functionality to write transforms typedef itk::TransformFileWriterTemplate TransformFileWriterFloat; typename TransformFileWriterFloat::Pointer fw_writer=TransformFileWriterFloat::New(); if( this->m_AffineTransform ) fw_writer->AddTransform(this->m_AffineTransform); if(this->m_DisplacementField) { typename DisplacementFieldTransform::Pointer disp = DisplacementFieldTransform::New(); disp->SetDisplacementField(this->m_DisplacementField); fw_writer->AddTransform(disp); } fw_writer->SetFileName(fw_filename); fw_writer->Update(); typename TransformFileWriterFloat::Pointer bw_writer=TransformFileWriterFloat::New(); if(this->m_InverseDisplacementField) { typename DisplacementFieldTransform::Pointer disp = DisplacementFieldTransform::New(); disp->SetDisplacementField(this->m_InverseDisplacementField); bw_writer->AddTransform(disp); } typename AffineTransformType::Pointer tmp=AffineTransformType::New(); if( this->m_AffineTransform ) { this->m_AffineTransform->GetInverse(tmp); bw_writer->AddTransform(tmp); } bw_writer->SetFileName(bw_filename); bw_writer->Update(); } else { // Added by songgang if( this->m_AffineTransform ) { std::cout << " writing " << filePrefix << " affine " << std::endl; const std::string filename = filePrefix + std::string( "Affine.txt" ); WriteAffineTransformFile(this->m_AffineTransform, filename); } if( this->m_DisplacementField ) { std::cout << " writing " << filePrefix << " def " << std::endl; if( extension != std::string( ".mha" ) ) { std::string filename = filePrefix + std::string( "Warp" ) + extension + gzExtension; typedef ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); std::cout << "filename " << filename << std::endl; writer->SetFileName( filename ); // writer->SetUseAvantsNamingConvention( true ); writer->SetInput( this->m_DisplacementField ); writer->Update(); } else { std::string filename = filePrefix + std::string( "Warp.nii.gz" ); typedef ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( filename ); writer->SetInput( this->m_DisplacementField ); writer->Update(); } } if( this->m_InverseDisplacementField ) { if( extension != std::string( ".mha" ) ) { std::string filename = filePrefix + std::string( "InverseWarp" ) + extension + gzExtension; typedef ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( filename ); // writer->SetUseAvantsNamingConvention( true ); writer->SetInput( this->m_InverseDisplacementField ); writer->Update(); } else { std::string filename = filePrefix + std::string( "InverseWarp.mha" ); typedef ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( filename ); writer->SetInput( this->m_InverseDisplacementField ); writer->Update(); } } } } /** * Standard "PrintSelf" method */ template void ANTSImageTransformation ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkANTSImageTransformation.h000066400000000000000000000146221311104306400234130ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkANTSImageTransformation_h #define __itkANTSImageTransformation_h #include "itkObject.h" #include "itkObjectFactory.h" #include "antsCommandLineParser.h" #include "itkImage.h" #include "itkMacro.h" #include "itkCenteredEuler3DTransform.h" #include "itkQuaternionRigidTransform.h" #include "itkANTSCenteredAffine2DTransform.h" #include "itkANTSAffine3DTransform.h" // #include "itkCenteredRigid2DTransform.h" #include "itkCenteredTransformInitializer.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" namespace itk { template class ANTSImageTransformation : public Object { public: /** Standard class typedefs. */ typedef ANTSImageTransformation Self; typedef Object Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); typedef double TComp; /** Run-time type information (and related methods). */ itkTypeMacro( ANTSImageTransformation, Object ); itkStaticConstMacro( Dimension, unsigned int, TDimension ); itkStaticConstMacro( ImageDimension, unsigned int, TDimension ); typedef TReal RealType; typedef Image ImageType; /** declare transformation types */ typedef itk::MatrixOffsetTransformBase AffineTransformType; typedef typename AffineTransformType::Pointer AffineTransformPointer; typedef itk::Vector VectorType; typedef itk::Image DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldPointer; typedef typename DisplacementFieldType::RegionType DeformationRegionOfInterestType; typedef typename DisplacementFieldType::SizeType DeformationRegionOfInterestSizeType; typedef typename DisplacementFieldType::PointType DeformationRegionOfInterestCenterType; typedef typename ants::CommandLineParser ParserType; typedef typename ParserType::OptionType OptionType; /** Set functions */ void SetDeformationRegionOfInterest( DeformationRegionOfInterestCenterType DRC, DeformationRegionOfInterestSizeType DRS, typename DisplacementFieldType::SpacingType DRSp) { m_DeformationRegionOfInterestCenter = DRC; m_DeformationRegionOfInterestSize = DRS; m_DeformationRegionSpacing = DRSp; } void SetAffineTransform(AffineTransformPointer A) { this->m_AffineTransform = A; } void SetDisplacementField(DisplacementFieldPointer A) { this->m_DisplacementField = A; } void SetInverseDisplacementField(DisplacementFieldPointer A) { this->m_InverseDisplacementField = A; } void SetNamingConvention(std::string name) { this->m_NamingConvention = name; } /** Get functions */ AffineTransformPointer GetAffineTransform() { return this->m_AffineTransform; } DisplacementFieldPointer GetDisplacementField() { return this->m_DisplacementField; } DisplacementFieldPointer GetInverseDisplacementField() { return this->m_InverseDisplacementField; } void SetFixedImageAffineTransform(AffineTransformPointer A) { this->m_FixedImageAffineTransform = A; } AffineTransformPointer GetFixedImageAffineTransform() { return this->m_FixedImageAffineTransform; } /** Initialize the mapping */ void InitializeTransform() { if( !this->m_AffineTransform ) { this->m_AffineTransform = AffineTransformType::New(); this->m_AffineTransform->SetIdentity(); } // deformation fields too if( !this->m_DisplacementField ) { VectorType zero; zero.Fill(0); this->m_DisplacementField = DisplacementFieldType::New(); /* m_DeformationRegionOfInterest.SetSize( m_DeformationRegionOfInterestSize ); this->m_DisplacementField->SetSpacing( m_DeformationRegionSpacing ); this->m_DisplacementField->SetOrigin( m_DeformationRegionOfInterestCenter ); this->m_DisplacementField->SetLargestPossibleRegion( m_DeformationRegionOfInterest ); this->m_DisplacementField->SetRequestedRegion( m_DeformationRegionOfInterest ); this->m_DisplacementField->SetBufferedRegion( m_DeformationRegionOfInterest ); this->m_DisplacementField->Allocate(); this->m_DisplacementField->FillBuffer(zero); */ } } /** Write the transformations out */ void Write(); /** Concatenate all transformations */ void Compose(); /** Concatenate all transformations in inverse direction */ void ComposeInverse(); itkSetMacro( WriteComponentImages, bool ); itkGetMacro( WriteComponentImages, bool ); itkBooleanMacro( WriteComponentImages ); protected: ANTSImageTransformation(); virtual ~ANTSImageTransformation() { } void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; private: ANTSImageTransformation( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented AffineTransformPointer m_AffineTransform; AffineTransformPointer m_FixedImageAffineTransform; DisplacementFieldPointer m_DisplacementField; DeformationRegionOfInterestType m_DeformationRegionOfInterest; DeformationRegionOfInterestCenterType m_DeformationRegionOfInterestCenter; DeformationRegionOfInterestSizeType m_DeformationRegionOfInterestSize; typename DisplacementFieldType::SpacingType m_DeformationRegionSpacing; DisplacementFieldPointer m_InverseDisplacementField; std::string m_NamingConvention; bool m_WriteComponentImages; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkANTSImageTransformation.cxx" #endif #endif ants-2.2.0/ImageRegistration/itkANTSLabeledPointSet.h000066400000000000000000000060241311104306400224550ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkANTSLabeledPointSet_h #define __itkANTSLabeledPointSet_h #include "itkObject.h" #include "itkObjectFactory.h" #include "itkMacro.h" #include "itkVector.h" #include "itkPointSet.h" namespace itk { template class ANTSLabeledPointSet : public Object { public: /** Standard class typedefs. */ typedef ANTSLabeledPointSet Self; typedef Object Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( ANTSLabeledPointSet, Object ); itkStaticConstMacro( Dimension, unsigned int, TDimension ); typedef float RealType; typedef Image ImageType; typedef typename ImageType::Pointer ImagePointer; typedef Vector VectorType; typedef Image DisplacementFieldType; /** Point Types for landmarks and labeled point-sets */ typedef long PointDataVectorType; typedef itk::PointSet PointSetType; typedef typename PointSetType::Pointer PointSetPointer; typedef typename PointSetType::PointType PointType; typedef typename PointSetType::PixelType PointDataType; typedef typename ImageType::PointType ImagePointType; itkSetMacro( PointSet, PointSetPointer ); itkGetConstMacro( PointSet, PointSetPointer ); PointType GetPoint( unsigned long ii) { PointType point; this->m_PointSet->GetPoint(ii, &point); return point; } PointDataType GetPointData( unsigned long ii) { PointDataType data; this->m_PointSet->GetPointData(ii, &data); return data; } void SetPoint( unsigned long ii, PointType point ) { this->m_PointSet->SetPoint(ii, point); } void SetPointData( unsigned long ii, PointDataType label ) { this->m_PointSet->SetPointData(ii, label); } void SetPointAndData( unsigned long ii, PointType point, PointDataType label ) { this->m_PointSet->SetPoint(ii, point); this->m_PointSet->SetPointData(ii, label); } private: PointSetPointer m_PointSet; }; } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkANTSSimilarityMetric.h000066400000000000000000000073471311104306400227420ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkANTSSimilarityMetric_h #define __itkANTSSimilarityMetric_h #include "itkObject.h" #include "itkObjectFactory.h" #include "itkAvantsPDEDeformableRegistrationFunction.h" #include "itkMacro.h" #include "itkVector.h" #include "itkANTSLabeledPointSet.h" namespace itk { template class ANTSSimilarityMetric : public Object { public: /** Standard class typedefs. */ typedef ANTSSimilarityMetric Self; typedef Object Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( ANTSSimilarityMetric, Object ); itkStaticConstMacro( Dimension, unsigned int, TDimension ); typedef TReal RealType; typedef Image ImageType; typedef typename ImageType::Pointer ImagePointer; typedef Vector VectorType; typedef Image DisplacementFieldType; /** Point Types for landmarks and labeled point-sets */ typedef itk::ANTSLabeledPointSet LabeledPointSetType; typedef typename LabeledPointSetType::Pointer LabeledPointSetPointer; typedef typename LabeledPointSetType::PointSetType PointSetType; typedef typename PointSetType::Pointer PointSetPointer; typedef typename PointSetType::PointType PointType; typedef typename PointSetType::PixelType PointDataType; typedef typename ImageType::PointType ImagePointType; /** Typedefs for similarity metrics */ typedef AvantsPDEDeformableRegistrationFunction MetricType; typedef typename MetricType::Pointer MetricPointer; typedef typename MetricType::RadiusType RadiusType; itkSetMacro( FixedImage, ImagePointer ); itkGetConstMacro( FixedImage, ImagePointer ); itkSetMacro( MovingImage, ImagePointer ); itkGetConstMacro( MovingImage, ImagePointer ); itkSetMacro( FixedPointSet, PointSetPointer ); itkGetConstMacro( FixedPointSet, PointSetPointer ); itkSetMacro( MovingPointSet, PointSetPointer ); itkGetConstMacro( MovingPointSet, PointSetPointer ); itkSetMacro( WeightImage, ImagePointer ); itkGetConstMacro( WeightImage, ImagePointer ); itkSetClampMacro( WeightScalar, RealType, 0.0, NumericTraits::max() ); itkGetConstMacro( WeightScalar, RealType ); itkSetObjectMacro( Metric, MetricType ); itkGetModifiableObjectMacro( Metric, MetricType ); itkSetMacro( MaximizeMetric, bool ); itkGetConstMacro( MaximizeMetric, bool ); itkBooleanMacro( MaximizeMetric ); private: MetricPointer m_Metric; bool m_MaximizeMetric; ImagePointer m_FixedImage; ImagePointer m_MovingImage; PointSetPointer m_FixedPointSet; PointSetPointer m_MovingPointSet; ImagePointer m_WeightImage; RealType m_WeightScalar; }; } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkAvantsMutualInformationRegistrationFunction.cxx000066400000000000000000000461151311104306400302640ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkAvantsMutualInformationRegistrationFunction_hxx #define _itkAvantsMutualInformationRegistrationFunction_hxx #include "antsAllocImage.h" #include "itkAvantsMutualInformationRegistrationFunction.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkCovariantVector.h" #include "itkImageRandomConstIteratorWithIndex.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIterator.h" #include "itkImageIterator.h" #include "vnl/vnl_math.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkImageRegionConstIteratorWithIndex.h" namespace itk { /** * Constructor */ template AvantsMutualInformationRegistrationFunction ::AvantsMutualInformationRegistrationFunction() { this->Superclass::m_NormalizeGradient = true; this->m_NumberOfSpatialSamples = 5000; this->m_NumberOfHistogramBins = 50; // this->SetComputeGradient(false); // don't use the default gradient for no // Initialize PDFs to NULL m_JointPDF = ITK_NULLPTR; m_OpticalFlow = false; typename TransformType::Pointer transformer = TransformType::New(); this->SetTransform(transformer); typename BSplineInterpolatorType::Pointer interpolator = BSplineInterpolatorType::New(); this->SetInterpolator(interpolator); m_FixedImageMask = ITK_NULLPTR; m_MovingImageMask = ITK_NULLPTR; // Initialize memory m_MovingImageNormalizedMin = 0.0; m_FixedImageNormalizedMin = 0.0; m_MovingImageTrueMin = 0.0; m_MovingImageTrueMax = 0.0; m_FixedImageBinSize = 0.0; m_MovingImageBinSize = 0.0; m_CubicBSplineDerivativeKernel = ITK_NULLPTR; m_BSplineInterpolator = ITK_NULLPTR; m_DerivativeCalculator = ITK_NULLPTR; m_NumberOfParameters = ImageDimension; m_FixedImageGradientCalculator = GradientCalculatorType::New(); m_MovingImageGradientCalculator = GradientCalculatorType::New(); this->m_Padding = 2; typename DefaultInterpolatorType::Pointer interp = DefaultInterpolatorType::New(); typename DefaultInterpolatorType::Pointer interp2 = DefaultInterpolatorType::New(); m_MovingImageInterpolator = static_cast( interp.GetPointer() ); m_FixedImageInterpolator = static_cast( interp2.GetPointer() ); m_Interpolator = static_cast( interp.GetPointer() ); this->m_RobustnessParameter = -1.e19; } /** * Print out internal information about this class */ template void AvantsMutualInformationRegistrationFunction ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "NumberOfSpatialSamples: "; os << m_NumberOfSpatialSamples << std::endl; os << indent << "NumberOfHistogramBins: "; os << m_NumberOfHistogramBins << std::endl; // Debugging information os << indent << "NumberOfParameters: "; os << m_NumberOfParameters << std::endl; os << indent << "FixedImageNormalizedMin: "; os << m_FixedImageNormalizedMin << std::endl; os << indent << "MovingImageNormalizedMin: "; os << m_MovingImageNormalizedMin << std::endl; os << indent << "MovingImageTrueMin: "; os << m_MovingImageTrueMin << std::endl; os << indent << "MovingImageTrueMax: "; os << m_MovingImageTrueMax << std::endl; os << indent << "FixedImageBinSize: "; os << m_FixedImageBinSize << std::endl; os << indent << "MovingImageBinSize: "; os << m_MovingImageBinSize << std::endl; os << indent << "InterpolatorIsBSpline: "; os << m_InterpolatorIsBSpline << std::endl; } /** * Initialize */ template void AvantsMutualInformationRegistrationFunction ::InitializeIteration() { m_CubicBSplineKernel = CubicBSplineFunctionType::New(); m_CubicBSplineDerivativeKernel = CubicBSplineDerivativeFunctionType::New(); this->m_Energy = 0; pdfinterpolator = pdfintType::New(); dpdfinterpolator = dpdfintType::New(); pdfinterpolator2 = pdfintType2::New(); pdfinterpolator3 = pdfintType2::New(); m_DerivativeCalculator = DerivativeFunctionType::New(); // this->ComputeMetricImage(); /* bool makenewimage=false; if (!this->m_MetricImage ) makenewimage=true; else if (imagesize[0] != this->m_MetricImage->GetLargestPossibleRegion().GetSize()[0]) makenewimage = true; else this->m_MetricImage->FillBuffer(0); if (makenewimage) { this->m_MetricImage = TFixedImage::New(); this->m_MetricImage->SetLargestPossibleRegion(img->GetLargestPossibleRegion() ); this->m_MetricImage->SetBufferedRegion(img->GetLargestPossibleRegion()); this->m_MetricImage->SetSpacing(img->GetSpacing()); this->m_MetricImage->SetOrigin(img->GetOrigin()); this->m_MetricImage->Allocate(); ittype it(this->m_MetricImage,this->m_MetricImage->GetLargestPossibleRegion().GetSize()); for( it.GoToBegin(); !it.IsAtEnd(); ++it ) it.Set(0); } */ m_FixedImageGradientCalculator->SetInputImage( this->m_FixedImage ); m_MovingImageGradientCalculator->SetInputImage( this->m_MovingImage ); m_FixedImageInterpolator->SetInputImage( this->m_FixedImage ); m_Interpolator->SetInputImage( this->m_MovingImage ); m_FixedImageSpacing = this->m_FixedImage->GetSpacing(); m_FixedImageOrigin = this->m_FixedImage->GetOrigin(); m_Normalizer = 0.0; m_NumberOfSpatialSamples = 1; for( unsigned int k = 0; k < ImageDimension; k++ ) { m_Normalizer += m_FixedImageSpacing[k] * m_FixedImageSpacing[k]; m_NumberOfSpatialSamples *= this->m_FixedImage->GetLargestPossibleRegion().GetSize()[k]; } m_Normalizer /= static_cast( ImageDimension ); /** * Compute binsize for the histograms. * * 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. * double fixedImageMax = 1.0; double fixedImageMin = 0.0; double movingImageMax = 1.0; double movingImageMin = 0.0; m_MovingImageTrueMin = movingImageMin; m_MovingImageTrueMax = movingImageMax; */ double movingImageMin = NumericTraits::max(); double movingImageMax = NumericTraits::NonpositiveMin(); double fixedImageMin = NumericTraits::max(); double fixedImageMax = NumericTraits::NonpositiveMin(); typedef ImageRegionConstIterator MovingIteratorType; MovingIteratorType movingImageIterator( this->m_MovingImage, this->m_MovingImage->GetBufferedRegion() ); for( movingImageIterator.GoToBegin(); !movingImageIterator.IsAtEnd(); ++movingImageIterator ) { bool takesample = true; if( this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( movingImageIterator.GetIndex() ) < 1.e-6 ) { takesample = false; } } if( takesample ) { double sample = static_cast( movingImageIterator.Get() ); double fsample = static_cast( this->m_FixedImage->GetPixel( movingImageIterator.GetIndex() ) ); if( sample < movingImageMin ) { movingImageMin = sample; } if( sample > movingImageMax ) { movingImageMax = sample; } if( fsample < fixedImageMin ) { fixedImageMin = fsample; } if( fsample > fixedImageMax ) { fixedImageMax = fsample; } } } this->m_MovingImageTrueMax = movingImageMax; this->m_FixedImageTrueMax = fixedImageMax; this->m_MovingImageTrueMin = movingImageMin; this->m_FixedImageTrueMin = fixedImageMin; // Instantiate a region, index, size JointPDFRegionType jointPDFRegion; JointPDFIndexType jointPDFIndex; JointPDFSizeType jointPDFSize; // the jointPDF is of size NumberOfBins x NumberOfBins jointPDFSize.Fill(m_NumberOfHistogramBins); jointPDFIndex.Fill(0); jointPDFRegion.SetIndex(jointPDFIndex); jointPDFRegion.SetSize(jointPDFSize); this->m_JointPDF = AllocImage(jointPDFRegion); // By setting these values, the joint histogram physical locations will correspond to intensity values. JointPDFSpacingType spacing; spacing[0] = 1 / (double)(this->m_NumberOfHistogramBins - (double)this->m_Padding * 2 - 1); spacing[1] = spacing[0]; this->m_JointPDF->SetSpacing(spacing); this->m_JointPDFSpacing = this->m_JointPDF->GetSpacing(); JointPDFPointType origin; origin[0] = this->m_JointPDFSpacing[0] * (double)this->m_Padding * (-1.0); origin[1] = origin[0]; this->m_JointPDF->SetOrigin(origin); // Instantiate a region, index, size typedef typename MarginalPDFType::RegionType MarginalPDFRegionType; typedef typename MarginalPDFType::SizeType MarginalPDFSizeType; MarginalPDFRegionType marginalPDFRegion; MarginalPDFIndexType marginalPDFIndex; MarginalPDFSizeType marginalPDFSize; // the marginalPDF is of size NumberOfBins x NumberOfBins marginalPDFSize.Fill(m_NumberOfHistogramBins); marginalPDFIndex.Fill(0); marginalPDFRegion.SetIndex(marginalPDFIndex); marginalPDFRegion.SetSize(marginalPDFSize); // do the same thing for the marginal pdfs this->m_FixedImageMarginalPDF = AllocImage(marginalPDFRegion); this->m_MovingImageMarginalPDF = AllocImage(marginalPDFRegion); // By setting these values, the marginal histogram physical locations will correspond to intensity values. typename MarginalPDFType::PointType fixedorigin; typename MarginalPDFType::PointType movingorigin; fixedorigin[0] = origin[0]; movingorigin[0] = origin[1]; this->m_FixedImageMarginalPDF->SetOrigin(fixedorigin); this->m_MovingImageMarginalPDF->SetOrigin(movingorigin); typename MarginalPDFType::SpacingType mspacing; mspacing[0] = spacing[0]; this->m_FixedImageMarginalPDF->SetSpacing(mspacing); mspacing[0] = spacing[1]; this->m_MovingImageMarginalPDF->SetSpacing(mspacing); // For the derivatives of the joint PDF define a region starting from {0,0,0} // with size {m_NumberOfParameters,m_NumberOfHistogramBins, // m_NumberOfHistogramBins}. The dimension represents transform parameters, // fixed image parzen window index and moving image parzen window index, // respectively. m_NormalizeMetric = 1.0; for( unsigned int i = 0; i < ImageDimension; i++ ) { m_NormalizeMetric *= this->m_FixedImage->GetLargestPossibleRegion().GetSize()[i]; } this->GetProbabilities(); this->ComputeMutualInformation(); pdfinterpolator->SetInputImage(m_JointPDF); pdfinterpolator2->SetInputImage(m_FixedImageMarginalPDF); pdfinterpolator3->SetInputImage(m_MovingImageMarginalPDF); /* pdfinterpolator->SetSplineOrder(3); pdfinterpolator2->SetSplineOrder(3); pdfinterpolator3->SetSplineOrder(3); */ } /** * Get the both Value and Derivative Measure */ template void AvantsMutualInformationRegistrationFunction ::GetProbabilities() { typedef ImageRegionConstIteratorWithIndex RandomIterator; RandomIterator randIter( this->m_FixedImage, this->m_FixedImage->GetLargestPossibleRegion() ); this->m_FixedImageMarginalPDF->FillBuffer(0); this->m_MovingImageMarginalPDF->FillBuffer(0); // Reset the joint pdfs to zero m_JointPDF->FillBuffer( 0.0 ); unsigned long nSamples = 0; RandomIterator iter( this->m_FixedImage, this->m_FixedImage->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { bool takesample = true; if( this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( iter.GetIndex() ) < 1.e-6 ) { takesample = false; } } if( takesample ) { // Get sampled index FixedImageIndexType index = iter.GetIndex(); /* bool inimage=true; for (unsigned int dd=0; dd= static_cast(imagesize[dd]-1) ) inimage=false; } */ double movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( index ) ); double fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( index ) ); /** add the paired intensity points to the joint histogram */ JointPDFPointType jointPDFpoint; this->ComputeJointPDFPoint(fixedImageValue, movingImageValue, jointPDFpoint); JointPDFIndexType jointPDFIndex; jointPDFIndex.Fill(0); this->m_JointPDF->TransformPhysicalPointToIndex(jointPDFpoint, jointPDFIndex); this->m_JointPDF->SetPixel(jointPDFIndex, this->m_JointPDF->GetPixel(jointPDFIndex) + 1); ++nSamples; } } /** * Normalize the PDFs, compute moving image marginal PDF * */ typedef ImageRegionIterator JointPDFIteratorType; JointPDFIteratorType jointPDFIterator( m_JointPDF, m_JointPDF->GetBufferedRegion() ); // Compute joint PDF normalization factor (to ensure joint PDF sum adds to 1.0) double jointPDFSum = 0.0; jointPDFIterator.GoToBegin(); while( !jointPDFIterator.IsAtEnd() ) { float temp = jointPDFIterator.Get(); jointPDFSum += temp; ++jointPDFIterator; } // of derivatives if( jointPDFSum == 0.0 ) { itkExceptionMacro( "Joint PDF summed to zero" ); } // Normalize the PDF bins jointPDFIterator.GoToEnd(); while( !jointPDFIterator.IsAtBegin() ) { --jointPDFIterator; jointPDFIterator.Value() /= static_cast( jointPDFSum ); } bool smoothjh = true; if( smoothjh ) { typedef DiscreteGaussianImageFilter dgtype; typename dgtype::Pointer dg = dgtype::New(); dg->SetInput(this->m_JointPDF); dg->SetVariance(1.5); dg->SetUseImageSpacingOff(); dg->SetMaximumError(.01f); dg->Update(); this->m_JointPDF = dg->GetOutput(); } // Compute moving image marginal PDF by summing over fixed image bins. typedef ImageLinearIteratorWithIndex JointPDFLinearIterator; JointPDFLinearIterator linearIter( m_JointPDF, m_JointPDF->GetBufferedRegion() ); linearIter.SetDirection( 0 ); linearIter.GoToBegin(); unsigned int fixedIndex = 0; while( !linearIter.IsAtEnd() ) { double sum = 0.0; while( !linearIter.IsAtEndOfLine() ) { sum += linearIter.Get(); ++linearIter; } MarginalPDFIndexType mind; mind[0] = fixedIndex; m_FixedImageMarginalPDF->SetPixel(mind, static_cast(sum) ); linearIter.NextLine(); ++fixedIndex; } linearIter.SetDirection( 1 ); linearIter.GoToBegin(); unsigned int movingIndex = 0; while( !linearIter.IsAtEnd() ) { double sum = 0.0; while( !linearIter.IsAtEndOfLine() ) { sum += linearIter.Get(); ++linearIter; } MarginalPDFIndexType mind; mind[0] = movingIndex; m_MovingImageMarginalPDF->SetPixel(mind, static_cast(sum) ); linearIter.NextLine(); ++movingIndex; } } /** * Get the both Value and Derivative Measure */ template double AvantsMutualInformationRegistrationFunction ::GetValueAndDerivative(IndexType oindex, MeasureType & /* valuei */, DerivativeType & /* derivative1 */, DerivativeType & /* derivative2 */) { double value = 0; DerivativeType zero(ImageDimension); zero.Fill(0); double movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( oindex ) ); double fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( oindex ) ); JointPDFPointType pdfind; this->ComputeJointPDFPoint(fixedImageValue, movingImageValue, pdfind); const double jointPDFValue = pdfinterpolator->Evaluate(pdfind); const double dJPDF = this->ComputeJointPDFDerivative( pdfind, 0, 0 ); typename pdfintType2::ContinuousIndexType mind; mind[0] = pdfind[0]; const double fixedImagePDFValue = pdfinterpolator2->Evaluate(mind); const double dFmPDF = this->ComputeFixedImageMarginalPDFDerivative( mind, 0 ); const double eps = 1.e-16; if( jointPDFValue > eps && (fixedImagePDFValue) > 0 ) { const double pRatio = std::log(jointPDFValue) - std::log(fixedImagePDFValue); const double term1 = dJPDF * pRatio; const double term2 = std::log( (double)2) * dFmPDF * jointPDFValue / fixedImagePDFValue; value = (term2 - term1); } // end if-block to check non-zero bin contribution else { value = 0; } return value; } template double AvantsMutualInformationRegistrationFunction ::GetValueAndDerivativeInv(IndexType oindex, MeasureType & /* valuei */, DerivativeType & /* derivative1 */, DerivativeType & /* derivative2 */) { double value = 0; DerivativeType zero(ImageDimension); zero.Fill(0); double movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( oindex ) ); double fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( oindex ) ); JointPDFPointType pdfind; this->ComputeJointPDFPoint(fixedImageValue, movingImageValue, pdfind); const double jointPDFValue = pdfinterpolator->Evaluate(pdfind); const double dJPDF = this->ComputeJointPDFDerivative( pdfind, 0, 1 ); typename pdfintType2::ContinuousIndexType mind; mind[0] = pdfind[1]; const double movingImagePDFValue = pdfinterpolator3->EvaluateAtContinuousIndex(mind); const double dMmPDF = this->ComputeMovingImageMarginalPDFDerivative( mind, 0 ); const double eps = 1.e-16; if( jointPDFValue > eps && (movingImagePDFValue) > 0 ) { const double pRatio = std::log(jointPDFValue) - std::log(movingImagePDFValue); const double term1 = dJPDF * pRatio; const double term2 = std::log( (double)2) * dMmPDF * jointPDFValue / movingImagePDFValue; value = (term2 - term1); } // end if-block to check non-zero bin contribution else { value = 0; } return value; } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkAvantsMutualInformationRegistrationFunction.h000066400000000000000000000720551311104306400277130ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkAvantsMutualInformationRegistrationFunction_h #define __itkAvantsMutualInformationRegistrationFunction_h #include #include #include "cmath" #include "itkImageFileWriter.h" #include "itkImageToImageMetric.h" #include "itkAvantsPDEDeformableRegistrationFunction.h" #include "itkCovariantVector.h" #include "itkPoint.h" #include "itkIndex.h" #include "itkBSplineKernelFunction.h" #include "itkBSplineDerivativeKernelFunction.h" #include "itkCentralDifferenceImageFunction.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkTranslationTransform.h" #include "itkArray2D.h" #include "itkImageBase.h" #include "itkTransform.h" #include "itkInterpolateImageFunction.h" #include "itkSingleValuedCostFunction.h" #include "itkExceptionObject.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkSpatialObject.h" #include "itkConstNeighborhoodIterator.h" namespace itk { /** \class AvantsMutualInformationRegistrationFunction * \brief Computes the mutual information between two images to be * registered using the method of Avants et al. * * AvantsMutualInformationRegistrationFunction computes the mutual * information between a fixed and moving image to be registered. * * This class is templated over the FixedImage type and the MovingImage * type. * * The fixed and moving images are set via methods SetFixedImage() and * SetMovingImage(). This metric makes use of user specified Transform and * Interpolator. The Transform is used to map points from the fixed image to * the moving image domain. The Interpolator is used to evaluate the image * intensity at user specified geometric points in the moving image. * The Transform and Interpolator are set via methods SetTransform() and * SetInterpolator(). * * If a BSplineInterpolationFunction is used, this class obtain * image derivatives from the BSpline interpolator. Otherwise, * image derivatives are computed using central differencing. * * \warning This metric assumes that the moving image has already been * connected to the interpolator outside of this class. * * The method GetValue() computes of the mutual information * while method GetValueAndDerivative() computes * both the mutual information and its derivatives with respect to the * transform parameters. * * The calculations are based on the method of Avants et al [1,2] * where the probability density distribution are estimated using * Parzen histograms. Since the fixed image PDF does not contribute * to the derivatives, it does not need to be smooth. Hence, * a zero order (box car) BSpline kernel is used * for the fixed image intensity PDF. On the other hand, to ensure * smoothness a third order BSpline kernel is used for the * moving image intensity PDF. * * On Initialize(), the FixedImage is uniformly sampled within * the FixedImageRegion. The number of samples used can be set * via SetNumberOfSpatialSamples(). Typically, the number of * spatial samples used should increase with the image size. * * During each call of GetValue(), GetDerivatives(), * GetValueAndDerivatives(), marginal and joint intensity PDF's * values are estimated at discrete position or bins. * The number of bins used can be set via SetNumberOfHistogramBins(). * To handle data with arbitray magnitude and dynamic range, * the image intensity is scale such that any contribution to the * histogram will fall into a valid bin. * * One the PDF's have been contructed, the mutual information * is obtained by doubling summing over the discrete PDF values. * * * Notes: * 1. This class returns the negative mutual information value. * 2. This class in not thread safe due the private data structures * used to the store the sampled points and the marginal and joint pdfs. * * References: * [1] "Nonrigid multimodality image registration" * D. Avants, D. R. Haynor, H. Vesselle, T. Lewellen and W. Eubank * Medical Imaging 2001: Image Processing, 2001, pp. 1609-1620. * [2] "PET-CT Image Registration in the Chest Using Free-form Deformations" * D. Avants, D. R. Haynor, H. Vesselle, T. Lewellen and W. Eubank * IEEE Transactions in Medical Imaging. Vol.22, No.1, January 2003. pp.120-128. * [3] "Optimization of Mutual Information for MultiResolution Image * Registration" * P. Thevenaz and M. Unser * IEEE Transactions in Image Processing, 9(12) December 2000. * * \ingroup RegistrationMetrics * \ingroup ThreadUnSafe */ template class AvantsMutualInformationRegistrationFunction : public AvantsPDEDeformableRegistrationFunction { public: /** Standard class typedefs. */ typedef AvantsMutualInformationRegistrationFunction Self; typedef AvantsPDEDeformableRegistrationFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro( AvantsMutualInformationRegistrationFunction, AvantsPDEDeformableRegistrationFunction ); /** MovingImage image type. */ typedef typename Superclass::MovingImageType MovingImageType; typedef typename Superclass::MovingImagePointer MovingImagePointer; /** FixedImage image type. */ typedef typename Superclass::FixedImageType FixedImageType; typedef typename Superclass::FixedImagePointer FixedImagePointer; typedef typename FixedImageType::IndexType IndexType; typedef typename FixedImageType::SizeType SizeType; typedef typename FixedImageType::SpacingType SpacingType; /** Deformation field type. */ typedef typename Superclass::VectorType VectorType; typedef typename Superclass::DisplacementFieldType DisplacementFieldType; typedef typename Superclass::DisplacementFieldTypePointer DisplacementFieldTypePointer; /** Inherit some enums from the superclass. */ itkStaticConstMacro(ImageDimension, unsigned int, Superclass::ImageDimension); /** Inherit some enums from the superclass. */ typedef typename Superclass::PixelType PixelType; typedef typename Superclass::RadiusType RadiusType; typedef typename Superclass::NeighborhoodType NeighborhoodType; typedef typename Superclass::FloatOffsetType FloatOffsetType; typedef typename Superclass::TimeStepType TimeStepType; /** Interpolator type. */ typedef double CoordRepType; typedef // // LinearInterpolateImageFunction BSplineInterpolateImageFunction InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; typedef typename InterpolatorType::PointType PointType; typedef InterpolatorType DefaultInterpolatorType; // typedef LinearInterpolateImageFunction // DefaultInterpolatorType; /** Covariant vector type. */ typedef CovariantVector CovariantVectorType; /** Gradient calculator type. */ typedef CentralDifferenceImageFunction GradientCalculatorType; typedef typename GradientCalculatorType::Pointer GradientCalculatorPointer; /** Set the moving image interpolator. */ void SetMovingImageInterpolator( InterpolatorType * ptr ) { m_MovingImageInterpolator = ptr; } /** Get the moving image interpolator. */ InterpolatorType * GetMovingImageInterpolator(void) { return m_MovingImageInterpolator; } /** This class uses a constant timestep of 1. */ virtual TimeStepType ComputeGlobalTimeStep(void *itkNotUsed(GlobalData) ) const ITK_OVERRIDE { return 1; } /** Return a pointer to a global data structure that is passed to * this object from the solver at each calculation. */ virtual void * GetGlobalDataPointer() const ITK_OVERRIDE { GlobalDataStruct *global = new GlobalDataStruct(); // global->m_SumOfSquaredDifference = 0.0; // / global->m_NumberOfPixelsProcessed = 0L; // global->m_SumOfSquaredChange = 0; return global; } /** Release memory for global data structure. */ virtual void ReleaseGlobalDataPointer( void *GlobalData ) const ITK_OVERRIDE { delete (GlobalDataStruct *) GlobalData; } /** Set the object's state before each iteration. */ virtual void InitializeIteration() ITK_OVERRIDE; typedef double CoordinateRepresentationType; /** Types inherited from Superclass. */ typedef TranslationTransform TransformType; typedef ImageToImageMetric Metricclass; typedef typename TransformType::Pointer TransformPointer; typedef typename Metricclass::TransformJacobianType TransformJacobianType; // typedef typename Metricclass::InterpolatorType InterpolatorType; typedef typename Metricclass::MeasureType MeasureType; typedef typename Metricclass::DerivativeType DerivativeType; typedef typename TransformType::ParametersType ParametersType; typedef typename Metricclass::FixedImageConstPointer FixedImageConstPointer; typedef typename Metricclass::MovingImageConstPointer MovingImageCosntPointer; // typedef typename TransformType::CoordinateRepresentationType CoordinateRepresentationType; /** Index and Point typedef support. */ typedef typename FixedImageType::IndexType FixedImageIndexType; typedef typename FixedImageIndexType::IndexValueType FixedImageIndexValueType; typedef typename MovingImageType::IndexType MovingImageIndexType; typedef typename TransformType::InputPointType FixedImagePointType; typedef typename TransformType::OutputPointType MovingImagePointType; /** The marginal PDFs are stored as std::vector. */ typedef float PDFValueType; // typedef std::vector MarginalPDFType; typedef Image MarginalPDFType; typedef typename MarginalPDFType::IndexType MarginalPDFIndexType; /** Typedef for the joint PDF and PDF derivatives are stored as ITK Images. */ typedef Image JointPDFType; typedef Image JointPDFDerivativesType; typedef JointPDFType::IndexType JointPDFIndexType; typedef JointPDFType::PixelType JointPDFValueType; typedef JointPDFType::RegionType JointPDFRegionType; typedef JointPDFType::SizeType JointPDFSizeType; typedef JointPDFDerivativesType::IndexType JointPDFDerivativesIndexType; typedef JointPDFDerivativesType::PixelType JointPDFDerivativesValueType; typedef JointPDFDerivativesType::RegionType JointPDFDerivativesRegionType; typedef JointPDFDerivativesType::SizeType JointPDFDerivativesSizeType; typedef typename MarginalPDFType::PointType MarginalPDFPointType; typedef typename JointPDFType::PointType JointPDFPointType; typedef typename JointPDFType::SpacingType JointPDFSpacingType; /** Get the value and derivatives for single valued optimizers. */ double GetValueAndDerivative( IndexType index, MeasureType& Value, DerivativeType& Derivative1, DerivativeType& Derivative2 ); /** Get the value and derivatives for single valued optimizers. */ double GetValueAndDerivativeInv( IndexType index, MeasureType& Value, DerivativeType& Derivative1, DerivativeType& Derivative2 ); /** Number of spatial samples to used to compute metric */ // itkSetClampMacro( NumberOfSpatialSamples, unsigned long, // 1, NumericTraits::max() ); // itkGetConstReferenceMacro( NumberOfSpatialSamples, unsigned long); /** Number of bins to used in the histogram. Typical value is 50. */ // itkSetClampMacro( NumberOfHistogramBins, unsigned long, // 1, NumericTraits::max() ); // itkGetConstReferenceMacro( NumberOfHistogramBins, unsigned long); void SetNumberOfHistogramBins(unsigned long nhb) { m_NumberOfHistogramBins = nhb + 2 * this->m_Padding; } unsigned long GetNumberOfHistogramBins() { return m_NumberOfHistogramBins; } void SetTransform(TransformPointer t) { m_Transform = t; } TransformPointer GetTransform() { return m_Transform; } void SetInterpolator(InterpolatorPointer t) { m_Interpolator = t; } InterpolatorPointer GetInterpolator() { return m_Interpolator; } void GetProbabilities(); void ComputeJointPDFPoint( double fixedImageValue, double movingImageValue, JointPDFPointType& jointPDFpoint ) { double a = (fixedImageValue - this->m_FixedImageTrueMin) / (this->m_FixedImageTrueMax - this->m_FixedImageTrueMin); double b = (movingImageValue - this->m_MovingImageTrueMin) / (this->m_MovingImageTrueMax - this->m_MovingImageTrueMin); jointPDFpoint[0] = a; jointPDFpoint[1] = b; } inline double ComputeFixedImageMarginalPDFDerivative( MarginalPDFPointType margPDFpoint, unsigned int /* threadID */) { double offset = 0.25 * this->m_JointPDFSpacing[0], eps = this->m_JointPDFSpacing[0]; // offset in // voxels MarginalPDFPointType leftpoint = margPDFpoint; leftpoint[0] -= offset; MarginalPDFPointType rightpoint = margPDFpoint; rightpoint[0] += offset; if( leftpoint[0] < eps ) { leftpoint[0] = eps; } if( rightpoint[0] < eps ) { rightpoint[0] = eps; } // if (leftpoint[0] > 1-eps ) leftpoint[0]=1-eps; // if (rightpoint[0] > 1-eps ) rightpoint[0]=1-eps; double delta = rightpoint[0] - leftpoint[0]; if( delta > 0 ) { double deriv = pdfinterpolator2->Evaluate(rightpoint) - pdfinterpolator2->Evaluate(leftpoint); return deriv; } else { return 0; } } inline double ComputeMovingImageMarginalPDFDerivative( MarginalPDFPointType margPDFpoint, unsigned int /* threadID */ ) { double offset = 0.5 * this->m_JointPDFSpacing[0]; double eps = this->m_JointPDFSpacing[0]; // offset in voxels MarginalPDFPointType leftpoint = margPDFpoint; leftpoint[0] -= offset; MarginalPDFPointType rightpoint = margPDFpoint; rightpoint[0] += offset; if( leftpoint[0] < eps ) { leftpoint[0] = eps; } if( rightpoint[0] < eps ) { rightpoint[0] = eps; } if( leftpoint[0] > 1 ) { leftpoint[0] = 1; } if( rightpoint[0] > 1 ) { rightpoint[0] = 1; } double delta = rightpoint[0] - leftpoint[0]; if( delta > 0 ) { double deriv = pdfinterpolator3->Evaluate(rightpoint) - pdfinterpolator3->Evaluate(leftpoint); return deriv / delta; } else { return 0; } } inline double ComputeJointPDFDerivative( JointPDFPointType jointPDFpoint, unsigned int /* threadID */, unsigned int ind ) { double offset = 0.5 * this->m_JointPDFSpacing[ind]; double eps = this->m_JointPDFSpacing[ind]; // offset in voxels JointPDFPointType leftpoint = jointPDFpoint; leftpoint[ind] -= offset; JointPDFPointType rightpoint = jointPDFpoint; rightpoint[ind] += offset; if( leftpoint[ind] < eps ) { leftpoint[ind] = eps; } if( rightpoint[ind] < eps ) { rightpoint[ind] = eps; } if( leftpoint[ind] > 1 ) { leftpoint[ind] = 1; } if( rightpoint[ind] > 1 ) { rightpoint[ind] = 1; } double delta = rightpoint[ind] - leftpoint[ind]; double deriv = 0; if( delta > 0 ) { deriv = pdfinterpolator->Evaluate(rightpoint) - pdfinterpolator->Evaluate(leftpoint); return deriv / delta; } else { return deriv; } } double ComputeMutualInformation() { /** Ray Razlighi changes : May 3, 2010: improves convergence. 1- The padding is lowered to 2. 2- The MI energy is changed to actual MI, please note the value of this MI is in the range of [0 min(H(x),H(y))]. in case you need it to be normalized. 3- The Natural logarithm is changed to log2. 4- In the ComputeMutualInformation() the iterator range has been changed to cover the entire PDF. 5- In the FitIndexInBins() the truncation by floor has been modified to round. 6- The normalization is done based on NomberOfHistogramBins-1 instead of NomberOfHistogramBins. */ float px, py, pxy; double mival = 0; double mi; unsigned long ct = 0; typename JointPDFType::IndexType index; for( unsigned int ii = 0; ii < m_NumberOfHistogramBins; ii++ ) { MarginalPDFIndexType mind; mind[0] = ii; px = m_FixedImageMarginalPDF->GetPixel(mind); for( unsigned int jj = 0; jj < m_NumberOfHistogramBins; jj++ ) { mind[0] = jj; py = m_MovingImageMarginalPDF->GetPixel(mind); float denom = px * py; index[0] = ii; index[1] = jj; // pxy=m_JointPDF->GetPixel(index); JointPDFValueType *pdfPtr = m_JointPDF->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ); // Move the pointer to the first affected bin int pdfMovingIndex = static_cast( jj ); pdfPtr += pdfMovingIndex; pxy = *(pdfPtr); mi = 0; if( fabs(denom) > 0 ) { if( pxy / denom > 0 ) { // true mi mi = pxy * std::log(pxy / denom); // test mi // mi = 1.0 + log(pxy/denom); ct++; } } mival += mi; } // std::cout << " II " << ii << " JJ " << ii << " pxy " << pxy << " px " << px << std::endl; } // GS: temp edit to make sure if this is decreasing (should be ) // this->m_Energy = -1.0*mival/std::log((double)2.0); this->m_Energy = -1.0 * mival / std::log( (double)2.0); return this->m_Energy; } virtual VectorType ComputeUpdateInv(const NeighborhoodType & neighborhood, void * /* globalData */, const FloatOffsetType & /* offset */ = FloatOffsetType(0.0) ) ITK_OVERRIDE { VectorType update; update.Fill(0.0); IndexType oindex = neighborhood.GetIndex(); FixedImageType* img = const_cast(this->Superclass::m_FixedImage.GetPointer() ); if( !img ) { return update; } typename FixedImageType::SpacingType spacing = img->GetSpacing(); typename FixedImageType::SizeType imagesize = img->GetLargestPossibleRegion().GetSize(); for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( oindex[dd] < 1 || oindex[dd] >= static_cast(imagesize[dd] - 1) ) { return update; } } CovariantVectorType fixedGradient; ParametersType fdvec1(ImageDimension); ParametersType fdvec2(ImageDimension); fdvec1.Fill(0); fdvec2.Fill(0); fixedGradient = m_FixedImageGradientCalculator->EvaluateAtIndex( oindex ); double nccm1 = 0; const double loce = this->GetValueAndDerivative(oindex, nccm1, fdvec1, fdvec2); // if ( loce > 1.5 ) std::cout << " loce " << loce << " ind " << oindex << std::endl; for( unsigned int imd = 0; imd < ImageDimension; imd++ ) { update[imd] = loce * fixedGradient[imd] * spacing[imd] * (1); } // if (this->m_MetricImage) this->m_MetricImage->SetPixel(oindex,loce); return update; } /* Normalizing the image to the range of [0 1] */ double GetMovingParzenTerm( double intensity ) { return intensity; /* double windowTerm = static_cast( intensity ) - this->m_MovingImageTrueMin; windowTerm = windowTerm / ( this->m_MovingImageTrueMax - this->m_MovingImageTrueMin ); return windowTerm; */ } double GetFixedParzenTerm( double intensity ) { return intensity; /* double windowTerm = static_cast( intensity ) - this->m_FixedImageTrueMin; windowTerm = windowTerm / ( this->m_FixedImageTrueMax - this->m_FixedImageTrueMin ); return windowTerm; */ } /* find the image index in the number of histogram bins */ unsigned int FitIndexInBins( double windowTerm ) { unsigned int movingImageParzenWindowIndex = static_cast( this->m_Padding + (unsigned int)( windowTerm * (float)(this->m_NumberOfHistogramBins - 1 - this->m_Padding) + 0.5 ) ); // Make sure the extreme values are in valid bins if( movingImageParzenWindowIndex < this->m_Padding ) { movingImageParzenWindowIndex = this->m_Padding - 1; } else if( movingImageParzenWindowIndex > (m_NumberOfHistogramBins - this->m_Padding ) ) { movingImageParzenWindowIndex = m_NumberOfHistogramBins - this->m_Padding - 1; } return movingImageParzenWindowIndex; } double FitContIndexInBins( double windowTerm ) { return (double) this->m_Padding + windowTerm * (float)(this->m_NumberOfHistogramBins - this->m_Padding); } virtual VectorType ComputeUpdate(const NeighborhoodType & neighborhood, void * /* globalData */, const FloatOffsetType & /* offset */ = FloatOffsetType(0.0) ) ITK_OVERRIDE { VectorType update; update.Fill(0.0); IndexType oindex = neighborhood.GetIndex(); FixedImageType* img = const_cast(this->Superclass::m_MovingImage.GetPointer() ); if( !img ) { return update; } typename FixedImageType::SpacingType spacing = img->GetSpacing(); typename FixedImageType::SizeType imagesize = img->GetLargestPossibleRegion().GetSize(); for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( oindex[dd] < 1 || oindex[dd] >= static_cast(imagesize[dd] - 1) ) { return update; } } CovariantVectorType movingGradient; ParametersType fdvec1(ImageDimension); ParametersType fdvec2(ImageDimension); fdvec1.Fill(0); fdvec2.Fill(0); movingGradient = m_MovingImageGradientCalculator->EvaluateAtIndex( oindex ); double nccm1 = 0; const double loce = this->GetValueAndDerivativeInv(oindex, nccm1, fdvec1, fdvec2); for( unsigned int imd = 0; imd < ImageDimension; imd++ ) { update[imd] = loce * movingGradient[imd] * spacing[imd] * (1); } // if( oindex[1] == 250 && oindex[0] == 250 ) WriteImages(); return update; } void WriteImages() { if( this->m_MetricImage ) { typedef ImageFileWriter writertype; typename writertype::Pointer w = writertype::New(); w->SetInput( this->m_MetricImage); w->SetFileName("ZZmetric.nii.gz"); w->Write(); } } void SetOpticalFlow(bool b) { m_OpticalFlow = b; } typename JointPDFType::Pointer GetJointPDF() { return m_JointPDF; } void SetFixedImageMask( FixedImageType* img) { m_FixedImageMask = img; } /** FixedImage image neighborhood iterator type. */ typedef ConstNeighborhoodIterator FixedImageNeighborhoodIteratorType; /** A global data type for this class of equation. Used to store * iterators for the fixed image. */ struct GlobalDataStruct { FixedImageNeighborhoodIteratorType m_FixedImageIterator; }; /** The fixed image marginal PDF. */ mutable MarginalPDFType::Pointer m_FixedImageMarginalPDF; /** The moving image marginal PDF. */ mutable MarginalPDFType::Pointer m_MovingImageMarginalPDF; /** The joint PDF and PDF derivatives. */ typename JointPDFType::Pointer m_JointPDF; unsigned long m_NumberOfSpatialSamples; unsigned long m_NumberOfParameters; /** Variables to define the marginal and joint histograms. */ unsigned long m_NumberOfHistogramBins; double m_MovingImageNormalizedMin; double m_FixedImageNormalizedMin; double m_MovingImageTrueMin; double m_MovingImageTrueMax; double m_FixedImageTrueMin; double m_FixedImageTrueMax; double m_FixedImageBinSize; double m_MovingImageBinSize; protected: AvantsMutualInformationRegistrationFunction(); virtual ~AvantsMutualInformationRegistrationFunction() { }; void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; private: AvantsMutualInformationRegistrationFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented typename JointPDFDerivativesType::Pointer m_JointPDFDerivatives; /** Typedefs for BSpline kernel and derivative functions. */ typedef BSplineKernelFunction<3> CubicBSplineFunctionType; typedef BSplineDerivativeKernelFunction<3> CubicBSplineDerivativeFunctionType; /** Cubic BSpline kernel for computing Parzen histograms. */ typename CubicBSplineFunctionType::Pointer m_CubicBSplineKernel; typename CubicBSplineDerivativeFunctionType::Pointer m_CubicBSplineDerivativeKernel; /** * Types and variables related to image derivative calculations. * If a BSplineInterpolationFunction is used, this class obtain * image derivatives from the BSpline interpolator. Otherwise, * image derivatives are computed using central differencing. */ typedef CovariantVector ImageDerivativesType; /** Boolean to indicate if the interpolator BSpline. */ bool m_InterpolatorIsBSpline; // boolean to determine if we use mono-modality assumption bool m_OpticalFlow; /** Typedefs for using BSpline interpolator. */ typedef BSplineInterpolateImageFunction BSplineInterpolatorType; /** Pointer to BSplineInterpolator. */ typename BSplineInterpolatorType::Pointer m_BSplineInterpolator; /** Typedefs for using central difference calculator. */ typedef CentralDifferenceImageFunction DerivativeFunctionType; /** Pointer to central difference calculator. */ typename DerivativeFunctionType::Pointer m_DerivativeCalculator; /** * Types and variables related to BSpline deformable transforms. * If the transform is of type third order BSplineTransform, * then we can speed up the metric derivative calculation by * only inspecting the parameters within the support region * of a mapped point. */ /** Boolean to indicate if the transform is BSpline deformable. */ /** * Enum of the deformabtion field spline order. */ enum { DeformationSplineOrder = 3 }; typename TFixedImage::SpacingType m_FixedImageSpacing; typename TFixedImage::PointType m_FixedImageOrigin; typedef FixedArray ParametersOffsetType; ParametersOffsetType m_ParametersOffset; mutable TransformPointer m_Transform; InterpolatorPointer m_Interpolator; InterpolatorPointer m_FixedImageInterpolator; InterpolatorPointer m_MovingImageInterpolator; GradientCalculatorPointer m_FixedImageGradientCalculator; GradientCalculatorPointer m_MovingImageGradientCalculator; FixedImagePointer m_FixedImageMask; MovingImagePointer m_MovingImageMask; double m_NormalizeMetric; float m_Normalizer; typedef LinearInterpolateImageFunction pdfintType; typename pdfintType::Pointer pdfinterpolator; typedef LinearInterpolateImageFunction dpdfintType; typename dpdfintType::Pointer dpdfinterpolator; typedef BSplineInterpolateImageFunction pdfintType2; typename pdfintType2::Pointer pdfinterpolator2; typename pdfintType2::Pointer pdfinterpolator3; unsigned int m_Padding; JointPDFSpacingType m_JointPDFSpacing; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkAvantsMutualInformationRegistrationFunction.cxx" #endif #endif ants-2.2.0/ImageRegistration/itkAvantsPDEDeformableRegistrationFunction.h000066400000000000000000000252671311104306400266320ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkAvantsPDEDeformableRegistrationFunction_h_ #define _itkAvantsPDEDeformableRegistrationFunction_h_ #include "antsAllocImage.h" #include "itkPDEDeformableRegistrationFunction.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkPointSet.h" namespace itk { /** \class AvantsPDEDeformableRegistrationFunction * * This is an abstract base class for all PDE functions which drives a * deformable registration algorithm. It is used by * PDEDeformationRegistrationFilter subclasses to compute the * output deformation field which will map a moving image onto * a fixed image. * * This class is templated over the fixed image type, moving image type * and the deformation field type. * * \sa AvantsPDEDeformableRegistrationFilter * \ingroup PDEDeformableRegistrationFunctions */ template class AvantsPDEDeformableRegistrationFunction : public PDEDeformableRegistrationFunction { public: /** Standard class typedefs. */ typedef AvantsPDEDeformableRegistrationFunction Self; typedef PDEDeformableRegistrationFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods) */ itkTypeMacro( AvantsPDEDeformableRegistrationFunction, PDEDeformableRegistrationFunction ); /** MovingImage image type. */ typedef TMovingImage MovingImageType; typedef typename MovingImageType::ConstPointer MovingImagePointer; typedef typename TMovingImage::IndexType IndexType; enum { ImageDimension = MovingImageType::ImageDimension }; /** FixedImage image type. */ typedef TFixedImage FixedImageType; typedef typename FixedImageType::ConstPointer FixedImagePointer; typedef typename Superclass::NeighborhoodType NeighborhoodType; typedef typename Superclass::FloatOffsetType FloatOffsetType; /** Deformation field type. */ typedef TDisplacementField DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldTypePointer; typedef typename TDisplacementField::PixelType VectorType; /** PointSet Types */ typedef itk::PointSet PointSetType; typedef typename PointSetType::Pointer PointSetPointer; typedef typename PointSetType::PointType PointType; typedef typename PointSetType::PixelType PointDataType; typedef Image MetricImageType; typedef typename Image::Pointer MetricImagePointer; /** Set the moving image. */ void SetMovingImage( const MovingImageType * ptr ) { Superclass::m_MovingImage = ptr; } /** Get the moving image. */ MovingImageType * GetMovingImage(void) { return const_cast(Superclass::m_MovingImage.GetPointer() ); } /** Set the fixed image. */ void SetFixedImage( const FixedImageType * ptr ) { Superclass::m_FixedImage = ptr; } /** Get the fixed image. */ FixedImageType * GetFixedImage(void) { return const_cast(Superclass::m_FixedImage.GetPointer() ); } /** Set the fixed image. */ void SetDisplacementField( DisplacementFieldTypePointer ptr ) { Superclass::m_DisplacementField = ptr; } /** Get the fixed image. */ DisplacementFieldTypePointer GetDisplacementField(void) { return Superclass::m_DisplacementField; } void SetEnergy( double /* e */) { this->m_Energy = 0.0; } double GetEnergy() { return this->m_Energy; } void SetGradientStep( double e) { this->m_GradientStep = e; } void SetMaxAllowedStep( double e) { this->m_MaxAllowedStep = e; } double GetGradientStep() { return this->m_GradientStep; } void SetNormalizeGradient( bool e) { this->m_NormalizeGradient = e; } bool GetNormalizeGradient() { return this->m_NormalizeGradient; } /* * allows one to compute the metric everywhere */ void ComputeMetricImage() { bool makenewimage = false; FixedImageType* img = const_cast(this->m_FixedImage.GetPointer() ); typename FixedImageType::SizeType imagesize = img->GetLargestPossibleRegion().GetSize(); if( !m_MetricImage ) { makenewimage = true; } else if( imagesize[0] != m_MetricImage->GetLargestPossibleRegion().GetSize()[0] ) { makenewimage = true; } if( makenewimage ) { m_MetricImage = AllocImage(img->GetLargestPossibleRegion(), 0); m_MetricImage->SetSpacing(img->GetSpacing() ); m_MetricImage->SetOrigin(img->GetOrigin() ); } return; } virtual double ComputeMetricAtPair(IndexType /* fixedindex */, typename TDisplacementField::PixelType /* vec */) { return 0.0; } virtual VectorType ComputeUpdateInv(const NeighborhoodType & neighborhood, void * /* globalData */, const FloatOffsetType & /* offset */ = FloatOffsetType(0.0) ) { bool m_Use1SidedDiff = false; VectorType update; update.Fill(0.0); typename FixedImageType::SpacingType spacing = this->GetFixedImage()->GetSpacing(); IndexType oindex = neighborhood.GetIndex(); typename TDisplacementField::PixelType vec = this->m_DisplacementField->GetPixel(oindex); float loce = 0.0; double nccp1, nccm1; if( m_Use1SidedDiff ) { nccp1 = this->ComputeMetricAtPair(oindex, vec); } for( int imd = 0; imd < ImageDimension; imd++ ) { typename TDisplacementField::PixelType fdvec1 = this->m_DisplacementField->GetPixel(oindex); typename TDisplacementField::PixelType fdvec2 = this->m_DisplacementField->GetPixel(oindex); float step = 0.1 * spacing[imd]; fdvec1[imd] = vec[imd] + step; if( !m_Use1SidedDiff ) { nccp1 = this->ComputeMetricAtPair(oindex, fdvec1); } fdvec2[imd] = vec[imd] - step; nccm1 = this->ComputeMetricAtPair(oindex, fdvec2); update[imd] = nccp1 - nccm1; loce += (nccp1 + nccm1); } loce /= (2.0 * (float)ImageDimension); // this->ComputeMetricAtPair(oindex,vec); this->m_Energy += loce; if( m_MetricImage ) { m_MetricImage->SetPixel(oindex, loce); } return update * this->m_GradientStep; } virtual VectorType ComputeUpdate(const NeighborhoodType & neighborhood, void * /* globalData */, const FloatOffsetType & /* offset */ = FloatOffsetType(0.0) ) ITK_OVERRIDE { bool m_Use1SidedDiff = false; VectorType update; update.Fill(0.0); typename FixedImageType::SpacingType spacing = this->GetFixedImage()->GetSpacing(); IndexType oindex = neighborhood.GetIndex(); typename TDisplacementField::PixelType vec = this->m_DisplacementField->GetPixel(oindex); float loce = 0.0; double nccp1, nccm1; if( m_Use1SidedDiff ) { nccp1 = this->ComputeMetricAtPair(oindex, vec); } for( int imd = 0; imd < ImageDimension; imd++ ) { typename TDisplacementField::PixelType fdvec1 = this->m_DisplacementField->GetPixel(oindex); typename TDisplacementField::PixelType fdvec2 = this->m_DisplacementField->GetPixel(oindex); float step = 0.1 * spacing[imd]; fdvec1[imd] = vec[imd] + step; if( !m_Use1SidedDiff ) { nccp1 = this->ComputeMetricAtPair(oindex, fdvec1); } fdvec2[imd] = vec[imd] - step; nccm1 = this->ComputeMetricAtPair(oindex, fdvec2); update[imd] = nccp1 - nccm1; loce += (nccp1 + nccm1); } loce /= (2.0 * (float)ImageDimension); // this->ComputeMetricAtPair(oindex,vec); this->m_Energy += loce; if( m_MetricImage ) { m_MetricImage->SetPixel(oindex, loce); } // float mag=0; // for (int imd=0; imd 1) update.Fill(0.0); // //std::cout << " update " << update << " ind " << oindex << std::endl; return update * this->m_GradientStep; } void SetIterations( unsigned int i ) { this->m_Iterations = i; } /** this parameter is used to set a minimum value for the metric -- a minimum value that is treated as " close enough" -- */ void SetRobustnessParameter( float i ) { this->m_RobustnessParameter = i; } void SetFixedPointSet( PointSetPointer p ) { this->m_FixedPointSet = p; } void SetMovingPointSet( PointSetPointer p ) { this->m_MovingPointSet = p; } bool ThisIsAPointSetMetric() { return this->m_IsPointSetMetric; } protected: AvantsPDEDeformableRegistrationFunction() { this->m_MovingImage = ITK_NULLPTR; m_MetricImage = ITK_NULLPTR; this->m_FixedImage = ITK_NULLPTR; this->m_DisplacementField = ITK_NULLPTR; this->m_Energy = 0.0; m_BestEnergy = 0.0; this->m_NormalizeGradient = true; this->m_GradientStep = 1.0; m_AverageStepMag = 0; m_MaxStepMag = 0; m_AvgCt = 0; m_Iterations = 0; this->m_FixedPointSet = ITK_NULLPTR; this->m_MovingPointSet = ITK_NULLPTR; this->m_IsPointSetMetric = false; this->m_RobustnessParameter = -1.e12; } ~AvantsPDEDeformableRegistrationFunction() { } mutable double m_BestEnergy; mutable double m_LastLastEnergy; mutable double m_MaxAllowedStep; mutable double m_AverageStepMag; mutable unsigned long m_AvgCt; mutable unsigned long m_Iterations; mutable double m_MaxStepMag; PointSetPointer m_FixedPointSet; PointSetPointer m_MovingPointSet; bool m_IsPointSetMetric; MetricImagePointer m_MetricImage; float m_RobustnessParameter; private: AvantsPDEDeformableRegistrationFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkCrossCorrelationRegistrationFunction.cxx000066400000000000000000000357631311104306400267340ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkCrossCorrelationRegistrationFunction_hxx_ #define _itkCrossCorrelationRegistrationFunction_hxx_ #include "itkCrossCorrelationRegistrationFunction.h" #include "itkExceptionObject.h" #include "vnl/vnl_math.h" #include "itkImageFileWriter.h" #include "itkImageLinearConstIteratorWithIndex.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkMeanImageFilter.h" #include "itkMedianImageFilter.h" #include "itkImageFileWriter.h" #include namespace itk { /* * Default constructor */ template CrossCorrelationRegistrationFunction ::CrossCorrelationRegistrationFunction() { m_AvgMag = 0; m_Iteration = 0; RadiusType r; unsigned int j; for( j = 0; j < ImageDimension; j++ ) { r[j] = 2; } this->SetRadius(r); this->m_Energy = 0.0; m_TimeStep = 1.0; m_DenominatorThreshold = 1e-9; m_IntensityDifferenceThreshold = 0.001; Superclass::m_MovingImage = ITK_NULLPTR; m_MetricGradientImage = ITK_NULLPTR; Superclass::m_FixedImage = ITK_NULLPTR; m_FixedImageSpacing.Fill( 1.0 ); m_FixedImageOrigin.Fill( 0.0 ); m_FixedImageGradientCalculator = GradientCalculatorType::New(); binaryimage = ITK_NULLPTR; m_FullyRobust = false; m_MovingImageGradientCalculator = GradientCalculatorType::New(); typename DefaultInterpolatorType::Pointer interp = DefaultInterpolatorType::New(); m_MovingImageInterpolator = static_cast( interp.GetPointer() ); for( int i = 0; i < 5; i++ ) { finitediffimages[i] = ITK_NULLPTR; } m_NumberOfHistogramBins = 32; m_FixedImageMask = ITK_NULLPTR; m_MovingImageMask = ITK_NULLPTR; } /* * Standard "PrintSelf" method. */ template void CrossCorrelationRegistrationFunction ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); /* os << indent << "MovingImageIterpolator: "; os << m_MovingImageInterpolator.GetPointer() << std::endl; os << indent << "FixedImageGradientCalculator: "; os << m_FixedImageGradientCalculator.GetPointer() << std::endl; os << indent << "DenominatorThreshold: "; os << m_DenominatorThreshold << std::endl; os << indent << "IntensityDifferenceThreshold: "; os << m_IntensityDifferenceThreshold << std::endl; */ } /* * Set the function state values before each iteration */ template void CrossCorrelationRegistrationFunction ::InitializeIteration() { if( !Superclass::m_MovingImage || !Superclass::m_FixedImage || !m_MovingImageInterpolator ) { itkExceptionMacro( << "MovingImage, FixedImage and/or Interpolator not set" ); throw ExceptionObject(__FILE__, __LINE__); } // cache fixed image information m_FixedImageSpacing = Superclass::m_FixedImage->GetSpacing(); m_FixedImageOrigin = Superclass::m_FixedImage->GetOrigin(); // setup gradient calculator m_FixedImageGradientCalculator->SetInputImage( Superclass::m_FixedImage ); m_MovingImageGradientCalculator->SetInputImage( Superclass::m_MovingImage ); // setup moving image interpolator m_MovingImageInterpolator->SetInputImage( Superclass::m_MovingImage ); m_MetricTotal = 0.0; this->m_Energy = 0.0; // compute the normalizer m_Normalizer = 0.0; for( unsigned int k = 0; k < ImageDimension; k++ ) { m_Normalizer += m_FixedImageSpacing[k] * m_FixedImageSpacing[k]; } m_Normalizer /= static_cast( ImageDimension ); bool makeimg = false; if( m_Iteration == 0 ) { makeimg = true; } else if( !finitediffimages[0] ) { makeimg = true; } else { for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( finitediffimages[0]->GetLargestPossibleRegion().GetSize()[dd] != this->GetFixedImage()->GetLargestPossibleRegion().GetSize()[dd] ) { makeimg = true; } } } if( makeimg ) { finitediffimages[0] = this->MakeImage(); finitediffimages[1] = this->MakeImage(); finitediffimages[2] = this->MakeImage(); finitediffimages[3] = this->MakeImage(); finitediffimages[4] = this->MakeImage(); } // float sig=15.; RadiusType r; for( unsigned int j = 0; j < ImageDimension; j++ ) { r[j] = this->GetRadius()[j]; } typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator tIter(this->GetFixedImage(), this->GetFixedImage()->GetLargestPossibleRegion() ); // compute local means // typedef itk::ImageRegionIteratorWithIndex Iterator; // // The following change was made to speed up the correlation calculation. // typedef std::deque SumQueueType; SumQueueType Qsuma2; SumQueueType Qsumb2; SumQueueType Qsuma; SumQueueType Qsumb; SumQueueType Qsumab; SumQueueType Qcount; ImageLinearConstIteratorWithIndex outIter( this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion() ); outIter.SetDirection( 0 ); outIter.GoToBegin(); while( !outIter.IsAtEnd() ) { // Push the zeros onto the stack that are outsized the image boundary at // the beginning of the line. Qsuma2 = SumQueueType( r[0], 0.0 ); Qsumb2 = SumQueueType( r[0], 0.0 ); Qsuma = SumQueueType( r[0], 0.0 ); Qsumb = SumQueueType( r[0], 0.0 ); Qsumab = SumQueueType( r[0], 0.0 ); Qcount = SumQueueType( r[0], 0.0 ); NeighborhoodIterator hoodIt( this->GetRadius(), this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion() ); IndexType oindex = outIter.GetIndex(); hoodIt.SetLocation( oindex ); unsigned int hoodlen = hoodIt.Size(); // Now add the rest of the values from each hyperplane for( unsigned int i = r[0]; i < ( 2 * r[0] + 1 ); i++ ) { float suma2 = 0.0; float sumb2 = 0.0; float suma = 0.0; float sumb = 0.0; float sumab = 0.0; float count = 0.0; for( unsigned int indct = i; indct < hoodlen; indct += ( 2 * r[0] + 1 ) ) { bool isInBounds = true; hoodIt.GetPixel( indct, isInBounds ); IndexType index = hoodIt.GetIndex( indct ); if( !isInBounds || ( this->m_FixedImageMask && this->m_FixedImageMask->GetPixel( index ) < 0.25 ) ) { continue; } float a = this->GetFixedImage()->GetPixel( index ); float b = this->GetMovingImage()->GetPixel( index ); suma2 += a * a; sumb2 += b * b; suma += a; sumb += b; sumab += a * b; count += 1.0; } Qsuma2.push_back( suma2 ); Qsumb2.push_back( sumb2 ); Qsuma.push_back( suma ); Qsumb.push_back( sumb ); Qsumab.push_back( sumab ); Qcount.push_back( count ); } while( !outIter.IsAtEndOfLine() ) { // Test to see if there are any voxels we need to handle in the current // window. float suma2 = 0.0; float sumb2 = 0.0; float suma = 0.0; float sumb = 0.0; float sumab = 0.0; float count = 0.0; typename SumQueueType::iterator itcount = Qcount.begin(); while( itcount != Qcount.end() ) { count += *itcount; ++itcount; } // If there are values, we need to calculate the different quantities if( count > 0 ) { typename SumQueueType::iterator ita2 = Qsuma2.begin(); typename SumQueueType::iterator itb2 = Qsumb2.begin(); typename SumQueueType::iterator ita = Qsuma.begin(); typename SumQueueType::iterator itb = Qsumb.begin(); typename SumQueueType::iterator itab = Qsumab.begin(); while( ita2 != Qsuma2.end() ) { suma2 += *ita2; sumb2 += *itb2; suma += *ita; sumb += *itb; sumab += *itab; ++ita2; ++itb2; ++ita; ++itb; ++itab; } float fixedMean = suma / count; float movingMean = sumb / count; float sff = suma2 - fixedMean * suma - fixedMean * suma + count * fixedMean * fixedMean; float smm = sumb2 - movingMean * sumb - movingMean * sumb + count * movingMean * movingMean; float sfm = sumab - movingMean * suma - fixedMean * sumb + count * movingMean * fixedMean; IndexType _oindex = outIter.GetIndex(); float val = this->GetFixedImage()->GetPixel( _oindex ) - fixedMean; this->finitediffimages[0]->SetPixel( _oindex, val ); val = this->GetMovingImage()->GetPixel( _oindex ) - movingMean; this->finitediffimages[1]->SetPixel( _oindex, val ); this->finitediffimages[2]->SetPixel( _oindex, sfm ); // A this->finitediffimages[3]->SetPixel( _oindex, sff ); // B this->finitediffimages[4]->SetPixel( _oindex, smm ); // C } // Increment the iterator and check to see if we're at the end of the // line. If so, go to the next line. Otherwise, add the // the values for the next hyperplane. ++outIter; if( !outIter.IsAtEndOfLine() ) { hoodIt.SetLocation( outIter.GetIndex() ); suma2 = 0.0; sumb2 = 0.0; suma = 0.0; sumb = 0.0; sumab = 0.0; count = 0.0; for( unsigned int indct = 2 * r[0]; indct < hoodlen; indct += ( 2 * r[0] + 1 ) ) { bool isInBounds = true; hoodIt.GetPixel( indct, isInBounds ); IndexType index = hoodIt.GetIndex( indct ); if( !isInBounds || ( this->m_FixedImageMask && this->m_FixedImageMask->GetPixel( index ) < 0.25 ) ) { continue; } float a = this->GetFixedImage()->GetPixel( index ); float b = this->GetMovingImage()->GetPixel( index ); suma2 += a * a; sumb2 += b * b; suma += a; sumb += b; sumab += a * b; count += 1.0; } Qsuma2.push_back( suma2 ); Qsumb2.push_back( sumb2 ); Qsuma.push_back( suma ); Qsumb.push_back( sumb ); Qsumab.push_back( sumab ); Qcount.push_back( count ); } Qsuma2.pop_front(); Qsumb2.pop_front(); Qsuma.pop_front(); Qsumb.pop_front(); Qsumab.pop_front(); Qcount.pop_front(); } outIter.NextLine(); } // m_FixedImageGradientCalculator->SetInputImage(finitediffimages[0]); m_MaxMag = 0.0; m_MinMag = 9.e9; m_AvgMag = 0.0; m_Iteration++; } /* * Compute the ncc metric everywhere */ template typename TDisplacementField::PixelType CrossCorrelationRegistrationFunction ::ComputeMetricAtPairB(IndexType oindex, typename TDisplacementField::PixelType /* vec */) { typename TDisplacementField::PixelType deriv; deriv.Fill(0.0); double sff = 0.0; double smm = 0.0; double sfm = 0.0; // double fixedValue; // double movingValue; PointType mappedPoint; CovariantVectorType gradI, gradJ; if( this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( oindex ) < 0.25 ) { return deriv; } } sfm = finitediffimages[2]->GetPixel(oindex); sff = finitediffimages[3]->GetPixel(oindex); smm = finitediffimages[4]->GetPixel(oindex); if( sff == 0.0 || smm == 0.0 ) { return deriv; } this->localCrossCorrelation = 0; if( sff * smm > 1.e-5 ) { this->localCrossCorrelation = sfm * sfm / ( sff * smm ); } IndexType index = oindex; // hoodIt.GetIndex(indct); gradI = m_FixedImageGradientCalculator->EvaluateAtIndex( index ); // gradJ = m_MovingImageGradientCalculator->EvaluateAtIndex( index ); float Ji = finitediffimages[1]->GetPixel(index); float Ii = finitediffimages[0]->GetPixel(index); m_TEMP = 2.0 * sfm / (sff * smm) * ( Ji - sfm / sff * Ii ); for( unsigned int qq = 0; qq < ImageDimension; qq++ ) { deriv[qq] -= 2.0 * sfm / (sff * smm) * ( Ji - sfm / sff * Ii ) * gradI[qq]; // derivinv[qq]-=2.0*sfm/(sff*smm)*( Ii - sfm/smm*Ji )*gradJ[qq]; } // if ( this->localCrossCorrelation*(-1.0) < this->m_RobustnessParameter) deriv.Fill(0); // if ( this->localCrossCorrelation*(-1.0) < this->m_RobustnessParameter) { // std::cout << " localC " << this->localCrossCorrelation << std::endl; } if( this->localCrossCorrelation < 1 ) { this->m_Energy -= this->localCrossCorrelation; } return deriv; // localCrossCorrelation; } /* * Compute the ncc metric everywhere */ template typename TDisplacementField::PixelType CrossCorrelationRegistrationFunction ::ComputeMetricAtPairC(IndexType oindex, typename TDisplacementField::PixelType /* vec */) { typename TDisplacementField::PixelType deriv; deriv.Fill(0.0); double sff = 0.0; double smm = 0.0; double sfm = 0.0; PointType mappedPoint; CovariantVectorType gradI, gradJ; if( this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( oindex ) < 0.25 ) { return deriv; } } sfm = finitediffimages[2]->GetPixel(oindex); sff = finitediffimages[3]->GetPixel(oindex); smm = finitediffimages[4]->GetPixel(oindex); if( sff == 0.0 || smm == 0.0 ) { return deriv; } IndexType index = oindex; // hoodIt.GetIndex(indct); if( sff == 0.0 ) { sff = 1.0; } if( smm == 0.0 ) { smm = 1.0; } // /gradI = m_FixedImageGradientCalculator->EvaluateAtIndex( index ); gradJ = m_MovingImageGradientCalculator->EvaluateAtIndex( index ); float Ji = finitediffimages[1]->GetPixel(index); float Ii = finitediffimages[0]->GetPixel(index); for( unsigned int qq = 0; qq < ImageDimension; qq++ ) { // deriv[qq] -=2.0*sfm/(sff*smm)*( Ji - sfm/sff*Ii )*gradI[qq]; deriv[qq] -= 2.0 * sfm / (sff * smm) * ( Ii - sfm / smm * Ji ) * gradJ[qq]; } if( sff * smm != 0.0 ) { this->localCrossCorrelation = sfm * sfm / ( sff * smm ); } else { this->localCrossCorrelation = 1.0; } // if ( this->localCrossCorrelation*(-1.0) < this->m_RobustnessParameter) deriv.Fill(0); return deriv; // localCrossCorrelation; } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkCrossCorrelationRegistrationFunction.h000066400000000000000000000350631311104306400263520ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkCrossCorrelationRegistrationFunction_h_ #define _itkCrossCorrelationRegistrationFunction_h_ #include "antsAllocImage.h" #include "itkAvantsPDEDeformableRegistrationFunction.h" #include "itkPoint.h" #include "itkCovariantVector.h" #include "itkInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkCentralDifferenceImageFunction.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkAvantsMutualInformationRegistrationFunction.h" namespace itk { /** * \class CrossCorrelationRegistrationFunction * * This class encapsulate the PDE which drives the demons registration * algorithm. It is used by CrossCorrelationRegistrationFilter to compute the * output deformation field which will map a moving image onto a * a fixed image. * * Non-integer moving image values are obtained by using * interpolation. The default interpolator is of type * LinearInterpolateImageFunction. The user may set other * interpolators via method SetMovingImageInterpolator. Note that the input * interpolator must derive from baseclass InterpolateImageFunction. * * This class is templated over the fixed image type, moving image type, * and the deformation field type. * * \warning This filter assumes that the fixed image type, moving image type * and deformation field type all have the same number of dimensions. * * \sa CrossCorrelationRegistrationFilter * \ingroup FiniteDifferenceFunctions */ template class CrossCorrelationRegistrationFunction : public AvantsPDEDeformableRegistrationFunction { public: /** Standard class typedefs. */ typedef CrossCorrelationRegistrationFunction Self; typedef AvantsPDEDeformableRegistrationFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro( CrossCorrelationRegistrationFunction, AvantsPDEDeformableRegistrationFunction ); /** MovingImage image type. */ typedef typename Superclass::MovingImageType MovingImageType; typedef typename Superclass::MovingImagePointer MovingImagePointer; /** FixedImage image type. */ typedef typename Superclass::MetricImageType MetricImageType; typedef typename Superclass::MetricImageType::Pointer MetricImagePointer; typedef typename Superclass::FixedImageType FixedImageType; typedef typename Superclass::FixedImagePointer FixedImagePointer; typedef typename FixedImageType::IndexType IndexType; typedef typename FixedImageType::SizeType SizeType; /** Deformation field type. */ typedef typename Superclass::DisplacementFieldType DisplacementFieldType; typedef typename Superclass::DisplacementFieldTypePointer DisplacementFieldTypePointer; typedef typename TDisplacementField::PixelType VectorType; typedef CovariantVector GradientPixelType; typedef Image GradientImageType; typedef SmartPointer GradientImagePointer; typedef GradientRecursiveGaussianImageFilter GradientImageFilterType; typedef typename GradientImageFilterType::Pointer GradientImageFilterPointer; typedef Image BinaryImageType; typedef typename BinaryImageType::Pointer BinaryImagePointer; /** Inherit some enums from the superclass. */ itkStaticConstMacro(ImageDimension, unsigned int, Superclass::ImageDimension); /** Inherit some enums from the superclass. */ typedef typename Superclass::PixelType PixelType; typedef typename Superclass::RadiusType RadiusType; typedef typename Superclass::NeighborhoodType NeighborhoodType; // typedef typename Superclass::NeighborhoodType BoundaryNeighborhoodType; typedef typename Superclass::FloatOffsetType FloatOffsetType; typedef typename Superclass::TimeStepType TimeStepType; /** Interpolator type. */ typedef double CoordRepType; typedef InterpolateImageFunction InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; typedef typename InterpolatorType::PointType PointType; typedef LinearInterpolateImageFunction DefaultInterpolatorType; /** Covariant vector type. */ typedef CovariantVector CovariantVectorType; /** Gradient calculator type. */ typedef CentralDifferenceImageFunction GradientCalculatorType; typedef typename GradientCalculatorType::Pointer GradientCalculatorPointer; /** Set the moving image interpolator. */ void SetMovingImageInterpolator( InterpolatorType * ptr ) { m_MovingImageInterpolator = ptr; } /** Get the moving image interpolator. */ InterpolatorType * GetMovingImageInterpolator(void) { return m_MovingImageInterpolator; } typename TDisplacementField::PixelType ComputeMetricAtPairB(IndexType fixedindex, typename TDisplacementField::PixelType vec ); typename TDisplacementField::PixelType ComputeMetricAtPairC(IndexType fixedindex, typename TDisplacementField::PixelType vec ); /** This class uses a constant timestep of 1. */ virtual TimeStepType ComputeGlobalTimeStep(void * /* GlobalData */) const ITK_OVERRIDE { return m_TimeStep; } /** Return a pointer to a global data structure that is passed to * this object from the solver at each calculation. */ virtual void * GetGlobalDataPointer() const ITK_OVERRIDE { GlobalDataStruct *global = new GlobalDataStruct(); return global; } /** Release memory for global data structure. */ virtual void ReleaseGlobalDataPointer( void *GlobalData ) const ITK_OVERRIDE { //HACK: This code is suspicous and a possible source of // very difficult to diagnose failures. // I would recommend rewriting this so that // a reinterpret_cast is not needed. // In particular, the void pointer in the function // signature should be reconsidered. delete reinterpret_cast( GlobalData ); } /** Set the object's state before each iteration. */ virtual void InitializeIteration() ITK_OVERRIDE; double ComputeCrossCorrelation() { if( finitediffimages[0] ) { double totalcc = 0; unsigned long ct = 0; typedef ImageRegionIteratorWithIndex ittype; ittype it(this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion().GetSize() ); for( it.GoToBegin(); !it.IsAtEnd(); ++it ) { IndexType oindex = it.GetIndex(); double sfm = finitediffimages[2]->GetPixel(oindex); double sff = finitediffimages[3]->GetPixel(oindex); double smm = finitediffimages[4]->GetPixel(oindex); double cc = 0; if( fabs(sff * smm) > 0 ) { cc += sfm * sfm / (sff * smm); ct++; } if( this->m_MetricImage ) { this->m_MetricImage->SetPixel(oindex, cc); // if ( fabs(cc) > 0) // std::cout << " set cc " << cc << std::endl; } totalcc += cc; } this->m_Energy = totalcc / (float)ct * (-1.0); return this->m_Energy; } else { return 0; } } virtual VectorType OpticalFlowUpdate(const NeighborhoodType & neighborhood) { // Get fixed image related information IndexType index = neighborhood.GetIndex(); typename TDisplacementField::PixelType vec = Superclass::m_DisplacementField->GetPixel(index); VectorType update; update.Fill(0.0); double fixedValue; CovariantVectorType fixedGradient; double fixedGradientSquaredMagnitude = 0; fixedValue = (double) Superclass::Superclass::m_FixedImage->GetPixel( index ); fixedGradient = m_FixedImageGradientCalculator->EvaluateAtIndex( index ); unsigned int j = 0; for( j = 0; j < ImageDimension; j++ ) { fixedGradientSquaredMagnitude += vnl_math_sqr( fixedGradient[j] ); } double movingValue; PointType mappedPoint; for( j = 0; j < ImageDimension; j++ ) { mappedPoint[j] = double( index[j] ) * m_FixedImageSpacing[j] + m_FixedImageOrigin[j]; mappedPoint[j] += vec[j]; } if( m_MovingImageInterpolator->IsInsideBuffer( mappedPoint ) ) { movingValue = m_MovingImageInterpolator->Evaluate( mappedPoint ); } else { for( j = 0; j < ImageDimension; j++ ) { update[j] = 0.0; } return update; } double speedValue = fixedValue - movingValue; if( fabs(speedValue) < this->m_RobustnessParameter ) { speedValue = 0; } double denominator = vnl_math_sqr( speedValue ) / m_Normalizer + fixedGradientSquaredMagnitude; double DenominatorThreshold = 1e-9; double IntensityDifferenceThreshold = 0.001; if( vnl_math_abs(speedValue) < IntensityDifferenceThreshold || denominator < DenominatorThreshold ) { for( j = 0; j < ImageDimension; j++ ) { update[j] = 0.0; } return update; } for( j = 0; j < ImageDimension; j++ ) { update[j] = speedValue * fixedGradient[j] / denominator; } return update; } void SetFixedImageMask( MetricImageType* img) { m_FixedImageMask = img; } virtual VectorType ComputeUpdate(const NeighborhoodType & neighborhood, void * /* globalData */, const FloatOffsetType & /* offset */ = FloatOffsetType(0.0) ) ITK_OVERRIDE { VectorType update; update.Fill(0.0); IndexType oindex = neighborhood.GetIndex(); FixedImageType* img = const_cast(Superclass::m_FixedImage.GetPointer() ); if( !img ) { return update; } update = this->ComputeMetricAtPairB(oindex, update); return update; } virtual VectorType ComputeUpdateInv(const NeighborhoodType & neighborhood, void * /* globalData */, const FloatOffsetType & /* offset */ = FloatOffsetType(0.0) ) ITK_OVERRIDE { VectorType update; update.Fill(0.0); IndexType oindex = neighborhood.GetIndex(); FixedImageType* img = const_cast(Superclass::m_FixedImage.GetPointer() ); if( !img ) { return update; } update = this->ComputeMetricAtPairC(oindex, update); return update; } void SetFullyRobust(bool b) { m_FullyRobust = b; } void GetProbabilities(); double localCrossCorrelation; float m_TEMP; MetricImagePointer MakeImage() { FixedImageType* img = const_cast(Superclass::m_FixedImage.GetPointer() ); this->m_MetricImage = AllocImage(img, 0); bool makebinimg = false; if( makebinimg ) { m_Iteration = 0; binaryimage = AllocImage(img->GetLargestPossibleRegion(), 1); binaryimage->SetSpacing(img->GetSpacing() ); binaryimage->SetOrigin(img->GetOrigin() ); } return this->m_MetricImage; } protected: CrossCorrelationRegistrationFunction(); ~CrossCorrelationRegistrationFunction() { } void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; /** FixedImage image neighborhood iterator type. */ typedef ConstNeighborhoodIterator FixedImageNeighborhoodIteratorType; /** A global data type for this class of equation. Used to store * iterators for the fixed image. */ struct GlobalDataStruct { FixedImageNeighborhoodIteratorType m_FixedImageIterator; }; private: CrossCorrelationRegistrationFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** Cache fixed image information. */ typename TFixedImage::SpacingType m_FixedImageSpacing; typename TFixedImage::PointType m_FixedImageOrigin; /** Function to compute derivatives of the fixed image. */ GradientCalculatorPointer m_FixedImageGradientCalculator; GradientCalculatorPointer m_MovingImageGradientCalculator; /** Function to interpolate the moving image. */ InterpolatorPointer m_MovingImageInterpolator; /** The global timestep. */ TimeStepType m_TimeStep; /** Threshold below which the denominator term is considered zero. */ double m_DenominatorThreshold; /** Threshold below which two intensity value are assumed to match. */ double m_IntensityDifferenceThreshold; mutable double m_MetricTotal; mutable float m_MinMag; mutable float m_MaxMag; mutable float m_AvgMag; mutable float m_Thresh; GradientImagePointer m_MetricGradientImage; MetricImagePointer finitediffimages[5]; BinaryImagePointer binaryimage; MetricImagePointer m_FixedImageMask; MetricImagePointer m_MovingImageMask; typedef itk::AvantsMutualInformationRegistrationFunction MetricType2; typename MetricType2::Pointer m_MIFunct; unsigned int m_NumberOfHistogramBins; bool m_FullyRobust; unsigned int m_Iteration; float m_Normalizer; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkCrossCorrelationRegistrationFunction.cxx" #endif #endif ants-2.2.0/ImageRegistration/itkExpectationBasedPointSetRegistrationFunction.h000066400000000000000000000311621311104306400277630ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkExpectationBasedPointSetRegistrationFunction_h_ #define _itkExpectationBasedPointSetRegistrationFunction_h_ #include "itkPointSetFunction.h" #include "itkGaussianMembershipFunction.h" #include "itkKdTreeGenerator.h" #include "itkListSample.h" #include "itkMatrix.h" #include "itkMersenneTwisterRandomVariateGenerator.h" #include "itkMeshSource.h" #include "itkPointSet.h" #include "itkVector.h" #include "itkWeightedCentroidKdTreeGenerator.h" #include "itkPDEDeformableRegistrationFunction.h" #include "itkPoint.h" #include "itkCovariantVector.h" #include "itkInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkCentralDifferenceImageFunction.h" namespace itk { /** * \class ExpectationBasedPointSetRegistrationFunction * * This class encapsulate the PDE which drives the demons registration * algorithm. It is used by ExpectationBasedPointSetRegistrationFilter to compute the * output deformation field which will map a moving image onto a * a fixed image. * * Non-integer moving image values are obtained by using * interpolation. The default interpolator is of type * LinearInterpolateImageFunction. The user may set other * interpolators via method SetMovingImageInterpolator. Note that the input * interpolator must derive from baseclass InterpolateImageFunction. * * This class is templated over the fixed image type, moving image type, * and the deformation field type. * * \warning This filter assumes that the fixed image type, moving image type * and deformation field type all have the same number of dimensions. * * \sa ExpectationBasedPointSetRegistrationFilter * \ingroup FiniteDifferenceFunctions */ template class ExpectationBasedPointSetRegistrationFunction : public AvantsPDEDeformableRegistrationFunction { public: /** Standard class typedefs. */ typedef ExpectationBasedPointSetRegistrationFunction Self; typedef PDEDeformableRegistrationFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro( ExpectationBasedPointSetRegistrationFunction, PDEDeformableRegistrationFunction ); /** MovingImage image type. */ typedef typename Superclass::MovingImageType MovingImageType; typedef typename Superclass::MovingImagePointer MovingImagePointer; /** FixedImage image type. */ typedef typename Superclass::FixedImageType FixedImageType; typedef typename Superclass::FixedImagePointer FixedImagePointer; typedef typename FixedImageType::PointType ImagePointType; typedef typename FixedImageType::IndexType IndexType; typedef typename FixedImageType::SizeType SizeType; typedef typename FixedImageType::SpacingType SpacingType; /** Deformation field type. */ typedef typename Superclass::DisplacementFieldType DisplacementFieldType; typedef typename Superclass::DisplacementFieldTypePointer DisplacementFieldTypePointer; typedef typename DisplacementFieldType::PixelType VectorType; /** Inherit some enums from the superclass. */ itkStaticConstMacro(ImageDimension, unsigned int, Superclass::ImageDimension); itkStaticConstMacro(MeasurementDimension, unsigned int, Superclass::ImageDimension); /** Inherit some enums from the superclass. */ typedef typename Superclass::PixelType PixelType; typedef typename Superclass::RadiusType RadiusType; typedef typename Superclass::NeighborhoodType NeighborhoodType; typedef typename Superclass::FloatOffsetType FloatOffsetType; typedef typename Superclass::TimeStepType TimeStepType; /** Covariant vector type. */ typedef CovariantVector CovariantVectorType; /** PointSet Types */ typedef TPointSet PointSetType; typedef typename PointSetType::Pointer PointSetPointer; typedef typename PointSetType::PointType PointType; typedef typename PointSetType::PixelType PointDataType; typedef std::vector LabelSetType; // typedef long PointDataType; typedef Vector MeasurementVectorType; typedef typename Statistics::ListSample SampleType; typedef typename Statistics::WeightedCentroidKdTreeGenerator TreeGeneratorType; typedef typename TreeGeneratorType::KdTreeType:: InstanceIdentifierVectorType NeighborhoodIdentifierType; /** Bspline stuff */ typedef PointSet BSplinePointSetType; typedef BSplineScatteredDataPointSetToImageFilter BSplineFilterType; typedef typename BSplineFilterType::WeightsContainerType BSplineWeightsType; typedef typename BSplineFilterType::PointDataImageType ControlPointLatticeType; typedef typename BSplineFilterType::ArrayType ArrayType; /** Other typedef */ typedef float RealType; typedef float OutputType; typedef typename Statistics ::MersenneTwisterRandomVariateGenerator RandomizerType; typedef typename Statistics ::GaussianMembershipFunction GaussianType; /** Fixed image gradient calculator type. */ typedef CentralDifferenceImageFunction GradientCalculatorType; typedef typename GradientCalculatorType::Pointer GradientCalculatorPointer; /** Moving image gradient calculator type. */ typedef CentralDifferenceImageFunction MovingImageGradientCalculatorType; typedef typename MovingImageGradientCalculatorType::Pointer MovingImageGradientCalculatorPointer; /** This class uses a constant timestep of 1. */ virtual TimeStepType ComputeGlobalTimeStep(void * itkNotUsed(GlobalData) ) const ITK_OVERRIDE { return m_TimeStep; } /** Return a pointer to a global data structure that is passed to * this object from the solver at each calculation. */ virtual void * GetGlobalDataPointer() const ITK_OVERRIDE { GlobalDataStruct *global = new GlobalDataStruct(); global->m_SumOfSquaredDifference = 0.0; global->m_NumberOfPixelsProcessed = 0L; global->m_SumOfSquaredChange = 0; return global; } /** Release memory for global data structure. */ virtual void ReleaseGlobalDataPointer( void *GlobalData ) const ITK_OVERRIDE; void ExpectationLandmarkField(float weight, bool whichdirection); void FastExpectationLandmarkField(float weight, bool whichdirection, long whichlabel, bool dobsp); /** Set the object's state before each iteration. */ virtual void InitializeIteration() ITK_OVERRIDE; /** This method is called by a finite difference solver image filter at * each pixel that does not lie on a data set boundary */ virtual PixelType ComputeUpdate(const NeighborhoodType & neighborhood, void *globalData, const FloatOffsetType & offset = FloatOffsetType( 0.0) ) ITK_OVERRIDE; virtual PixelType ComputeUpdateInv(const NeighborhoodType & neighborhood, void *globalData, const FloatOffsetType & offset = FloatOffsetType( 0.0) ) ITK_OVERRIDE; /** Get the metric value. The metric value is the mean square difference * in intensity between the fixed image and transforming moving image * computed over the the overlapping region between the two images. */ virtual double GetMetric() const { return m_Metric; } /** Get the rms change in deformation field. */ virtual double GetRMSChange() const { return m_RMSChange; } /** Set/Get the threshold below which the absolute difference of * intensity yields a match. When the intensities match between a * moving and fixed image pixel, the update vector (for that * iteration) will be the zero vector. Default is 0.001. */ virtual void SetEuclideanDistanceThreshold(double); virtual double GetEuclideanDistanceThreshold() const; void SetFixedPointSetSigma( float f ) { this->m_FixedPointSetSigma = f; } float GetFixedPointSetSigma() { return this->m_FixedPointSetSigma; } void SetMovingPointSetSigma( float f ) { this->m_MovingPointSetSigma = f; } float GetMovingPointSetSigma() { return this->m_MovingPointSetSigma; } void SetKNeighborhood( unsigned int n ) { this->m_KNeighborhood = n; } unsigned int GetKNeighborhood() { return this->m_KNeighborhood; } void SetUseSymmetricMatching(unsigned int b) { this->m_UseSymmetricMatching = b; } protected: ExpectationBasedPointSetRegistrationFunction(); ~ExpectationBasedPointSetRegistrationFunction() { } void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; /** FixedImage image neighborhood iterator type. */ typedef ConstNeighborhoodIterator FixedImageNeighborhoodIteratorType; /** A global data type for this class of equation. Used to store * information for computing the metric. */ struct GlobalDataStruct { double m_SumOfSquaredDifference; unsigned long m_NumberOfPixelsProcessed; double m_SumOfSquaredChange; }; void SetUpKDTrees(long whichlabel); private: ExpectationBasedPointSetRegistrationFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** Cache fixed image information. */ SpacingType m_FixedImageSpacing; ImagePointType m_FixedImageOrigin; double m_Normalizer; /** Function to compute derivatives of the fixed image. */ GradientCalculatorPointer m_FixedImageGradientCalculator; /** Function to compute derivatives of the moving image. */ MovingImageGradientCalculatorPointer m_MovingImageGradientCalculator; bool m_UseMovingImageGradient; /** The global timestep. */ TimeStepType m_TimeStep; /** Threshold below which the denominator term is considered zero. */ double m_DenominatorThreshold; /** Threshold below which two intensity value are assumed to match. */ double m_EuclideanDistanceThreshold; /** The metric value is the mean square difference in intensity between * the fixed image and transforming moving image computed over the * the overlapping region between the two images. */ mutable double m_Metric; mutable double m_SumOfSquaredDifference; mutable unsigned long m_NumberOfPixelsProcessed; mutable double m_RMSChange; mutable double m_SumOfSquaredChange; /** Mutex lock to protect modification to metric. */ // mutable SimpleFastMutexLock m_MetricCalculationLock; DisplacementFieldTypePointer m_DerivativeFixedField; DisplacementFieldTypePointer m_DerivativeMovingField; float m_FixedPointSetSigma; float m_MovingPointSetSigma; float m_LandmarkEnergy; unsigned int m_KNeighborhood; unsigned int m_BucketSize; RealType m_Sigma; typename TreeGeneratorType::Pointer m_FixedKdTreeGenerator; typename SampleType::Pointer m_FixedSamplePoints; typename TreeGeneratorType::Pointer m_MovingKdTreeGenerator; typename SampleType::Pointer m_MovingSamplePoints; typename RandomizerType::Pointer m_Randomizer; bool m_Normalize; LabelSetType m_LabelSet; unsigned int m_UseSymmetricMatching; typename BSplinePointSetType::Pointer m_bpoints; typename BSplineWeightsType::Pointer m_bweights; unsigned int m_bcount; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkExpectationBasedPointSetRegistrationFunction.hxx" #endif #endif ants-2.2.0/ImageRegistration/itkExpectationBasedPointSetRegistrationFunction.hxx000066400000000000000000000642631311104306400303530ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkExpectationBasedPointSetRegistrationFunction_hxx_ #define _itkExpectationBasedPointSetRegistrationFunction_hxx_ #include "itkExpectationBasedPointSetRegistrationFunction.h" #include "itkExceptionObject.h" #include "vnl/vnl_math.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkPointSet.h" namespace itk { /* * Default constructor */ template ExpectationBasedPointSetRegistrationFunction ::ExpectationBasedPointSetRegistrationFunction() { RadiusType r; unsigned int j; for( j = 0; j < ImageDimension; j++ ) { r[j] = 0; } this->SetRadius(r); m_TimeStep = 1.0; m_DenominatorThreshold = 1e-9; m_EuclideanDistanceThreshold = 0.01; this->SetMovingImage(ITK_NULLPTR); this->SetFixedImage(ITK_NULLPTR); m_FixedImageSpacing.Fill( 1.0 ); m_FixedImageOrigin.Fill( 0.0 ); m_Normalizer = 1.0; m_FixedImageGradientCalculator = GradientCalculatorType::New(); m_Metric = NumericTraits::max(); m_SumOfSquaredDifference = 0.0; m_NumberOfPixelsProcessed = 0L; m_RMSChange = NumericTraits::max(); m_SumOfSquaredChange = 0.0; this->m_KNeighborhood = 100; m_MovingImageGradientCalculator = MovingImageGradientCalculatorType::New(); m_UseMovingImageGradient = false; this->m_FixedPointSet = ITK_NULLPTR; this->m_MovingPointSet = ITK_NULLPTR; this->m_DerivativeFixedField = ITK_NULLPTR; this->m_DerivativeMovingField = ITK_NULLPTR; this->m_IsPointSetMetric = true; this->m_UseSymmetricMatching = 100000; this->m_Iterations = 0; } /* * Standard "PrintSelf" method. */ template void ExpectationBasedPointSetRegistrationFunction ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); } /** * */ template void ExpectationBasedPointSetRegistrationFunction ::SetEuclideanDistanceThreshold(double threshold) { m_EuclideanDistanceThreshold = threshold; } /** * */ template double ExpectationBasedPointSetRegistrationFunction ::GetEuclideanDistanceThreshold() const { return m_EuclideanDistanceThreshold; } /* * Set the function state values before each iteration */ template void ExpectationBasedPointSetRegistrationFunction ::ExpectationLandmarkField(float weight, bool whichdirection) { SpacingType spacing = this->GetFixedImage()->GetSpacing(); unsigned long sz1 = this->m_FixedPointSet->GetNumberOfPoints(); unsigned long sz2 = this->m_MovingPointSet->GetNumberOfPoints(); if( !whichdirection ) { sz2 = this->m_FixedPointSet->GetNumberOfPoints(); sz1 = this->m_MovingPointSet->GetNumberOfPoints(); } typedef vnl_matrix MatrixType; MatrixType EucDist(sz1, sz2); EucDist.fill(0); MatrixType fixedlms(sz1, ImageDimension); MatrixType movinglms(sz2, ImageDimension); fixedlms.fill(0); movinglms.fill(0); if( sz1 <= 0 || sz2 <= 0 ) { return; } DisplacementFieldTypePointer lmField = this->m_DerivativeFixedField; if( !whichdirection ) { lmField = this->m_DerivativeMovingField; } float inweight = weight; this->m_LandmarkEnergy = 0.0; std::cout << " sz1 " << sz1 << " sz2 " << sz2 << std::endl; // if whichdirection is true, then the fixed direction, else moving for( unsigned long ii = 0; ii < sz1; ii++ ) { PointType fixedpoint; PointDataType fixedlabel = 0; if( whichdirection ) { this->m_FixedPointSet->GetPoint(ii, &fixedpoint); } else { this->m_MovingPointSet->GetPoint(ii, &fixedpoint); } if( whichdirection ) { this->m_FixedPointSet->GetPointData(ii, &fixedlabel); } else { this->m_MovingPointSet->GetPointData(ii, &fixedlabel); } float min = 1.e9; ImagePointType fpt; IndexType oindex; for( int j = 0; j < ImageDimension; j++ ) { fpt[j] = fixedpoint[j]; fixedlms(ii, j) = fpt[j]; } bool convok = false; convok = this->GetFixedImage()->TransformPhysicalPointToIndex(fpt, oindex); if( !convok ) { std::cout << " fpt " << fpt << std::endl; } // if whichdirection is true, then the fixed direction, else moving for( unsigned long jj = 0; jj < sz2; jj++ ) { VectorType distance; IndexType fixedindex; IndexType movingindex; PointType movingpoint; if( whichdirection ) { this->m_MovingPointSet->GetPoint(jj, &movingpoint); } else { this->m_FixedPointSet->GetPoint(jj, &movingpoint); } ImagePointType mpt; for( int j = 0; j < ImageDimension; j++ ) { mpt[j] = movingpoint[j]; movinglms(jj, j) = movingpoint[j]; } if( ii == sz1 - 2 && jj == sz2 - 2 ) { std::cout << " fpt " << fpt << " mpt " << mpt << std::endl; } this->GetMovingImage()->TransformPhysicalPointToIndex(mpt, movingindex); double prob = 0; if( convok ) { float mag = 0.0; VectorType force; for( int j = 0; j < ImageDimension; j++ ) { distance[j] = movingpoint[j] - fixedpoint[j]; mag += distance[j] / spacing[j] * distance[j] / spacing[j]; force[j] = distance[j] * inweight; } float sigma = this->m_FixedPointSetSigma; if( !whichdirection ) { sigma = this->m_MovingPointSetSigma; } // KW -- rename 'prob' to '_prob' because of 'shadow variable' warning. double _prob = 1.0 / sqrt(3.14186 * 2.0 * sigma * sigma) * exp(-1.0 * mag / (2.0 * sigma * sigma) ); force = force * _prob; PointDataType movinglabel = 0; if( whichdirection ) { this->m_MovingPointSet->GetPointData(jj, &movinglabel); } else { this->m_FixedPointSet->GetPointData(jj, &movinglabel); } // if (ii == 2 && jj==2) std::cout << "_prob " << _prob << " sigma " << sigma << " " << mag << " fl " << // fixedlabel << " ml " << movinglabel << std::endl; if( fixedlabel != movinglabel ) { _prob = 0; } // || fixedlabel !=4) _prob=0; mag = sqrt(mag); } EucDist(ii, jj) = prob; } if( min < 1.e5 ) { this->m_LandmarkEnergy += min; } } MatrixType sinkhorn = EucDist; for( unsigned int iter = 0; iter < 1; iter++ ) { for( unsigned int jj = 0; jj < sz2; jj++ ) { float total = 0; for( unsigned int ii = 0; ii < sz1; ii++ ) { total += sinkhorn(ii, jj); } if( total <= 0 ) { total = 1; } for( unsigned int ii = 0; ii < sz1; ii++ ) { sinkhorn(ii, jj) /= total; } } for( unsigned int ii = 0; ii < sz1; ii++ ) { float total = 0; for( unsigned int jj = 0; jj < sz2; jj++ ) { total += sinkhorn(ii, jj); } if( total <= 0 ) { total = 1; } for( unsigned int jj = 0; jj < sz2; jj++ ) { sinkhorn(ii, jj) /= total; } } } MatrixType resultlms = sinkhorn * movinglms; MatrixType difflms = fixedlms - resultlms; VectorType sforce; sforce.Fill(0); float energy = 0, maxerr = 0; for( unsigned long ii = 0; ii < sz1; ii++ ) { VectorType distance; distance.Fill(0); PointType movingpoint; PointType fixedpoint; PointDataType fixedlabel = 0; if( whichdirection ) { this->m_FixedPointSet->GetPoint(ii, &fixedpoint); } else { this->m_MovingPointSet->GetPoint(ii, &fixedpoint); } if( whichdirection ) { this->m_FixedPointSet->GetPointData(ii, &fixedlabel); } else { this->m_MovingPointSet->GetPointData(ii, &fixedlabel); } ImagePointType mpt; ImagePointType fpt; for( int j = 0; j < ImageDimension; j++ ) { fpt[j] = fixedpoint[j]; } for( int j = 0; j < ImageDimension; j++ ) { mpt[j] = resultlms(ii, j); } bool convok = false; IndexType fixedindex; convok = this->GetFixedImage()->TransformPhysicalPointToIndex(fpt, fixedindex); if( convok ) { float mag = 0.0; VectorType force; for( int j = 0; j < ImageDimension; j++ ) { distance[j] = mpt[j] - fixedpoint[j]; mag += distance[j] / spacing[j] * distance[j] / spacing[j]; force[j] = distance[j] * inweight; } float sigma = this->m_FixedPointSetSigma; if( !whichdirection ) { sigma = this->m_MovingPointSetSigma; } double prob = 1.0 / sqrt(3.14186 * 2.0 * sigma * sigma) * exp(-1.0 * mag / (2.0 * sigma * sigma) ); force = force * prob; // if (fixedlabel !=4 ) force.Fill(0); /* if (mag > 50) { float tot=0; for (int k=0; k < sz2; k++) { tot+=sinkhorn(ii,k); } std::cout << "TOT " << tot << std::endl; force.Fill(0); mag=0; } */ if( mag > maxerr ) { maxerr = mag; } energy += mag; std::cout << " ii " << ii << " force " << force << " mag " << sqrt(mag) << " mpt " << mpt << " fpt " << fixedpoint << " nrg " << energy / (float)ii << std::endl; lmField->SetPixel(fixedindex, force + lmField->GetPixel(fixedindex) ); } // lmField->SetPixel(fixedindex,sforce); } // std::cout << " max " << maxerr << std::endl; this->m_LandmarkEnergy = energy / (float)sz1; this->m_Energy = this->m_LandmarkEnergy; } /* * Set the function state values before each iteration */ template void ExpectationBasedPointSetRegistrationFunction ::InitializeIteration() { // std::cout << " INIT ITER " << std::endl; if( !this->GetMovingImage() || !this->GetFixedImage() ) { itkExceptionMacro( << "MovingImage, FixedImage not set" ); } // cache fixed image information m_FixedImageSpacing = this->GetFixedImage()->GetSpacing(); m_FixedImageOrigin = this->GetFixedImage()->GetOrigin(); // compute the normalizer m_Normalizer = 0.0; for( unsigned int k = 0; k < ImageDimension; k++ ) { m_Normalizer += m_FixedImageSpacing[k] * m_FixedImageSpacing[k]; } m_Normalizer /= static_cast( ImageDimension ); // setup gradient calculator m_FixedImageGradientCalculator->SetInputImage( this->GetFixedImage() ); m_MovingImageGradientCalculator->SetInputImage( this->GetMovingImage() ); // initialize metric computation variables m_SumOfSquaredDifference = 0.0; m_NumberOfPixelsProcessed = 0L; m_SumOfSquaredChange = 0.0; typename DisplacementFieldType::PixelType zero; zero.Fill(0); this->m_DerivativeFixedField = AllocImage(this->GetFixedImage()->GetLargestPossibleRegion(), zero); this->m_DerivativeFixedField->SetSpacing( this->GetFixedImage()->GetSpacing() ); this->m_DerivativeFixedField->SetOrigin( this->GetFixedImage()->GetOrigin() ); this->m_DerivativeMovingField = AllocImage(this->GetMovingImage()->GetLargestPossibleRegion(), zero); this->m_DerivativeMovingField->SetSpacing( this->GetMovingImage()->GetSpacing() ); this->m_DerivativeMovingField->SetOrigin( this->GetMovingImage()->GetOrigin() ); // acquire labels if( this->m_LabelSet.size() < 1 ) { this->m_LabelSet.clear(); unsigned long sz1 = this->m_FixedPointSet->GetNumberOfPoints(); std::cout << " NPTS " << sz1 << std::endl; for( unsigned long ii = 0; ii < sz1; ii++ ) { PointType fixedpoint; PointDataType label; this->m_FixedPointSet->GetPoint(ii, &fixedpoint); this->m_FixedPointSet->GetPointData(ii, &label); if( label > 0 ) { if( find( this->m_LabelSet.begin(), this->m_LabelSet.end(), label ) == this->m_LabelSet.end() ) { this->m_LabelSet.push_back( label ); } } } } else { std::cout << " #of Label Values to match " << this->m_LabelSet.size() << std::endl; } this->m_bpoints = BSplinePointSetType::New(); this->m_bpoints->Initialize(); this->m_bweights = BSplineWeightsType::New(); this->m_bweights->Initialize(); this->m_bcount = 0; unsigned int lct = 0; typename LabelSetType::const_iterator it; for( it = this->m_LabelSet.begin(); it != this->m_LabelSet.end(); ++it ) { lct++; PointDataType label = (PointDataType) * it; // std::cout << " doing label " << label << std::endl; this->SetUpKDTrees(label); bool dobsp = false; // if (lct == this->m_LabelSet.size() ) dobsp=true; this->FastExpectationLandmarkField(1.0, true, label, dobsp); this->FastExpectationLandmarkField(1.0, false, label, dobsp); } // follow up with BSpline if dospb is true . } /* * Compute update at a specify neighbourhood */ template typename ExpectationBasedPointSetRegistrationFunction ::PixelType ExpectationBasedPointSetRegistrationFunction ::ComputeUpdate(const NeighborhoodType & it, void * /* gd */, const FloatOffsetType & itkNotUsed(offset) ) { IndexType index = it.GetIndex(); PixelType update = this->m_DerivativeFixedField->GetPixel(index); if( this->m_Iterations > this->m_UseSymmetricMatching ) { update.Fill(0); } return update; } /* * Compute update at a specify neighbourhood */ template typename ExpectationBasedPointSetRegistrationFunction ::PixelType ExpectationBasedPointSetRegistrationFunction ::ComputeUpdateInv(const NeighborhoodType & it, void * /* gd */, const FloatOffsetType & itkNotUsed(offset) ) { IndexType index = it.GetIndex(); return this->m_DerivativeMovingField->GetPixel(index); } /* * Update the metric and release the per-thread-global data. */ template void ExpectationBasedPointSetRegistrationFunction ::ReleaseGlobalDataPointer( void *gd ) const { GlobalDataStruct * globalData = (GlobalDataStruct *) gd; m_SumOfSquaredDifference += globalData->m_SumOfSquaredDifference; m_NumberOfPixelsProcessed += globalData->m_NumberOfPixelsProcessed; m_SumOfSquaredChange += globalData->m_SumOfSquaredChange; if( m_NumberOfPixelsProcessed ) { m_Metric = m_SumOfSquaredDifference / static_cast( m_NumberOfPixelsProcessed ); m_RMSChange = std::sqrt( m_SumOfSquaredChange / static_cast( m_NumberOfPixelsProcessed ) ); } delete globalData; } /* * Set the function state values before each iteration */ template void ExpectationBasedPointSetRegistrationFunction::SetUpKDTrees( long whichlabel) { // convert this->m_FixedPointSet to a sample type this->m_FixedSamplePoints = SampleType::New(); this->m_FixedSamplePoints->SetMeasurementVectorSize( MeasurementDimension ); MeasurementVectorType mv; unsigned int bucketsize = 4; unsigned int npts = this->m_FixedPointSet->GetNumberOfPoints(); // std::cout << " NP MOV " << npts << std::endl; for( unsigned int i = 0; i < npts; i++ ) { PointType fixedpoint; const bool validFixedPoint = this->m_FixedPointSet->GetPoint(i, &fixedpoint); if( !validFixedPoint ) { itkExceptionMacro( << "Invalid FixedPoint Requested at " << i ); } else { PointDataType fixedlabel = 0; this->m_FixedPointSet->GetPointData(i, &fixedlabel); for( unsigned int d = 0; d < ImageDimension; d++ ) { mv[d] = fixedpoint[d]; } // mv[ImageDimension]=(float) fixedlabel*1.e6; if( fixedlabel == whichlabel ) { this->m_FixedSamplePoints->PushBack( mv ); } } } this->m_FixedKdTreeGenerator = TreeGeneratorType::New(); this->m_FixedKdTreeGenerator->SetSample( this->m_FixedSamplePoints ); this->m_FixedKdTreeGenerator->SetBucketSize( bucketsize ); this->m_FixedKdTreeGenerator->Update(); this->m_MovingSamplePoints = SampleType::New(); this->m_MovingSamplePoints->SetMeasurementVectorSize( ImageDimension ); npts = this->m_MovingPointSet->GetNumberOfPoints(); // std::cout << " NP MOV " << npts << std::endl; for( unsigned int i = 0; i < npts; i++ ) { PointType movingpoint; const bool validMovingPoint = this->m_MovingPointSet->GetPoint(i, &movingpoint); if( !validMovingPoint ) { itkExceptionMacro( << "Invalid MovingPoint Requested at " << i ); } else { PointDataType movinglabel = 0; this->m_MovingPointSet->GetPointData(i, &movinglabel); for( unsigned int d = 0; d < ImageDimension; d++ ) { mv[d] = movingpoint[d]; } // mv[ImageDimension]=(float) movinglabel*1.e6; if( movinglabel == whichlabel ) { this->m_MovingSamplePoints->PushBack( mv ); } } } this->m_MovingKdTreeGenerator = TreeGeneratorType::New(); this->m_MovingKdTreeGenerator->SetSample( this->m_MovingSamplePoints ); this->m_MovingKdTreeGenerator->SetBucketSize( bucketsize ); this->m_MovingKdTreeGenerator->Update(); } /* * Set the function state values before each iteration */ template void ExpectationBasedPointSetRegistrationFunction ::FastExpectationLandmarkField(float weight, bool whichdirection, long /* whichlabel */, bool dobspline) { /** * BSpline typedefs */ /** Typedefs for B-spline filter */ unsigned int m_SplineOrder = 3; unsigned int m_NumberOfBLevels = 5; ArrayType m_MeshResolution; m_MeshResolution.Fill(1); unsigned int PointDimension = ImageDimension; SpacingType spacing = this->GetFixedImage()->GetSpacing(); typename TreeGeneratorType::Pointer fkdtree; typename TreeGeneratorType::Pointer mkdtree; if( whichdirection ) { mkdtree = this->m_MovingKdTreeGenerator; fkdtree = this->m_FixedKdTreeGenerator; } else { fkdtree = this->m_MovingKdTreeGenerator; mkdtree = this->m_FixedKdTreeGenerator; } unsigned long sz1 = fkdtree->GetOutput()->Size(); unsigned long sz2 = mkdtree->GetOutput()->Size(); // std::cout << " s1 " << sz1 << " s2 " << sz2 << std::endl; if( sz1 <= 0 || sz2 <= 0 ) { return; } DisplacementFieldTypePointer lmField = this->m_DerivativeFixedField; if( !whichdirection ) { lmField = this->m_DerivativeMovingField; } unsigned int KNeighbors = this->m_KNeighborhood; if( KNeighbors > sz2 ) { KNeighbors = sz2; } float inweight = weight; this->m_LandmarkEnergy = 0.0; // float max=0; vnl_vector probabilities(KNeighbors); probabilities.fill(0); float energy = 0, maxerr = 0; for( unsigned long ii = 0; ii < sz1; ii++ ) { VectorType distance; distance.Fill(0); MeasurementVectorType fixedpoint = fkdtree->GetOutput()->GetMeasurementVector(ii); ImagePointType mpt; mpt.Fill(0); ImagePointType fpt; fpt.Fill(0); for( unsigned int j = 0; j < ImageDimension; j++ ) { fpt[j] = fixedpoint[j]; } // float err=0; bool convok = false; IndexType fixedindex; convok = this->GetFixedImage()->TransformPhysicalPointToIndex(fpt, fixedindex); // std::cout << " Orig " << this->GetFixedImage()->GetOrigin() << " ind " << fixedindex << " pt " << fpt << // std::endl; if( convok ) { VectorType force; force.Fill(0); typename TreeGeneratorType::KdTreeType::InstanceIdentifierVectorType neighbors; mkdtree->GetOutput()->Search( fixedpoint, static_cast( KNeighbors ), neighbors ); double probtotal = 0.0; for( unsigned int dd = 0; dd < KNeighbors; dd++ ) { PointType movingpoint; unsigned long wpt = neighbors[dd]; MeasurementVectorType npt = mkdtree->GetOutput()->GetMeasurementVector(wpt); float _mag = 0; for( unsigned int qq = 0; qq < ImageDimension; qq++ ) { _mag += (fixedpoint[qq] - npt[qq]) * (fixedpoint[qq] - npt[qq]); } float sigma = this->m_FixedPointSetSigma; if( !whichdirection ) { sigma = this->m_MovingPointSetSigma; } double prob = 1.0 / sqrt(3.14186 * 2.0 * sigma * sigma) * exp(-1.0 * _mag / (2.0 * sigma * sigma) ); probtotal += prob; probabilities(dd) = prob; } if( probtotal > 0 ) { for( unsigned int dd = 0; dd < KNeighbors; dd++ ) { PointType movingpoint; unsigned long wpt = neighbors[dd]; MeasurementVectorType npt = mkdtree->GetOutput()->GetMeasurementVector(wpt); double pp = probabilities(dd) / probtotal; if( pp > 0 ) { for( unsigned int j = 0; j < ImageDimension; j++ ) { mpt[j] += pp * npt[j]; } } // // if (ii % 245 && pp > 1.e-3) std::cout << " prob " << pp << " mpt " << mpt << " dd " << dd <<" wpt " << wpt // << " // movinpoint " << movingpoint << " ptot " << probtotal << std::endl; } } float mag = 0.0; typename BSplinePointSetType::PointType bpoint; for( unsigned int j = 0; j < ImageDimension; j++ ) { distance[j] = mpt[j] - fixedpoint[j]; mag += distance[j] / spacing[j] * distance[j] / spacing[j]; force[j] = distance[j] * inweight; bpoint[j] = fixedpoint[j]; } float sigma = this->m_FixedPointSetSigma; if( !whichdirection ) { sigma = this->m_MovingPointSetSigma; } double prob = 1.0 / sqrt(3.14186 * 2.0 * sigma * sigma) * exp(-1.0 * mag / (2.0 * sigma * sigma) ); force = force * prob; // this->m_bpoints->SetPoint( this->m_bcount, bpoint ); this->m_bpoints->SetPointData( this->m_bcount, distance ); float bwt = 1; this->m_bweights->InsertElement( this->m_bcount, static_cast( bwt ) ); this->m_bcount++; mag = sqrt(mag); if( mag > maxerr ) { maxerr = mag; } energy += mag; lmField->SetPixel(fixedindex, force + lmField->GetPixel(fixedindex) ); } } // std::cout << " max " << maxerr << std::endl; this->m_LandmarkEnergy = energy / (float)sz1; this->m_Energy = this->m_LandmarkEnergy; if( dobspline ) { /** * Calculate derivative field with respect to the moving points */ { // std::cout << " start bsp " << std::endl; typename BSplineFilterType::ArrayType nlevels; typename BSplineFilterType::ArrayType ncps; nlevels.Fill( m_NumberOfBLevels ); for( unsigned int d = 0; d < PointDimension; d++ ) { ncps[d] = m_MeshResolution[d] + m_SplineOrder; } typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); bspliner->SetInput( this->m_bpoints ); bspliner->SetOrigin( this->GetMovingImage()->GetOrigin() ); bspliner->SetSpacing( this->GetMovingImage()->GetSpacing() ); bspliner->SetSize( this->GetMovingImage()->GetLargestPossibleRegion().GetSize() ); bspliner->SetNumberOfLevels( nlevels ); bspliner->SetSplineOrder( m_SplineOrder ); bspliner->SetNumberOfControlPoints( ncps ); bspliner->SetGenerateOutputImage( true ); bspliner->SetPointWeights( this->m_bweights ); bspliner->Update(); lmField = bspliner->GetOutput(); /** * Now calculate the distances after matching. ItM = this->m_MovingPointSet->GetPoints()->Begin(); ItMD = this->m_MovingPointSet->GetPointData()->Begin(); ItF = this->m_FixedPointSet->GetPoints()->Begin(); ItFD = this->m_FixedPointSet->GetPointData()->Begin(); typename BSplineWeightsType::ConstIterator ItW = weights->Begin(); while( ItM != this->m_MovingPointSet->GetPoints()->End() && ItF != this->m_FixedPointSet->GetPoints()->End() ) { typename BSplinePointSetType::PixelType vector; bspliner->EvaluateAtPoint( ItM.Value(), vector ); RealType distance = 0.0; for( unsigned int d = 0; d < PointDimension; d++ ) { distance += vnl_math_sqr( ItM.Value()[d] + vector[d] - ItF.Value()[d] ); } this->m_Energy += ItW.Value() * std::sqrt( distance ); ++ItF; ++ItFD; ++ItM; ++ItMD; ++ItW; } */ } } } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkJensenHavrdaCharvatTsallisLabeledPointSetMetric.h000066400000000000000000000173251311104306400302770ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkJensenHavrdaCharvatTsallisLabeledPointSetMetric_h #define _itkJensenHavrdaCharvatTsallisLabeledPointSetMetric_h #include "itkPointSetToPointSetMetric.h" #include "itkIdentityTransform.h" namespace itk { /** \class JensenHavrdaCharvatTsallisLabeledPointSetMetric * * * This class is templated over the fixed point-set type, moving * point-set type. * */ template class JensenHavrdaCharvatTsallisLabeledPointSetMetric : public PointSetToPointSetMetric { public: /** Standard class typedefs. */ typedef JensenHavrdaCharvatTsallisLabeledPointSetMetric Self; typedef PointSetToPointSetMetric Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods) */ itkTypeMacro( JensenHavrdaCharvatTsallisLabeledPointSetMetric, PointSetToLabelPointSetMetric ); itkStaticConstMacro( PointDimension, unsigned int, TPointSet::PointDimension ); /** Types transferred from the base class */ typedef typename Superclass::TransformType TransformType; typedef typename Superclass::TransformPointer TransformPointer; typedef typename Superclass::TransformParametersType TransformParametersType; typedef typename Superclass::TransformJacobianType TransformJacobianType; typedef typename Superclass::MeasureType MeasureType; typedef typename Superclass::DerivativeType DerivativeType; typedef TPointSet PointSetType; typedef typename PointSetType::PointType PointType; typedef typename PointSetType::PixelType PixelType; /** * Other typedefs */ typedef double RealType; typedef IdentityTransform DefaultTransformType; typedef std::vector LabelSetType; /** * Public function definitions */ itkSetConstObjectMacro( FixedPointSet, PointSetType ); itkGetConstObjectMacro( FixedPointSet, PointSetType ); itkSetConstObjectMacro( MovingPointSet, PointSetType ); itkGetConstObjectMacro( MovingPointSet, PointSetType ); itkSetObjectMacro( Transform, TransformType ); itkGetModifiableObjectMacro( Transform, TransformType ); /** * The only transform type used is Identity */ unsigned int GetNumberOfParameters( void ) const { return m_Transform->GetNumberOfParameters(); } /** Initialize the Metric by making sure that all the components * are present and plugged together correctly */ virtual void Initialize( void ) throw ( ExceptionObject ); /** Get the number of values */ unsigned int GetNumberOfValues() const; /** Get the derivatives of the match measure. */ void GetDerivative( const TransformParametersType & parameters, DerivativeType & Derivative ) const; /** Get the value for single valued optimizers. */ MeasureType GetValue( const TransformParametersType & parameters ) const; /** Get value and derivatives for multiple valued optimizers. */ void GetValueAndDerivative( const TransformParametersType & parameters, MeasureType& Value, DerivativeType& Derivative ) const; itkSetClampMacro( Alpha, RealType, 1.0, 2.0 ); itkGetConstMacro( Alpha, RealType ); itkSetMacro( UseRegularizationTerm, bool ); itkGetConstMacro( UseRegularizationTerm, bool ); itkBooleanMacro( UseRegularizationTerm ); itkSetMacro( UseWithRespectToTheMovingPointSet, bool ); itkGetConstMacro( UseWithRespectToTheMovingPointSet, bool ); itkBooleanMacro( UseWithRespectToTheMovingPointSet ); itkSetMacro( MovingPointSetSigma, RealType ); itkGetConstMacro( MovingPointSetSigma, RealType ); itkSetMacro( MovingEvaluationKNeighborhood, unsigned int ); itkGetConstMacro( MovingEvaluationKNeighborhood, unsigned int ); itkSetMacro( FixedPointSetSigma, RealType ); itkGetConstMacro( FixedPointSetSigma, RealType ); itkSetMacro( FixedEvaluationKNeighborhood, unsigned int ); itkGetConstMacro( FixedEvaluationKNeighborhood, unsigned int ); itkSetMacro( UseInputAsSamples, bool ); itkGetConstMacro( UseInputAsSamples, bool ); itkBooleanMacro( UseInputAsSamples ); /** * If this->m_UseInputAsSamples = true, the following * two variables are not used. */ itkSetMacro( NumberOfMovingSamples, unsigned long ); itkGetConstMacro( NumberOfMovingSamples, unsigned long ); itkSetMacro( NumberOfFixedSamples, unsigned long ); itkGetConstMacro( NumberOfFixedSamples, unsigned long ); itkSetMacro( UseAnisotropicCovariances, bool ); itkGetConstMacro( UseAnisotropicCovariances, bool ); itkBooleanMacro( UseAnisotropicCovariances ); /** * If this->m_UseAnisotropicCovariances = false, the * following four variables are not used. */ itkSetMacro( FixedCovarianceKNeighborhood, unsigned int ); itkGetConstMacro( FixedCovarianceKNeighborhood, unsigned int ); itkSetMacro( FixedKernelSigma, RealType ); itkGetConstMacro( FixedKernelSigma, RealType ); itkSetMacro( MovingCovarianceKNeighborhood, unsigned int ); itkGetConstMacro( MovingCovarianceKNeighborhood, unsigned int ); itkSetMacro( MovingKernelSigma, RealType ); itkGetConstMacro( MovingKernelSigma, RealType ); void SetFixedLabelSet( LabelSetType labels ) { typename LabelSetType::const_iterator iter; for( iter = labels.begin(); iter != labels.end(); ++iter ) { this->m_FixedLabelSet.push_back( *iter ); } this->Modified(); } LabelSetType * GetFixedLabelSet() { return &this->m_FixedLabelSet; } void SetMovingLabelSet( LabelSetType labels ) { typename LabelSetType::const_iterator iter; for( iter = labels.begin(); iter != labels.end(); ++iter ) { this->m_MovingLabelSet.push_back( *iter ); } this->Modified(); } LabelSetType * GetMovingLabelSet() { return &this->m_MovingLabelSet; } protected: JensenHavrdaCharvatTsallisLabeledPointSetMetric(); ~JensenHavrdaCharvatTsallisLabeledPointSetMetric() { } void PrintSelf( std::ostream& os, Indent indent ) const; private: JensenHavrdaCharvatTsallisLabeledPointSetMetric(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented bool m_UseRegularizationTerm; bool m_UseInputAsSamples; bool m_UseAnisotropicCovariances; bool m_UseWithRespectToTheMovingPointSet; RealType m_MovingPointSetSigma; RealType m_MovingKernelSigma; unsigned int m_MovingCovarianceKNeighborhood; unsigned int m_MovingEvaluationKNeighborhood; unsigned long m_NumberOfMovingSamples; RealType m_FixedPointSetSigma; RealType m_FixedKernelSigma; unsigned int m_FixedCovarianceKNeighborhood; unsigned int m_FixedEvaluationKNeighborhood; unsigned long m_NumberOfFixedSamples; RealType m_Alpha; TransformPointer m_Transform; LabelSetType m_FixedLabelSet; LabelSetType m_MovingLabelSet; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkJensenHavrdaCharvatTsallisLabeledPointSetMetric.hxx" #endif #endif ants-2.2.0/ImageRegistration/itkJensenHavrdaCharvatTsallisLabeledPointSetMetric.hxx000066400000000000000000000447451311104306400306650ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkJensenHavrdaCharvatTsallisLabeledPointSetMetric_hxx #define _itkJensenHavrdaCharvatTsallisLabeledPointSetMetric_hxx #include "itkJensenHavrdaCharvatTsallisLabeledPointSetMetric.h" #include "itkJensenHavrdaCharvatTsallisPointSetMetric.h" namespace itk { template JensenHavrdaCharvatTsallisLabeledPointSetMetric ::JensenHavrdaCharvatTsallisLabeledPointSetMetric() { this->m_UseRegularizationTerm = false; this->m_UseInputAsSamples = true; this->m_UseAnisotropicCovariances = false; this->m_NumberOfFixedSamples = 100; this->m_FixedPointSetSigma = 1.0; this->m_FixedKernelSigma = 10.0; this->m_FixedCovarianceKNeighborhood = 5; this->m_FixedEvaluationKNeighborhood = 50; this->m_NumberOfMovingSamples = 100; this->m_MovingPointSetSigma = 1.0; this->m_MovingKernelSigma = 10.0; this->m_MovingCovarianceKNeighborhood = 5; this->m_MovingEvaluationKNeighborhood = 50; this->m_Alpha = 2.0; this->m_UseWithRespectToTheMovingPointSet = true; typename DefaultTransformType::Pointer transform = DefaultTransformType::New(); transform->SetIdentity(); Superclass::SetTransform( transform ); } /** Initialize the metric */ template void JensenHavrdaCharvatTsallisLabeledPointSetMetric ::Initialize( void ) throw ( ExceptionObject ) { Superclass::Initialize(); if( this->m_FixedLabelSet.size() <= 0 && this->m_FixedPointSet->GetNumberOfPoints() > 0 ) { typename PointSetType::PointDataContainerIterator It = this->m_FixedPointSet->GetPointData()->Begin(); while( It != this->m_FixedPointSet->GetPointData()->End() ) { if( find( this->m_FixedLabelSet.begin(), this->m_FixedLabelSet.end(), It.Value() ) == this->m_FixedLabelSet.end() ) { this->m_FixedLabelSet.push_back( It.Value() ); } ++It; } } if( this->m_MovingLabelSet.size() <= 0 && this->m_MovingPointSet->GetNumberOfPoints() > 0 ) { typename PointSetType::PointDataContainerIterator It = this->m_MovingPointSet->GetPointData()->Begin(); while( It != this->m_MovingPointSet->GetPointData()->End() ) { if( find( this->m_MovingLabelSet.begin(), this->m_MovingLabelSet.end(), It.Value() ) == this->m_MovingLabelSet.end() ) { this->m_MovingLabelSet.push_back( It.Value() ); } ++It; } } } /** Return the number of values, i.e the number of points in the moving set */ template unsigned int JensenHavrdaCharvatTsallisLabeledPointSetMetric ::GetNumberOfValues() const { if( this->m_UseWithRespectToTheMovingPointSet ) { if( this->m_MovingPointSet ) { return this->m_MovingPointSet->GetPoints()->Size(); } } else { if( this->m_FixedPointSet ) { return this->m_FixedPointSet->GetPoints()->Size(); } } return 0; } /** Get the match Measure */ template typename JensenHavrdaCharvatTsallisLabeledPointSetMetric::MeasureType JensenHavrdaCharvatTsallisLabeledPointSetMetric ::GetValue( const TransformParametersType & parameters ) const { MeasureType measure; measure.SetSize( 1 ); measure.Fill( 0 ); typename LabelSetType::const_iterator iter; for( iter = this->m_FixedLabelSet.begin(); iter != this->m_FixedLabelSet.end(); ++iter ) { PixelType currentLabel = *iter; /** * check to see if the moving label set contains the same label */ if( find( this->m_MovingLabelSet.begin(), this->m_MovingLabelSet.end(), currentLabel ) == this->m_MovingLabelSet.end() ) { continue; } /** * Collect all the fixed and moving points with the currentLabel */ typename PointSetType::Pointer fixedLabelPoints = PointSetType::New(); fixedLabelPoints->Initialize(); unsigned long fixedCount = 0; typename PointSetType::PointsContainerConstIterator ItF = this->m_FixedPointSet->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator ItFD = this->m_FixedPointSet->GetPointData()->Begin(); while( ItF != this->m_FixedPointSet->GetPoints()->End() ) { if( ItFD.Value() == currentLabel ) { fixedLabelPoints->SetPoint( fixedCount++, ItF.Value() ); } ++ItF; ++ItFD; } typename PointSetType::Pointer movingLabelPoints = PointSetType::New(); movingLabelPoints->Initialize(); unsigned long movingCount = 0; typename PointSetType::PointsContainerConstIterator ItM = this->m_MovingPointSet->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator ItMD = this->m_MovingPointSet->GetPointData()->Begin(); while( ItM != this->m_MovingPointSet->GetPoints()->End() ) { if( ItMD.Value() == currentLabel ) { movingLabelPoints->SetPoint( movingCount++, ItM.Value() ); } ++ItM; ++ItMD; } /** * Invoke the single label JensenTsallis measure */ typedef JensenHavrdaCharvatTsallisPointSetMetric MetricType; typename MetricType::Pointer metric = MetricType::New(); metric->SetFixedPointSet( fixedLabelPoints ); metric->SetNumberOfFixedSamples( this->m_NumberOfFixedSamples ); metric->SetFixedPointSetSigma( this->m_FixedPointSetSigma ); metric->SetFixedKernelSigma( this->m_FixedKernelSigma ); metric->SetFixedCovarianceKNeighborhood( this->m_FixedCovarianceKNeighborhood ); metric->SetFixedEvaluationKNeighborhood( this->m_FixedEvaluationKNeighborhood ); metric->SetMovingPointSet( movingLabelPoints ); metric->SetNumberOfMovingSamples( this->m_NumberOfMovingSamples ); metric->SetMovingPointSetSigma( this->m_MovingPointSetSigma ); metric->SetMovingKernelSigma( this->m_MovingKernelSigma ); metric->SetMovingCovarianceKNeighborhood( this->m_MovingCovarianceKNeighborhood ); metric->SetMovingEvaluationKNeighborhood( this->m_MovingEvaluationKNeighborhood ); metric->SetUseRegularizationTerm( this->m_UseRegularizationTerm ); metric->SetUseInputAsSamples( this->m_UseInputAsSamples ); metric->SetUseAnisotropicCovariances( this->m_UseAnisotropicCovariances ); metric->SetUseWithRespectToTheMovingPointSet( this->m_UseWithRespectToTheMovingPointSet ); metric->SetAlpha( this->m_Alpha ); metric->Initialize(); MeasureType value = metric->GetValue( parameters ); measure[0] += value[0]; } return measure; } /** Get the Derivative Measure */ template void JensenHavrdaCharvatTsallisLabeledPointSetMetric ::GetDerivative( const TransformParametersType & parameters, DerivativeType & derivative ) const { unsigned long numberOfDerivatives = this->m_FixedPointSet->GetNumberOfPoints(); if( this->m_UseWithRespectToTheMovingPointSet ) { numberOfDerivatives = this->m_MovingPointSet->GetNumberOfPoints(); } derivative.SetSize( numberOfDerivatives, PointDimension ); derivative.Fill( 0 ); typename LabelSetType::const_iterator iter; for( iter = this->m_FixedLabelSet.begin(); iter != this->m_FixedLabelSet.end(); ++iter ) { PixelType currentLabel = *iter; /** * check to see if the moving label set contains the same label */ if( find( this->m_MovingLabelSet.begin(), this->m_MovingLabelSet.end(), currentLabel ) == this->m_MovingLabelSet.end() ) { continue; } /** * Collect all the fixed and moving points with the currentLabel */ typename PointSetType::Pointer fixedLabelPoints = PointSetType::New(); fixedLabelPoints->Initialize(); unsigned long fixedCount = 0; std::vector fixedIndices; fixedIndices.clear(); typename PointSetType::PointsContainerConstIterator ItF = this->m_FixedPointSet->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator ItFD = this->m_FixedPointSet->GetPointData()->Begin(); while( ItF != this->m_FixedPointSet->GetPoints()->End() ) { if( ItFD.Value() == currentLabel ) { fixedLabelPoints->SetPoint( fixedCount++, ItF.Value() ); fixedIndices.push_back( ItF.Index() ); } ++ItF; ++ItFD; } typename PointSetType::Pointer movingLabelPoints = PointSetType::New(); movingLabelPoints->Initialize(); unsigned long movingCount = 0; std::vector movingIndices; movingIndices.clear(); typename PointSetType::PointsContainerConstIterator ItM = this->m_MovingPointSet->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator ItMD = this->m_MovingPointSet->GetPointData()->Begin(); while( ItM != this->m_MovingPointSet->GetPoints()->End() ) { if( ItMD.Value() == currentLabel ) { movingLabelPoints->SetPoint( movingCount++, ItM.Value() ); movingIndices.push_back( ItM.Index() ); } ++ItM; ++ItMD; } /** * Invoke the single label JensenTsallis measure */ typedef JensenHavrdaCharvatTsallisPointSetMetric MetricType; typename MetricType::Pointer metric = MetricType::New(); metric->SetFixedPointSet( fixedLabelPoints ); metric->SetNumberOfFixedSamples( this->m_NumberOfFixedSamples ); metric->SetFixedPointSetSigma( this->m_FixedPointSetSigma ); metric->SetFixedKernelSigma( this->m_FixedKernelSigma ); metric->SetFixedCovarianceKNeighborhood( this->m_FixedCovarianceKNeighborhood ); metric->SetFixedEvaluationKNeighborhood( this->m_FixedEvaluationKNeighborhood ); metric->SetMovingPointSet( movingLabelPoints ); metric->SetNumberOfMovingSamples( this->m_NumberOfMovingSamples ); metric->SetMovingPointSetSigma( this->m_MovingPointSetSigma ); metric->SetMovingKernelSigma( this->m_MovingKernelSigma ); metric->SetMovingCovarianceKNeighborhood( this->m_MovingCovarianceKNeighborhood ); metric->SetMovingEvaluationKNeighborhood( this->m_MovingEvaluationKNeighborhood ); metric->SetUseRegularizationTerm( this->m_UseRegularizationTerm ); metric->SetUseInputAsSamples( this->m_UseInputAsSamples ); metric->SetUseAnisotropicCovariances( this->m_UseAnisotropicCovariances ); metric->SetUseWithRespectToTheMovingPointSet( this->m_UseWithRespectToTheMovingPointSet ); metric->SetAlpha( this->m_Alpha ); metric->Initialize(); DerivativeType labelDerivative; metric->GetDerivative( parameters, labelDerivative ); RealType avgNorm = 0.0; for( unsigned int i = 0; i < metric->GetNumberOfValues(); i++ ) { RealType norm = 0.0; for( unsigned int j = 0; j < PointDimension; j++ ) { norm += ( labelDerivative(i, j) * labelDerivative(i, j) ); } avgNorm += std::sqrt( norm ); } avgNorm /= static_cast( metric->GetNumberOfValues() ); labelDerivative /= avgNorm; if( this->m_UseWithRespectToTheMovingPointSet ) { std::vector::const_iterator it; unsigned long index = 0; for( it = movingIndices.begin(); it != movingIndices.end(); ++it ) { for( unsigned int d = 0; d < PointDimension; d++ ) { derivative( *it, d ) = labelDerivative( index, d ); } index++; } } else { std::vector::const_iterator it; unsigned long index = 0; for( it = fixedIndices.begin(); it != fixedIndices.end(); ++it ) { for( unsigned int d = 0; d < PointDimension; d++ ) { derivative( *it, d ) = labelDerivative( index, d ); } index++; } } } } /** Get both the match Measure and theDerivative Measure */ template void JensenHavrdaCharvatTsallisLabeledPointSetMetric ::GetValueAndDerivative( const TransformParametersType & parameters, MeasureType & value, DerivativeType & derivative ) const { unsigned long numberOfDerivatives = this->m_FixedPointSet->GetNumberOfPoints(); if( this->m_UseWithRespectToTheMovingPointSet ) { numberOfDerivatives = this->m_MovingPointSet->GetNumberOfPoints(); } derivative.SetSize( numberOfDerivatives, PointDimension ); derivative.Fill( 0 ); value.SetSize( 1 ); value.Fill( 0 ); typename LabelSetType::const_iterator iter; for( iter = this->m_FixedLabelSet.begin(); iter != this->m_FixedLabelSet.end(); ++iter ) { PixelType currentLabel = *iter; /** * check to see if the moving label set contains the same label */ if( find( this->m_MovingLabelSet.begin(), this->m_MovingLabelSet.end(), currentLabel ) == this->m_MovingLabelSet.end() ) { continue; } /** * Collect all the fixed and moving points with the currentLabel */ typename PointSetType::Pointer fixedLabelPoints = PointSetType::New(); fixedLabelPoints->Initialize(); unsigned long fixedCount = 0; std::vector fixedIndices; fixedIndices.clear(); typename PointSetType::PointsContainerConstIterator ItF = this->m_FixedPointSet->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator ItFD = this->m_FixedPointSet->GetPointData()->Begin(); while( ItF != this->m_FixedPointSet->GetPoints()->End() ) { if( ItFD.Value() == currentLabel ) { fixedLabelPoints->SetPoint( fixedCount++, ItF.Value() ); fixedIndices.push_back( ItF.Index() ); } ++ItF; ++ItFD; } typename PointSetType::Pointer movingLabelPoints = PointSetType::New(); movingLabelPoints->Initialize(); unsigned long movingCount = 0; std::vector movingIndices; movingIndices.clear(); typename PointSetType::PointsContainerConstIterator ItM = this->m_MovingPointSet->GetPoints()->Begin(); typename PointSetType::PointDataContainerIterator ItMD = this->m_MovingPointSet->GetPointData()->Begin(); while( ItM != this->m_MovingPointSet->GetPoints()->End() ) { if( ItMD.Value() == currentLabel ) { movingLabelPoints->SetPoint( movingCount++, ItM.Value() ); movingIndices.push_back( ItM.Index() ); } ++ItM; ++ItMD; } /** * Invoke the single label JensenTsallis measure */ typedef JensenHavrdaCharvatTsallisPointSetMetric MetricType; typename MetricType::Pointer metric = MetricType::New(); metric->SetFixedPointSet( fixedLabelPoints ); metric->SetNumberOfFixedSamples( this->m_NumberOfFixedSamples ); metric->SetFixedPointSetSigma( this->m_FixedPointSetSigma ); metric->SetFixedKernelSigma( this->m_FixedKernelSigma ); metric->SetFixedCovarianceKNeighborhood( this->m_FixedCovarianceKNeighborhood ); metric->SetFixedEvaluationKNeighborhood( this->m_FixedEvaluationKNeighborhood ); metric->SetMovingPointSet( movingLabelPoints ); metric->SetNumberOfMovingSamples( this->m_NumberOfMovingSamples ); metric->SetMovingPointSetSigma( this->m_MovingPointSetSigma ); metric->SetMovingKernelSigma( this->m_MovingKernelSigma ); metric->SetMovingCovarianceKNeighborhood( this->m_MovingCovarianceKNeighborhood ); metric->SetMovingEvaluationKNeighborhood( this->m_MovingEvaluationKNeighborhood ); metric->SetUseRegularizationTerm( this->m_UseRegularizationTerm ); metric->SetUseInputAsSamples( this->m_UseInputAsSamples ); metric->SetUseAnisotropicCovariances( this->m_UseAnisotropicCovariances ); metric->SetUseWithRespectToTheMovingPointSet( this->m_UseWithRespectToTheMovingPointSet ); metric->SetAlpha( this->m_Alpha ); metric->Initialize(); DerivativeType labelDerivative; MeasureType labelValue; metric->GetValueAndDerivative( parameters, labelValue, labelDerivative ); value[0] += labelValue[0]; RealType avgNorm = 0.0; for( unsigned int i = 0; i < metric->GetNumberOfValues(); i++ ) { RealType norm = 0.0; for( unsigned int j = 0; j < PointDimension; j++ ) { norm += ( labelDerivative(i, j) * labelDerivative(i, j) ); } avgNorm += std::sqrt( norm ); } avgNorm /= static_cast( metric->GetNumberOfValues() ); labelDerivative /= avgNorm; if( this->m_UseWithRespectToTheMovingPointSet ) { std::vector::const_iterator it; unsigned long index = 0; for( it = movingIndices.begin(); it != movingIndices.end(); ++it ) { for( unsigned int d = 0; d < PointDimension; d++ ) { derivative( *it, d ) = labelDerivative( index, d ); } index++; } } else { std::vector::const_iterator it; unsigned long index = 0; for( it = fixedIndices.begin(); it != fixedIndices.end(); ++it ) { for( unsigned int d = 0; d < PointDimension; d++ ) { derivative( *it, d ) = labelDerivative( index, d ); } index++; } } } } template void JensenHavrdaCharvatTsallisLabeledPointSetMetric ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << "Use with respect to the moving point set: " << this->m_UseWithRespectToTheMovingPointSet << std::endl; os << indent << "Use regularization term: " << this->m_UseRegularizationTerm << std::endl; if( !this->m_UseInputAsSamples ) { os << indent << "Number of fixed samples: " << this->m_NumberOfFixedSamples << std::endl; os << indent << "Number of moving samples: " << this->m_NumberOfMovingSamples << std::endl; } os << indent << "Alpha: " << this->m_Alpha << std::endl; os << indent << "Fixed sigma: " << this->m_FixedPointSetSigma << std::endl; os << indent << "Moving sigma: " << this->m_MovingPointSetSigma << std::endl; } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkJensenHavrdaCharvatTsallisPointSetMetric.h000066400000000000000000000163241311104306400270240ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 __itkJensenHavrdaCharvatTsallisPointSetMetric_h #define __itkJensenHavrdaCharvatTsallisPointSetMetric_h #include "itkPointSetToPointSetMetric.h" #include "itkIdentityTransform.h" #include "itkManifoldParzenWindowsPointSetFunction.h" namespace itk { /** \class JensenHavrdaCharvatTsallisPointSetMetric * * * * */ template class JensenHavrdaCharvatTsallisPointSetMetric : public PointSetToPointSetMetric { public: /** Standard class typedefs. */ typedef JensenHavrdaCharvatTsallisPointSetMetric Self; typedef PointSetToPointSetMetric Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods) */ itkTypeMacro( JensenHavrdaCharvatTsallisPointSetMetric, PointSetToPointSetMetric ); itkStaticConstMacro( PointDimension, unsigned int, TPointSet::PointDimension ); /** Types transferred from the base class */ typedef typename Superclass::TransformType TransformType; typedef typename Superclass::TransformPointer TransformPointer; typedef typename Superclass::TransformParametersType TransformParametersType; typedef typename Superclass::TransformJacobianType TransformJacobianType; typedef typename Superclass::MeasureType MeasureType; typedef typename Superclass::DerivativeType DerivativeType; typedef TPointSet PointSetType; typedef typename PointSetType::Pointer PointSetPointer; typedef typename PointSetType::PointType PointType; /** * Other typedefs */ typedef double RealType; typedef ManifoldParzenWindowsPointSetFunction DensityFunctionType; typedef typename DensityFunctionType::GaussianType GaussianType; typedef IdentityTransform DefaultTransformType; /** * Public function definitions */ itkSetConstObjectMacro( FixedPointSet, PointSetType ); itkGetConstObjectMacro( FixedPointSet, PointSetType ); itkSetConstObjectMacro( MovingPointSet, PointSetType ); itkGetConstObjectMacro( MovingPointSet, PointSetType ); itkSetObjectMacro( Transform, TransformType ); itkGetModifiableObjectMacro( Transform, TransformType ); /** * The only transform type used is Identity */ unsigned int GetNumberOfParameters( void ) const { return m_Transform->GetNumberOfParameters(); } /** Initialize the Metric by making sure that all the components * are present and plugged together correctly */ virtual void Initialize( void ) throw ( ExceptionObject ); /** Get the number of values */ unsigned int GetNumberOfValues() const; /** Get the derivatives of the match measure. */ void GetDerivative( const TransformParametersType & parameters, DerivativeType & Derivative ) const; /** Get the value for single valued optimizers. */ MeasureType GetValue( const TransformParametersType & parameters ) const; /** Get value and derivatives for multiple valued optimizers. */ void GetValueAndDerivative( const TransformParametersType & parameters, MeasureType& Value, DerivativeType& Derivative ) const; // itkSetClampMacro( Alpha, RealType, 1.0, 2.0 ); itkSetMacro( Alpha, RealType ); itkGetConstMacro( Alpha, RealType ); itkSetMacro( UseRegularizationTerm, bool ); itkGetConstMacro( UseRegularizationTerm, bool ); itkBooleanMacro( UseRegularizationTerm ); itkSetMacro( UseWithRespectToTheMovingPointSet, bool ); itkGetConstMacro( UseWithRespectToTheMovingPointSet, bool ); itkBooleanMacro( UseWithRespectToTheMovingPointSet ); itkSetMacro( MovingPointSetSigma, RealType ); itkGetConstMacro( MovingPointSetSigma, RealType ); itkSetMacro( MovingEvaluationKNeighborhood, unsigned int ); itkGetConstMacro( MovingEvaluationKNeighborhood, unsigned int ); itkSetMacro( FixedPointSetSigma, RealType ); itkGetConstMacro( FixedPointSetSigma, RealType ); itkSetMacro( FixedEvaluationKNeighborhood, unsigned int ); itkGetConstMacro( FixedEvaluationKNeighborhood, unsigned int ); itkSetMacro( UseInputAsSamples, bool ); itkGetConstMacro( UseInputAsSamples, bool ); itkBooleanMacro( UseInputAsSamples ); /** * If this->m_UseInputAsSamples = true, the following * two variables are not used. */ itkSetMacro( NumberOfMovingSamples, unsigned long ); itkGetConstMacro( NumberOfMovingSamples, unsigned long ); itkSetMacro( NumberOfFixedSamples, unsigned long ); itkGetConstMacro( NumberOfFixedSamples, unsigned long ); itkSetMacro( UseAnisotropicCovariances, bool ); itkGetConstMacro( UseAnisotropicCovariances, bool ); itkBooleanMacro( UseAnisotropicCovariances ); /** * If this->m_UseAnisotropicCovariances = false, the * following four variables are not used. */ itkSetMacro( FixedCovarianceKNeighborhood, unsigned int ); itkGetConstMacro( FixedCovarianceKNeighborhood, unsigned int ); itkSetMacro( FixedKernelSigma, RealType ); itkGetConstMacro( FixedKernelSigma, RealType ); itkSetMacro( MovingCovarianceKNeighborhood, unsigned int ); itkGetConstMacro( MovingCovarianceKNeighborhood, unsigned int ); itkSetMacro( MovingKernelSigma, RealType ); itkGetConstMacro( MovingKernelSigma, RealType ); protected: JensenHavrdaCharvatTsallisPointSetMetric(); ~JensenHavrdaCharvatTsallisPointSetMetric() { } void PrintSelf( std::ostream& os, Indent indent ) const; private: // purposely not implemented JensenHavrdaCharvatTsallisPointSetMetric(const Self &); void operator=(const Self &); bool m_UseRegularizationTerm; bool m_UseInputAsSamples; bool m_UseAnisotropicCovariances; bool m_UseWithRespectToTheMovingPointSet; typename DensityFunctionType::Pointer m_MovingDensityFunction; PointSetPointer m_MovingSamplePoints; RealType m_MovingPointSetSigma; RealType m_MovingKernelSigma; unsigned int m_MovingCovarianceKNeighborhood; unsigned int m_MovingEvaluationKNeighborhood; unsigned long m_NumberOfMovingSamples; typename DensityFunctionType::Pointer m_FixedDensityFunction; PointSetPointer m_FixedSamplePoints; RealType m_FixedPointSetSigma; RealType m_FixedKernelSigma; unsigned int m_FixedCovarianceKNeighborhood; unsigned int m_FixedEvaluationKNeighborhood; unsigned long m_NumberOfFixedSamples; RealType m_Alpha; TransformPointer m_Transform; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkJensenHavrdaCharvatTsallisPointSetMetric.hxx" #endif #endif ants-2.2.0/ImageRegistration/itkJensenHavrdaCharvatTsallisPointSetMetric.hxx000066400000000000000000000631301311104306400274010ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 __itkJensenHavrdaCharvatTsallisPointSetMetric_hxx #define __itkJensenHavrdaCharvatTsallisPointSetMetric_hxx #include "itkJensenHavrdaCharvatTsallisPointSetMetric.h" namespace itk { template JensenHavrdaCharvatTsallisPointSetMetric ::JensenHavrdaCharvatTsallisPointSetMetric() { this->m_UseRegularizationTerm = false; this->m_UseInputAsSamples = true; this->m_UseAnisotropicCovariances = false; this->m_NumberOfFixedSamples = 100; this->m_FixedPointSetSigma = 1.0; this->m_FixedKernelSigma = 10.0; this->m_FixedCovarianceKNeighborhood = 5; this->m_FixedEvaluationKNeighborhood = 50; this->m_NumberOfMovingSamples = 100; this->m_MovingPointSetSigma = 1.0; this->m_MovingKernelSigma = 10.0; this->m_MovingCovarianceKNeighborhood = 5; this->m_MovingEvaluationKNeighborhood = 50; this->m_Alpha = 2.0; this->m_UseWithRespectToTheMovingPointSet = true; typename DefaultTransformType::Pointer transform = DefaultTransformType::New(); transform->SetIdentity(); Superclass::SetTransform( transform ); } /** Initialize the metric */ template void JensenHavrdaCharvatTsallisPointSetMetric ::Initialize( void ) throw ( ExceptionObject ) { Superclass::Initialize(); /** * Initialize the fixed points */ this->m_FixedDensityFunction = DensityFunctionType::New(); this->m_FixedDensityFunction->SetBucketSize( 4 ); this->m_FixedDensityFunction->SetKernelSigma( this->m_FixedKernelSigma ); this->m_FixedDensityFunction->SetRegularizationSigma( this->m_FixedPointSetSigma ); this->m_FixedDensityFunction->SetNormalize( true ); this->m_FixedDensityFunction->SetUseAnisotropicCovariances( this->m_UseAnisotropicCovariances ); this->m_FixedDensityFunction->SetCovarianceKNeighborhood( this->m_FixedCovarianceKNeighborhood ); this->m_FixedDensityFunction->SetEvaluationKNeighborhood( this->m_FixedEvaluationKNeighborhood ); this->m_FixedDensityFunction->SetInputPointSet( this->m_FixedPointSet ); if( !this->m_UseInputAsSamples ) { this->m_FixedSamplePoints = PointSetType::New(); this->m_FixedSamplePoints->Initialize(); for( unsigned long i = 0; i < this->m_NumberOfFixedSamples; i++ ) { this->m_FixedSamplePoints->SetPoint( i, this->m_FixedDensityFunction->GenerateRandomSample() ); } } /** * Initialize the moving points */ this->m_MovingDensityFunction = DensityFunctionType::New(); this->m_MovingDensityFunction->SetBucketSize( 4 ); this->m_MovingDensityFunction->SetKernelSigma( this->m_MovingKernelSigma ); this->m_MovingDensityFunction->SetRegularizationSigma( this->m_MovingPointSetSigma ); this->m_MovingDensityFunction->SetNormalize( true ); this->m_MovingDensityFunction->SetUseAnisotropicCovariances( this->m_UseAnisotropicCovariances ); this->m_MovingDensityFunction->SetCovarianceKNeighborhood( this->m_MovingCovarianceKNeighborhood ); this->m_MovingDensityFunction->SetEvaluationKNeighborhood( this->m_MovingEvaluationKNeighborhood ); this->m_MovingDensityFunction->SetInputPointSet( this->m_MovingPointSet ); if( !this->m_UseInputAsSamples ) { this->m_MovingSamplePoints = PointSetType::New(); this->m_MovingSamplePoints->Initialize(); for( unsigned long i = 0; i < this->m_NumberOfMovingSamples; i++ ) { this->m_MovingSamplePoints->SetPoint( i, this->m_MovingDensityFunction->GenerateRandomSample() ); } } } /** Return the number of values, i.e the number of points in the moving set */ template unsigned int JensenHavrdaCharvatTsallisPointSetMetric ::GetNumberOfValues() const { if( this->m_UseWithRespectToTheMovingPointSet ) { if( this->m_MovingPointSet ) { return this->m_MovingPointSet->GetPoints()->Size(); } } else { if( this->m_FixedPointSet ) { return this->m_FixedPointSet->GetPoints()->Size(); } } return 0; } /** Get the match Measure */ template typename JensenHavrdaCharvatTsallisPointSetMetric ::MeasureType JensenHavrdaCharvatTsallisPointSetMetric ::GetValue( const TransformParametersType & parameters ) const { /** * Only identity transform is valid */ // this->SetTransformParameters( parameters ); PointSetPointer points[2]; PointSetPointer samples[2]; typename DensityFunctionType::Pointer densityFunctions[2]; if( this->m_UseWithRespectToTheMovingPointSet ) { points[0] = const_cast( static_cast( this->m_FixedPointSet ) ); points[1] = const_cast( static_cast( this->m_MovingPointSet ) ); if( this->m_UseInputAsSamples ) { samples[0] = points[0]; samples[1] = points[1]; } else { samples[0] = this->m_FixedSamplePoints; samples[1] = this->m_MovingSamplePoints; } densityFunctions[0] = this->m_FixedDensityFunction; densityFunctions[1] = this->m_MovingDensityFunction; } else { points[1] = const_cast( static_cast( this->m_FixedPointSet ) ); points[0] = const_cast( static_cast( this->m_MovingPointSet ) ); if( this->m_UseInputAsSamples ) { samples[1] = points[1]; samples[0] = points[0]; } else { samples[1] = this->m_FixedSamplePoints; samples[0] = this->m_MovingSamplePoints; } densityFunctions[1] = this->m_FixedDensityFunction; densityFunctions[0] = this->m_MovingDensityFunction; } RealType totalNumberOfPoints = static_cast( points[0]->GetNumberOfPoints() ) + static_cast( points[1]->GetNumberOfPoints() ); RealType totalNumberOfSamples = static_cast( samples[0]->GetNumberOfPoints() ) + static_cast( samples[1]->GetNumberOfPoints() ); MeasureType measure; measure.SetSize( 1 ); measure.Fill( 0 ); RealType energyTerm1 = 0.0; RealType energyTerm2 = 0.0; /** * first term */ RealType prefactor = -1.0 / totalNumberOfSamples; if( this->m_Alpha != 1.0 ) { prefactor /= ( this->m_Alpha - 1.0 ); } typename PointSetType::PointsContainerConstIterator It = samples[0]->GetPoints()->Begin(); while( It != samples[0]->GetPoints()->End() ) { PointType samplePoint = It.Value(); RealType probabilityStar = // densityFunctions[0]->Evaluate( samplePoint ) * // static_cast( points[0]->GetNumberOfPoints() ) + densityFunctions[1]->Evaluate( samplePoint ) * static_cast( points[1]->GetNumberOfPoints() ); probabilityStar /= totalNumberOfPoints; if( probabilityStar == 0 ) { ++It; continue; } if( this->m_Alpha == 1.0 ) { energyTerm1 += std::log( probabilityStar ); } else { energyTerm1 += std::pow( probabilityStar, static_cast( this->m_Alpha - 1.0 ) ); } ++It; } if( this->m_Alpha != 1.0 ) { energyTerm1 -= 1.0; } energyTerm1 *= prefactor; /** * second term, i.e. regularization term */ if( this->m_UseRegularizationTerm ) { RealType prefactor2 = -static_cast( points[1]->GetNumberOfPoints() ) / ( totalNumberOfPoints * static_cast( samples[1]->GetNumberOfPoints() ) ); if( this->m_Alpha != 1.0 ) { prefactor2 /= ( this->m_Alpha - 1.0 ); } typename PointSetType::PointsContainerConstIterator It = samples[1]->GetPoints()->Begin(); while( It != samples[1]->GetPoints()->End() ) { PointType samplePoint = It.Value(); RealType probability = densityFunctions[1]->Evaluate( samplePoint ); if( probability == 0 ) { ++It; continue; } if( this->m_Alpha == 1.0 ) { energyTerm2 += ( prefactor2 * std::log( probability ) ); } else { energyTerm2 += ( prefactor2 * std::pow( probability, static_cast( this->m_Alpha - 1.0 ) ) ); } ++It; } if( this->m_Alpha != 1.0 ) { energyTerm2 -= 1.0; } energyTerm2 *= prefactor2; } measure[0] = energyTerm1 - energyTerm2; return measure; } /** Get the Derivative Measure */ template void JensenHavrdaCharvatTsallisPointSetMetric ::GetDerivative( const TransformParametersType & parameters, DerivativeType & derivative ) const { /** * Only identity transform is valid */ // this->SetTransformParameters( parameters ); PointSetPointer points[2]; PointSetPointer samples[2]; typename DensityFunctionType::Pointer densityFunctions[2]; unsigned int kNeighborhood; if( this->m_UseWithRespectToTheMovingPointSet ) { points[0] = const_cast( static_cast( this->m_FixedPointSet ) ); points[1] = const_cast( static_cast( this->m_MovingPointSet ) ); if( this->m_UseInputAsSamples ) { samples[0] = points[0]; samples[1] = points[1]; } else { samples[0] = this->m_FixedSamplePoints; samples[1] = this->m_MovingSamplePoints; } densityFunctions[0] = this->m_FixedDensityFunction; densityFunctions[1] = this->m_MovingDensityFunction; kNeighborhood = this->m_MovingEvaluationKNeighborhood; } else { points[1] = const_cast( static_cast( this->m_FixedPointSet ) ); points[0] = const_cast( static_cast( this->m_MovingPointSet ) ); if( this->m_UseInputAsSamples ) { samples[1] = points[1]; samples[0] = points[0]; } else { samples[1] = this->m_FixedSamplePoints; samples[0] = this->m_MovingSamplePoints; } densityFunctions[1] = this->m_FixedDensityFunction; densityFunctions[0] = this->m_MovingDensityFunction; kNeighborhood = this->m_FixedEvaluationKNeighborhood; } RealType totalNumberOfPoints = static_cast( points[0]->GetNumberOfPoints() ) + static_cast( points[1]->GetNumberOfPoints() ); RealType totalNumberOfSamples = static_cast( samples[0]->GetNumberOfPoints() ) + static_cast( samples[1]->GetNumberOfPoints() ); derivative.SetSize( points[1]->GetPoints()->Size(), PointDimension ); derivative.Fill( 0 ); /** * first term */ RealType prefactor = 1.0 / ( totalNumberOfSamples * totalNumberOfPoints ); typename PointSetType::PointsContainerConstIterator It = samples[0]->GetPoints()->Begin(); while( It != samples[0]->GetPoints()->End() ) { PointType fixedSamplePoint = It.Value(); RealType probabilityStar = // densityFunctions[0]->Evaluate( fixedSamplePoint ) * // static_cast( points[0]->GetNumberOfPoints() ) + densityFunctions[1]->Evaluate( fixedSamplePoint ) * static_cast( points[1]->GetNumberOfPoints() ); probabilityStar /= totalNumberOfPoints; if( probabilityStar == 0 ) { ++It; continue; } RealType probabilityStarFactor = std::pow( probabilityStar, static_cast( 2.0 - this->m_Alpha ) ); typename GaussianType::MeasurementVectorType sampleMeasurement; for( unsigned int d = 0; d < PointDimension; d++ ) { sampleMeasurement[d] = fixedSamplePoint[d]; } typename DensityFunctionType::NeighborhoodIdentifierType neighbors = densityFunctions[1]->GetNeighborhoodIdentifiers( sampleMeasurement, kNeighborhood ); for( unsigned int n = 0; n < neighbors.size(); n++ ) { RealType gaussian = densityFunctions[1]-> GetGaussian( neighbors[n] )->Evaluate( sampleMeasurement ); if( gaussian == 0 ) { continue; } typename GaussianType::MeanType mean = densityFunctions[1]->GetGaussian( neighbors[n] )->GetMean(); for( unsigned int d = 0; d < PointDimension; d++ ) { mean[d] -= fixedSamplePoint[d]; } if( this->m_UseAnisotropicCovariances ) { typename GaussianType::MatrixType Ci = densityFunctions[1]->GetGaussian( neighbors[n] ) ->GetInverseCovariance(); mean = Ci * mean; } else { mean /= vnl_math_sqr( densityFunctions[1]->GetGaussian( neighbors[n] )->GetSigma() ); } mean *= ( prefactor * gaussian / probabilityStarFactor ); for( unsigned int d = 0; d < PointDimension; d++ ) { derivative(neighbors[n], d) += mean[d]; } } ++It; } /** * second term, i.e. regularization term */ if( this->m_UseRegularizationTerm ) { RealType prefactor2 = -1.0 / ( static_cast( samples[1]->GetNumberOfPoints() ) * totalNumberOfPoints ); typename PointSetType::PointsContainerConstIterator It = samples[1]->GetPoints()->Begin(); while( It != samples[1]->GetPoints()->End() ) { PointType movingSamplePoint = It.Value(); RealType probability = densityFunctions[1]->Evaluate( movingSamplePoint ); if( probability == 0 ) { ++It; continue; } RealType probabilityFactor = std::pow( probability, static_cast( 2.0 - this->m_Alpha ) ); probabilityFactor *= ( samples[1]->GetNumberOfPoints() / totalNumberOfSamples ); typename GaussianType::MeasurementVectorType sampleMeasurement; for( unsigned int d = 0; d < PointDimension; d++ ) { sampleMeasurement[d] = movingSamplePoint[d]; } typename DensityFunctionType::NeighborhoodIdentifierType neighbors = densityFunctions[1]->GetNeighborhoodIdentifiers( sampleMeasurement, kNeighborhood ); for( unsigned int i = 0; i < neighbors.size(); i++ ) { RealType gaussian = densityFunctions[1]-> GetGaussian( neighbors[i] )->Evaluate( sampleMeasurement ); if( gaussian == 0 ) { continue; } typename GaussianType::MeanType mean = densityFunctions[1]->GetGaussian( neighbors[i] )->GetMean(); for( unsigned int d = 0; d < PointDimension; d++ ) { mean[d] -= movingSamplePoint[d]; } if( this->m_UseAnisotropicCovariances ) { typename GaussianType::MatrixType Ci = densityFunctions[1]->GetGaussian( neighbors[i] ) ->GetInverseCovariance(); mean = Ci * mean; } else { mean /= vnl_math_sqr( densityFunctions[1]->GetGaussian( neighbors[i] )->GetSigma() ); } mean *= ( prefactor2 * gaussian / probabilityFactor ); for( unsigned int d = 0; d < PointDimension; d++ ) { derivative(neighbors[i], d) += mean[d]; } } ++It; } } } /** Get both the match Measure and theDerivative Measure */ template void JensenHavrdaCharvatTsallisPointSetMetric ::GetValueAndDerivative( const TransformParametersType & parameters, MeasureType & value, DerivativeType & derivative ) const { /** * Only identity transform is valid */ // this->SetTransformParameters( parameters ); PointSetPointer points[2]; PointSetPointer samples[2]; typename DensityFunctionType::Pointer densityFunctions[2]; unsigned int kNeighborhood; if( this->m_UseWithRespectToTheMovingPointSet ) { points[0] = const_cast( static_cast( this->m_FixedPointSet ) ); points[1] = const_cast( static_cast( this->m_MovingPointSet ) ); if( this->m_UseInputAsSamples ) { samples[0] = points[0]; samples[1] = points[1]; } else { samples[0] = this->m_FixedSamplePoints; samples[1] = this->m_MovingSamplePoints; } densityFunctions[0] = this->m_FixedDensityFunction; densityFunctions[1] = this->m_MovingDensityFunction; kNeighborhood = this->m_MovingEvaluationKNeighborhood; } else { points[1] = const_cast( static_cast( this->m_FixedPointSet ) ); points[0] = const_cast( static_cast( this->m_MovingPointSet ) ); if( this->m_UseInputAsSamples ) { samples[1] = points[1]; samples[0] = points[0]; } else { samples[1] = this->m_FixedSamplePoints; samples[0] = this->m_MovingSamplePoints; } densityFunctions[1] = this->m_FixedDensityFunction; densityFunctions[0] = this->m_MovingDensityFunction; kNeighborhood = this->m_FixedEvaluationKNeighborhood; } RealType totalNumberOfPoints = static_cast( points[0]->GetNumberOfPoints() ) + static_cast( points[1]->GetNumberOfPoints() ); RealType totalNumberOfSamples = static_cast( samples[0]->GetNumberOfPoints() ) + static_cast( samples[1]->GetNumberOfPoints() ); derivative.SetSize( points[1]->GetPoints()->Size(), PointDimension ); derivative.Fill( 0 ); value.SetSize( 1 ); value.Fill( 0 ); /** * first term */ RealType energyTerm1 = 0.0; RealType energyTerm2 = 0.0; RealType prefactor[2]; prefactor[0] = -1.0 / totalNumberOfSamples; if( this->m_Alpha != 1.0 ) { prefactor[0] /= ( this->m_Alpha - 1.0 ); } prefactor[1] = 1.0 / ( totalNumberOfSamples * totalNumberOfPoints ); typename PointSetType::PointsContainerConstIterator It = samples[0]->GetPoints()->Begin(); while( It != samples[0]->GetPoints()->End() ) { PointType fixedSamplePoint = It.Value(); RealType probabilityStar = // densityFunctions[0]->Evaluate( fixedSamplePoint ) * // static_cast( points[0]->GetNumberOfPoints() ) + densityFunctions[1]->Evaluate( fixedSamplePoint ) * static_cast( points[1]->GetNumberOfPoints() ); probabilityStar /= totalNumberOfPoints; if( probabilityStar == 0 ) { ++It; continue; } if( this->m_Alpha == 1.0 ) { energyTerm1 += ( prefactor[0] * std::log( probabilityStar ) ); } else { energyTerm1 += ( prefactor[0] * std::pow( probabilityStar, static_cast( this->m_Alpha - 1.0 ) ) ); } RealType probabilityStarFactor = std::pow( probabilityStar, static_cast( 2.0 - this->m_Alpha ) ); typename GaussianType::MeasurementVectorType sampleMeasurement; for( unsigned int d = 0; d < PointDimension; d++ ) { sampleMeasurement[d] = fixedSamplePoint[d]; } typename DensityFunctionType::NeighborhoodIdentifierType neighbors = densityFunctions[1]->GetNeighborhoodIdentifiers( sampleMeasurement, kNeighborhood ); for( unsigned int n = 0; n < neighbors.size(); n++ ) { RealType gaussian = densityFunctions[1]-> GetGaussian( neighbors[n] )->Evaluate( sampleMeasurement ); if( gaussian == 0 ) { continue; } typename GaussianType::MeanType mean = densityFunctions[1]->GetGaussian( neighbors[n] )->GetMean(); for( unsigned int d = 0; d < PointDimension; d++ ) { mean[d] -= fixedSamplePoint[d]; } if( this->m_UseAnisotropicCovariances ) { typename GaussianType::MatrixType Ci = densityFunctions[1]->GetGaussian( neighbors[n] ) ->GetInverseCovariance(); mean = Ci * mean; } else { mean /= vnl_math_sqr( densityFunctions[1]->GetGaussian( neighbors[n] )->GetSigma() ); } mean *= ( prefactor[1] * gaussian / probabilityStarFactor ); for( unsigned int d = 0; d < PointDimension; d++ ) { derivative(neighbors[n], d) += mean[d]; } } ++It; } if( this->m_Alpha != 1.0 ) { energyTerm1 -= 1.0; } energyTerm1 *= prefactor[0]; /** * second term, i.e. regularization term */ if( this->m_UseRegularizationTerm ) { RealType prefactor2[2]; prefactor2[0] = -static_cast( points[1]->GetNumberOfPoints() ) / ( totalNumberOfPoints * static_cast( samples[1]->GetNumberOfPoints() ) ); prefactor2[1] = -1.0 / ( static_cast( samples[1]->GetNumberOfPoints() ) * totalNumberOfPoints ); if( this->m_Alpha != 1.0 ) { prefactor2[0] /= ( this->m_Alpha - 1.0 ); } typename PointSetType::PointsContainerConstIterator It = samples[1]->GetPoints()->Begin(); while( It != samples[1]->GetPoints()->End() ) { PointType movingSamplePoint = It.Value(); RealType probability = densityFunctions[1]->Evaluate( movingSamplePoint ); if( probability == 0 ) { ++It; continue; } if( this->m_Alpha == 1.0 ) { energyTerm2 += ( prefactor2[0] * std::log( probability ) ); } else { energyTerm2 += ( prefactor2[0] * std::pow( probability, static_cast( this->m_Alpha - 1.0 ) ) ); } RealType probabilityFactor = std::pow( probability, static_cast( 2.0 - this->m_Alpha ) ); probabilityFactor *= ( samples[1]->GetNumberOfPoints() / totalNumberOfSamples ); typename GaussianType::MeasurementVectorType sampleMeasurement; for( unsigned int d = 0; d < PointDimension; d++ ) { sampleMeasurement[d] = movingSamplePoint[d]; } typename DensityFunctionType::NeighborhoodIdentifierType neighbors = densityFunctions[1]->GetNeighborhoodIdentifiers( sampleMeasurement, kNeighborhood ); for( unsigned int i = 0; i < neighbors.size(); i++ ) { RealType gaussian = densityFunctions[1]-> GetGaussian( neighbors[i] )->Evaluate( sampleMeasurement ); if( gaussian == 0 ) { continue; } typename GaussianType::MeanType mean = densityFunctions[1]->GetGaussian( neighbors[i] )->GetMean(); for( unsigned int d = 0; d < PointDimension; d++ ) { mean[d] -= movingSamplePoint[d]; } if( this->m_UseAnisotropicCovariances ) { typename GaussianType::MatrixType Ci = densityFunctions[1]->GetGaussian( neighbors[i] ) ->GetInverseCovariance(); mean = Ci * mean; } else { mean /= vnl_math_sqr( densityFunctions[1]->GetGaussian( neighbors[i] )->GetSigma() ); } mean *= ( prefactor2[1] * gaussian / probabilityFactor ); for( unsigned int d = 0; d < PointDimension; d++ ) { derivative(neighbors[i], d) += mean[d]; } } ++It; } if( this->m_Alpha != 1.0 ) { energyTerm2 -= 1.0; } energyTerm2 *= prefactor2[0]; } value[0] = energyTerm1 - energyTerm2; } template void JensenHavrdaCharvatTsallisPointSetMetric ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf( os, indent ); os << indent << "Use with respect to the moving point set: " << this->m_UseWithRespectToTheMovingPointSet << std::endl; os << indent << "Use regularization term: " << this->m_UseRegularizationTerm << std::endl; os << indent << "Alpha: " << this->m_Alpha << std::endl; os << indent << "Fixed sigma: " << this->m_FixedPointSetSigma << std::endl; os << indent << "Moving sigma: " << this->m_MovingPointSetSigma << std::endl; if( !this->m_UseInputAsSamples ) { os << indent << "Number of fixed samples: " << this->m_NumberOfFixedSamples << std::endl; os << indent << "Number of moving samples: " << this->m_NumberOfMovingSamples << std::endl; } else { os << indent << "Use input points as samples." << std::endl; } if( this->m_UseAnisotropicCovariances ) { os << indent << "Fixed kernel sigma: " << this->m_FixedKernelSigma << std::endl; os << indent << "Moving kernel sigma: " << this->m_MovingKernelSigma << std::endl; os << indent << "Fixed covariance k-neighborhood: " << this->m_FixedCovarianceKNeighborhood << std::endl; os << indent << "Moving covariance k-neighborhood: " << this->m_MovingCovarianceKNeighborhood << std::endl; } else { os << indent << "Isotropic covariances are used." << std::endl; } } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkJensenTsallisBSplineRegistrationFunction.h000066400000000000000000000233411311104306400271060ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkJensenTsallisBSplineRegistrationFunction_h_ #define _itkJensenTsallisBSplineRegistrationFunction_h_ #include "itkAvantsPDEDeformableRegistrationFunction.h" #include "itkJensenHavrdaCharvatTsallisLabeledPointSetMetric.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkPointSet.h" namespace itk { /** * \class JensenTsallisBSplineRegistrationFunction * \ingroup FiniteDifferenceFunctions */ template class JensenTsallisBSplineRegistrationFunction : public AvantsPDEDeformableRegistrationFunction { public: /** Standard class typedefs. */ typedef JensenTsallisBSplineRegistrationFunction Self; typedef AvantsPDEDeformableRegistrationFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods) */ itkTypeMacro( JensenTsallisBSplineRegistrationFunction, AvantsPDEDeformableRegistrationFunction ); /** * Inherit some enums from the superclass. */ itkStaticConstMacro( ImageDimension, unsigned int, Superclass::ImageDimension); itkStaticConstMacro( PointDimension, unsigned int, TFixedPointSet::PointDimension ); typedef typename Superclass::NeighborhoodType NeighborhoodType; typedef typename Superclass::FloatOffsetType FloatOffsetType; typedef typename Superclass::TimeStepType TimeStepType; typedef JensenHavrdaCharvatTsallisLabeledPointSetMetric PointSetMetricType; typedef typename PointSetMetricType::RealType RealType; /** * Moving typedefs */ typedef TMovingImage MovingImageType; typedef TMovingPointSet MovingPointSetType; typedef typename MovingImageType::Pointer MovingImagePointer; typedef typename MovingPointSetType::Pointer MovingPointSetPointer; /** * Fixed typedefs */ typedef TFixedImage FixedImageType; typedef TFixedPointSet FixedPointSetType; typedef typename FixedImageType::Pointer FixedImagePointer; typedef typename FixedPointSetType::Pointer FixedPointSetPointer; /** * Deformation field typedefs */ typedef TDisplacementField DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldPointer; typedef typename DisplacementFieldType::PixelType VectorType; /** * BSpline typedefs */ /** Typedefs for B-spline filter */ typedef PointSet BSplinePointSetType; typedef BSplineScatteredDataPointSetToImageFilter BSplineFilterType; typedef typename BSplineFilterType::WeightsContainerType BSplineWeightsType; typedef typename BSplineFilterType::PointDataImageType ControlPointLatticeType; typedef typename BSplineFilterType::ArrayType ArrayType; void SetAlpha( RealType r ) { this->m_Alpha = r; } RealType GetAlpha() { return this->m_Alpha; } void SetUseRegularizationTerm( bool b ) { this->m_UseRegularizationTerm = b; } RealType GetUseRegularizationTerm() { return this->m_UseRegularizationTerm; } void SetUseInputAsSamples( bool b ) { this->m_UseInputAsSamples = b; } RealType GetUseInputAsSamples() { return this->m_UseInputAsSamples; } void SetUseAnisotropicCovariances( bool b ) { this->m_UseAnisotropicCovariances = b; } RealType GetUseAnisotropicCovariances() { return this->m_UseAnisotropicCovariances; } void SetFixedPointSetSigma( RealType r ) { this->m_FixedPointSetSigma = r; } RealType GetFixedPointSetSigma() { return this->m_FixedPointSetSigma; } void SetFixedKernelSigma( RealType r ) { this->m_FixedKernelSigma = r; } RealType GetFixedKernelSigma() { return this->m_FixedKernelSigma; } void SetFixedEvaluationKNeighborhood( unsigned int r ) { this->m_FixedEvaluationKNeighborhood = r; } unsigned int GetFixedEvaluationKNeighborhood() { return this->m_FixedEvaluationKNeighborhood; } void SetFixedCovarianceKNeighborhood( unsigned int r ) { this->m_FixedCovarianceKNeighborhood = r; } unsigned int GetFixedCovarianceKNeighborhood() { return this->m_FixedCovarianceKNeighborhood; } void SetNumberOfFixedSamples( unsigned long r ) { this->m_NumberOfFixedSamples = r; } unsigned long GetNumberOfFixedSamples() { return this->m_NumberOfFixedSamples; } void SetMovingPointSetSigma( RealType r ) { this->m_MovingPointSetSigma = r; } RealType GetMovingPointSetSigma() { return this->m_MovingPointSetSigma; } void SetMovingKernelSigma( RealType r ) { this->m_MovingKernelSigma = r; } RealType GetMovingKernelSigma() { return this->m_MovingKernelSigma; } void SetMovingEvaluationKNeighborhood( unsigned int r ) { this->m_MovingEvaluationKNeighborhood = r; } unsigned int GetMovingEvaluationKNeighborhood() { return this->m_MovingEvaluationKNeighborhood; } void SetMovingCovarianceKNeighborhood( unsigned int r ) { this->m_MovingCovarianceKNeighborhood = r; } unsigned int GetMovingCovarianceKNeighborhood() { return this->m_MovingCovarianceKNeighborhood; } void SetNumberOfMovingSamples( unsigned long r ) { this->m_NumberOfMovingSamples = r; } unsigned long GetNumberOfMovingSamples() { return this->m_NumberOfMovingSamples; } void SetMeshResolution( ArrayType a ) { this->m_MeshResolution = a; } ArrayType GetMeshResolution() { return this->m_MeshResolution; } void SetNumberOfLevels( unsigned int r ) { this->m_NumberOfLevels = r; } unsigned int GetNumberOfLevels() { return this->m_NumberOfLevels; } void SetSplineOrder( unsigned int r ) { this->m_SplineOrder = r; } unsigned int GetSplineOrder() { return this->m_SplineOrder; } /** This class uses a constant timestep of 1. */ virtual TimeStepType ComputeGlobalTimeStep(void *itkNotUsed(GlobalData) ) const { return m_TimeStep; } /** Return a pointer to a global data structure that is passed to * this object from the solver at each calculation. */ virtual void * GetGlobalDataPointer() const { GlobalDataStruct *global = new GlobalDataStruct(); return global; } /** Release memory for global data structure. */ virtual void ReleaseGlobalDataPointer( void *GlobalData ) const { delete (GlobalDataStruct *) GlobalData; } /** Set the object's state before each iteration. */ virtual void InitializeIteration(); virtual VectorType ComputeUpdate(const NeighborhoodType & neighborhood, void *globalData, const FloatOffsetType & offset = FloatOffsetType( 0.0 ) ); virtual VectorType ComputeUpdateInv(const NeighborhoodType & neighborhood, void *globalData, const FloatOffsetType & offset = FloatOffsetType( 0.0 ) ); protected: JensenTsallisBSplineRegistrationFunction(); ~JensenTsallisBSplineRegistrationFunction() { } /** FixedImage image neighborhood iterator type. */ typedef ConstNeighborhoodIterator FixedImageNeighborhoodIteratorType; /** A global data type for this class of equation. Used to store * iterators for the fixed image. */ struct GlobalDataStruct { FixedImageNeighborhoodIteratorType m_FixedImageIterator; }; void PrintSelf(std::ostream& os, Indent indent) const; private: // typename ControlPointLatticeType::Pointer m_FixedControlPointLattice; // typename ControlPointLatticeType::Pointer m_MovingControlPointLattice; /** The global timestep. */ TimeStepType m_TimeStep; /** * point-set related variables */ bool m_UseRegularizationTerm; bool m_UseInputAsSamples; bool m_UseAnisotropicCovariances; typename DisplacementFieldType::Pointer m_DerivativeFixedField; typename DisplacementFieldType::Pointer m_DerivativeMovingField; RealType m_FixedPointSetSigma; RealType m_FixedKernelSigma; unsigned int m_FixedEvaluationKNeighborhood; unsigned int m_FixedCovarianceKNeighborhood; unsigned long m_NumberOfFixedSamples; RealType m_MovingPointSetSigma; RealType m_MovingKernelSigma; unsigned int m_MovingEvaluationKNeighborhood; unsigned int m_MovingCovarianceKNeighborhood; unsigned long m_NumberOfMovingSamples; RealType m_Alpha; /** * Bspline related variables */ unsigned int m_SplineOrder; unsigned int m_NumberOfLevels; ArrayType m_MeshResolution; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkJensenTsallisBSplineRegistrationFunction.hxx" #endif #endif ants-2.2.0/ImageRegistration/itkJensenTsallisBSplineRegistrationFunction.hxx000066400000000000000000000457411311104306400274760ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkJensenTsallisBSplineRegistrationFunction_hxx_ #define _itkJensenTsallisBSplineRegistrationFunction_hxx_ #include "itkJensenTsallisBSplineRegistrationFunction.h" #include "itkContinuousIndex.h" #include "itkImageLinearConstIteratorWithIndex.h" // #include "itkBSplineControlPointImageFilter.h" namespace itk { template JensenTsallisBSplineRegistrationFunction ::JensenTsallisBSplineRegistrationFunction() { this->m_UseRegularizationTerm = false; this->m_UseInputAsSamples = true; this->m_UseAnisotropicCovariances = false; this->m_NumberOfFixedSamples = 100; this->m_FixedPointSetSigma = 1.0; this->m_FixedKernelSigma = 0.0; this->m_FixedEvaluationKNeighborhood = 50; this->m_NumberOfMovingSamples = 100; this->m_MovingPointSetSigma = 1.0; this->m_MovingKernelSigma = 0.0; this->m_MovingEvaluationKNeighborhood = 50; unsigned int covarianceKNeighborhood = static_cast( std::pow( 3.0, static_cast( ImageDimension ) ) ) - 1; this->m_FixedCovarianceKNeighborhood = covarianceKNeighborhood; this->m_MovingCovarianceKNeighborhood = covarianceKNeighborhood; this->m_Alpha = 2.0; // this->m_FixedControlPointLattice = NULL; // this->m_MovingControlPointLattice = NULL; this->m_DerivativeFixedField = NULL; this->m_DerivativeMovingField = NULL; this->m_IsPointSetMetric = true; this->m_SplineOrder = 3; this->m_NumberOfLevels = 1; this->m_MeshResolution.Fill( 1 ); } template void JensenTsallisBSplineRegistrationFunction ::InitializeIteration( void ) { if( this->m_FixedKernelSigma == 0 ) { double maxFixedSpacing = 0.0; for( unsigned int d = 0; d < ImageDimension; d++ ) { if( this->GetFixedImage()->GetSpacing()[d] ) { maxFixedSpacing = this->GetFixedImage()->GetSpacing()[d]; } } this->m_FixedKernelSigma = 2.0 * maxFixedSpacing; } if( this->m_MovingKernelSigma == 0 ) { double maxMovingSpacing = 0.0; for( unsigned int d = 0; d < ImageDimension; d++ ) { if( this->GetMovingImage()->GetSpacing()[d] ) { maxMovingSpacing = this->GetMovingImage()->GetSpacing()[d]; } } this->m_MovingKernelSigma = 2.0 * maxMovingSpacing; } typename PointSetMetricType::Pointer pointSetMetric = PointSetMetricType::New(); pointSetMetric->SetFixedPointSet( this->m_FixedPointSet ); pointSetMetric->SetMovingPointSet( this->m_MovingPointSet ); pointSetMetric->SetUseRegularizationTerm( this->m_UseRegularizationTerm ); pointSetMetric->SetUseInputAsSamples( this->m_UseInputAsSamples ); pointSetMetric->SetUseAnisotropicCovariances( this->m_UseAnisotropicCovariances ); pointSetMetric->SetNumberOfFixedSamples( this->m_NumberOfFixedSamples ); pointSetMetric->SetFixedPointSetSigma( this->m_FixedPointSetSigma ); pointSetMetric->SetFixedKernelSigma( this->m_FixedKernelSigma ); pointSetMetric->SetFixedCovarianceKNeighborhood( this->m_FixedCovarianceKNeighborhood ); pointSetMetric->SetFixedEvaluationKNeighborhood( this->m_FixedEvaluationKNeighborhood ); pointSetMetric->SetNumberOfMovingSamples( this->m_NumberOfMovingSamples ); pointSetMetric->SetMovingPointSetSigma( this->m_MovingPointSetSigma ); pointSetMetric->SetMovingKernelSigma( this->m_MovingKernelSigma ); pointSetMetric->SetMovingCovarianceKNeighborhood( this->m_MovingCovarianceKNeighborhood ); pointSetMetric->SetMovingEvaluationKNeighborhood( this->m_MovingEvaluationKNeighborhood ); pointSetMetric->SetAlpha( this->m_Alpha ); pointSetMetric->Initialize(); typename PointSetMetricType::DefaultTransformType::ParametersType parameters; parameters.Fill( 0.0 ); /** * Calculate with respect to the moving point set */ pointSetMetric->SetUseWithRespectToTheMovingPointSet( true ); typename PointSetMetricType::DerivativeType movingGradient; typename PointSetMetricType::MeasureType movingMeasure; pointSetMetric->GetValueAndDerivative( parameters, movingMeasure, movingGradient ); this->m_Energy += movingMeasure[0]; typename BSplinePointSetType::Pointer movingGradientPoints = BSplinePointSetType::New(); movingGradientPoints->Initialize(); typename BSplineWeightsType::Pointer movingWeights = BSplineWeightsType::New(); movingWeights->Initialize(); typename MovingImageType::SizeType movingSize = this->GetMovingImage()->GetLargestPossibleRegion().GetSize(); typename MovingImageType::IndexType movingIndex = this->GetMovingImage()->GetLargestPossibleRegion().GetIndex(); unsigned long count = 0; for( unsigned int n = 0; n < pointSetMetric->GetNumberOfValues(); n++ ) { typename MovingPointSetType::PointType point; this->m_MovingPointSet->GetPoint( n, &point ); typename MovingImageType::PointType imagePoint; imagePoint.CastFrom( point ); ContinuousIndex cidx; bool isInside = this->GetMovingImage()-> TransformPhysicalPointToContinuousIndex( imagePoint, cidx ); typename BSplinePointSetType::PointType bsplinePoint; for( unsigned int d = 0; d < PointDimension; d++ ) { if( cidx[d] - movingIndex[d] > movingSize[d] - 1.0 ) { isInside = false; break; } bsplinePoint[d] = cidx[d]; } VectorType gradient; if( isInside ) { for( unsigned int d = 0; d < PointDimension; d++ ) { gradient[d] = movingGradient(n, d); } movingGradientPoints->SetPoint( count, bsplinePoint ); movingGradientPoints->SetPointData( count, gradient ); movingWeights->InsertElement( count, 1.0 ); count++; } } VectorType zeroVector; zeroVector.Fill( 0.0 ); ImageLinearConstIteratorWithIndex ItM( this->GetMovingImage(), this->GetMovingImage()->GetLargestPossibleRegion() ); for( unsigned int d = 0; d < PointDimension; d++ ) { ItM.SetDirection( d ); ItM.GoToBegin(); while( !ItM.IsAtEnd() ) { typename MovingImageType::PointType point; typename BSplinePointSetType::PointType bsplinePoint; ItM.GoToBeginOfLine(); typename MovingImageType::IndexType index = ItM.GetIndex(); for( unsigned int m = 0; m < PointDimension; m++ ) { bsplinePoint[m] = index[m]; } movingGradientPoints->SetPoint( count, bsplinePoint ); movingGradientPoints->SetPointData( count, zeroVector ); count++; ItM.GoToEndOfLine(); --ItM; index = ItM.GetIndex(); for( unsigned int m = 0; m < PointDimension; m++ ) { bsplinePoint[m] = index[m]; } movingGradientPoints->SetPoint( count, bsplinePoint ); movingGradientPoints->SetPointData( count, zeroVector ); movingWeights->InsertElement( count, 1000.0 ); count++; ItM.NextLine(); } } ArrayType numberOfMovingControlPoints; for( unsigned int d = 0; d < ImageDimension; d++ ) { numberOfMovingControlPoints[d] = this->m_SplineOrder + static_cast( std::floor( 0.5 + static_cast( this->GetMovingImage()->GetLargestPossibleRegion().GetSize()[d] ) / static_cast( this->m_MeshResolution[d] ) ) ); } typename MovingImageType::SpacingType movingParametricSpacing; movingParametricSpacing.Fill( 1 ); typename MovingImageType::PointType movingParametricOrigin; for( unsigned int d = 0; d < PointDimension; d++ ) { movingParametricOrigin[d] = this->GetMovingImage()->GetLargestPossibleRegion().GetIndex()[d]; } typename BSplineFilterType::Pointer movingBSpliner = BSplineFilterType::New(); movingBSpliner->SetInput( movingGradientPoints ); movingBSpliner->SetPointWeights( movingWeights.GetPointer() ); movingBSpliner->SetOrigin( movingParametricOrigin ); movingBSpliner->SetSpacing( movingParametricSpacing ); movingBSpliner->SetSize( this->GetMovingImage()->GetLargestPossibleRegion().GetSize() ); movingBSpliner->SetDirection( this->GetMovingImage()->GetDirection() ); movingBSpliner->SetNumberOfLevels( this->m_NumberOfLevels ); movingBSpliner->SetSplineOrder( this->m_SplineOrder ); movingBSpliner->SetNumberOfControlPoints( numberOfMovingControlPoints ); movingBSpliner->SetGenerateOutputImage( true ); movingBSpliner->Update(); movingBSpliner->GetOutput()->SetSpacing( this->GetMovingImage()->GetSpacing() ); this->m_DerivativeMovingField = movingBSpliner->GetOutput(); this->m_DerivativeMovingField->DisconnectPipeline(); // this->m_MovingControlPointLattice = movingBSpliner->GetPhiLattice(); /** * Calculate with respect to the fixed point set */ pointSetMetric->SetUseWithRespectToTheMovingPointSet( false ); typename PointSetMetricType::DerivativeType fixedGradient; typename PointSetMetricType::MeasureType fixedMeasure; pointSetMetric->GetValueAndDerivative( parameters, fixedMeasure, fixedGradient ); this->m_Energy += fixedMeasure[0]; typename BSplinePointSetType::Pointer fixedGradientPoints = BSplinePointSetType::New(); fixedGradientPoints->Initialize(); typename BSplineWeightsType::Pointer fixedWeights = BSplineWeightsType::New(); fixedWeights->Initialize(); typename FixedImageType::SizeType fixedSize = this->GetFixedImage()->GetLargestPossibleRegion().GetSize(); typename FixedImageType::IndexType fixedIndex = this->GetFixedImage()->GetLargestPossibleRegion().GetIndex(); count = 0; for( unsigned int n = 0; n < pointSetMetric->GetNumberOfValues(); n++ ) { typename FixedPointSetType::PointType point; this->m_FixedPointSet->GetPoint( n, &point ); typename FixedImageType::PointType imagePoint; imagePoint.CastFrom( point ); ContinuousIndex cidx; bool isInside = this->GetFixedImage()-> TransformPhysicalPointToContinuousIndex( imagePoint, cidx ); typename BSplinePointSetType::PointType bsplinePoint; for( unsigned int d = 0; d < PointDimension; d++ ) { if( cidx[d] - fixedIndex[d] > fixedSize[d] - 1.0 ) { isInside = false; break; } bsplinePoint[d] = cidx[d]; } VectorType gradient; if( isInside ) { for( unsigned int d = 0; d < PointDimension; d++ ) { gradient[d] = fixedGradient(n, d); } fixedGradientPoints->SetPoint( count, bsplinePoint ); fixedGradientPoints->SetPointData( count, gradient ); fixedWeights->InsertElement( count, 1.0 ); count++; } } ImageLinearConstIteratorWithIndex ItF( this->GetFixedImage(), this->GetFixedImage()->GetLargestPossibleRegion() ); for( unsigned int d = 0; d < PointDimension; d++ ) { ItF.SetDirection( d ); ItF.GoToBegin(); while( !ItF.IsAtEnd() ) { typename FixedImageType::PointType point; typename BSplinePointSetType::PointType bsplinePoint; ItF.GoToBeginOfLine(); typename FixedImageType::IndexType index = ItF.GetIndex(); for( unsigned int m = 0; m < PointDimension; m++ ) { bsplinePoint[m] = index[m]; } fixedGradientPoints->SetPoint( count, bsplinePoint ); fixedGradientPoints->SetPointData( count, zeroVector ); fixedWeights->InsertElement( count, 1.0 ); count++; ItF.GoToEndOfLine(); --ItF; index = ItF.GetIndex(); for( unsigned int m = 0; m < PointDimension; m++ ) { bsplinePoint[m] = index[m]; } fixedGradientPoints->SetPoint( count, bsplinePoint ); fixedGradientPoints->SetPointData( count, zeroVector ); fixedWeights->InsertElement( count, 1000.0 ); count++; ItF.NextLine(); } } ArrayType numberOfFixedControlPoints; for( unsigned int d = 0; d < ImageDimension; d++ ) { numberOfFixedControlPoints[d] = this->m_SplineOrder + static_cast( std::floor( 0.5 + static_cast( this->GetFixedImage()->GetLargestPossibleRegion().GetSize()[d] ) / static_cast( this->m_MeshResolution[d] ) ) ); } typename FixedImageType::SpacingType fixedParametricSpacing; fixedParametricSpacing.Fill( 1.0 ); typename FixedImageType::PointType fixedParametricOrigin; for( unsigned int d = 0; d < PointDimension; d++ ) { fixedParametricOrigin[d] = this->GetFixedImage()->GetLargestPossibleRegion().GetIndex()[d]; } typename BSplineFilterType::Pointer fixedBSpliner = BSplineFilterType::New(); fixedBSpliner->SetInput( fixedGradientPoints ); fixedBSpliner->SetPointWeights( fixedWeights.GetPointer() ); fixedBSpliner->SetOrigin( fixedParametricOrigin ); fixedBSpliner->SetSpacing( fixedParametricSpacing ); fixedBSpliner->SetDirection( this->GetFixedImage()->GetDirection() ); fixedBSpliner->SetSize( this->GetFixedImage()->GetLargestPossibleRegion().GetSize() ); fixedBSpliner->SetNumberOfLevels( this->m_NumberOfLevels ); fixedBSpliner->SetSplineOrder( this->m_SplineOrder ); fixedBSpliner->SetNumberOfControlPoints( numberOfFixedControlPoints ); fixedBSpliner->SetGenerateOutputImage( true ); fixedBSpliner->Update(); fixedBSpliner->GetOutput()->SetSpacing( this->GetFixedImage()->GetSpacing() ); this->m_DerivativeFixedField = fixedBSpliner->GetOutput(); this->m_DerivativeFixedField->DisconnectPipeline(); // this->m_FixedControlPointLattice = fixedBSpliner->GetPhiLattice(); } template typename JensenTsallisBSplineRegistrationFunction::VectorType JensenTsallisBSplineRegistrationFunction ::ComputeUpdate( const NeighborhoodType & neighborhood, void *globalData, const FloatOffsetType & offset ) { if( this->m_DerivativeFixedField ) { return -this->m_DerivativeFixedField->GetPixel( neighborhood.GetIndex() ); } else { itkExceptionMacro( "Initialize() has not been called." ); } /* typedef BSplineControlPointImageFilter BSplineControlPointImageFilterType; typename BSplineControlPointImageFilterType::Pointer movingBSpliner = BSplineControlPointImageFilterType::New(); movingBSpliner->SetInput( this->m_MovingControlPointLattice ); movingBSpliner->SetOrigin( this->GetMovingImage()->GetOrigin() ); movingBSpliner->SetSpacing( this->GetMovingImage()->GetSpacing() ); movingBSpliner->SetSize( this->GetMovingImage()->GetLargestPossibleRegion().GetSize() ); movingBSpliner->SetSplineOrder( this->m_SplineOrder ); VectorType gradient; movingBSpliner->EvaluateAtIndex( neighborhood.GetIndex(), gradient ); return gradient; */ } template typename JensenTsallisBSplineRegistrationFunction::VectorType JensenTsallisBSplineRegistrationFunction ::ComputeUpdateInv( const NeighborhoodType & neighborhood, void *globalData, const FloatOffsetType & offset ) { if( this->m_DerivativeMovingField ) { return -this->m_DerivativeMovingField->GetPixel( neighborhood.GetIndex() ); } else { itkExceptionMacro( "Initialize() has not been called." ); } /* typedef BSplineControlPointImageFilter BSplineControlPointImageFilterType; typename BSplineControlPointImageFilterType::Pointer fixedBSpliner = BSplineControlPointImageFilterType::New(); fixedBSpliner->SetInput( this->m_FixedControlPointLattice ); fixedBSpliner->SetOrigin( this->GetFixedImage()->GetOrigin() ); fixedBSpliner->SetSpacing( this->GetFixedImage()->GetSpacing() ); fixedBSpliner->SetSize( this->GetFixedImage()->GetLargestPossibleRegion().GetSize() ); fixedBSpliner->SetSplineOrder( this->m_SplineOrder ); VectorType gradient; fixedBSpliner->EvaluateAtIndex( neighborhood.GetIndex(), gradient ); return gradient; */ } template void JensenTsallisBSplineRegistrationFunction ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf( os, indent ); os << indent << "Use regularization term: " << this->m_UseRegularizationTerm << std::endl; if( !this->m_UseInputAsSamples ) { os << indent << "Number of fixed samples: " << this->m_NumberOfFixedSamples << std::endl; os << indent << "Number of moving samples: " << this->m_NumberOfMovingSamples << std::endl; } os << indent << "Alpha: " << this->m_Alpha << std::endl; os << indent << "Fixed sigma: " << this->m_FixedPointSetSigma << std::endl; os << indent << "Moving sigma: " << this->m_MovingPointSetSigma << std::endl; os << indent << "Spline order: " << this->m_SplineOrder << std::endl; os << indent << "Number of levels: " << this->m_NumberOfLevels << std::endl; os << indent << "Number of control points: " << this->m_MeshResolution << std::endl; } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkLabelImageGenericInterpolateImageFunction.h000066400000000000000000000067511311104306400271170ustar00rootroot00000000000000#ifndef __itkLabelImageGenericInterpolateImageFunction_h #define __itkLabelImageGenericInterpolateImageFunction_h #include #include "itkLabelSelectionAdaptor.h" #include #include namespace itk { /** \class LabelImageGenericInterpolateImageFunction * \brief Interpolation function for multi-label images that implicitly interpolates each * unique value in the image corresponding to each label set element and returns the * corresponding label set element with the largest weight. * * This filter is an alternative to nearest neighbor interpolation for multi-label * images. It can use almost any underlying interpolator. * * \ingroup ITKImageFunction */ template class TInterpolator, typename TCoordRep=double > class LabelImageGenericInterpolateImageFunction : public InterpolateImageFunction { public: /** Standard class typedefs. */ typedef LabelImageGenericInterpolateImageFunction Self; typedef InterpolateImageFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef typename TInputImage::PixelType InputPixelType; /** Run-time type information (and related methods). */ itkTypeMacro( LabelImageGenericInterpolateImageFunction, InterpolateImageFunction ); /** Method for creation through the object factory. */ itkNewMacro( Self ); /** ImageDimension constant */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); /** OutputType typedef support. */ typedef typename Superclass::OutputType OutputType; /** InputImageType typedef support. */ typedef typename Superclass::InputImageType InputImageType; /** RealType typedef support. */ typedef typename Superclass::RealType RealType; /** Index typedef support. */ typedef typename Superclass::IndexType IndexType; /** ContinuousIndex typedef support. */ typedef typename Superclass::ContinuousIndexType ContinuousIndexType; typedef LabelSelectionImageAdaptor LabelSelectionAdaptorType; // The interpolator used for individual binary masks corresponding to each label typedef TInterpolator InternalInterpolatorType; /** * Evaluate at the given index */ virtual OutputType EvaluateAtContinuousIndex( const ContinuousIndexType & cindex ) const ITK_OVERRIDE { return this->EvaluateAtContinuousIndex( cindex, NULL ); } virtual void SetInputImage( const TInputImage *image ) ITK_OVERRIDE; protected: LabelImageGenericInterpolateImageFunction(); ~LabelImageGenericInterpolateImageFunction(){}; std::vector m_InternalInterpolators; std::vector m_LabelSelectionAdaptors; typedef std::set LabelSetType; LabelSetType m_Labels; private: LabelImageGenericInterpolateImageFunction( const Self& ); //purposely not implemented void operator=( const Self& ); //purposely not implemented /** * Evaluate function value at the given index */ virtual OutputType EvaluateAtContinuousIndex( const ContinuousIndexType &, OutputType * ) const; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkLabelImageGenericInterpolateImageFunction.hxx" #endif #endif ants-2.2.0/ImageRegistration/itkLabelImageGenericInterpolateImageFunction.hxx000066400000000000000000000051621311104306400274720ustar00rootroot00000000000000#ifndef __itkLabelImageGenericInterpolateImageFunction_hxx #define __itkLabelImageGenericInterpolateImageFunction_hxx #include "itkLabelImageGenericInterpolateImageFunction.h" #include namespace itk { template class TInterpolator , typename TCoordRep> LabelImageGenericInterpolateImageFunction ::LabelImageGenericInterpolateImageFunction() { } template class TInterpolator , typename TCoordRep> void LabelImageGenericInterpolateImageFunction ::SetInputImage( const TInputImage *image ) { /** We have one adaptor and one interpolator per label to keep the class thread-safe: * changing the adaptor's accepted value wouldn't work when called from a multi-threaded filter */ if (image) { m_Labels.clear(); typedef itk::ImageRegionConstIterator IteratorType; IteratorType it(image,image->GetLargestPossibleRegion()); it.GoToBegin(); for (; !it.IsAtEnd(); ++it) { m_Labels.insert(it.Get()); } m_InternalInterpolators.clear(); m_LabelSelectionAdaptors.clear(); for(typename LabelSetType::const_iterator i=m_Labels.begin(); i != m_Labels.end(); ++i) { typename LabelSelectionAdaptorType::Pointer adapt = LabelSelectionAdaptorType::New(); // This adaptor doesn't implement Set() so this should be safe adapt->SetImage(const_cast(image)); adapt->SetAcceptedValue(*i); m_LabelSelectionAdaptors.push_back(adapt); typename InternalInterpolatorType::Pointer interp = InternalInterpolatorType::New(); interp->SetInputImage(adapt); m_InternalInterpolators.push_back(interp); } } Superclass::SetInputImage(image); } template class TInterpolator , typename TCoordRep> typename LabelImageGenericInterpolateImageFunction ::OutputType LabelImageGenericInterpolateImageFunction ::EvaluateAtContinuousIndex( const ContinuousIndexType & cindex, OutputType * itkNotUsed( grad ) ) const { /** Interpolate the binary mask corresponding to each label and return the label * with the highest value */ double value=0; typename TInputImage::PixelType best_label=0; typename LabelSetType::const_iterator it; int i; for(it=m_Labels.begin(), i = 0; it != m_Labels.end(); ++it, ++i) { double tmp = m_InternalInterpolators[i]->EvaluateAtContinuousIndex(cindex); if( tmp > value) { value = tmp; best_label = (*it); } } return best_label; } } // namespace itk #endif ants-2.2.0/ImageRegistration/itkLabelSelectionAdaptor.h000066400000000000000000000052311311104306400231500ustar00rootroot00000000000000#ifndef __itkLabelSelectionImageAdaptor_h #define __itkLabelSelectionImageAdaptor_h #include "itkImageAdaptor.h" namespace itk { namespace Accessor { /** \class LabelSelectionPixelAccessor * \brief Return a binary mask of the selected label * * LabelSelectionPixelAccessor is templated over an internal type and an * external type representation. This class cast the input * applies the function to it and cast the result according * to the types defined as template parameters * * \ingroup ImageAdaptors * \ingroup ITKImageAdaptors */ template< class TInternalType, class TExternalType > class ITK_EXPORT LabelSelectionPixelAccessor { public: /** External typedef. It defines the external aspect * that this class will exhibit. */ typedef TExternalType ExternalType; /** Internal typedef. It defines the internal real * representation of data. */ typedef TInternalType InternalType; void SetAcceptedValue(TInternalType value) { m_AcceptedValue = value; } inline TExternalType Get(const TInternalType & input) const { return (TExternalType)( ( input == m_AcceptedValue ) ? 1 : 0 ); } protected: TInternalType m_AcceptedValue; }; } // end namespace Accessor /** \class LabelSelectionImageAdaptor * \brief Presents a label image as a binary image of one label * * Additional casting is performed according to the input and output image * types following C++ default casting rules. * * \ingroup ImageAdaptors * \ingroup ITKImageAdaptors */ template< class TImage, class TOutputPixelType > class ITK_EXPORT LabelSelectionImageAdaptor:public ImageAdaptor< TImage, Accessor::LabelSelectionPixelAccessor< typename TImage::PixelType, TOutputPixelType > > { public: /** Standard class typedefs. */ typedef LabelSelectionImageAdaptor Self; typedef ImageAdaptor< TImage, Accessor::LabelSelectionPixelAccessor< typename TImage::PixelType, TOutputPixelType > > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(LabelSelectionImageAdaptor, ImageAdaptor); void SetAcceptedValue(typename TImage::PixelType value) { this->GetPixelAccessor().SetAcceptedValue(value); } protected: LabelSelectionImageAdaptor() {} virtual ~LabelSelectionImageAdaptor() {} private: LabelSelectionImageAdaptor(const Self &); //purposely not implemented void operator=(const Self &); //purposely not implemented }; } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkPICSLAdvancedNormalizationToolKit.h000066400000000000000000000113331311104306400253250ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkPICSLAdvancedNormalizationToolKit_h #define __itkPICSLAdvancedNormalizationToolKit_h #include "itkObject.h" #include "itkObjectFactory.h" #include "itkANTSImageTransformation.h" #include "itkANTSImageRegistrationOptimizer.h" #include "itkANTSSimilarityMetric.h" #include "antsCommandLineParser.h" #include "itkImage.h" #include "itkMacro.h" #include "itkANTSLabeledPointSet.h" namespace itk { template class PICSLAdvancedNormalizationToolKit : public Object { public: /** Standard class typedefs. */ typedef PICSLAdvancedNormalizationToolKit Self; typedef Object Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( PICSLAdvancedNormalizationToolKit, Object ); itkStaticConstMacro( Dimension, unsigned int, TDimension ); typedef double TComp; typedef TReal RealType; typedef Image ImageType; typedef typename ImageType::Pointer ImagePointer; typedef typename ImageType::PixelType PixelType; typedef itk::ANTSImageTransformation TransformationModelType; typedef typename TransformationModelType::Pointer TransformationModelPointer; typedef itk::ANTSImageRegistrationOptimizer RegistrationOptimizerType; typedef typename RegistrationOptimizerType::Pointer RegistrationOptimizerPointer; typedef typename TransformationModelType::DisplacementFieldType DisplacementFieldType; typedef typename TransformationModelType::AffineTransformType AffineTransformType; typedef typename RegistrationOptimizerType::OptAffineType OptAffineType; /** Point Set Type */ typedef itk::ANTSLabeledPointSet LabeledPointSetType; typedef typename LabeledPointSetType::Pointer LabeledPointSetPointer; typedef typename LabeledPointSetType::PointSetType PointSetType; /** Typedefs for similarity metrics */ typedef ANTSSimilarityMetric SimilarityMetricType; typedef typename SimilarityMetricType::Pointer SimilarityMetricPointer; typedef std::vector SimilarityMetricListType; typedef typename SimilarityMetricType::MetricType MetricBaseType; typedef ants::CommandLineParser ParserType; typedef typename ParserType::OptionType OptionType; void ParseCommandLine( int argc, char * *argv ); TransformationModelPointer GetTransformationModel() { return this->m_TransformationModel; } RegistrationOptimizerPointer SetRegistrationOptimizer() { return this->m_RegistrationOptimizer; } void SetTransformationModel( TransformationModelPointer T ) { this->m_TransformationModel = T; } void SetRegistrationOptimizer( RegistrationOptimizerPointer T ) { this->m_RegistrationOptimizer = T; } void InitializeTransformAndOptimizer(); void RunRegistration(); protected: PICSLAdvancedNormalizationToolKit(); virtual ~PICSLAdvancedNormalizationToolKit() { } void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; private: PICSLAdvancedNormalizationToolKit( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented ImagePointer SubsampleImage( ImagePointer, RealType ); ImagePointer PreprocessImage( ImagePointer ); ImagePointer ReplaceProblematicPixelValues( ImagePointer, PixelType ); void InitializeCommandLineOptions(); void ReadImagesAndMetrics(); typename ParserType::Pointer m_Parser; TransformationModelPointer m_TransformationModel; RegistrationOptimizerPointer m_RegistrationOptimizer; SimilarityMetricListType m_SimilarityMetrics; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkPICSLAdvancedNormalizationToolKit.hxx" #endif #endif ants-2.2.0/ImageRegistration/itkPICSLAdvancedNormalizationToolKit.hxx000066400000000000000000001652621311104306400257200ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkPICSLAdvancedNormalizationToolKit_hxx_ #define _itkPICSLAdvancedNormalizationToolKit_hxx_ // disable debug warnings in MS compiler #ifdef _MSC_VER #pragma warning(disable: 4786) #endif #include "itkPICSLAdvancedNormalizationToolKit.h" #include "itkHistogramMatchingImageFilter.h" #include "itkSpatialMutualInformationRegistrationFunction.h" #include "itkIdentityTransform.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkLabeledPointSetFileReader.h" #include "itkLinearInterpolateImageFunction.h" #include "itkRecursiveGaussianImageFilter.h" #include "itkResampleImageFilter.h" // Image similarity metrics #include "itkAvantsMutualInformationRegistrationFunction.h" #include "itkSyNDemonsRegistrationFunction.h" #include "itkProbabilisticRegistrationFunction.h" #include "itkCrossCorrelationRegistrationFunction.h" #include "itkExpectationBasedPointSetRegistrationFunction.h" // #include "itkRobustDemonsRegistrationFunction.h" // #include "itkRobustOpticalFlow.h" // #include "itkSectionMutualInformationRegistrationFunction.h" // #include "itkJensenTsallisBSplineRegistrationFunction.h" #include "vnl/vnl_math.h" #include "ANTS_affine_registration2.h" namespace itk { template PICSLAdvancedNormalizationToolKit ::PICSLAdvancedNormalizationToolKit() { this->InitializeCommandLineOptions(); } template void PICSLAdvancedNormalizationToolKit ::ParseCommandLine( int argc, char * *argv ) { this->m_Parser->Parse( argc, argv ); typename ParserType::OptionListType unknownOptions = this->m_Parser->GetUnknownOptions(); if( unknownOptions.size() ) { std::cout << std::endl << "WARNING: Unknown options" << std::endl; typename ParserType::OptionListType::const_iterator its; for( its = unknownOptions.begin(); its != unknownOptions.end(); its++ ) { if( (*its)->GetShortName() != '\0' ) { std::cout << " " << '-' << (*its)->GetShortName() << std::endl; } else { std::cout << " " << "--" << (*its)->GetLongName() << std::endl; } } std::cout << std::endl; } std::string printhelp_long = this->m_Parser->GetOption( "help" )->GetFunction( 0 )->GetName(); unsigned int help_long = this->m_Parser->template Convert( printhelp_long ); if( help_long ) { this->m_Parser->PrintMenu( std::cout, 7, false ); std::exception(); exit( EXIT_SUCCESS ); } std::string printhelp_short = this->m_Parser->GetOption( 'h' )->GetFunction( 0 )->GetName(); unsigned int help_short = this->m_Parser->template Convert( printhelp_short ); if( help_short ) { this->m_Parser->PrintMenu( std::cout, 7, true ); std::exception(); exit( EXIT_SUCCESS ); } } template void PICSLAdvancedNormalizationToolKit ::InitializeTransformAndOptimizer() { if( !this->m_TransformationModel ) { this->m_TransformationModel = TransformationModelType::New(); } if( !this->m_RegistrationOptimizer ) { this->m_RegistrationOptimizer = RegistrationOptimizerType::New(); } std::string temp = this->m_Parser->GetOption( "output-naming" )->GetFunction( 0 )->GetName(); this->m_TransformationModel->SetNamingConvention( temp ); this->m_TransformationModel->InitializeTransform(); this->m_RegistrationOptimizer->SetParser( this->m_Parser ); this->m_RegistrationOptimizer->SetSimilarityMetrics( this->m_SimilarityMetrics ); } template void PICSLAdvancedNormalizationToolKit ::RunRegistration() { /** parse the command line and get input objects */ this->ReadImagesAndMetrics(); // std::exception(); /** initializes the transformation model and the optimizer */ this->InitializeTransformAndOptimizer(); /** Get the mask if there is one */ typename OptionType::Pointer option = this->m_Parser->GetOption( "mask-image" ); if( option && option->GetNumberOfFunctions() ) { typedef ImageFileReader ReaderType; std::string maskfn = this->m_Parser->GetOption( "mask-image" )->GetFunction( 0 )->GetName(); if( maskfn.length() > 4 ) { typename ReaderType::Pointer maskImageFileReader = ReaderType::New(); maskImageFileReader->SetFileName( maskfn.c_str() ); maskImageFileReader->Update(); ImagePointer maskImage = this->PreprocessImage( maskImageFileReader->GetOutput() ); this->m_RegistrationOptimizer->SetMaskImage(maskImage); } } typename TransformationModelType::AffineTransformPointer aff_init, aff, fixed_aff_init; // added by songgang // try initialize the affine transform typename OptionType::Pointer initialAffineOption = this->m_Parser->GetOption( "initial-affine" ); if( initialAffineOption && initialAffineOption->GetNumberOfFunctions() ) { std::string initial_affine_filename = initialAffineOption->GetFunction( 0 )->GetName(); std::cout << "Loading affine registration from: " << initial_affine_filename << std::endl; ReadAffineTransformFile(initial_affine_filename, aff_init); } else { std::cout << "Use identity affine transform as initial affine para." << std::endl; std::cout << "aff_init.IsNull()==" << aff_init.IsNull() << std::endl; } std::string fixed_initial_affine_filename = std::string( "" ); typename OptionType::Pointer fixedInitialAffineOption = this->m_Parser->GetOption( "fixed-image-initial-affine" ); if( fixedInitialAffineOption && fixedInitialAffineOption->GetNumberOfFunctions() ) { fixed_initial_affine_filename = fixedInitialAffineOption->GetFunction( 0 )->GetName(); std::cout << "Loading affine registration from: " << fixed_initial_affine_filename << std::endl; fixed_aff_init = TransformationModelType::AffineTransformType::New(); ReadAffineTransformFile(fixed_initial_affine_filename, fixed_aff_init); std::cout << " FIXME! currently, if one passes a fixed initial affine mapping, then NO affine mapping will be performed subsequently! " << std::endl; typename OptionType::Pointer fixedImageInitialAffineRegImageOption = this->m_Parser->GetOption( "fixed-image-initial-affine-ref-image" ); if( fixedImageInitialAffineRegImageOption && fixedImageInitialAffineRegImageOption->GetNumberOfFunctions() ) { std::string refheader = fixedImageInitialAffineRegImageOption->GetFunction( 0 )->GetName(); std::cout << " Setting reference deformation space by " << refheader << std::endl; typedef ImageFileReader ReaderType; typename ReaderType::Pointer fixedImageFileReader = ReaderType::New(); fixedImageFileReader->SetFileName( refheader.c_str() ); fixedImageFileReader->Update(); ImagePointer temp = fixedImageFileReader->GetOutput(); this->m_RegistrationOptimizer->SetReferenceSpaceImage(temp); } } else { std::cout << "Use identity affine transform as initial fixed affine para." << std::endl; std::cout << "fixed_aff_init.IsNull()==" << fixed_aff_init.IsNull() << std::endl; } typename OptionType::Pointer useNNOption = this->m_Parser->GetOption( "use-NN" ); if( useNNOption && useNNOption->GetNumberOfFunctions() && this->m_Parser->template Convert( useNNOption->GetFunction( 0 )->GetName() ) ) { this->m_RegistrationOptimizer->SetUseNearestNeighborInterpolation(true); } else { this->m_RegistrationOptimizer->SetUseNearestNeighborInterpolation(false); } std::string continue_affine = this->m_Parser->GetOption( "continue-affine" )->GetFunction( 0 )->GetName(); if( fixed_initial_affine_filename != "" ) { continue_affine = std::string("false"); } if( continue_affine == "true" ) { std::cout << "Continue affine registration from the input" << std::endl; // << aff_init << std::endl; OptAffineType affine_opt; // InitializeAffineOption() { affine_opt.transform_initial = aff_init; std::string temp = this->m_Parser->GetOption( "number-of-affine-iterations" )->GetFunction( 0 )->GetName(); affine_opt.number_of_iteration_list = this->m_Parser->template ConvertVector(temp); affine_opt.number_of_levels = affine_opt.number_of_iteration_list.size(); temp = this->m_Parser->GetOption( "affine-metric-type" )->GetFunction( 0 )->GetName(); if( temp == "MI" ) { affine_opt.metric_type = AffineWithMutualInformation; } if( temp == "MSQ" ) { affine_opt.metric_type = AffineWithMeanSquareDifference; } if( temp == "CCH" ) { affine_opt.metric_type = AffineWithHistogramCorrelation; } if( temp == "CC" ) { affine_opt.metric_type = AffineWithNormalizedCorrelation; } if( temp == "GD" ) { affine_opt.metric_type = AffineWithGradientDifference; } temp = this->m_Parser->GetOption( "MI-option" )->GetFunction( 0 )->GetName(); std::vector mi_option = this->m_Parser->template ConvertVector(temp); affine_opt.MI_bins = mi_option[0]; affine_opt.MI_samples = mi_option[1]; temp = this->m_Parser->GetOption( "rigid-affine" )->GetFunction( 0 )->GetName(); std::string temp2 = this->m_Parser->GetOption( "do-rigid" )->GetFunction( 0 )->GetName(); affine_opt.is_rigid = ( ( temp == "true" ) || ( temp2 == "true" ) || ( temp == "1" ) || ( temp2 == "1" ) ); temp = this->m_Parser->GetOption( "affine-gradient-descent-option" )->GetFunction( 0 )->GetName(); std::vector gradient_option = this->m_Parser->template ConvertVector(temp); affine_opt.maximum_step_length = gradient_option[0]; affine_opt.relaxation_factor = gradient_option[1]; affine_opt.minimum_step_length = gradient_option[2]; affine_opt.translation_scales = gradient_option[3]; // std::cout << affine_opt; temp = this->m_Parser->GetOption( "use-rotation-header" )->GetFunction( 0 )->GetName(); affine_opt.use_rotation_header = (temp == "true"); std::cout << "affine_opt.use_rotation_header = " << affine_opt.use_rotation_header << std::endl; temp = this->m_Parser->GetOption( "ignore-void-origin")->GetFunction( 0 )->GetName(); affine_opt.ignore_void_orgin = (temp == "true"); std::cout << "affine_opt.ignore_void_orgin = " << affine_opt.ignore_void_orgin << std::endl; } aff = this->m_RegistrationOptimizer->AffineOptimization(affine_opt); } else { std::cout << "Use fixed initial affine para." << std::endl; if( aff_init.IsNull() ) { aff_init = TransformationModelType::AffineTransformType::New(); aff_init->SetIdentity(); } if( fixed_aff_init.IsNull() ) { fixed_aff_init = TransformationModelType::AffineTransformType::New(); fixed_aff_init->SetIdentity(); } aff = aff_init; } // std::cout << aff << std::endl; this->m_TransformationModel->SetAffineTransform(aff); this->m_TransformationModel->SetFixedImageAffineTransform(fixed_aff_init); this->m_RegistrationOptimizer->SetAffineTransform(aff); this->m_RegistrationOptimizer->SetFixedImageAffineTransform( this->m_TransformationModel->GetFixedImageAffineTransform() ); /** Second, optimize Diff */ this->m_RegistrationOptimizer->DeformableOptimization(); std::cout << " Registration Done " << std::endl; this->m_TransformationModel->SetDisplacementField(this->m_RegistrationOptimizer->GetDisplacementField() ); this->m_TransformationModel->SetInverseDisplacementField(this->m_RegistrationOptimizer->GetInverseDisplacementField() ); } template typename PICSLAdvancedNormalizationToolKit::ImagePointer PICSLAdvancedNormalizationToolKit ::PreprocessImage( ImagePointer image ) { image = this->ReplaceProblematicPixelValues( image, 0 ); return image; } template typename PICSLAdvancedNormalizationToolKit::ImagePointer PICSLAdvancedNormalizationToolKit ::ReplaceProblematicPixelValues( ImagePointer image, PixelType value) { ImageRegionIterator It( image, image->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType pixel = It.Get(); if( vnl_math_isinf( pixel ) || vnl_math_isnan( pixel ) ) { It.Set( value ); } } typedef itk::MinimumMaximumImageFilter MinMaxFilterType; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetInput( image ); minMaxFilter->Update(); double min = minMaxFilter->GetMinimum(); double shift = -1.0 * static_cast( min ); double scale = static_cast( minMaxFilter->GetMaximum() ); scale += shift; scale = 1.0 / scale; typedef itk::ShiftScaleImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetShift( shift ); filter->SetScale( scale ); filter->Update(); // when the image is a constant image, the scale filter will fail // replace again everything into $pixel ImagePointer image2 = filter->GetOutput(); ImageRegionIterator It2( image2, image2->GetRequestedRegion() ); for( It2.GoToBegin(); !It2.IsAtEnd(); ++It2 ) { PixelType pixel = It2.Get(); if( vnl_math_isinf( pixel ) || vnl_math_isnan( pixel ) ) { It2.Set( value ); } } return image2; } /** * Standard "PrintSelf" method */ template void PICSLAdvancedNormalizationToolKit ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); } template void PICSLAdvancedNormalizationToolKit ::ReadImagesAndMetrics() { /** * Read in all images and preprocess them before * storing them in their corresponding image lists. */ this->m_SimilarityMetrics.clear(); typedef ImageFileReader ReaderType; bool useHistMatch = this->m_Parser->template Convert( this->m_Parser->GetOption( "use-Histogram-Matching" )->GetFunction()->GetName() ); /** * Read the metrics and image files */ if( typename OptionType::Pointer option = this->m_Parser->GetOption( "image-metric" ) ) { std::cout << " values " << option->GetNumberOfFunctions() << std::endl; for( unsigned int i = 0; i < option->GetNumberOfFunctions(); i++ ) { SimilarityMetricPointer similarityMetric = SimilarityMetricType::New(); RealType similarityMetricScalarWeight = 1.0; similarityMetric->SetWeightScalar( similarityMetricScalarWeight ); unsigned int parameterCount = 0; typename ReaderType::Pointer fixedImageFileReader = ReaderType::New(); fixedImageFileReader->SetFileName( option->GetFunction( i )->GetParameter( parameterCount ) ); fixedImageFileReader->Update(); ImagePointer fixedImage = this->PreprocessImage( fixedImageFileReader->GetOutput() ); similarityMetric->SetFixedImage( fixedImage ); parameterCount++; std::cout << " Fixed image file: " << fixedImageFileReader->GetFileName() << std::endl; typename ReaderType::Pointer movingImageFileReader = ReaderType::New(); movingImageFileReader->SetFileName( option->GetFunction( i )->GetParameter( parameterCount ) ); movingImageFileReader->Update(); ImagePointer movingImage = this->PreprocessImage( movingImageFileReader->GetOutput() ); similarityMetric->SetMovingImage( movingImage ); typename SimilarityMetricType::RadiusType radius; radius.Fill( 0 ); parameterCount++; std::cout << " Moving image file: " << movingImageFileReader->GetFileName() << std::endl; /** * Check if similarity metric is image based or point-set based. * Image metrics: * > mean-squares/MeanSquares/MSQ * > mutual-information/MutualInformation/MI * > cross-correlation/CrossCorrelation/CC * > probabilistic/Probabilistic/PR * * Point-set metrics: * > point-set-expectation/PointSetExpectation/PSE * > jensen-tsallis-bspline/JensenTsallisBSpline/JTB * */ bool isMetricPointSetBased = false; std::string whichMetric = option->GetFunction( i )->GetName(); if( whichMetric == "point-set-expectation" || whichMetric == "PointSetExpectation" || whichMetric == "PSE" // whichMetric == "jensen-tsallis-bspline" || // whichMetric == "JensenTsallisBSpline" || // whichMetric == "JTB" ) { isMetricPointSetBased = true; } if( isMetricPointSetBased ) { std::cout << "Metric " << i << ": " << " Point-set " << whichMetric << " n-params " << option->GetFunction( i )->GetNumberOfParameters() << std::endl; /** * Read in the point-set metric parameters */ typedef LabeledPointSetFileReader PointSetReaderType; typename PointSetReaderType::Pointer fixedPointSetReader = PointSetReaderType::New(); fixedPointSetReader->SetFileName( option->GetFunction( i )->GetParameter( parameterCount ) ); parameterCount++; typename PointSetReaderType::Pointer movingPointSetReader = PointSetReaderType::New(); movingPointSetReader->SetFileName( option->GetFunction( i )->GetParameter( parameterCount ) ); parameterCount++; if( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) { similarityMetricScalarWeight = this->m_Parser->template Convert( option->GetFunction( i )->GetParameter( parameterCount ) ); parameterCount++; } similarityMetric->SetWeightScalar( similarityMetricScalarWeight ); std::cout << " similarity metric weight: " << similarityMetricScalarWeight << std::endl; TReal pointSetPercent = 0.5; TReal pointSetSigma = 5.0; bool extractBoundaryPointsOnly = false; unsigned int kNeighborhood = 50; if( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) { pointSetPercent = this->m_Parser->template Convert( option->GetFunction( i )->GetParameter( parameterCount ) ); parameterCount++; } if( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) { pointSetSigma = this->m_Parser->template Convert( option->GetFunction( i )->GetParameter( parameterCount ) ); parameterCount++; } if( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) { extractBoundaryPointsOnly = this->m_Parser->template Convert( option->GetFunction( i )->GetParameter( parameterCount ) ); parameterCount++; } if( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) { kNeighborhood = this->m_Parser->template Convert( option->GetFunction( i )->GetParameter( parameterCount ) ); parameterCount++; } std::cout << " point-set sigma = " << pointSetSigma << std::endl; std::cout << " percentage of points = " << pointSetPercent << std::endl; std::cout << " k-neighborhood = " << kNeighborhood << std::endl; if( extractBoundaryPointsOnly ) { std::cout << " use only boundary points. " << pointSetPercent << std::endl; } fixedPointSetReader->SetRandomPercentage( pointSetPercent ); fixedPointSetReader->SetExtractBoundaryPoints( extractBoundaryPointsOnly ); fixedPointSetReader->Update(); std::cout << " Fixed point-set file: " << fixedPointSetReader->GetFileName() << std::endl; std::cout << " Number of fixed labels: " << fixedPointSetReader->GetLabelSet()->size() << std::endl; std::cout << " Distinct fixed labels: "; for( unsigned int n = 0; n < fixedPointSetReader->GetLabelSet()->size(); n++ ) { std::cout << fixedPointSetReader->GetLabelSet()->operator[]( n ) << " "; } std::cout << std::endl; movingPointSetReader->SetRandomPercentage( pointSetPercent ); movingPointSetReader->SetExtractBoundaryPoints( extractBoundaryPointsOnly ); movingPointSetReader->Update(); movingPointSetReader->SetRandomPercentage( pointSetPercent ); movingPointSetReader->SetExtractBoundaryPoints( extractBoundaryPointsOnly ); movingPointSetReader->Update(); std::cout << " Moving point-set file: " << movingPointSetReader->GetFileName() << std::endl; std::cout << " Number of moving labels: " << movingPointSetReader->GetLabelSet()->size() << std::endl; std::cout << " Distinct moving labels: "; for( unsigned int n = 0; n < movingPointSetReader->GetLabelSet()->size(); n++ ) { std::cout << movingPointSetReader->GetLabelSet()->operator[]( n ) << " "; } std::cout << std::endl; if( whichMetric == "point-set-expectation" || whichMetric == "PointSetExpectation" || whichMetric == "PSE" ) { typedef itk::ExpectationBasedPointSetRegistrationFunction MetricType; typename MetricType::Pointer metric = MetricType::New(); metric->SetRadius( radius ); metric->SetFixedPointSet( fixedPointSetReader->GetOutput() ); metric->SetMovingPointSet( movingPointSetReader->GetOutput() ); metric->SetFixedPointSetSigma( pointSetSigma ); metric->SetMovingPointSetSigma( pointSetSigma ); metric->SetKNeighborhood( kNeighborhood ); if( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) { unsigned int pm = this->m_Parser->template Convert( option->GetFunction( i )->GetParameter( parameterCount ) ); metric->SetUseSymmetricMatching( pm ); std::cout << " Symmetric match iterations -- going Asymmeric for the rest " << pm << std::endl; parameterCount++; } similarityMetric->SetMetric( metric ); similarityMetric->SetMaximizeMetric( true ); similarityMetric->SetFixedPointSet( fixedPointSetReader->GetOutput() ); similarityMetric->SetMovingPointSet(movingPointSetReader->GetOutput() ); this->m_SimilarityMetrics.push_back( similarityMetric ); } // else if ( whichMetric == "jensen-tsallis-bspline" || // whichMetric == "JensenTsallisBSpline" || // whichMetric == "JTB" ) // { // typedef itk::JensenTsallisBSplineRegistrationFunction // MetricType; // typename MetricType::Pointer metric = MetricType::New(); // metric->SetRadius( radius ); // metric->SetFixedPointSet( fixedPointSetReader->GetOutput() ); // metric->SetMovingPointSet( movingPointSetReader->GetOutput() ); // metric->SetFixedPointSetSigma( pointSetSigma ); // metric->SetMovingPointSetSigma( pointSetSigma ); // metric->SetFixedEvaluationKNeighborhood( kNeighborhood ); // metric->SetMovingEvaluationKNeighborhood( kNeighborhood ); // // if ( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) // { // metric->SetAlpha( this->m_Parser->template // Convert( option->GetFunction( i )->GetParameter( parameterCount ) ) ); // parameterCount++; // } // if ( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) // { // typename RegistrationOptimizerType::ArrayType meshResolution; // std::vector resolution = this->m_Parser->template // ConvertVector( option->GetFunction( i )->GetParameter( parameterCount ) ); // if ( resolution.size() != TDimension ) // { // itkExceptionMacro( "Mesh resolution does not match image dimension." ); // } // for ( unsigned int d = 0; d < TDimension; d++ ) // { // meshResolution[d] = resolution[d]; // } // metric->SetMeshResolution( meshResolution ); // parameterCount++; // } // if ( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) // { // metric->SetSplineOrder( this->m_Parser->template // Convert( option->GetFunction( i )->GetParameter( parameterCount ) ) ); // parameterCount++; // } // if ( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) // { // metric->SetNumberOfLevels( this->m_Parser->template // Convert( option->GetFunction( i )->GetParameter( parameterCount ) ) ); // parameterCount++; // } // if ( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) // { // metric->SetUseAnisotropicCovariances( // this->m_Parser->template Convert( // option->GetFunction( i )->GetParameter( parameterCount ) ) ); // parameterCount++; // } // // // std::cout << " B-spline parameters " << std::endl; // std::cout << " mesh resolution: " << metric->GetMeshResolution() << std::endl; // std::cout << " spline order: " << metric->GetSplineOrder() << std::endl; // std::cout << " number of levels: " << metric->GetNumberOfLevels() << std::endl; // std::cout << " Alpha: " << metric->GetAlpha() << std::endl; // if ( metric->GetUseAnisotropicCovariances() ) // { // std::cout << " using anisotropic covariances." << std::endl; // } // // similarityMetric->SetMetric( metric ); // similarityMetric->SetMaximizeMetric( true ); // similarityMetric->SetFixedPointSet( fixedPointSetReader->GetOutput() ); // similarityMetric->SetMovingPointSet( movingPointSetReader->GetOutput() ); // this->m_SimilarityMetrics.push_back( similarityMetric ); // } } else // similarity metric is image-based { std::cout << "Metric " << i << ": " << " Not a Point-set" << std::endl; std::cout << " Fixed image file: " << fixedImageFileReader->GetFileName() << std::endl; std::cout << " Moving image file: " << movingImageFileReader->GetFileName() << std::endl; similarityMetric->SetFixedPointSet( ITK_NULLPTR); similarityMetric->SetMovingPointSet( ITK_NULLPTR ); if( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) { similarityMetricScalarWeight = this->m_Parser->template Convert( option->GetFunction( i )->GetParameter( parameterCount ) ); parameterCount++; } similarityMetric->SetWeightScalar( similarityMetricScalarWeight ); std::cout << " similarity metric weight: " << similarityMetricScalarWeight << std::endl; radius.Fill( 0 ); if( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) { std::vector rad = this->m_Parser->template ConvertVector( option->GetFunction( i )->GetParameter( parameterCount ) ); if( rad.size() == 1 ) { radius.Fill( rad[0] ); } else if( rad.size() == TDimension ) { for( unsigned int n = 0; n < TDimension; n++ ) { radius[n] = rad[n]; } } else { std::cout << "Badly formed radius specification" << std::endl; std::exception(); } parameterCount++; } std::cout << " Radius: " << radius << std::endl; TReal extraparam = -1.e12; if( option->GetFunction( i )->GetNumberOfParameters() > parameterCount ) { extraparam = this->m_Parser->template Convert( option->GetFunction( i )->GetParameter( parameterCount ) ); std::cout << " Setting Extra Param to : " << extraparam << " often used as a robustness parameter for longitudinal studies " << std::endl; parameterCount++; } std::cout << " radius: " << radius << std::endl; unsigned int numberOfHistogramBins = 64; if( Dimension == 2 ) { numberOfHistogramBins = 32; } if( whichMetric == "mean-squares" || whichMetric == "MeanSquares" || whichMetric == "MSQ" ) { typedef itk::HistogramMatchingImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetSourceImage(movingImage ); filter->SetReferenceImage(fixedImage); filter->SetNumberOfHistogramLevels( 256 ); filter->SetNumberOfMatchPoints( 12 ); filter->ThresholdAtMeanIntensityOn(); // filter->ThresholdAtMeanIntensityOff(); if( useHistMatch ) { filter->Update(); std::cout << " use Histogram Matching " << std::endl; movingImage = filter->GetOutput(); movingImage = this->PreprocessImage(movingImage); similarityMetric->SetMovingImage( movingImage ); } typedef SyNDemonsRegistrationFunction MetricType; typename MetricType::Pointer metric = MetricType::New(); if( radius[0] > 0 ) { metric->SetUseMovingImageGradient( true ); } metric->SetIntensityDifferenceThreshold( extraparam ); metric->SetRobustnessParameter( extraparam ); // metric->SetRobust( true ); // metric->SetSymmetric( false ); // / metric->SetNormalizeGradient( false ); metric->SetRadius( radius ); similarityMetric->SetMetric( metric ); similarityMetric->SetMaximizeMetric( true ); this->m_SimilarityMetrics.push_back( similarityMetric ); } else if( whichMetric == "SSD" ) { if( useHistMatch ) { movingImage = this->PreprocessImage(movingImage); typedef itk::HistogramMatchingImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetSourceImage(movingImage ); filter->SetReferenceImage(fixedImage); filter->SetNumberOfHistogramLevels( 256 ); filter->SetNumberOfMatchPoints( 12 ); filter->ThresholdAtMeanIntensityOn(); filter->Update(); std::cout << " use Histogram Matching " << std::endl; movingImage = filter->GetOutput(); } similarityMetric->SetMovingImage( movingImage ); typedef SyNDemonsRegistrationFunction MetricType; typename MetricType::Pointer metric = MetricType::New(); if( radius[0] > 0 ) { metric->SetUseMovingImageGradient( true ); } metric->SetIntensityDifferenceThreshold( extraparam ); metric->SetRobustnessParameter( extraparam ); metric->SetUseSSD(true); metric->SetRadius( radius ); similarityMetric->SetMetric( metric ); similarityMetric->SetMaximizeMetric( true ); this->m_SimilarityMetrics.push_back( similarityMetric ); } else if( whichMetric == "mutual-information" || whichMetric == "MutualInformation" || whichMetric == "MI" ) { typedef itk::AvantsMutualInformationRegistrationFunction MetricType; typename MetricType::Pointer metric = MetricType::New(); metric->SetNumberOfHistogramBins( numberOfHistogramBins ); metric->SetNormalizeGradient( false ); metric->SetRobustnessParameter( extraparam ); unsigned int histbins = radius[0]; if( histbins < 8 ) { histbins = 8; } metric->SetNumberOfHistogramBins(histbins); radius.Fill(0); metric->SetRadius( radius ); similarityMetric->SetMetric( metric ); similarityMetric->SetMaximizeMetric( true ); this->m_SimilarityMetrics.push_back( similarityMetric ); } else if( whichMetric == "spatial-mutual-information" || whichMetric == "SpatialMutualInformation" || whichMetric == "SMI" ) { typedef itk::SpatialMutualInformationRegistrationFunction MetricType; typename MetricType::Pointer metric = MetricType::New(); metric->SetNumberOfHistogramBins( numberOfHistogramBins ); metric->SetNormalizeGradient( false ); metric->SetRobustnessParameter( extraparam ); unsigned int histbins = radius[0]; if( histbins < 8 ) { histbins = 8; } metric->SetNumberOfHistogramBins(histbins); radius.Fill(0); metric->SetRadius( radius ); similarityMetric->SetMetric( metric ); similarityMetric->SetMaximizeMetric( true ); this->m_SimilarityMetrics.push_back( similarityMetric ); } else if( whichMetric == "cross-correlation" || whichMetric == "CrossCorrelation" || whichMetric == "CC" ) { typedef itk::HistogramMatchingImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetSourceImage(movingImage ); filter->SetReferenceImage(fixedImage); filter->SetNumberOfHistogramLevels( 256 ); filter->SetNumberOfMatchPoints( 12 ); filter->ThresholdAtMeanIntensityOn(); // filter->ThresholdAtMeanIntensityOff(); if( useHistMatch ) { std::cout << " use Histogram Matching " << std::endl; filter->Update(); movingImage = filter->GetOutput(); std::cout << " prepro " << std::endl; movingImage = this->PreprocessImage(movingImage); std::cout << " set " << std::endl; similarityMetric->SetMovingImage( movingImage ); } typedef itk::CrossCorrelationRegistrationFunction MetricType; typename MetricType::Pointer metric = MetricType::New(); metric->SetNormalizeGradient( false ); metric->SetRadius( radius ); metric->SetRobustnessParameter( extraparam ); similarityMetric->SetMetric( metric ); similarityMetric->SetMaximizeMetric( true ); this->m_SimilarityMetrics.push_back( similarityMetric ); } else if( whichMetric == "probabilistic" || whichMetric == "Probabilistic" || whichMetric == "PR" ) { typedef itk::HistogramMatchingImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetSourceImage(movingImage ); filter->SetReferenceImage(fixedImage); filter->SetNumberOfHistogramLevels( 256 ); filter->SetNumberOfMatchPoints( 12 ); filter->ThresholdAtMeanIntensityOn(); // filter->ThresholdAtMeanIntensityOff(); if( useHistMatch ) { filter->Update(); std::cout << " use Histogram Matching " << std::endl; movingImage = filter->GetOutput(); movingImage = this->PreprocessImage(movingImage); similarityMetric->SetMovingImage( movingImage ); } typedef itk::ProbabilisticRegistrationFunction MetricType; typename MetricType::Pointer metric = MetricType::New(); metric->SetNormalizeGradient( false ); metric->SetRadius( radius ); metric->SetRobustnessParameter( extraparam ); similarityMetric->SetMetric( metric ); similarityMetric->SetMaximizeMetric( true ); this->m_SimilarityMetrics.push_back( similarityMetric ); } else { itkWarningMacro( "Could not decipher image metric choice: " << whichMetric ); } } } } else { itkExceptionMacro( "No image metric specified on the command line." ); } } template void PICSLAdvancedNormalizationToolKit ::InitializeCommandLineOptions() { this->m_Parser = ParserType::New(); // this->m_Parser->SetCommandDescription( " PICSL Advanced Image Normalization Toolkit" ); if( false ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "weight-image" ); option->SetShortName( 'w' ); option->SetDescription( "blah-blah" ); this->m_Parser->AddOption( option ); } if( true ) { std::string description = std::string( "this mask -- defined in the 'fixed' image space defines " ) + std::string( "the region of interest over which the registration is " ) + std::string( "computed ==> above 0.1 means inside mask ==> continuous " ) + std::string( "values in range [0.1,1.0] effect optimization like a " ) + std::string( "probability. ==> values > 1 are treated as = 1.0 " ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask-image" ); option->SetShortName( 'x' ); // option->SetDescription( "this mask -- defined in the 'fixed' image space defines the region of interest over // which the registration is computed ==> above 0.1 means inside mask \n\t\t==> continuous values in range [0.1 , 1.0] // effect optimization like a probability. \n\t\t==> values > 1 are treated as = 1.0 " ); option->SetDescription( description ); option->SetUsageOption( 0, "maskFileName" ); this->m_Parser->AddOption( option ); } if( false ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "mask-threshold" ); option->SetShortName( 'M' ); option->SetDescription( "blah-blah" ); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "image-metric" ); option->SetShortName( 'm' ); std::string newLineTabs( "\n\t\t" ); std::string intensityBasedDescription( "Intensity-Based Metrics: " ); std::string weightDescription( "The metric weights are relative to the weights on the N other metrics passed to ANTS --- N is unlimited. So, the weight, w_i on the i^{th} metric will be w_i=w_i/ ( sum_i w_i )."); std::string intensityBasedOptions( "[fixedImage,movingImage,weight,radius/OrForMI-#histogramBins]" ); std::string ccDescription( "CC/cross-correlation/CrossCorrelation" ); std::string miDescription( "MI/mutual-information/MutualInformation" ); std::string smiDescription( "SMI/spatial-mutual-information/SpatialMutualInformation" ); std::string prDescription( "PR/probabilistic/Probabilistic" ); std::string msqDescription( "MSQ/mean-squares/MeanSquares -- demons-like, radius > 0 uses moving image gradient in metric deriv." ); std::string ssdDescription( "SSD --- standard intensity difference." ); intensityBasedDescription += ( newLineTabs + ccDescription + intensityBasedOptions + newLineTabs + miDescription + intensityBasedOptions + newLineTabs + smiDescription + intensityBasedOptions + newLineTabs + prDescription + intensityBasedOptions + newLineTabs + ssdDescription + intensityBasedOptions + newLineTabs + msqDescription + intensityBasedOptions ); std::string pointBasedDescription( "\n\t Point-Set-Based Metrics: " ); std::string pointBasedOptions = std::string( "[fixedImage,movingImage,fixedPoints,movingPoints" ) + std::string( ",weight,pointSetPercentage,pointSetSigma,boundaryPointsOnly" ) + std::string( ",kNeighborhood" ); std::string pseDescription( "PSE/point-set-expectation/PointSetExpectation" ); std::string pseOptions( ", PartialMatchingIterations=100000] \n the partial matching option assumes the complete labeling is in the first set of label parameters ... more iterations leads to more symmetry in the matching - 0 iterations means full asymmetry " ); // std::string jtbDescription( "JTB/jensen-tsallis-bspline/JensenTsallisBSpline" ); std::string jtbOptions = std::string( ",alpha,meshResolution,splineOrder,numberOfLevels" ) + std::string( ",useAnisotropicCovariances]" ); pointBasedDescription += ( newLineTabs + pseDescription + pointBasedOptions + pseOptions + newLineTabs + /*jtbDescription + */ pointBasedOptions + jtbOptions ); std::string description = weightDescription + intensityBasedDescription + pointBasedDescription; option->SetDescription( description ); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "output-naming" ); option->SetShortName( 'o' ); option->SetDescription( "The name for the output - a prefix or a name+type : e.g. -o OUT or -o OUT.nii or -o OUT.mha " ); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "roi" ); option->SetLongName( "R" ); option->SetDescription( "TODO/FIXME: the --R sets an ROI option -- it passes a vector of parameters that sets the center and bounding box \n of the region of interest for a sub-field registration. e.g. in 3D the option setting \n -r 10x12x15x50x50x25 \n sets up a bounding box of size 50,50,25 with origin at 10,12,15 in voxel (should this be physical?) coordinates. " ); std::string roidefault = std::string("0"); /** set up a default parameter */ option->AddFunction(roidefault); this->m_Parser->AddOption( option ); } if( false ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "number-of-levels" ); option->SetShortName( 'n' ); option->SetDescription( "number of levels in multi-resolution optimization -- an integer :: 3 is a common choice " ); std::string nlevdefault = std::string("3"); /** set up a default parameter */ option->AddFunction(nlevdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "number-of-iterations" ); option->SetShortName( 'i' ); option->SetDescription( "number of iterations per level -- a 'vector' e.g. : 100x100x20 " ); std::string nitdefault = std::string("10x10x5"); /** set up a default parameter */ option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "Restrict-Deformation" ); option->SetDescription( "restrict the gradient that drives the deformation by scalar factors along specified dimensions -- a TReal 'vector' of length ImageDimension to multiply against the similarity metric's gradient values --- e.g. in 3D : 0.1x1x0 --- will set the z gradient to zero and scale the x gradient by 0.1 and y by 1 (no change). Thus, you get a 2.5-Dimensional registration as there is still 3D continuity in the mapping. " ); std::string nitdefault; if( TDimension == 2 ) { nitdefault = std::string("1x1"); } if( TDimension == 3 ) { nitdefault = std::string("1x1x1"); } /** set up a default parameter */ option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( false ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "number-to-interpolate" ); option->SetShortName( 'b' ); option->SetDescription( "blah-blah" ); this->m_Parser->AddOption( option ); } if( false ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-converge-criteria" ); option->SetShortName( 'a' ); option->SetDescription( "blah-blah" ); this->m_Parser->AddOption( option ); } if( false ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "thickness-limit" ); option->SetShortName( 'T' ); option->SetDescription( "blah-blah" ); this->m_Parser->AddOption( option ); } if( false ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "prior-image-list" ); option->SetShortName( 'P' ); option->SetDescription( "blah-blah" ); this->m_Parser->AddOption( option ); } if( false ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-laplacian" ); option->SetShortName( 'i' ); option->SetDescription( "blah-blah" ); this->m_Parser->AddOption( option ); } if( false ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-histogram-matching" ); option->SetShortName( 'e' ); option->SetDescription( "blah-blah" ); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "verbose" ); option->SetShortName( 'v' ); option->SetDescription( " verbose output " ); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName("use-all-metrics-for-convergence"); option->SetDescription( " enable to use weighted sum of all metric terms for convergence computation. By default, only the first metric is used"); std::string zero = std::string( "0" ); option->AddFunction( zero ); this->m_Parser->AddOption( option ); } if( true ) { std::string description = std::string( "Print the help menu (short version)." ); OptionType::Pointer option = OptionType::New(); option->SetShortName( 'h' ); option->SetDescription( description ); std::string zero = std::string( "0" ); option->AddFunction( zero ); this->m_Parser->AddOption( option ); } if( true ) { std::string description = std::string( "Print the help menu." ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "help" ); option->SetDescription( description ); std::string zero = std::string( "0" ); option->AddFunction( zero ); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "transformation-model" ); option->SetShortName( 't' ); option->SetDescription( "TRANSFORMATION[gradient-step-length,number-of-time-steps,DeltaTime,symmetry-type].\n\t Choose one of the following TRANSFORMATIONS:\n\t\tDiff = diffeomorphic\n\t\tElast = Elastic\n\t\tExp = exponential diff\n\t\t Greedy Exp = greedy exponential diff, like diffeomorphic demons. same parameters. \n\t\tSyN -- symmetric normalization \n \n DeltaTime is the integration time-discretization step - sub-voxel - n-time steps currently fixed at 2 " ); std::string nitdefault = std::string("SyN[0.5]"); /** set up a default parameter */ option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } // if (true) // { // OptionType::Pointer option = OptionType::New(); // option->SetLongName( "def-field-sigma" ); // option->SetShortName( 's' ); // option->SetDescription( "smoothing of deformation field " ); // // this->m_Parser->AddOption( option ); // } // // if (true) // { // OptionType::Pointer option = OptionType::New(); // option->SetLongName( "gradient-field-sigma" ); // option->SetShortName( 'g' ); // option->SetDescription( "this smooths the gradient update field" ); // option->AddFunction("0.0"); // this->m_Parser->AddOption( option ); // } // // if (true) // { // OptionType::Pointer option = OptionType::New(); // option->SetLongName( "gradient-step-length" ); // option->SetShortName( 'l' ); // option->SetDescription( "gradient descent parameter - a TReal :: e.g. 1.0 " ); // option->AddFunction("1.0"); // this->m_Parser->AddOption( option ); // } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "regularization" ); option->SetShortName( 'r' ); option->SetDescription( "REGULARIZATION[gradient-field-sigma,def-field-sigma,truncation].\n\t Choose one of the following REGULARIZATIONS:\n\t\tGauss = gaussian\n\t\tDMFFD = directly manipulated free form deformation" ); std::string nitdefault = std::string("Gauss[3,0.5]"); /** set up a default parameter */ option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } // added by songgang if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "initial-affine" ); option->SetShortName( 'a' ); option->SetDescription( "use the input file as the initial affine parameter" ); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "fixed-image-initial-affine" ); option->SetShortName( 'F' ); option->SetDescription( "use the input file as the initial affine parameter for the fixed image " ); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "fixed-image-initial-affine-ref-image" ); option->SetDescription( "reference space for using the input file as the initial affine parameter for the fixed image " ); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "geodesic" ); option->SetShortName( 'T' ); option->SetDescription( " = 0 / 1 / 2, 0 = not time-dependent, 1 = asymmetric , 2 = symmetric "); // compute the Euclidean length of the diffeomorphism and write to an image -- OutputNamethick.nii.gz -- This is a Beta // version of thickness computation -- Not Full-Blown DiReCT , Das, 2009, Neuroimage --- syn with time is the only // model that can be used with this option " ); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "go-faster" ); option->SetShortName( 'G' ); option->SetDescription( " true / false -- if true, SyN is faster but loses some accuracy wrt inverse-identity constraint, see Avants MIA 2008."); std::string nitdefault = std::string("false"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } // added by songgang if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "continue-affine"); option->SetDescription( "true (default) | false, do (not) perform affine given the initial affine parameters"); std::string nitdefault = std::string("true"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } // added by songgang if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "number-of-affine-iterations" ); option->SetDescription( "number of iterations per level -- a 'vector' e.g. : 100x100x20 " ); std::string nitdefault = std::string("10000x10000x10000"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-NN" ); option->SetDescription( "use nearest neighbor interpolation " ); std::string nitdefault = std::string("0"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-Histogram-Matching" ); option->SetDescription( "use histogram matching of moving to fixed image " ); std::string nitdefault = std::string("0"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "affine-metric-type" ); option->SetDescription( "MI: mutual information (default), MSQ: mean square error, SSD, CC: Normalized correlation, CCH: Histogram-based correlation coefficient (not recommended), GD: gradient difference (not recommended) " ); std::string nitdefault = std::string("MI"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "MI-option" ); option->SetDescription( "option of mutual information: MI_bins x MI_samples (default: 32x32000)" ); switch( TDimension ) { case 2: { option->AddFunction(std::string("32x5000") ); } break; case 3: { option->AddFunction(std::string("32x32000") ); } break; } // std::string nitdefault=std::string("32x8000"); // option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "rigid-affine" ); option->SetDescription( "use rigid transformation : true / false(default)" ); std::string nitdefault = std::string("false"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "do-rigid" ); option->SetDescription( "use rigid transformation : true / false(default)" ); std::string nitdefault = std::string("false"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "affine-gradient-descent-option" ); option->SetDescription( "option of gradient descent in affine transformation: maximum_step_length x relaxation_factor x minimum_step_length x translation_scales "); std::string nitdefault = std::string("0.1x0.5x1.e-4x1.e-4"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "use-rotation-header" ); option->SetDescription( "use rotation matrix in image headers: true (default) / false" ); std::string nitdefault = std::string("false"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { OptionType::Pointer option = OptionType::New(); option->SetLongName( "ignore-void-origin" ); option->SetDescription( "ignore the apparently unmatched origins (when use-rotation-header is false and the rotation matrix is identity: true (default) / false" ); std::string nitdefault = std::string("false"); option->AddFunction(nitdefault); this->m_Parser->AddOption( option ); } if( true ) { std::string description = std::string( "At each resolution level the image is subsampled " ) + std::string( "and smoothed by Gaussian convolution. This option " ) + std::string( "allows the user to override the default smoothing " ) + std::string( "by specifying sigma values (in mm) for smoothing " ) + std::string( "both fixed and moving images for each resolution " ) + std::string( "level." ); std::string defaultFunction = std::string( "" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "gaussian-smoothing-sigmas" ); option->SetDescription( description ); option->AddFunction( defaultFunction ); this->m_Parser->AddOption( option ); } if( true ) { std::string description = std::string( "At each resolution level the image is subsampled " ) + std::string( "and smoothed by Gaussian convolution. This option " ) + std::string( "allows the user to override the default subsampling " ) + std::string( "by specifying the subsampling factor for " ) + std::string( "both fixed and moving images for each resolution " ) + std::string( "level." ); std::string defaultFunction = std::string( "" ); OptionType::Pointer option = OptionType::New(); option->SetLongName( "subsampling-factors" ); option->SetDescription( description ); option->AddFunction( defaultFunction ); this->m_Parser->AddOption( option ); } } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkProbabilisticRegistrationFunction.cxx000066400000000000000000000703411311104306400262160ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkProbabilisticRegistrationFunction_hxx_ #define _itkProbabilisticRegistrationFunction_hxx_ #include "itkProbabilisticRegistrationFunction.h" #include "itkExceptionObject.h" #include "vnl/vnl_math.h" #include "itkImageFileWriter.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkMeanImageFilter.h" #include "itkMedianImageFilter.h" #include "itkImageFileWriter.h" #include namespace itk { /* * Default constructor */ template ProbabilisticRegistrationFunction ::ProbabilisticRegistrationFunction() { m_AvgMag = 0; m_Iteration = 0; RadiusType r; unsigned int j; for( j = 0; j < ImageDimension; j++ ) { r[j] = 2; } this->SetRadius(r); this->m_Energy = 0.0; m_TimeStep = 1.0; m_DenominatorThreshold = 1e-9; m_IntensityDifferenceThreshold = 0.001; Superclass::m_MovingImage = ITK_NULLPTR; m_MetricGradientImage = ITK_NULLPTR; Superclass::m_FixedImage = ITK_NULLPTR; m_FixedImageSpacing.Fill( 1.0 ); m_FixedImageOrigin.Fill( 0.0 ); m_FixedImageGradientCalculator = GradientCalculatorType::New(); binaryimage = ITK_NULLPTR; m_FullyRobust = false; m_MovingImageGradientCalculator = GradientCalculatorType::New(); typename DefaultInterpolatorType::Pointer interp = DefaultInterpolatorType::New(); m_MovingImageInterpolator = static_cast( interp.GetPointer() ); for( int i = 0; i < 5; i++ ) { finitediffimages[i] = ITK_NULLPTR; } m_NumberOfHistogramBins = 32; m_FixedImageMask = ITK_NULLPTR; m_MovingImageMask = ITK_NULLPTR; } /* * Standard "PrintSelf" method. */ template void ProbabilisticRegistrationFunction ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); /* os << indent << "MovingImageIterpolator: "; os << m_MovingImageInterpolator.GetPointer() << std::endl; os << indent << "FixedImageGradientCalculator: "; os << m_FixedImageGradientCalculator.GetPointer() << std::endl; os << indent << "DenominatorThreshold: "; os << m_DenominatorThreshold << std::endl; os << indent << "IntensityDifferenceThreshold: "; os << m_IntensityDifferenceThreshold << std::endl; */ } /* * Set the function state values before each iteration */ template void ProbabilisticRegistrationFunction ::InitializeIteration() { if( !Superclass::m_MovingImage || !Superclass::m_FixedImage || !m_MovingImageInterpolator ) { itkExceptionMacro( << "MovingImage, FixedImage and/or Interpolator not set" ); throw ExceptionObject(__FILE__, __LINE__); } // cache fixed image information m_FixedImageSpacing = Superclass::m_FixedImage->GetSpacing(); m_FixedImageOrigin = Superclass::m_FixedImage->GetOrigin(); // setup gradient calculator m_FixedImageGradientCalculator->SetInputImage( Superclass::m_FixedImage ); m_MovingImageGradientCalculator->SetInputImage( Superclass::m_MovingImage ); // setup moving image interpolator m_MovingImageInterpolator->SetInputImage( Superclass::m_MovingImage ); m_MetricTotal = 0.0; this->m_Energy = 0.0; // compute the normalizer m_Normalizer = 0.0; for( unsigned int k = 0; k < ImageDimension; k++ ) { m_Normalizer += m_FixedImageSpacing[k] * m_FixedImageSpacing[k]; } m_Normalizer /= static_cast( ImageDimension ); bool makeimg = false; if( m_Iteration == 0 ) { makeimg = true; } else if( !finitediffimages[0] ) { makeimg = true; } else { for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( finitediffimages[0]->GetLargestPossibleRegion().GetSize()[dd] != this->GetFixedImage()->GetLargestPossibleRegion().GetSize()[dd] ) { makeimg = true; } } } if( makeimg ) { finitediffimages[0] = this->MakeImage(); finitediffimages[1] = this->MakeImage(); finitediffimages[2] = this->MakeImage(); finitediffimages[3] = this->MakeImage(); finitediffimages[4] = this->MakeImage(); } // float sig=15.; RadiusType r; for( unsigned int j = 0; j < ImageDimension; j++ ) { r[j] = this->GetRadius()[j]; } typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator tIter(this->GetFixedImage(), this->GetFixedImage()->GetLargestPossibleRegion() ); // compute local means // typedef itk::ImageRegionIteratorWithIndex Iterator; // // The following change was made to speed up the correlation calculation. // // first round { typedef std::deque SumQueueType; SumQueueType Qsuma2; SumQueueType Qsumb2; SumQueueType Qsuma; SumQueueType Qsumb; SumQueueType Qsumab; SumQueueType Qcount; ImageLinearConstIteratorWithIndex outIter( this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion() ); outIter.SetDirection( 0 ); outIter.GoToBegin(); while( !outIter.IsAtEnd() ) { // Push the zeros onto the stack that are outsized the image boundary at // the beginning of the line. Qsuma2 = SumQueueType( r[0], 0.0 ); Qsumb2 = SumQueueType( r[0], 0.0 ); Qsuma = SumQueueType( r[0], 0.0 ); Qsumb = SumQueueType( r[0], 0.0 ); Qsumab = SumQueueType( r[0], 0.0 ); Qcount = SumQueueType( r[0], 0.0 ); NeighborhoodIterator hoodIt( this->GetRadius(), this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion() ); IndexType oindex = outIter.GetIndex(); hoodIt.SetLocation( oindex ); unsigned int hoodlen = hoodIt.Size(); // Now add the rest of the values from each hyperplane for( unsigned int i = r[0]; i < ( 2 * r[0] + 1 ); i++ ) { float suma2 = 0.0; float sumb2 = 0.0; float suma = 0.0; float sumb = 0.0; float sumab = 0.0; float count = 0.0; for( unsigned int indct = i; indct < hoodlen; indct += ( 2 * r[0] + 1 ) ) { bool isInBounds = true; hoodIt.GetPixel( indct, isInBounds ); IndexType index = hoodIt.GetIndex( indct ); if( !isInBounds || ( this->m_FixedImageMask && this->m_FixedImageMask->GetPixel( index ) < 0.25 ) ) { continue; } float a = this->GetFixedImage()->GetPixel( index ); float b = this->GetMovingImage()->GetPixel( index ); suma2 += a * a; sumb2 += b * b; suma += a; sumb += b; sumab += a * b; count += 1.0; } Qsuma2.push_back( suma2 ); Qsumb2.push_back( sumb2 ); Qsuma.push_back( suma ); Qsumb.push_back( sumb ); Qsumab.push_back( sumab ); Qcount.push_back( count ); } while( !outIter.IsAtEndOfLine() ) { // Test to see if there are any voxels we need to handle in the current // window. float suma2 = 0.0; float sumb2 = 0.0; float suma = 0.0; float sumb = 0.0; float sumab = 0.0; float count = 0.0; typename SumQueueType::iterator itcount = Qcount.begin(); while( itcount != Qcount.end() ) { count += *itcount; ++itcount; } // If there are values, we need to calculate the different quantities if( count > 0 ) { typename SumQueueType::iterator ita2 = Qsuma2.begin(); typename SumQueueType::iterator itb2 = Qsumb2.begin(); typename SumQueueType::iterator ita = Qsuma.begin(); typename SumQueueType::iterator itb = Qsumb.begin(); typename SumQueueType::iterator itab = Qsumab.begin(); while( ita2 != Qsuma2.end() ) { suma2 += *ita2; sumb2 += *itb2; suma += *ita; sumb += *itb; sumab += *itab; ++ita2; ++itb2; ++ita; ++itb; ++itab; } float fixedMean = suma / count; float movingMean = sumb / count; float sff = suma2 - fixedMean * suma - fixedMean * suma + count * fixedMean * fixedMean; float smm = sumb2 - movingMean * sumb - movingMean * sumb + count * movingMean * movingMean; float sfm = sumab - movingMean * suma - fixedMean * sumb + count * movingMean * fixedMean; IndexType _oindex = outIter.GetIndex(); float val = this->GetFixedImage()->GetPixel( _oindex ) - fixedMean; this->finitediffimages[0]->SetPixel( _oindex, val ); val = this->GetMovingImage()->GetPixel( _oindex ) - movingMean; this->finitediffimages[1]->SetPixel( _oindex, val ); this->finitediffimages[2]->SetPixel( _oindex, sfm ); // A this->finitediffimages[3]->SetPixel( _oindex, sff ); // B this->finitediffimages[4]->SetPixel( _oindex, smm ); // C } // Increment the iterator and check to see if we're at the end of the // line. If so, go to the next line. Otherwise, add the // the values for the next hyperplane. ++outIter; if( !outIter.IsAtEndOfLine() ) { hoodIt.SetLocation( outIter.GetIndex() ); suma2 = 0.0; sumb2 = 0.0; suma = 0.0; sumb = 0.0; sumab = 0.0; count = 0.0; for( unsigned int indct = 2 * r[0]; indct < hoodlen; indct += ( 2 * r[0] + 1 ) ) { bool isInBounds = true; hoodIt.GetPixel( indct, isInBounds ); IndexType index = hoodIt.GetIndex( indct ); if( !isInBounds || ( this->m_FixedImageMask && this->m_FixedImageMask->GetPixel( index ) < 0.25 ) ) { continue; } float a = this->GetFixedImage()->GetPixel( index ); float b = this->GetMovingImage()->GetPixel( index ); suma2 += a * a; sumb2 += b * b; suma += a; sumb += b; sumab += a * b; count += 1.0; } Qsuma2.push_back( suma2 ); Qsumb2.push_back( sumb2 ); Qsuma.push_back( suma ); Qsumb.push_back( sumb ); Qsumab.push_back( sumab ); Qcount.push_back( count ); } Qsuma2.pop_front(); Qsumb2.pop_front(); Qsuma.pop_front(); Qsumb.pop_front(); Qsumab.pop_front(); Qcount.pop_front(); } outIter.NextLine(); } } // second round { typedef std::deque SumQueueType; SumQueueType Qsuma2; SumQueueType Qsumb2; SumQueueType Qsuma; SumQueueType Qsumb; SumQueueType Qsumab; SumQueueType Qcount; ImageLinearConstIteratorWithIndex outIter( this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion() ); outIter.SetDirection( 0 ); outIter.GoToBegin(); while( !outIter.IsAtEnd() ) { // Push the zeros onto the stack that are outsized the image boundary at // the beginning of the line. Qsuma2 = SumQueueType( r[0], 0.0 ); Qsumb2 = SumQueueType( r[0], 0.0 ); Qsuma = SumQueueType( r[0], 0.0 ); Qsumb = SumQueueType( r[0], 0.0 ); Qsumab = SumQueueType( r[0], 0.0 ); Qcount = SumQueueType( r[0], 0.0 ); NeighborhoodIterator hoodIt( this->GetRadius(), this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion() ); IndexType oindex = outIter.GetIndex(); hoodIt.SetLocation( oindex ); unsigned int hoodlen = hoodIt.Size(); // Now add the rest of the values from each hyperplane for( unsigned int i = r[0]; i < ( 2 * r[0] + 1 ); i++ ) { float suma2 = 0.0; float sumb2 = 0.0; float suma = 0.0; float sumb = 0.0; float sumab = 0.0; float count = 0.0; for( unsigned int indct = i; indct < hoodlen; indct += ( 2 * r[0] + 1 ) ) { bool isInBounds = true; hoodIt.GetPixel( indct, isInBounds ); IndexType index = hoodIt.GetIndex( indct ); if( !isInBounds || ( this->m_FixedImageMask && this->m_FixedImageMask->GetPixel( index ) < 0.25 ) ) { continue; } float a = this->finitediffimages[0]->GetPixel( index ); float b = this->finitediffimages[1]->GetPixel( index ); suma2 += a * a; sumb2 += b * b; suma += a; sumb += b; sumab += a * b; count += 1.0; } Qsuma2.push_back( suma2 ); Qsumb2.push_back( sumb2 ); Qsuma.push_back( suma ); Qsumb.push_back( sumb ); Qsumab.push_back( sumab ); Qcount.push_back( count ); } while( !outIter.IsAtEndOfLine() ) { // Test to see if there are any voxels we need to handle in the current // window. float suma2 = 0.0; float sumb2 = 0.0; float suma = 0.0; float sumb = 0.0; float sumab = 0.0; float count = 0.0; typename SumQueueType::iterator itcount = Qcount.begin(); while( itcount != Qcount.end() ) { count += *itcount; ++itcount; } // If there are values, we need to calculate the different quantities if( count > 0 ) { typename SumQueueType::iterator ita2 = Qsuma2.begin(); typename SumQueueType::iterator itb2 = Qsumb2.begin(); typename SumQueueType::iterator ita = Qsuma.begin(); typename SumQueueType::iterator itb = Qsumb.begin(); typename SumQueueType::iterator itab = Qsumab.begin(); while( ita2 != Qsuma2.end() ) { suma2 += *ita2; sumb2 += *itb2; suma += *ita; sumb += *itb; sumab += *itab; ++ita2; ++itb2; ++ita; ++itb; ++itab; } float fixedMean = suma / count; float movingMean = sumb / count; float sff = suma2 - fixedMean * suma - fixedMean * suma + count * fixedMean * fixedMean; float smm = sumb2 - movingMean * sumb - movingMean * sumb + count * movingMean * movingMean; float sfm = sumab - movingMean * suma - fixedMean * sumb + count * movingMean * fixedMean; IndexType _oindex = outIter.GetIndex(); // float val = this->GetFixedImage()->GetPixel( oindex ) - fixedMean; // this->finitediffimages[0]->SetPixel( oindex, val ); // val = this->GetMovingImage()->GetPixel( oindex ) - movingMean; // this->finitediffimages[1]->SetPixel( oindex, val ); this->finitediffimages[2]->SetPixel( _oindex, sfm ); // A this->finitediffimages[3]->SetPixel( _oindex, sff ); // B this->finitediffimages[4]->SetPixel( _oindex, smm ); // C } // Increment the iterator and check to see if we're at the end of the // line. If so, go to the next line. Otherwise, add the // the values for the next hyperplane. ++outIter; if( !outIter.IsAtEndOfLine() ) { hoodIt.SetLocation( outIter.GetIndex() ); suma2 = 0.0; sumb2 = 0.0; suma = 0.0; sumb = 0.0; sumab = 0.0; count = 0.0; for( unsigned int indct = 2 * r[0]; indct < hoodlen; indct += ( 2 * r[0] + 1 ) ) { bool isInBounds = true; hoodIt.GetPixel( indct, isInBounds ); IndexType index = hoodIt.GetIndex( indct ); if( !isInBounds || ( this->m_FixedImageMask && this->m_FixedImageMask->GetPixel( index ) < 0.25 ) ) { continue; } float a = this->finitediffimages[0]->GetPixel( index ); float b = this->finitediffimages[1]->GetPixel( index ); suma2 += a * a; sumb2 += b * b; suma += a; sumb += b; sumab += a * b; count += 1.0; } Qsuma2.push_back( suma2 ); Qsumb2.push_back( sumb2 ); Qsuma.push_back( suma ); Qsumb.push_back( sumb ); Qsumab.push_back( sumab ); Qcount.push_back( count ); } Qsuma2.pop_front(); Qsumb2.pop_front(); Qsuma.pop_front(); Qsumb.pop_front(); Qsumab.pop_front(); Qcount.pop_front(); } outIter.NextLine(); } } m_MaxMag = 0.0; m_MinMag = 9.e9; m_AvgMag = 0.0; m_Iteration++; } /* * Set the function state values before each iteration */ template void ProbabilisticRegistrationFunction ::InitializeIterationOld() { if( !Superclass::m_MovingImage || !Superclass::m_FixedImage || !m_MovingImageInterpolator ) { itkExceptionMacro( << "MovingImage, FixedImage and/or Interpolator not set" ); throw ExceptionObject(__FILE__, __LINE__); } // cache fixed image information m_FixedImageSpacing = Superclass::m_FixedImage->GetSpacing(); m_FixedImageOrigin = Superclass::m_FixedImage->GetOrigin(); // setup gradient calculator m_FixedImageGradientCalculator->SetInputImage( Superclass::m_FixedImage ); m_MovingImageGradientCalculator->SetInputImage( Superclass::m_MovingImage ); // setup moving image interpolator m_MovingImageInterpolator->SetInputImage( Superclass::m_MovingImage ); m_MetricTotal = 0.0; this->m_Energy = 0.0; // compute the normalizer m_Normalizer = 0.0; for( unsigned int k = 0; k < ImageDimension; k++ ) { m_Normalizer += m_FixedImageSpacing[k] * m_FixedImageSpacing[k]; } m_Normalizer /= static_cast( ImageDimension ); typename FixedImageType::SpacingType spacing = this->GetFixedImage()->GetSpacing(); bool makeimg = false; if( m_Iteration == 0 ) { makeimg = true; } else if( !finitediffimages[0] ) { makeimg = true; } else { for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( finitediffimages[0]->GetLargestPossibleRegion().GetSize()[dd] != this->GetFixedImage()->GetLargestPossibleRegion().GetSize()[dd] ) { makeimg = true; } } } if( makeimg ) { finitediffimages[0] = this->MakeImage(); finitediffimages[1] = this->MakeImage(); finitediffimages[2] = this->MakeImage(); finitediffimages[3] = this->MakeImage(); finitediffimages[4] = this->MakeImage(); } // float sig=15.; RadiusType r; for( int j = 0; j < ImageDimension; j++ ) { r[j] = this->GetRadius()[j]; } typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator tIter(this->GetFixedImage(), this->GetFixedImage()->GetLargestPossibleRegion() ); typename FixedImageType::SizeType imagesize = this->GetFixedImage()->GetLargestPossibleRegion().GetSize(); // compute local means // typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator outIter(this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion() ); for( outIter.GoToBegin(); !outIter.IsAtEnd(); ++outIter ) { bool takesample = true; if( this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( outIter.GetIndex() ) < 0.25 ) { takesample = false; } } if( takesample ) { NeighborhoodIterator hoodIt( this->GetRadius(), this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion() ); IndexType oindex = outIter.GetIndex(); hoodIt.SetLocation(oindex); double fixedMean = 0; double movingMean = 0; PointType mappedPoint; unsigned int indct; unsigned int hoodlen = hoodIt.Size(); // unsigned int inct=0; double sumj = 0, sumi = 0; unsigned int cter = 0; for( indct = 0; indct < hoodlen; indct++ ) { IndexType index = hoodIt.GetIndex(indct); bool inimage = true; for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( index[dd] < 0 || index[dd] > static_cast(imagesize[dd] - 1) ) { inimage = false; } } if( inimage && this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( index ) < 0.25 ) { inimage = false; } } if( inimage ) { sumj += this->GetMovingImage()->GetPixel(index); sumi += this->GetFixedImage()->GetPixel(index); cter++; } } if( cter > 0 ) { movingMean = sumj / (float)cter; } if( cter > 0 ) { fixedMean = sumi / (float)cter; } float val = this->GetFixedImage()->GetPixel(oindex) - fixedMean; this->finitediffimages[0]->SetPixel( oindex, val ); val = this->GetMovingImage()->GetPixel(oindex) - movingMean; this->finitediffimages[1]->SetPixel( oindex, val ); } } for( outIter.GoToBegin(); !outIter.IsAtEnd(); ++outIter ) { IndexType oindex = outIter.GetIndex(); bool takesample = true; if( this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( oindex ) < 0.25 ) { takesample = false; } } if( takesample ) { NeighborhoodIterator hoodIt( this->GetRadius(), this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion() ); hoodIt.SetLocation(oindex); double sff = 0.0; double smm = 0.0; double sfm = 0.0; PointType mappedPoint; unsigned int indct; unsigned int hoodlen = hoodIt.Size(); // unsigned int inct=0; for( indct = 0; indct < hoodlen; indct++ ) { IndexType index = hoodIt.GetIndex(indct); bool inimage = true; for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( index[dd] < 0 || index[dd] > static_cast(imagesize[dd] - 1) ) { inimage = false; } } if( inimage && this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( index ) < 0.25 ) { inimage = false; } } if( inimage ) { double fixedValue = (double)this->finitediffimages[0]->GetPixel( index ); double movingValue = (double)this->finitediffimages[1]->GetPixel( index ); // double ofixedValue =(double)this->GetFixedImage()->GetPixel( index ); // double omovingValue=(double)this->GetMovingImage()->GetPixel( index ); sff += fixedValue * fixedValue; smm += movingValue * movingValue; sfm += fixedValue * movingValue; } } this->finitediffimages[2]->SetPixel( oindex, sfm ); // A this->finitediffimages[3]->SetPixel( oindex, sff ); // B this->finitediffimages[4]->SetPixel( oindex, smm ); // C // this->finitediffimages[5]->SetPixel( oindex , sumi);//B*C // this->finitediffimages[6]->SetPixel( oindex , sumj);//B*C } } // m_FixedImageGradientCalculator->SetInputImage(finitediffimages[0]); m_MaxMag = 0.0; m_MinMag = 9.e9; m_AvgMag = 0.0; m_Iteration++; } /* * Compute the ncc metric everywhere */ template typename TDisplacementField::PixelType ProbabilisticRegistrationFunction ::ComputeMetricAtPairB(IndexType oindex, typename TDisplacementField::PixelType /* vec */) { typename TDisplacementField::PixelType deriv; deriv.Fill(0.0); double sff = 0.0; double smm = 0.0; double sfm = 0.0; // double fixedValue; // double movingValue; PointType mappedPoint; CovariantVectorType gradI, gradJ; if( this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( oindex ) < 0.25 ) { return deriv; } } sfm = finitediffimages[2]->GetPixel(oindex); sff = finitediffimages[3]->GetPixel(oindex); smm = finitediffimages[4]->GetPixel(oindex); if( sff == 0.0 || smm == 0.0 ) { return deriv; } IndexType index = oindex; // hoodIt.GetIndex(indct); // bool inimage=true; if( sff == 0.0 ) { sff = 1.0; } if( smm == 0.0 ) { smm = 1.0; } gradI = m_FixedImageGradientCalculator->EvaluateAtIndex( index ); // gradJ = m_MovingImageGradientCalculator->EvaluateAtIndex( index ); float Ji = finitediffimages[1]->GetPixel(index); float Ii = finitediffimages[0]->GetPixel(index); m_TEMP = 2.0 * sfm / (sff * smm) * ( Ji - sfm / sff * Ii ); for( unsigned int qq = 0; qq < ImageDimension; qq++ ) { deriv[qq] -= 2.0 * sfm / (sff * smm) * ( Ji - sfm / sff * Ii ) * gradI[qq]; // derivinv[qq]-=2.0*sfm/(sff*smm)*( Ii - sfm/smm*Ji )*gradJ[qq]; } if( sff * smm != 0.0 ) { localProbabilistic = sfm * sfm / ( sff * smm ); } else { localProbabilistic = 1.0; } if( localProbabilistic * (-1.0) < this->m_RobustnessParameter ) { deriv.Fill(0); } // if ( localProbabilistic*(-1.0) < this->m_RobustnessParameter) { // std::cout << " localC " << localProbabilistic << std::endl; } this->m_Energy -= localProbabilistic; return deriv; // localProbabilistic; } /* * Compute the ncc metric everywhere */ template typename TDisplacementField::PixelType ProbabilisticRegistrationFunction ::ComputeMetricAtPairC(IndexType oindex, typename TDisplacementField::PixelType /* vec */) { typename TDisplacementField::PixelType deriv; deriv.Fill(0.0); double sff = 0.0; double smm = 0.0; double sfm = 0.0; // double fixedValue; // double movingValue; PointType mappedPoint; CovariantVectorType gradI, gradJ; if( this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( oindex ) < 0.25 ) { return deriv; } } sfm = finitediffimages[2]->GetPixel(oindex); sff = finitediffimages[3]->GetPixel(oindex); smm = finitediffimages[4]->GetPixel(oindex); if( sff == 0.0 || smm == 0.0 ) { return deriv; } IndexType index = oindex; // hoodIt.GetIndex(indct); if( sff == 0.0 ) { sff = 1.0; } if( smm == 0.0 ) { smm = 1.0; } // /gradI = m_FixedImageGradientCalculator->EvaluateAtIndex( index ); gradJ = m_MovingImageGradientCalculator->EvaluateAtIndex( index ); float Ji = finitediffimages[1]->GetPixel(index); float Ii = finitediffimages[0]->GetPixel(index); for( unsigned int qq = 0; qq < ImageDimension; qq++ ) { // deriv[qq] -=2.0*sfm/(sff*smm)*( Ji - sfm/sff*Ii )*gradI[qq]; deriv[qq] -= 2.0 * sfm / (sff * smm) * ( Ii - sfm / smm * Ji ) * gradJ[qq]; } if( sff * smm != 0.0 ) { localProbabilistic = sfm * sfm / ( sff * smm ); } else { localProbabilistic = 1.0; } if( localProbabilistic * (-1.0) < this->m_RobustnessParameter ) { deriv.Fill(0); } return deriv; // localProbabilistic; } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkProbabilisticRegistrationFunction.h000066400000000000000000000345571311104306400256540ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkProbabilisticRegistrationFunction_h_ #define _itkProbabilisticRegistrationFunction_h_ #include "antsAllocImage.h" #include "itkAvantsPDEDeformableRegistrationFunction.h" #include "itkPoint.h" #include "itkCovariantVector.h" #include "itkInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkCentralDifferenceImageFunction.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkAvantsMutualInformationRegistrationFunction.h" namespace itk { /** * \class ProbabilisticRegistrationFunction * * This class encapsulate the PDE which drives the demons registration * algorithm. It is used by ProbabilisticRegistrationFilter to compute the * output deformation field which will map a moving image onto a * a fixed image. * * Non-integer moving image values are obtained by using * interpolation. The default interpolator is of type * LinearInterpolateImageFunction. The user may set other * interpolators via method SetMovingImageInterpolator. Note that the input * interpolator must derive from baseclass InterpolateImageFunction. * * This class is templated over the fixed image type, moving image type, * and the deformation field type. * * \warning This filter assumes that the fixed image type, moving image type * and deformation field type all have the same number of dimensions. * * \sa ProbabilisticRegistrationFilter * \ingroup FiniteDifferenceFunctions */ template class ProbabilisticRegistrationFunction : public AvantsPDEDeformableRegistrationFunction { public: /** Standard class typedefs. */ typedef ProbabilisticRegistrationFunction Self; typedef AvantsPDEDeformableRegistrationFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro( ProbabilisticRegistrationFunction, AvantsPDEDeformableRegistrationFunction ); /** MovingImage image type. */ typedef typename Superclass::MovingImageType MovingImageType; typedef typename Superclass::MovingImagePointer MovingImagePointer; /** FixedImage image type. */ typedef typename Superclass::MetricImageType MetricImageType; typedef typename Superclass::MetricImageType::Pointer MetricImagePointer; typedef typename Superclass::FixedImageType FixedImageType; typedef typename Superclass::FixedImagePointer FixedImagePointer; typedef typename FixedImageType::IndexType IndexType; typedef typename FixedImageType::SizeType SizeType; /** Deformation field type. */ typedef typename Superclass::DisplacementFieldType DisplacementFieldType; typedef typename Superclass::DisplacementFieldTypePointer DisplacementFieldTypePointer; typedef typename TDisplacementField::PixelType VectorType; typedef CovariantVector GradientPixelType; typedef Image GradientImageType; typedef SmartPointer GradientImagePointer; typedef GradientRecursiveGaussianImageFilter GradientImageFilterType; typedef typename GradientImageFilterType::Pointer GradientImageFilterPointer; typedef Image BinaryImageType; typedef typename BinaryImageType::Pointer BinaryImagePointer; /** Inherit some enums from the superclass. */ itkStaticConstMacro(ImageDimension, unsigned int, Superclass::ImageDimension); /** Inherit some enums from the superclass. */ typedef typename Superclass::PixelType PixelType; typedef typename Superclass::RadiusType RadiusType; typedef typename Superclass::NeighborhoodType NeighborhoodType; // typedef typename Superclass::NeighborhoodType BoundaryNeighborhoodType; typedef typename Superclass::FloatOffsetType FloatOffsetType; typedef typename Superclass::TimeStepType TimeStepType; /** Interpolator type. */ typedef double CoordRepType; typedef InterpolateImageFunction InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; typedef typename InterpolatorType::PointType PointType; typedef LinearInterpolateImageFunction DefaultInterpolatorType; /** Covariant vector type. */ typedef CovariantVector CovariantVectorType; /** Gradient calculator type. */ typedef CentralDifferenceImageFunction GradientCalculatorType; typedef typename GradientCalculatorType::Pointer GradientCalculatorPointer; /** Set the moving image interpolator. */ void SetMovingImageInterpolator( InterpolatorType * ptr ) { m_MovingImageInterpolator = ptr; } /** Get the moving image interpolator. */ InterpolatorType * GetMovingImageInterpolator(void) { return m_MovingImageInterpolator; } typename TDisplacementField::PixelType ComputeMetricAtPairB(IndexType fixedindex, typename TDisplacementField::PixelType vec ); typename TDisplacementField::PixelType ComputeMetricAtPairC(IndexType fixedindex, typename TDisplacementField::PixelType vec ); /** This class uses a constant timestep of 1. */ virtual TimeStepType ComputeGlobalTimeStep(void * /* GlobalData */) const ITK_OVERRIDE { return m_TimeStep; } /** Return a pointer to a global data structure that is passed to * this object from the solver at each calculation. */ virtual void * GetGlobalDataPointer() const ITK_OVERRIDE { GlobalDataStruct *global = new GlobalDataStruct(); return global; } /** Release memory for global data structure. */ virtual void ReleaseGlobalDataPointer( void *GlobalData ) const ITK_OVERRIDE { //HACK: The signature of this function should be reconsidered // a delete of a re-interpret cast is a dangerous // proposition. delete reinterpret_cast( GlobalData ); } /** Set the object's state before each iteration. */ virtual void InitializeIteration() ITK_OVERRIDE; /** Set the object's state before each iteration. */ void InitializeIterationOld(); double ComputeCrossCorrelation() { if( finitediffimages[0] ) { double totalcc = 0; unsigned long ct = 0; typedef ImageRegionIteratorWithIndex ittype; ittype it(this->finitediffimages[0], this->finitediffimages[0]->GetLargestPossibleRegion().GetSize() ); for( it.GoToBegin(); !it.IsAtEnd(); ++it ) { IndexType oindex = it.GetIndex(); double sfm = finitediffimages[2]->GetPixel(oindex); double sff = finitediffimages[3]->GetPixel(oindex); double smm = finitediffimages[4]->GetPixel(oindex); double cc = 0; if( fabs(sff * smm) > 0 ) { cc += sfm * sfm / (sff * smm); ct++; } totalcc += cc; } this->m_Energy = totalcc / (float)ct * (-1.0); return this->m_Energy; } else { return 0; } } virtual VectorType OpticalFlowUpdate(const NeighborhoodType & neighborhood) { // Get fixed image related information IndexType index = neighborhood.GetIndex(); typename TDisplacementField::PixelType vec = Superclass::m_DisplacementField->GetPixel(index); VectorType update; update.Fill(0.0); double fixedValue; CovariantVectorType fixedGradient; double fixedGradientSquaredMagnitude = 0; fixedValue = (double) Superclass::Superclass::m_FixedImage->GetPixel( index ); fixedGradient = m_FixedImageGradientCalculator->EvaluateAtIndex( index ); unsigned int j = 0; for( j = 0; j < ImageDimension; j++ ) { fixedGradientSquaredMagnitude += vnl_math_sqr( fixedGradient[j] ); } double movingValue; PointType mappedPoint; for( j = 0; j < ImageDimension; j++ ) { mappedPoint[j] = double( index[j] ) * m_FixedImageSpacing[j] + m_FixedImageOrigin[j]; mappedPoint[j] += vec[j]; } if( m_MovingImageInterpolator->IsInsideBuffer( mappedPoint ) ) { movingValue = m_MovingImageInterpolator->Evaluate( mappedPoint ); } else { for( j = 0; j < ImageDimension; j++ ) { update[j] = 0.0; } return update; } double speedValue = fixedValue - movingValue; if( fabs(speedValue) < this->m_RobustnessParameter ) { speedValue = 0; } double denominator = vnl_math_sqr( speedValue ) / m_Normalizer + fixedGradientSquaredMagnitude; double DenominatorThreshold = 1e-9; double IntensityDifferenceThreshold = 0.001; if( vnl_math_abs(speedValue) < IntensityDifferenceThreshold || denominator < DenominatorThreshold ) { for( j = 0; j < ImageDimension; j++ ) { update[j] = 0.0; } return update; } for( j = 0; j < ImageDimension; j++ ) { update[j] = speedValue * fixedGradient[j] / denominator; } return update; } void SetFixedImageMask( MetricImageType* img) { m_FixedImageMask = img; } virtual VectorType ComputeUpdate(const NeighborhoodType & neighborhood, void * /* globalData */, const FloatOffsetType & /* offset */ = FloatOffsetType(0.0) ) ITK_OVERRIDE { VectorType update; update.Fill(0.0); IndexType oindex = neighborhood.GetIndex(); FixedImageType* img = const_cast(Superclass::m_FixedImage.GetPointer() ); if( !img ) { return update; } update = this->ComputeMetricAtPairB(oindex, update); return update; } virtual VectorType ComputeUpdateInv(const NeighborhoodType & neighborhood, void * /* globalData */, const FloatOffsetType & /* offset */ = FloatOffsetType(0.0) ) ITK_OVERRIDE { VectorType update; update.Fill(0.0); IndexType oindex = neighborhood.GetIndex(); FixedImageType* img = const_cast(Superclass::m_FixedImage.GetPointer() ); if( !img ) { return update; } update = this->ComputeMetricAtPairC(oindex, update); return update; } void SetFullyRobust(bool b) { m_FullyRobust = b; } void GetProbabilities(); double localProbabilistic; float m_TEMP; protected: ProbabilisticRegistrationFunction(); ~ProbabilisticRegistrationFunction() { } void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; /** FixedImage image neighborhood iterator type. */ typedef ConstNeighborhoodIterator FixedImageNeighborhoodIteratorType; /** A global data type for this class of equation. Used to store * iterators for the fixed image. */ struct GlobalDataStruct { FixedImageNeighborhoodIteratorType m_FixedImageIterator; }; MetricImagePointer MakeImage() { FixedImageType* img = const_cast(Superclass::m_FixedImage.GetPointer() ); this->m_MetricImage = AllocImage(img->GetLargestPossibleRegion(), 0); this->m_MetricImage->SetSpacing(img->GetSpacing() ); this->m_MetricImage->SetOrigin(img->GetOrigin() ); bool makebinimg = false; if( makebinimg ) { m_Iteration = 0; binaryimage = AllocImage(img->GetLargestPossibleRegion(), 1); binaryimage->SetSpacing(img->GetSpacing() ); binaryimage->SetOrigin(img->GetOrigin() ); } return this->m_MetricImage; } private: ProbabilisticRegistrationFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** Cache fixed image information. */ typename TFixedImage::SpacingType m_FixedImageSpacing; typename TFixedImage::PointType m_FixedImageOrigin; /** Function to compute derivatives of the fixed image. */ GradientCalculatorPointer m_FixedImageGradientCalculator; GradientCalculatorPointer m_MovingImageGradientCalculator; /** Function to interpolate the moving image. */ InterpolatorPointer m_MovingImageInterpolator; /** The global timestep. */ TimeStepType m_TimeStep; /** Threshold below which the denominator term is considered zero. */ double m_DenominatorThreshold; /** Threshold below which two intensity value are assumed to match. */ double m_IntensityDifferenceThreshold; mutable double m_MetricTotal; mutable float m_MinMag; mutable float m_MaxMag; mutable float m_AvgMag; mutable float m_Thresh; GradientImagePointer m_MetricGradientImage; MetricImagePointer finitediffimages[5]; BinaryImagePointer binaryimage; MetricImagePointer m_FixedImageMask; MetricImagePointer m_MovingImageMask; typedef itk::AvantsMutualInformationRegistrationFunction MetricType2; typename MetricType2::Pointer m_MIFunct; unsigned int m_NumberOfHistogramBins; bool m_FullyRobust; unsigned int m_Iteration; float m_Normalizer; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkProbabilisticRegistrationFunction.cxx" #endif #endif ants-2.2.0/ImageRegistration/itkSpatialMutualInformationRegistrationFunction.cxx000066400000000000000000001022441311104306400304210ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 _itkSpatialMutualInformationRegistrationFunction_hxx #define _itkSpatialMutualInformationRegistrationFunction_hxx #include "antsAllocImage.h" #include "itkSpatialMutualInformationRegistrationFunction.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkCovariantVector.h" #include "itkImageRandomConstIteratorWithIndex.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIterator.h" #include "itkImageIterator.h" #include "vnl/vnl_math.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkImageRegionConstIteratorWithIndex.h" namespace itk { /** * Constructor */ template SpatialMutualInformationRegistrationFunction ::SpatialMutualInformationRegistrationFunction() { this->Superclass::m_NormalizeGradient = true; this->m_NumberOfSpatialSamples = 5000; this->m_NumberOfHistogramBins = 50; // this->SetComputeGradient(false); // don't use the default gradient for now this->m_InterpolatorIsBSpline = false; // Initialize PDFs to NULL m_JointPDF = ITK_NULLPTR; m_OpticalFlow = false; typename TransformType::Pointer transformer = TransformType::New(); this->SetTransform(transformer); typename BSplineInterpolatorType::Pointer interpolator = BSplineInterpolatorType::New(); this->SetInterpolator(interpolator); m_FixedImageMask = ITK_NULLPTR; m_MovingImageMask = ITK_NULLPTR; // Initialize memory m_MovingImageNormalizedMin = 0.0; m_FixedImageNormalizedMin = 0.0; m_MovingImageTrueMin = 0.0; m_MovingImageTrueMax = 0.0; m_FixedImageBinSize = 0.0; m_MovingImageBinSize = 0.0; m_BSplineInterpolator = ITK_NULLPTR; m_NumberOfParameters = ImageDimension; m_FixedImageGradientCalculator = GradientCalculatorType::New(); m_MovingImageGradientCalculator = GradientCalculatorType::New(); this->m_Padding = 0; typename DefaultInterpolatorType::Pointer interp = DefaultInterpolatorType::New(); typename DefaultInterpolatorType::Pointer interp2 = DefaultInterpolatorType::New(); m_MovingImageInterpolator = static_cast( interp.GetPointer() ); m_FixedImageInterpolator = static_cast( interp2.GetPointer() ); m_Interpolator = static_cast( interp.GetPointer() ); this->m_RobustnessParameter = -1.e19; } /** * Print out internal information about this class */ template void SpatialMutualInformationRegistrationFunction ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "NumberOfSpatialSamples: "; os << m_NumberOfSpatialSamples << std::endl; os << indent << "NumberOfHistogramBins: "; os << m_NumberOfHistogramBins << std::endl; // Debugging information os << indent << "NumberOfParameters: "; os << m_NumberOfParameters << std::endl; os << indent << "FixedImageNormalizedMin: "; os << m_FixedImageNormalizedMin << std::endl; os << indent << "MovingImageNormalizedMin: "; os << m_MovingImageNormalizedMin << std::endl; os << indent << "MovingImageTrueMin: "; os << m_MovingImageTrueMin << std::endl; os << indent << "MovingImageTrueMax: "; os << m_MovingImageTrueMax << std::endl; os << indent << "FixedImageBinSize: "; os << m_FixedImageBinSize << std::endl; os << indent << "MovingImageBinSize: "; os << m_MovingImageBinSize << std::endl; os << indent << "InterpolatorIsBSpline: "; os << m_InterpolatorIsBSpline << std::endl; } /** * Initialize */ template void SpatialMutualInformationRegistrationFunction ::InitializeIteration() { m_CubicBSplineKernel = CubicBSplineFunctionType::New(); this->m_Energy = 0; this->pdfinterpolator = pdfintType::New(); this->pdfinterpolatorXuY = pdfintType::New(); this->pdfinterpolatorXYu = pdfintType::New(); this->pdfinterpolatorXlY = pdfintType::New(); this->pdfinterpolatorXYl = pdfintType::New(); this->pdfinterpolatorXuYl = pdfintType::New(); this->pdfinterpolatorXlYu = pdfintType::New(); this->pdfinterpolatorXuYr = pdfintType::New(); this->pdfinterpolatorXrYu = pdfintType::New(); pdfinterpolator2 = pdfintType2::New(); pdfinterpolator3 = pdfintType2::New(); // this->ComputeMetricImage(); // std::cout << " A " << std::endl; // bool makenewimage=false; // std::cout << " B " << std::endl; /* if (!this->m_MetricImage )makenewimage=true; else if (imagesize[0] != this->m_MetricImage->GetLargestPossibleRegion().GetSize()[0]) makenewimage = true; else this->m_MetricImage->FillBuffer(0); if (makenewimage) { this->m_MetricImage = TFixedImage::New(); this->m_MetricImage->SetLargestPossibleRegion(img->GetLargestPossibleRegion() ); this->m_MetricImage->SetBufferedRegion(img->GetLargestPossibleRegion()); this->m_MetricImage->SetSpacing(img->GetSpacing()); this->m_MetricImage->SetOrigin(img->GetOrigin()); this->m_MetricImage->Allocate(); ittype it(this->m_MetricImage,this->m_MetricImage->GetLargestPossibleRegion().GetSize()); for( it.GoToBegin(); !it.IsAtEnd(); ++it ) it.Set(0); } */ m_FixedImageGradientCalculator->SetInputImage( this->m_FixedImage ); m_MovingImageGradientCalculator->SetInputImage( this->m_MovingImage ); m_FixedImageInterpolator->SetInputImage( this->m_FixedImage ); m_Interpolator->SetInputImage( this->m_MovingImage ); m_FixedImageSpacing = this->m_FixedImage->GetSpacing(); m_FixedImageOrigin = this->m_FixedImage->GetOrigin(); m_Normalizer = 0.0; m_NumberOfSpatialSamples = 1; for( unsigned int k = 0; k < ImageDimension; k++ ) { m_Normalizer += m_FixedImageSpacing[k] * m_FixedImageSpacing[k]; m_NumberOfSpatialSamples *= this->m_FixedImage->GetLargestPossibleRegion().GetSize()[k]; } m_Normalizer /= static_cast( ImageDimension ); /** * Compute binsize for the histograms. * * 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. * double fixedImageMax = 1.0; double fixedImageMin = 0.0; double movingImageMax = 1.0; double movingImageMin = 0.0; m_MovingImageTrueMin = movingImageMin; m_MovingImageTrueMax = movingImageMax; */ double movingImageMin = NumericTraits::max(); double movingImageMax = NumericTraits::NonpositiveMin(); double fixedImageMin = NumericTraits::max(); double fixedImageMax = NumericTraits::NonpositiveMin(); typedef ImageRegionConstIterator MovingIteratorType; MovingIteratorType movingImageIterator( this->m_MovingImage, this->m_MovingImage->GetBufferedRegion() ); for( movingImageIterator.GoToBegin(); !movingImageIterator.IsAtEnd(); ++movingImageIterator ) { bool takesample = true; if( this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( movingImageIterator.GetIndex() ) < 1.e-6 ) { takesample = false; } } if( takesample ) { double sample = static_cast( movingImageIterator.Get() ); double fsample = static_cast( this->m_FixedImage->GetPixel( movingImageIterator.GetIndex() ) ); if( sample < movingImageMin ) { movingImageMin = sample; } if( sample > movingImageMax ) { movingImageMax = sample; } if( fsample < fixedImageMin ) { fixedImageMin = fsample; } if( fsample > fixedImageMax ) { fixedImageMax = fsample; } } } this->m_MovingImageTrueMax = movingImageMax; this->m_FixedImageTrueMax = fixedImageMax; this->m_MovingImageTrueMin = movingImageMin; this->m_FixedImageTrueMin = fixedImageMin; fixedImageMax = 1. * ( fixedImageMax - fixedImageMin ) + fixedImageMin; movingImageMax = 1. * ( movingImageMax - movingImageMin ) + movingImageMin; m_FixedImageBinSize = ( fixedImageMax - fixedImageMin ) / static_cast( m_NumberOfHistogramBins - 2 * this->m_Padding ); m_FixedImageNormalizedMin = fixedImageMin / m_FixedImageBinSize - static_cast( this->m_Padding ); m_MovingImageBinSize = ( movingImageMax - movingImageMin ) / static_cast( m_NumberOfHistogramBins - 2 * this->m_Padding ); m_MovingImageNormalizedMin = movingImageMin / m_MovingImageBinSize - static_cast( this->m_Padding ); typename MarginalPDFType::RegionType mPDFRegion; typename MarginalPDFType::SizeType mPDFSize; typename MarginalPDFType::IndexType mPDFIndex; typename MarginalPDFType::SpacingType mPDFspacing; mPDFspacing.Fill(1); mPDFIndex.Fill( 0 ); mPDFSize.Fill( m_NumberOfHistogramBins ); mPDFRegion.SetIndex( mPDFIndex ); mPDFRegion.SetSize( mPDFSize ); m_FixedImageMarginalPDF = AllocImage(mPDFRegion); m_FixedImageMarginalPDF->SetSpacing(mPDFspacing); m_MovingImageMarginalPDF = AllocImage( mPDFRegion ); m_MovingImageMarginalPDF->SetSpacing(mPDFspacing); // std::cout << " C " << std::endl; // std::cout << " D " << std::endl; // Instantiate a region, index, size JointPDFRegionType jointPDFRegion; JointPDFIndexType jointPDFIndex; JointPDFSizeType jointPDFSize; typename JointPDFType::SpacingType jspacing; jspacing.Fill(1); // For the joint PDF define a region starting from {0,0} // with size {m_NumberOfHistogramBins, m_NumberOfHistogramBins}. // The dimension represents fixed image parzen window index // and moving image parzen window index, respectively. jointPDFIndex.Fill( 0 ); jointPDFSize.Fill( m_NumberOfHistogramBins ); jointPDFRegion.SetIndex( jointPDFIndex ); jointPDFRegion.SetSize( jointPDFSize ); m_JointHist = AllocImage(jointPDFRegion); this->m_JointHist->SetSpacing(jspacing); // Set the regions and allocate this->m_JointPDF = AllocImage( jointPDFRegion ); this->m_JointPDF->SetSpacing(jspacing); this->m_JointPDFXYu = AllocImage( jointPDFRegion ); this->m_JointPDFXYu->SetSpacing(jspacing); this->m_JointPDFXuY = AllocImage( jointPDFRegion ); this->m_JointPDFXuY->SetSpacing(jspacing); this->m_JointPDFXlY = AllocImage( jointPDFRegion ); this->m_JointPDFXlY->SetSpacing(jspacing); this->m_JointPDFXYl = AllocImage( jointPDFRegion ); this->m_JointPDFXYl->SetSpacing(jspacing); // this->m_JointPDFXlYu = AllocImage( jointPDFRegion ); this->m_JointPDFXlYu->SetSpacing(jspacing); this->m_JointPDFXuYl = AllocImage( jointPDFRegion ); this->m_JointPDFXuYl->SetSpacing(jspacing); this->m_JointPDFXrYu = AllocImage( jointPDFRegion ); this->m_JointPDFXrYu->SetSpacing(jspacing); this->m_JointPDFXuYr = AllocImage( jointPDFRegion ); this->m_JointPDFXuYr->SetSpacing(jspacing); // std::cout << " E " << std::endl; m_NormalizeMetric = 1.0; for( unsigned int i = 0; i < ImageDimension; i++ ) { m_NormalizeMetric *= this->m_FixedImage->GetLargestPossibleRegion().GetSize()[i]; } // std::cout << " F " << std::endl; pdfinterpolator->SetInputImage(m_JointPDF); pdfinterpolator->SetSplineOrder(3); this->pdfinterpolatorXuY->SetInputImage(this->m_JointPDFXuY); this->pdfinterpolatorXYu->SetInputImage(this->m_JointPDFXYu); this->pdfinterpolatorXlY->SetInputImage(this->m_JointPDFXlY); this->pdfinterpolatorXYl->SetInputImage(this->m_JointPDFXYl); this->pdfinterpolatorXuYl->SetInputImage(this->m_JointPDFXuYl); this->pdfinterpolatorXlYu->SetInputImage(this->m_JointPDFXlYu); this->pdfinterpolatorXuYr->SetInputImage(this->m_JointPDFXuYr); this->pdfinterpolatorXrYu->SetInputImage(this->m_JointPDFXrYu); this->pdfinterpolatorXuY->SetSplineOrder(3); this->pdfinterpolatorXYu->SetSplineOrder(3); this->pdfinterpolatorXlY->SetSplineOrder(3); this->pdfinterpolatorXYl->SetSplineOrder(3); this->pdfinterpolatorXuYl->SetSplineOrder(3); this->pdfinterpolatorXlYu->SetSplineOrder(3); this->pdfinterpolatorXuYr->SetSplineOrder(3); this->pdfinterpolatorXrYu->SetSplineOrder(3); pdfinterpolator2->SetInputImage(m_FixedImageMarginalPDF); pdfinterpolator3->SetInputImage(m_MovingImageMarginalPDF); pdfinterpolator2->SetSplineOrder(3); pdfinterpolator3->SetSplineOrder(3); // std::cout << " Ga " << std::endl; this->GetProbabilities(); // std::cout << " G " << std::endl; this->ComputeSpatialMutualInformation(); // std::cout << " H " << std::endl; } /** * Get the both Value and Derivative Measure */ template void SpatialMutualInformationRegistrationFunction ::GetProbabilities() { typedef ImageRegionConstIteratorWithIndex RandomIterator; RandomIterator randIter( this->m_FixedImage, this->m_FixedImage->GetLargestPossibleRegion() ); for( unsigned int j = 0; j < m_NumberOfHistogramBins; j++ ) { MarginalPDFIndexType mind; mind[0] = j; m_FixedImageMarginalPDF->SetPixel(mind, 0); m_MovingImageMarginalPDF->SetPixel(mind, 0); } // Reset the joint pdfs to zero m_JointPDF->FillBuffer( 0.0 ); m_JointPDFXuY->FillBuffer( 0.0 ); m_JointPDFXYu->FillBuffer( 0.0 ); m_JointPDFXlY->FillBuffer( 0.0 ); m_JointPDFXYl->FillBuffer( 0.0 ); m_JointPDFXuYl->FillBuffer( 0.0 ); m_JointPDFXlYu->FillBuffer( 0.0 ); m_JointPDFXrYu->FillBuffer( 0.0 ); m_JointPDFXuYr->FillBuffer( 0.0 ); m_JointHist->FillBuffer( 0.0 ); unsigned long nSamples = 0; RandomIterator iter( this->m_FixedImage, this->m_FixedImage->GetLargestPossibleRegion() ); for( iter.GoToBegin(); !iter.IsAtEnd(); ++iter ) { bool takesample = true; if( this->m_FixedImageMask ) { if( this->m_FixedImageMask->GetPixel( iter.GetIndex() ) < 1.e-6 ) { takesample = false; } } if( takesample ) { // Get sampled index FixedImageIndexType index = iter.GetIndex(); FixedImageIndexType IndexU = index; FixedImageIndexType IndexL = index; FixedImageIndexType IndexR = index; // check the neighboring voxel to be in the image typename FixedImageType::SizeType imagesize = this->m_FixedImage->GetLargestPossibleRegion().GetSize(); bool inimage = true; for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( index[dd] < 1 || index[dd] > static_cast(imagesize[dd] - 2) ) { inimage = false; } } // std::cout << " Image size? " << imagesize << std::endl; if( inimage ) { IndexU[0] = index[0] - 1; IndexL[1] = index[1] - 1; IndexR[1] = index[1] + 1; double movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( index ) ); double fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( index ) ); unsigned int movingImageParzenWindowIndex = this->FitIndexInBins( movingImageValue ); unsigned int fixedImageParzenWindowIndex = this->FitIndexInBins( fixedImageValue ); JointPDFValueType *pdfPtr = m_JointPDF->GetBufferPointer() + ( fixedImageParzenWindowIndex * m_NumberOfHistogramBins ); int pdfMovingIndex = static_cast( movingImageParzenWindowIndex ); pdfPtr += pdfMovingIndex; *(pdfPtr) += static_cast( 1 ); movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( index ) ); fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( IndexU ) ); movingImageParzenWindowIndex = this->FitIndexInBins( movingImageValue ); fixedImageParzenWindowIndex = this->FitIndexInBins( fixedImageValue ); pdfPtr = m_JointPDFXuY->GetBufferPointer() + ( fixedImageParzenWindowIndex * m_NumberOfHistogramBins ); pdfMovingIndex = static_cast( movingImageParzenWindowIndex ); pdfPtr += pdfMovingIndex; *(pdfPtr) += static_cast( 1 ); movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( IndexU ) ); fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( index ) ); movingImageParzenWindowIndex = this->FitIndexInBins( movingImageValue ); fixedImageParzenWindowIndex = this->FitIndexInBins( fixedImageValue ); pdfPtr = m_JointPDFXYu->GetBufferPointer() + ( fixedImageParzenWindowIndex * m_NumberOfHistogramBins ); pdfMovingIndex = static_cast( movingImageParzenWindowIndex ); pdfPtr += pdfMovingIndex; *(pdfPtr) += static_cast( 1 ); movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( index ) ); fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( IndexL ) ); movingImageParzenWindowIndex = this->FitIndexInBins( movingImageValue ); fixedImageParzenWindowIndex = this->FitIndexInBins( fixedImageValue ); pdfPtr = m_JointPDFXlY->GetBufferPointer() + ( fixedImageParzenWindowIndex * m_NumberOfHistogramBins ); pdfMovingIndex = static_cast( movingImageParzenWindowIndex ); pdfPtr += pdfMovingIndex; *(pdfPtr) += static_cast( 1 ); movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( IndexL ) ); fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( index ) ); movingImageParzenWindowIndex = this->FitIndexInBins( movingImageValue ); fixedImageParzenWindowIndex = this->FitIndexInBins( fixedImageValue ); pdfPtr = m_JointPDFXYl->GetBufferPointer() + ( fixedImageParzenWindowIndex * m_NumberOfHistogramBins ); pdfMovingIndex = static_cast( movingImageParzenWindowIndex ); pdfPtr += pdfMovingIndex; *(pdfPtr) += static_cast( 1 ); movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( IndexL ) ); fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( IndexU ) ); movingImageParzenWindowIndex = this->FitIndexInBins( movingImageValue ); fixedImageParzenWindowIndex = this->FitIndexInBins( fixedImageValue ); pdfPtr = m_JointPDFXuYl->GetBufferPointer() + ( fixedImageParzenWindowIndex * m_NumberOfHistogramBins ); pdfMovingIndex = static_cast( movingImageParzenWindowIndex ); pdfPtr += pdfMovingIndex; *(pdfPtr) += static_cast( 1 ); movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( IndexU ) ); fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( IndexL ) ); movingImageParzenWindowIndex = this->FitIndexInBins( movingImageValue ); fixedImageParzenWindowIndex = this->FitIndexInBins( fixedImageValue ); pdfPtr = m_JointPDFXlYu->GetBufferPointer() + ( fixedImageParzenWindowIndex * m_NumberOfHistogramBins ); pdfMovingIndex = static_cast( movingImageParzenWindowIndex ); pdfPtr += pdfMovingIndex; *(pdfPtr) += static_cast( 1 ); movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( IndexU ) ); fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( IndexR ) ); movingImageParzenWindowIndex = this->FitIndexInBins( movingImageValue ); fixedImageParzenWindowIndex = this->FitIndexInBins( fixedImageValue ); pdfPtr = m_JointPDFXrYu->GetBufferPointer() + ( fixedImageParzenWindowIndex * m_NumberOfHistogramBins ); pdfMovingIndex = static_cast( movingImageParzenWindowIndex ); pdfPtr += pdfMovingIndex; *(pdfPtr) += static_cast( 1 ); movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( IndexR ) ); fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( IndexU ) ); movingImageParzenWindowIndex = this->FitIndexInBins( movingImageValue ); fixedImageParzenWindowIndex = this->FitIndexInBins( fixedImageValue ); pdfPtr = m_JointPDFXuYr->GetBufferPointer() + ( fixedImageParzenWindowIndex * m_NumberOfHistogramBins ); pdfMovingIndex = static_cast( movingImageParzenWindowIndex ); pdfPtr += pdfMovingIndex; *(pdfPtr) += static_cast( 1 ); ++nSamples; } } } // std::cout << " Image Rotation Number " << nSamples << std::endl; /** * Normalize the PDFs, compute moving image marginal PDF * */ typedef ImageRegionIterator JointPDFIteratorType; JointPDFIteratorType jointPDFIterator( m_JointPDF, m_JointPDF->GetBufferedRegion() ); JointPDFIteratorType jointPDFXuYIterator( m_JointPDFXuY, m_JointPDFXuY->GetBufferedRegion() ); JointPDFIteratorType jointPDFXYuIterator( m_JointPDFXYu, m_JointPDFXYu->GetBufferedRegion() ); JointPDFIteratorType jointPDFXlYIterator( m_JointPDFXlY, m_JointPDFXlY->GetBufferedRegion() ); JointPDFIteratorType jointPDFXYlIterator( m_JointPDFXYl, m_JointPDFXYl->GetBufferedRegion() ); JointPDFIteratorType jointPDFXuYlIterator( m_JointPDFXuYl, m_JointPDFXuYl->GetBufferedRegion() ); JointPDFIteratorType jointPDFXlYuIterator( m_JointPDFXlYu, m_JointPDFXlYu->GetBufferedRegion() ); JointPDFIteratorType jointPDFXrYuIterator( m_JointPDFXrYu, m_JointPDFXrYu->GetBufferedRegion() ); JointPDFIteratorType jointPDFXuYrIterator( m_JointPDFXuYr, m_JointPDFXuYr->GetBufferedRegion() ); // Compute joint PDF normalization factor (to ensure joint PDF sum adds to 1.0) double jointPDFSum = 0.0; jointPDFIterator.GoToBegin(); while( !jointPDFIterator.IsAtEnd() ) { float temp = jointPDFIterator.Get(); // jointPDFIterator.Set(temp); jointPDFSum += temp; ++jointPDFIterator; } // std::cout << " Joint PDF Summation? " << jointPDFSum << std::endl; // of derivatives if( jointPDFSum == 0.0 ) { itkExceptionMacro( "Joint PDF summed to zero" ); } // Normalize the PDF bins jointPDFIterator.GoToEnd(); jointPDFXuYIterator.GoToEnd(); jointPDFXYuIterator.GoToEnd(); jointPDFXlYIterator.GoToEnd(); jointPDFXYlIterator.GoToEnd(); jointPDFXuYlIterator.GoToEnd(); jointPDFXlYuIterator.GoToEnd(); jointPDFXrYuIterator.GoToEnd(); jointPDFXuYrIterator.GoToEnd(); while( !jointPDFIterator.IsAtBegin() ) { --jointPDFIterator; --jointPDFXuYIterator; --jointPDFXYuIterator; --jointPDFXlYIterator; --jointPDFXYlIterator; --jointPDFXuYlIterator; --jointPDFXlYuIterator; --jointPDFXrYuIterator; --jointPDFXuYrIterator; jointPDFIterator.Value() /= static_cast( jointPDFSum ); jointPDFXuYIterator.Value() /= static_cast( jointPDFSum ); jointPDFXYuIterator.Value() /= static_cast( jointPDFSum ); jointPDFXlYIterator.Value() /= static_cast( jointPDFSum ); jointPDFXYlIterator.Value() /= static_cast( jointPDFSum ); jointPDFXuYlIterator.Value() /= static_cast( jointPDFSum ); jointPDFXlYuIterator.Value() /= static_cast( jointPDFSum ); jointPDFXrYuIterator.Value() /= static_cast( jointPDFSum ); jointPDFXuYrIterator.Value() /= static_cast( jointPDFSum ); } bool smoothjh = false; if( smoothjh ) { typedef DiscreteGaussianImageFilter dgtype; typename dgtype::Pointer dg = dgtype::New(); dg->SetInput(this->m_JointPDF); dg->SetVariance(1.); dg->SetUseImageSpacingOff(); dg->SetMaximumError(.01f); dg->Update(); this->m_JointPDF = dg->GetOutput(); } // Compute moving image marginal PDF by summing over fixed image bins. typedef ImageLinearIteratorWithIndex JointPDFLinearIterator; JointPDFLinearIterator linearIter( m_JointPDF, m_JointPDF->GetBufferedRegion() ); linearIter.SetDirection( 0 ); linearIter.GoToBegin(); unsigned int fixedIndex = 0; while( !linearIter.IsAtEnd() ) { double sum = 0.0; while( !linearIter.IsAtEndOfLine() ) { sum += linearIter.Get(); ++linearIter; } MarginalPDFIndexType mind; mind[0] = fixedIndex; m_FixedImageMarginalPDF->SetPixel(mind, static_cast(sum) ); linearIter.NextLine(); ++fixedIndex; } linearIter.SetDirection( 1 ); linearIter.GoToBegin(); unsigned int movingIndex = 0; while( !linearIter.IsAtEnd() ) { double sum = 0.0; while( !linearIter.IsAtEndOfLine() ) { sum += linearIter.Get(); ++linearIter; } MarginalPDFIndexType mind; mind[0] = movingIndex; m_MovingImageMarginalPDF->SetPixel(mind, static_cast(sum) ); linearIter.NextLine(); ++movingIndex; } } /** * Get the both Value and Derivative Measure */ template double SpatialMutualInformationRegistrationFunction ::GetValueAndDerivative(IndexType oindex, MeasureType & /* valuei */, DerivativeType & /* derivative1 */, DerivativeType & /* derivative2 */) { double value = 0; DerivativeType zero(ImageDimension); zero.Fill(0); double movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( oindex ) ); double fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( oindex ) ); double movingImageParzenWindowContIndex = this->FitContIndexInBins( movingImageValue ); double fixedImageParzenWindowContIndex = this->FitContIndexInBins( fixedImageValue ); double dJPDF = 0, jointPDFValue = 0; double jointPDFValueXuY = 0, dJPDFXuY = 0, jointPDFValueXYu = 0, dJPDFXYu = 0, jointPDFValueXlY = 0, dJPDFXlY = 0, jointPDFValueXYl = 0; double dJPDFXYl = 0, jointPDFValueXuYl = 0, dJPDFXuYl = 0, jointPDFValueXlYu = 0, dJPDFXlYu = 0, jointPDFValueXuYr = 0, dJPDFXuYr = 0, jointPDFValueXrYu = 0, dJPDFXrYu = 0; { /** take derivative of joint pdf with respect to the b-spline */ typename pdfintType::ContinuousIndexType pdfind; pdfind[1] = fixedImageParzenWindowContIndex; pdfind[0] = movingImageParzenWindowContIndex; jointPDFValue = pdfinterpolator->EvaluateAtContinuousIndex(pdfind); dJPDF = (1.0) * (pdfinterpolator->EvaluateDerivativeAtContinuousIndex( pdfind ) )[1]; jointPDFValueXuY = pdfinterpolatorXuY->EvaluateAtContinuousIndex(pdfind); dJPDFXuY = (1.0) * (pdfinterpolatorXuY->EvaluateDerivativeAtContinuousIndex( pdfind ) )[1]; jointPDFValueXYu = pdfinterpolatorXYu->EvaluateAtContinuousIndex(pdfind); dJPDFXYu = (1.0) * (pdfinterpolatorXYu->EvaluateDerivativeAtContinuousIndex( pdfind ) )[1]; jointPDFValueXlY = pdfinterpolatorXlY->EvaluateAtContinuousIndex(pdfind); dJPDFXlY = (1.0) * (pdfinterpolatorXlY->EvaluateDerivativeAtContinuousIndex( pdfind ) )[1]; jointPDFValueXYl = pdfinterpolatorXYl->EvaluateAtContinuousIndex(pdfind); dJPDFXYl = (1.0) * (pdfinterpolatorXYl->EvaluateDerivativeAtContinuousIndex( pdfind ) )[1]; jointPDFValueXuYl = pdfinterpolatorXuYl->EvaluateAtContinuousIndex(pdfind); dJPDFXuYl = (1.0) * (pdfinterpolatorXuYl->EvaluateDerivativeAtContinuousIndex( pdfind ) )[1]; jointPDFValueXlYu = pdfinterpolatorXlYu->EvaluateAtContinuousIndex(pdfind); dJPDFXlYu = (1.0) * (pdfinterpolatorXlYu->EvaluateDerivativeAtContinuousIndex( pdfind ) )[1]; jointPDFValueXuYr = pdfinterpolatorXuYr->EvaluateAtContinuousIndex(pdfind); dJPDFXuYr = (1.0) * (pdfinterpolatorXuYr->EvaluateDerivativeAtContinuousIndex( pdfind ) )[1]; jointPDFValueXrYu = pdfinterpolatorXrYu->EvaluateAtContinuousIndex(pdfind); dJPDFXrYu = (1.0) * (pdfinterpolatorXrYu->EvaluateDerivativeAtContinuousIndex( pdfind ) )[1]; } double eps = 1.e-12; if( jointPDFValue > eps && jointPDFValueXuY > eps && jointPDFValueXYu > eps && jointPDFValueXlY > eps && jointPDFValueXYl > eps && jointPDFValueXuYl > eps && jointPDFValueXlYu > eps && jointPDFValueXrYu > eps && jointPDFValueXuYr > eps ) { value = 4 * dJPDF / jointPDFValue + dJPDFXuYl / jointPDFValueXuYl + dJPDFXlYu / jointPDFValueXlYu + dJPDFXuYr / jointPDFValueXuYr + dJPDFXrYu / jointPDFValueXrYu - 2 * dJPDFXYu / jointPDFValueXYu - 2 * dJPDFXuY / jointPDFValueXuY - 2 * dJPDFXYl / jointPDFValueXYl - 2 * dJPDFXlY / jointPDFValueXlY; } // end if-block to check non-zero bin contribution else { value = 0; } return (value / 4) * (-1); } template double SpatialMutualInformationRegistrationFunction ::GetValueAndDerivativeInv(IndexType oindex, MeasureType & /* valuei */, DerivativeType & /* derivative1 */, DerivativeType & /* derivative2 */) { double value = 0; DerivativeType zero(ImageDimension); zero.Fill(0); double movingImageValue = this->GetMovingParzenTerm( this->m_MovingImage->GetPixel( oindex ) ); double fixedImageValue = this->GetFixedParzenTerm( this->m_FixedImage->GetPixel( oindex ) ); double movingImageParzenWindowContIndex = this->FitContIndexInBins( movingImageValue ); double fixedImageParzenWindowContIndex = this->FitContIndexInBins( fixedImageValue ); double dJPDF = 0, jointPDFValue = 0; double jointPDFValueXuY = 0, dJPDFXuY = 0, jointPDFValueXYu = 0, dJPDFXYu = 0, jointPDFValueXlY = 0, dJPDFXlY = 0, jointPDFValueXYl = 0; double dJPDFXYl = 0, jointPDFValueXuYl = 0, dJPDFXuYl = 0, jointPDFValueXlYu = 0, dJPDFXlYu = 0, jointPDFValueXuYr = 0, dJPDFXuYr = 0, jointPDFValueXrYu = 0, dJPDFXrYu = 0; { /** take derivative of joint pdf with respect to the b-spline */ typename pdfintType::ContinuousIndexType pdfind; pdfind[1] = fixedImageParzenWindowContIndex; pdfind[0] = movingImageParzenWindowContIndex; jointPDFValue = pdfinterpolator->EvaluateAtContinuousIndex(pdfind); dJPDF = (1.0) * (pdfinterpolator->EvaluateDerivativeAtContinuousIndex( pdfind ) )[0]; jointPDFValueXuY = pdfinterpolatorXuY->EvaluateAtContinuousIndex(pdfind); dJPDFXuY = (1.0) * (pdfinterpolatorXuY->EvaluateDerivativeAtContinuousIndex( pdfind ) )[0]; jointPDFValueXYu = pdfinterpolatorXYu->EvaluateAtContinuousIndex(pdfind); dJPDFXYu = (1.0) * (pdfinterpolatorXYu->EvaluateDerivativeAtContinuousIndex( pdfind ) )[0]; jointPDFValueXlY = pdfinterpolatorXlY->EvaluateAtContinuousIndex(pdfind); dJPDFXlY = (1.0) * (pdfinterpolatorXlY->EvaluateDerivativeAtContinuousIndex( pdfind ) )[0]; jointPDFValueXYl = pdfinterpolatorXYl->EvaluateAtContinuousIndex(pdfind); dJPDFXYl = (1.0) * (pdfinterpolatorXYl->EvaluateDerivativeAtContinuousIndex( pdfind ) )[0]; jointPDFValueXuYl = pdfinterpolatorXuYl->EvaluateAtContinuousIndex(pdfind); dJPDFXuYl = (1.0) * (pdfinterpolatorXuYl->EvaluateDerivativeAtContinuousIndex( pdfind ) )[0]; jointPDFValueXlYu = pdfinterpolatorXlYu->EvaluateAtContinuousIndex(pdfind); dJPDFXlYu = (1.0) * (pdfinterpolatorXlYu->EvaluateDerivativeAtContinuousIndex( pdfind ) )[0]; jointPDFValueXuYr = pdfinterpolatorXuYr->EvaluateAtContinuousIndex(pdfind); dJPDFXuYr = (1.0) * (pdfinterpolatorXuYr->EvaluateDerivativeAtContinuousIndex( pdfind ) )[0]; jointPDFValueXrYu = pdfinterpolatorXrYu->EvaluateAtContinuousIndex(pdfind); dJPDFXrYu = (1.0) * (pdfinterpolatorXrYu->EvaluateDerivativeAtContinuousIndex( pdfind ) )[0]; } double eps = 1.e-12; if( jointPDFValue > eps && jointPDFValueXuY > eps && jointPDFValueXYu > eps && jointPDFValueXlY > eps && jointPDFValueXYl > eps && jointPDFValueXuYl > eps && jointPDFValueXlYu > eps && jointPDFValueXrYu > eps && jointPDFValueXuYr > eps ) { value = 4 * dJPDF / jointPDFValue + dJPDFXuYl / jointPDFValueXuYl + dJPDFXlYu / jointPDFValueXlYu + dJPDFXuYr / jointPDFValueXuYr + dJPDFXrYu / jointPDFValueXrYu - 2 * dJPDFXYu / jointPDFValueXYu - 2 * dJPDFXuY / jointPDFValueXuY - 2 * dJPDFXYl / jointPDFValueXYl - 2 * dJPDFXlY / jointPDFValueXlY; } // end if-block to check non-zero bin contribution else { value = 0; } // return 0; return (value / 4) * (0); } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkSpatialMutualInformationRegistrationFunction.h000066400000000000000000000723431311104306400300540ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 __itkSpatialMutualInformationRegistrationFunction_h #define __itkSpatialMutualInformationRegistrationFunction_h #include "itkImageFileWriter.h" #include "itkImageToImageMetric.h" #include "itkAvantsPDEDeformableRegistrationFunction.h" #include "itkCovariantVector.h" #include "itkPoint.h" #include "itkIndex.h" #include "itkBSplineKernelFunction.h" #include "itkBSplineDerivativeKernelFunction.h" #include "itkCentralDifferenceImageFunction.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkTranslationTransform.h" #include "itkArray2D.h" #include "itkImageBase.h" #include "itkTransform.h" #include "itkInterpolateImageFunction.h" #include "itkSingleValuedCostFunction.h" #include "itkExceptionObject.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkSpatialObject.h" #include "itkConstNeighborhoodIterator.h" namespace itk { /** \class SpatialMutualInformationRegistrationFunction * \brief Computes the mutual information between two images to be * registered using the method of Spatial et al. * * SpatialMutualInformationRegistrationFunction computes the mutual * information between a fixed and moving image to be registered. * * This class is templated over the FixedImage type and the MovingImage * type. * * The fixed and moving images are set via methods SetFixedImage() and * SetMovingImage(). This metric makes use of user specified Transform and * Interpolator. The Transform is used to map points from the fixed image to * the moving image domain. The Interpolator is used to evaluate the image * intensity at user specified geometric points in the moving image. * The Transform and Interpolator are set via methods SetTransform() and * SetInterpolator(). * * If a BSplineInterpolationFunction is used, this class obtain * image derivatives from the BSpline interpolator. Otherwise, * image derivatives are computed using central differencing. * * \warning This metric assumes that the moving image has already been * connected to the interpolator outside of this class. * * The method GetValue() computes of the mutual information * while method GetValueAndDerivative() computes * both the mutual information and its derivatives with respect to the * transform parameters. * * The calculations are based on the method of Spatial et al [1,2] * where the probability density distribution are estimated using * Parzen histograms. Since the fixed image PDF does not contribute * to the derivatives, it does not need to be smooth. Hence, * a zero order (box car) BSpline kernel is used * for the fixed image intensity PDF. On the other hand, to ensure * smoothness a third order BSpline kernel is used for the * moving image intensity PDF. * * On Initialize(), the FixedImage is uniformly sampled within * the FixedImageRegion. The number of samples used can be set * via SetNumberOfSpatialSamples(). Typically, the number of * spatial samples used should increase with the image size. * * During each call of GetValue(), GetDerivatives(), * GetValueAndDerivatives(), marginal and joint intensity PDF's * values are estimated at discrete position or bins. * The number of bins used can be set via SetNumberOfHistogramBins(). * To handle data with arbitray magnitude and dynamic range, * the image intensity is scale such that any contribution to the * histogram will fall into a valid bin. * * One the PDF's have been contructed, the mutual information * is obtained by doubling summing over the discrete PDF values. * * * Notes: * 1. This class returns the negative mutual information value. * 2. This class in not thread safe due the private data structures * used to the store the sampled points and the marginal and joint pdfs. * * References: * [1] "Nonrigid multimodality image registration" * D. Spatial, D. R. Haynor, H. Vesselle, T. Lewellen and W. Eubank * Medical Imaging 2001: Image Processing, 2001, pp. 1609-1620. * [2] "PET-CT Image Registration in the Chest Using Free-form Deformations" * D. Spatial, D. R. Haynor, H. Vesselle, T. Lewellen and W. Eubank * IEEE Transactions in Medical Imaging. Vol.22, No.1, January 2003. pp.120-128. * [3] "Optimization of Mutual Information for MultiResolution Image * Registration" * P. Thevenaz and M. Unser * IEEE Transactions in Image Processing, 9(12) December 2000. * * \ingroup RegistrationMetrics * \ingroup ThreadUnSafe */ template class SpatialMutualInformationRegistrationFunction : public AvantsPDEDeformableRegistrationFunction { public: /** Standard class typedefs. */ typedef SpatialMutualInformationRegistrationFunction Self; typedef AvantsPDEDeformableRegistrationFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro( SpatialMutualInformationRegistrationFunction, AvantsPDEDeformableRegistrationFunction ); /** MovingImage image type. */ typedef typename Superclass::MovingImageType MovingImageType; typedef typename Superclass::MovingImagePointer MovingImagePointer; /** FixedImage image type. */ typedef typename Superclass::FixedImageType FixedImageType; typedef typename Superclass::FixedImagePointer FixedImagePointer; typedef typename FixedImageType::IndexType IndexType; typedef typename FixedImageType::SizeType SizeType; typedef typename FixedImageType::SpacingType SpacingType; /** Deformation field type. */ typedef typename Superclass::VectorType VectorType; typedef typename Superclass::DisplacementFieldType DisplacementFieldType; typedef typename Superclass::DisplacementFieldTypePointer DisplacementFieldTypePointer; /** Inherit some enums from the superclass. */ itkStaticConstMacro(ImageDimension, unsigned int, Superclass::ImageDimension); /** Inherit some enums from the superclass. */ typedef typename Superclass::PixelType PixelType; typedef typename Superclass::RadiusType RadiusType; typedef typename Superclass::NeighborhoodType NeighborhoodType; typedef typename Superclass::FloatOffsetType FloatOffsetType; typedef typename Superclass::TimeStepType TimeStepType; /** Interpolator type. */ typedef double CoordRepType; typedef // // LinearInterpolateImageFunction BSplineInterpolateImageFunction InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; typedef typename InterpolatorType::PointType PointType; typedef InterpolatorType DefaultInterpolatorType; // typedef LinearInterpolateImageFunction // DefaultInterpolatorType; /** Covariant vector type. */ typedef CovariantVector CovariantVectorType; /** Gradient calculator type. */ typedef CentralDifferenceImageFunction GradientCalculatorType; typedef typename GradientCalculatorType::Pointer GradientCalculatorPointer; /** Set the moving image interpolator. */ void SetMovingImageInterpolator( InterpolatorType * ptr ) { m_MovingImageInterpolator = ptr; } /** Get the moving image interpolator. */ InterpolatorType * GetMovingImageInterpolator(void) { return m_MovingImageInterpolator; } /** This class uses a constant timestep of 1. */ virtual TimeStepType ComputeGlobalTimeStep(void *itkNotUsed(GlobalData) ) const ITK_OVERRIDE { return 1; } /** Return a pointer to a global data structure that is passed to * this object from the solver at each calculation. */ virtual void * GetGlobalDataPointer() const ITK_OVERRIDE { GlobalDataStruct *global = new GlobalDataStruct(); // global->m_SumOfSquaredDifference = 0.0; // / global->m_NumberOfPixelsProcessed = 0L; // global->m_SumOfSquaredChange = 0; return global; } /** Release memory for global data structure. */ virtual void ReleaseGlobalDataPointer( void *GlobalData ) const ITK_OVERRIDE { delete (GlobalDataStruct *) GlobalData; } /** Set the object's state before each iteration. */ virtual void InitializeIteration() ITK_OVERRIDE; typedef double CoordinateRepresentationType; /** Types inherited from Superclass. */ typedef TranslationTransform TransformType; typedef ImageToImageMetric Metricclass; typedef typename TransformType::Pointer TransformPointer; typedef typename Metricclass::TransformJacobianType TransformJacobianType; // typedef typename Metricclass::InterpolatorType InterpolatorType; typedef typename Metricclass::MeasureType MeasureType; typedef typename Metricclass::DerivativeType DerivativeType; typedef typename TransformType::ParametersType ParametersType; typedef typename Metricclass::FixedImageConstPointer FixedImageConstPointer; typedef typename Metricclass::MovingImageConstPointer MovingImageCosntPointer; // typedef typename TransformType::CoordinateRepresentationType CoordinateRepresentationType; /** Index and Point typedef support. */ typedef typename FixedImageType::IndexType FixedImageIndexType; typedef typename FixedImageIndexType::IndexValueType FixedImageIndexValueType; typedef typename MovingImageType::IndexType MovingImageIndexType; typedef typename TransformType::InputPointType FixedImagePointType; typedef typename TransformType::OutputPointType MovingImagePointType; /** The marginal PDFs are stored as std::vector. */ typedef float PDFValueType; // typedef std::vector MarginalPDFType; typedef Image MarginalPDFType; typedef typename MarginalPDFType::IndexType MarginalPDFIndexType; /** Typedef for the joint PDF and PDF derivatives are stored as ITK Images. */ typedef Image JointPDFType; typedef Image JointPDFDerivativesType; typedef JointPDFType::IndexType JointPDFIndexType; typedef JointPDFType::PixelType JointPDFValueType; typedef JointPDFType::RegionType JointPDFRegionType; typedef JointPDFType::SizeType JointPDFSizeType; typedef JointPDFDerivativesType::IndexType JointPDFDerivativesIndexType; typedef JointPDFDerivativesType::PixelType JointPDFDerivativesValueType; typedef JointPDFDerivativesType::RegionType JointPDFDerivativesRegionType; typedef JointPDFDerivativesType::SizeType JointPDFDerivativesSizeType; /** Get the value and derivatives for single valued optimizers. */ double GetValueAndDerivative( IndexType index, MeasureType& Value, DerivativeType& Derivative1, DerivativeType& Derivative2 ); /** Get the value and derivatives for single valued optimizers. */ double GetValueAndDerivativeInv( IndexType index, MeasureType& Value, DerivativeType& Derivative1, DerivativeType& Derivative2 ); /** Number of spatial samples to used to compute metric */ // itkSetClampMacro( NumberOfSpatialSamples, unsigned long, // 1, NumericTraits::max() ); // itkGetConstReferenceMacro( NumberOfSpatialSamples, unsigned long); /** Number of bins to used in the histogram. Typical value is 50. */ // itkSetClampMacro( NumberOfHistogramBins, unsigned long, // 1, NumericTraits::max() ); // itkGetConstReferenceMacro( NumberOfHistogramBins, unsigned long); void SetNumberOfHistogramBins(unsigned long nhb) { m_NumberOfHistogramBins = nhb + 2 * this->m_Padding; } unsigned long GetNumberOfHistogramBins() { return m_NumberOfHistogramBins; } void SetTransform(TransformPointer t) { m_Transform = t; } TransformPointer GetTransform() { return m_Transform; } void SetInterpolator(InterpolatorPointer t) { m_Interpolator = t; } InterpolatorPointer GetInterpolator() { return m_Interpolator; } void GetProbabilities(); double ComputeMutualInformation() { // typedef ImageRegionIterator JointPDFIteratorType; // JointPDFIteratorType jointPDFIterator ( m_JointPDF, m_JointPDF->GetBufferedRegion() ); float px, py, pxy; double mival = 0; double mi; unsigned long ct = 0; typename JointPDFType::IndexType index; // for (unsigned int ii=this->m_Padding+1; iim_Padding-2; ii++) for( unsigned int ii = 0; ii < m_NumberOfHistogramBins; ii++ ) { MarginalPDFIndexType mind; mind[0] = ii; px = m_FixedImageMarginalPDF->GetPixel(mind); // for (unsigned int jj=this->m_Padding+1; jjm_Padding-2; jj++) for( unsigned int jj = 0; jj < m_NumberOfHistogramBins; jj++ ) { mind[0] = jj; py = m_MovingImageMarginalPDF->GetPixel(mind); float denom = px * py; index[0] = ii; index[1] = jj; // pxy=m_JointPDF->GetPixel(index); JointPDFValueType *pdfPtr = m_JointPDF->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ); // Move the pointer to the first affected bin int pdfMovingIndex = static_cast( jj ); pdfPtr += pdfMovingIndex; pxy = *(pdfPtr); mi = 0; if( fabs(denom) > 0 ) { if( pxy / denom > 0 ) { // true mi mi = pxy * log(pxy / denom); // test mi // mi = 1.0 + log(pxy/denom); ct++; } } mival += mi; } // std::cout << " II " << ii << " JJ " << ii << " pxy " << pxy << " px " << px << std::endl; } this->m_Energy = (-1.0) * mival / log(2); return this->m_Energy; } double ComputeSpatialMutualInformation() { float pxy; double SMI = 0; double JointEntropy = 0, JointEntropyXuY = 0, JointEntropyXYu = 0, JointEntropyXlY = 0, JointEntropyXYl = 0; double JointEntropyXuYl = 0, JointEntropyXlYu = 0, JointEntropyXrYu = 0, JointEntropyXuYr = 0; for( unsigned int ii = 0; ii < m_NumberOfHistogramBins; ii++ ) { for( unsigned int jj = 0; jj < m_NumberOfHistogramBins; jj++ ) { int pdfMovingIndex = static_cast( jj ); JointPDFValueType *pdfPtr = m_JointPDF->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ) + pdfMovingIndex; pxy = *(pdfPtr); if( fabs(pxy) > 0 ) { JointEntropy -= pxy * log(pxy); } pdfPtr = m_JointPDFXuY->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ) + pdfMovingIndex; pxy = *(pdfPtr); if( fabs(pxy) > 0 ) { JointEntropyXuY -= pxy * log(pxy); } pdfPtr = m_JointPDFXYu->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ) + pdfMovingIndex; pxy = *(pdfPtr); if( fabs(pxy) > 0 ) { JointEntropyXYu -= pxy * log(pxy); } pdfPtr = m_JointPDFXlY->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ) + pdfMovingIndex; pxy = *(pdfPtr); if( fabs(pxy) > 0 ) { JointEntropyXlY -= pxy * log(pxy); } pdfPtr = m_JointPDFXYl->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ) + pdfMovingIndex; pxy = *(pdfPtr); if( fabs(pxy) > 0 ) { JointEntropyXYl -= pxy * log(pxy); } pdfPtr = m_JointPDFXuYl->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ) + pdfMovingIndex; pxy = *(pdfPtr); if( fabs(pxy) > 0 ) { JointEntropyXuYl -= pxy * log(pxy); } pdfPtr = m_JointPDFXlYu->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ) + pdfMovingIndex; pxy = *(pdfPtr); if( fabs(pxy) > 0 ) { JointEntropyXlYu -= pxy * log(pxy); } pdfPtr = m_JointPDFXrYu->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ) + pdfMovingIndex; pxy = *(pdfPtr); if( fabs(pxy) > 0 ) { JointEntropyXrYu -= pxy * log(pxy); } pdfPtr = m_JointPDFXuYr->GetBufferPointer() + ( ii * m_NumberOfHistogramBins ) + pdfMovingIndex; pxy = *(pdfPtr); if( fabs(pxy) > 0 ) { JointEntropyXuYr -= pxy * log(pxy); } } } SMI = (0.5) * (JointEntropyXuY + JointEntropyXYu + JointEntropyXlY + JointEntropyXYl) - (0.25) * (4 * JointEntropy + JointEntropyXuYr + JointEntropyXrYu + JointEntropyXlYu + JointEntropyXuYl); // std::cout << " JE " << JointEntropy << " JEXuY " << JointEntropyXuY << " JEXYu " << JointEntropyXYu << // " // JEXuYr " << JointEntropyXuYr << std::endl; this->m_Energy = (-1.0) * fabs(SMI); return this->m_Energy; } virtual VectorType ComputeUpdateInv(const NeighborhoodType & neighborhood, void * /* globalData */, const FloatOffsetType & /* offset */ = FloatOffsetType(0.0) ) ITK_OVERRIDE { VectorType update; update.Fill(0.0); IndexType oindex = neighborhood.GetIndex(); FixedImageType* img = const_cast(this->Superclass::m_FixedImage.GetPointer() ); if( !img ) { return update; } typename FixedImageType::SpacingType spacing = img->GetSpacing(); typename FixedImageType::SizeType imagesize = img->GetLargestPossibleRegion().GetSize(); for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( oindex[dd] < 1 || oindex[dd] >= static_cast(imagesize[dd] - 1) ) { return update; } } CovariantVectorType fixedGradient; ParametersType fdvec1(ImageDimension); ParametersType fdvec2(ImageDimension); fdvec1.Fill(0); fdvec2.Fill(0); fixedGradient = m_FixedImageGradientCalculator->EvaluateAtIndex( oindex ); double nccm1 = 0; double loce = this->GetValueAndDerivativeInv(oindex, nccm1, fdvec1, fdvec2); float eps = 10; if( loce > eps ) { loce = eps; } if( loce < eps * (-1.0) ) { loce = eps * (-1.0); } for( unsigned int imd = 0; imd < ImageDimension; imd++ ) { update[imd] = loce * fixedGradient[imd] * spacing[imd] * (1); } if( this->m_MetricImage ) { this->m_MetricImage->SetPixel(oindex, loce); } return update; } /* Normalizing the image to the range of [0 1] */ double GetMovingParzenTerm( double intensity ) { double windowTerm = static_cast( intensity ) - this->m_MovingImageTrueMin; windowTerm = windowTerm / ( this->m_MovingImageTrueMax - this->m_MovingImageTrueMin ); return windowTerm; } double GetFixedParzenTerm( double intensity ) { double windowTerm = static_cast( intensity ) - this->m_FixedImageTrueMin; windowTerm = windowTerm / ( this->m_FixedImageTrueMax - this->m_FixedImageTrueMin ); return windowTerm; } /* find the image index in the number of histogram bins */ unsigned int FitIndexInBins( double windowTerm ) { unsigned int movingImageParzenWindowIndex = static_cast( this->m_Padding + (unsigned int)( windowTerm * (float)(this->m_NumberOfHistogramBins - 1 - this->m_Padding + 0.5) ) ); // Make sure the extreme values are in valid bins if( movingImageParzenWindowIndex < this->m_Padding ) { movingImageParzenWindowIndex = this->m_Padding - 1; } else if( movingImageParzenWindowIndex > (m_NumberOfHistogramBins - this->m_Padding ) ) { movingImageParzenWindowIndex = m_NumberOfHistogramBins - this->m_Padding - 1; } return movingImageParzenWindowIndex; } double FitContIndexInBins( double windowTerm ) { return (double) this->m_Padding + windowTerm * (float)(this->m_NumberOfHistogramBins - this->m_Padding); } virtual VectorType ComputeUpdate(const NeighborhoodType & neighborhood, void * /* globalData */, const FloatOffsetType & /* offset */ = FloatOffsetType(0.0) ) ITK_OVERRIDE { VectorType update; update.Fill(0.0); IndexType oindex = neighborhood.GetIndex(); FixedImageType* img = const_cast(this->Superclass::m_MovingImage.GetPointer() ); if( !img ) { return update; } typename FixedImageType::SpacingType spacing = img->GetSpacing(); typename FixedImageType::SizeType imagesize = img->GetLargestPossibleRegion().GetSize(); for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { if( oindex[dd] < 1 || oindex[dd] >= static_cast(imagesize[dd] - 1) ) { return update; } } CovariantVectorType movingGradient; ParametersType fdvec1(ImageDimension); ParametersType fdvec2(ImageDimension); fdvec1.Fill(0); fdvec2.Fill(0); movingGradient = m_MovingImageGradientCalculator->EvaluateAtIndex( oindex ); double nccm1 = 0; double loce = this->GetValueAndDerivative(oindex, nccm1, fdvec1, fdvec2); float eps = 10; if( loce > eps ) { loce = eps; } if( loce < eps * (-1.0) ) { loce = eps * (-1.0); } for( unsigned int imd = 0; imd < ImageDimension; imd++ ) { update[imd] = loce * movingGradient[imd] * spacing[imd] * (1); } return update; } void WriteImages() { if( this->m_MetricImage ) { typedef ImageFileWriter writertype; typename writertype::Pointer w = writertype::New(); w->SetInput( this->m_MetricImage); w->SetFileName("ZZmetric.nii.gz"); w->Write(); } } void SetOpticalFlow(bool b) { m_OpticalFlow = b; } typename JointPDFType::Pointer GetJointPDF() { return m_JointPDF; } typename JointPDFType::Pointer GetJointHist() { return m_JointHist; } void SetFixedImageMask( FixedImageType* img) { m_FixedImageMask = img; } /** FixedImage image neighborhood iterator type. */ typedef ConstNeighborhoodIterator FixedImageNeighborhoodIteratorType; /** A global data type for this class of equation. Used to store * iterators for the fixed image. */ struct GlobalDataStruct { FixedImageNeighborhoodIteratorType m_FixedImageIterator; }; /** The fixed image marginal PDF. */ mutable MarginalPDFType::Pointer m_FixedImageMarginalPDF; /** The moving image marginal PDF. */ mutable MarginalPDFType::Pointer m_MovingImageMarginalPDF; /** The joint PDF and PDF derivatives. */ typename JointPDFType::Pointer m_JointPDF; unsigned long m_NumberOfSpatialSamples; unsigned long m_NumberOfParameters; /** Variables to define the marginal and joint histograms. */ unsigned long m_NumberOfHistogramBins; double m_MovingImageNormalizedMin; double m_FixedImageNormalizedMin; double m_MovingImageTrueMin; double m_MovingImageTrueMax; double m_FixedImageTrueMin; double m_FixedImageTrueMax; double m_FixedImageBinSize; double m_MovingImageBinSize; protected: SpatialMutualInformationRegistrationFunction(); virtual ~SpatialMutualInformationRegistrationFunction() { }; void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; private: SpatialMutualInformationRegistrationFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented typename JointPDFType::Pointer m_JointHist; typename JointPDFDerivativesType::Pointer m_JointPDFDerivatives; typedef BSplineInterpolateImageFunction pdfintType; typename pdfintType::Pointer pdfinterpolator; // typename JointPDFType::Pointer m_JointEntropy; typename JointPDFType::Pointer m_JointPDFXuY; typename JointPDFType::Pointer m_JointPDFXYu; typename JointPDFType::Pointer m_JointPDFXlY; typename JointPDFType::Pointer m_JointPDFXYl; typename JointPDFType::Pointer m_JointPDFXuYl; typename JointPDFType::Pointer m_JointPDFXlYu; typename JointPDFType::Pointer m_JointPDFXrYu; typename JointPDFType::Pointer m_JointPDFXuYr; typename pdfintType::Pointer pdfinterpolatorXuY; typename pdfintType::Pointer pdfinterpolatorXYu; typename pdfintType::Pointer pdfinterpolatorXlY; typename pdfintType::Pointer pdfinterpolatorXYl; typename pdfintType::Pointer pdfinterpolatorXuYl; typename pdfintType::Pointer pdfinterpolatorXlYu; typename pdfintType::Pointer pdfinterpolatorXuYr; typename pdfintType::Pointer pdfinterpolatorXrYu; /** Typedefs for BSpline kernel and derivative functions. */ typedef BSplineKernelFunction<3> CubicBSplineFunctionType; typedef BSplineDerivativeKernelFunction<3> CubicBSplineDerivativeFunctionType; /** Cubic BSpline kernel for computing Parzen histograms. */ typename CubicBSplineFunctionType::Pointer m_CubicBSplineKernel; typename CubicBSplineDerivativeFunctionType::Pointer m_CubicBSplineDerivativeKernel; /** * Types and variables related to image derivative calculations. * If a BSplineInterpolationFunction is used, this class obtain * image derivatives from the BSpline interpolator. Otherwise, * image derivatives are computed using central differencing. */ typedef CovariantVector ImageDerivativesType; /** Boolean to indicate if the interpolator BSpline. */ bool m_InterpolatorIsBSpline; // boolean to determine if we use mono-modality assumption bool m_OpticalFlow; /** Typedefs for using BSpline interpolator. */ typedef BSplineInterpolateImageFunction BSplineInterpolatorType; /** Pointer to BSplineInterpolator. */ typename BSplineInterpolatorType::Pointer m_BSplineInterpolator; /** Typedefs for using central difference calculator. */ typedef CentralDifferenceImageFunction DerivativeFunctionType; /** Pointer to central difference calculator. */ typename DerivativeFunctionType::Pointer m_DerivativeCalculator; /** * Types and variables related to BSpline deformable transforms. * If the transform is of type third order BSplineTransform, * then we can speed up the metric derivative calculation by * only inspecting the parameters within the support region * of a mapped point. */ /** Boolean to indicate if the transform is BSpline deformable. */ /** The number of BSpline parameters per image dimension. */ long m_NumParametersPerDim; /** * The number of BSpline transform weights is the number of * of parameter in the support region (per dimension ). */ unsigned long m_NumBSplineWeights; /** * Enum of the deformabtion field spline order. */ enum { DeformationSplineOrder = 3 }; typename TFixedImage::SpacingType m_FixedImageSpacing; typename TFixedImage::PointType m_FixedImageOrigin; typedef FixedArray ParametersOffsetType; ParametersOffsetType m_ParametersOffset; mutable TransformPointer m_Transform; InterpolatorPointer m_Interpolator; InterpolatorPointer m_FixedImageInterpolator; InterpolatorPointer m_MovingImageInterpolator; GradientCalculatorPointer m_FixedImageGradientCalculator; GradientCalculatorPointer m_MovingImageGradientCalculator; FixedImagePointer m_FixedImageMask; MovingImagePointer m_MovingImageMask; double m_NormalizeMetric; float m_Normalizer; // typedef BSplineInterpolateImageFunction dpdfintType; // typename dpdfintType::Pointer dpdfinterpolator; typedef BSplineInterpolateImageFunction pdfintType2; typename pdfintType2::Pointer pdfinterpolator2; typename pdfintType2::Pointer pdfinterpolator3; unsigned int m_Padding; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkSpatialMutualInformationRegistrationFunction.cxx" #endif #endif ants-2.2.0/ImageRegistration/itkSyNDemonsRegistrationFunction.cxx000066400000000000000000000270221311104306400253050ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkSyNDemonsRegistrationFunction_hxx_ #define _itkSyNDemonsRegistrationFunction_hxx_ #include "itkSyNDemonsRegistrationFunction.h" #include "itkExceptionObject.h" #include "vnl/vnl_math.h" namespace itk { /* * Default constructor */ template SyNDemonsRegistrationFunction ::SyNDemonsRegistrationFunction() { RadiusType r; unsigned int j; for( j = 0; j < ImageDimension; j++ ) { r[j] = 0; } this->SetRadius(r); m_TimeStep = 1.0; m_DenominatorThreshold = 1e-9; m_IntensityDifferenceThreshold = 0.001; this->SetMovingImage(ITK_NULLPTR); this->SetFixedImage(ITK_NULLPTR); m_FixedImageSpacing.Fill( 1.0 ); m_FixedImageOrigin.Fill( 0.0 ); m_Normalizer = 1.0; m_FixedImageGradientCalculator = GradientCalculatorType::New(); typename DefaultInterpolatorType::Pointer interp = DefaultInterpolatorType::New(); m_MovingImageInterpolator = static_cast( interp.GetPointer() ); m_Metric = NumericTraits::max(); m_SumOfSquaredDifference = 0.0; m_NumberOfPixelsProcessed = 0L; m_RMSChange = NumericTraits::max(); m_SumOfSquaredChange = 0.0; m_MovingImageGradientCalculator = MovingImageGradientCalculatorType::New(); m_UseMovingImageGradient = false; m_UseSSD = false; } /* * Standard "PrintSelf" method. */ template void SyNDemonsRegistrationFunction ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "MovingImageIterpolator: "; os << m_MovingImageInterpolator.GetPointer() << std::endl; os << indent << "FixedImageGradientCalculator: "; os << m_FixedImageGradientCalculator.GetPointer() << std::endl; os << indent << "DenominatorThreshold: "; os << m_DenominatorThreshold << std::endl; os << indent << "IntensityDifferenceThreshold: "; os << m_IntensityDifferenceThreshold << std::endl; os << indent << "UseMovingImageGradient: "; os << m_UseMovingImageGradient << std::endl; os << indent << "Metric: "; os << m_Metric << std::endl; os << indent << "SumOfSquaredDifference: "; os << m_SumOfSquaredDifference << std::endl; os << indent << "NumberOfPixelsProcessed: "; os << m_NumberOfPixelsProcessed << std::endl; os << indent << "RMSChange: "; os << m_RMSChange << std::endl; os << indent << "SumOfSquaredChange: "; os << m_SumOfSquaredChange << std::endl; } /** * */ template void SyNDemonsRegistrationFunction ::SetIntensityDifferenceThreshold(double threshold) { m_IntensityDifferenceThreshold = threshold; } /** * */ template double SyNDemonsRegistrationFunction ::GetIntensityDifferenceThreshold() const { return m_IntensityDifferenceThreshold; } /* * Set the function state values before each iteration */ template void SyNDemonsRegistrationFunction ::InitializeIteration() { // std::cout << " INIT ITER " << std::endl; if( !this->GetMovingImage() || !this->GetFixedImage() || !m_MovingImageInterpolator ) { itkExceptionMacro( << "MovingImage, FixedImage and/or Interpolator not set" ); } // cache fixed image information m_FixedImageSpacing = this->GetFixedImage()->GetSpacing(); m_FixedImageOrigin = this->GetFixedImage()->GetOrigin(); // compute the normalizer m_Normalizer = 0.0; for( unsigned int k = 0; k < ImageDimension; k++ ) { m_Normalizer += m_FixedImageSpacing[k] * m_FixedImageSpacing[k]; } m_Normalizer /= static_cast( ImageDimension ); this->m_Energy = 0; // setup gradient calculator m_FixedImageGradientCalculator->SetInputImage( this->GetFixedImage() ); m_MovingImageGradientCalculator->SetInputImage( this->GetMovingImage() ); // setup moving image interpolator m_MovingImageInterpolator->SetInputImage( this->GetMovingImage() ); // initialize metric computation variables m_SumOfSquaredDifference = 0.0; m_NumberOfPixelsProcessed = 0L; m_SumOfSquaredChange = 0.0; } /* * Compute update at a specify neighbourhood */ template typename SyNDemonsRegistrationFunction ::PixelType SyNDemonsRegistrationFunction ::ComputeUpdate(const NeighborhoodType & it, void * gd, const FloatOffsetType & itkNotUsed(offset) ) { PixelType update; unsigned int j; IndexType index = it.GetIndex(); // Get fixed image related information double fixedValue; CovariantVectorType gradient; CovariantVectorType mgradient; double gradientSquaredMagnitude = 0; // Note: no need to check the index is within // fixed image buffer. This is done by the external filter. fixedValue = (double) this->GetFixedImage()->GetPixel( index ); double movingValue = (double)this->GetMovingImage()->GetPixel( index ); // if (fixedValue > 0)std::cout << " fxv " << fixedValue << " movingValue " << movingValue << std::endl; gradient = m_FixedImageGradientCalculator->EvaluateAtIndex( index ); mgradient = m_MovingImageGradientCalculator->EvaluateAtIndex( index ); for( j = 0; j < ImageDimension; j++ ) { if( this->m_UseMovingImageGradient ) { gradient[j] = gradient[j] + mgradient[j]; } gradientSquaredMagnitude += vnl_math_sqr( gradient[j] ); } /** * Compute Update. * In the original equation the denominator is defined as (g-f)^2 + grad_mag^2. * However there is a mismatch in units between the two terms. * The units for the second term is intensity^2/mm^2 while the * units for the first term is intensity^2. This mismatch is particularly * problematic when the fixed image does not have unit spacing. * In this implemenation, we normalize the first term by a factor K, * such that denominator = (g-f)^2/K + grad_mag^2 * where K = mean square spacing to compensate for the mismatch in units. */ double speedValue = fixedValue - movingValue; if( fabs(speedValue) < this->m_RobustnessParameter ) { speedValue = 0; } // update the metric GlobalDataStruct *globalData = reinterpret_cast( gd ); if( globalData ) { globalData->m_SumOfSquaredDifference += vnl_math_sqr( speedValue ); globalData->m_NumberOfPixelsProcessed += 1; } double denominator = vnl_math_sqr( speedValue ) / m_Normalizer + gradientSquaredMagnitude; this->m_Energy += speedValue * speedValue; if( m_UseSSD ) { denominator = 1; } if( vnl_math_abs(speedValue) < m_IntensityDifferenceThreshold || denominator < m_DenominatorThreshold ) { for( j = 0; j < ImageDimension; j++ ) { update[j] = 0.0; } return update; } for( j = 0; j < ImageDimension; j++ ) { update[j] = speedValue * gradient[j] / denominator; if( globalData ) { globalData->m_SumOfSquaredChange += vnl_math_sqr( update[j] ); } } return update; } /* * Compute update at a specify neighbourhood */ template typename SyNDemonsRegistrationFunction ::PixelType SyNDemonsRegistrationFunction ::ComputeUpdateInv(const NeighborhoodType & it, void * gd, const FloatOffsetType & itkNotUsed(offset) ) { PixelType update; unsigned int j; IndexType index = it.GetIndex(); // Get fixed image related information double fixedValue; CovariantVectorType gradient; double gradientSquaredMagnitude = 0; // Note: no need to check the index is within // fixed image buffer. This is done by the external filter. fixedValue = (double) this->GetFixedImage()->GetPixel( index ); double movingValue = (double)this->GetMovingImage()->GetPixel( index ); // if (fixedValue > 0)std::cout << " fxv " << fixedValue << " movingValue " << movingValue << std::endl; // gradient = m_FixedImageGradientCalculator->EvaluateAtIndex( index ); gradient = m_MovingImageGradientCalculator->EvaluateAtIndex( index ); for( j = 0; j < ImageDimension; j++ ) { gradientSquaredMagnitude += vnl_math_sqr( gradient[j] ); } /** * Compute Update. * In the original equation the denominator is defined as (g-f)^2 + grad_mag^2. * However there is a mismatch in units between the two terms. * The units for the second term is intensity^2/mm^2 while the * units for the first term is intensity^2. This mismatch is particularly * problematic when the fixed image does not have unit spacing. * In this implemenation, we normalize the first term by a factor K, * such that denominator = (g-f)^2/K + grad_mag^2 * where K = mean square spacing to compensate for the mismatch in units. */ double speedValue = movingValue - fixedValue; if( fabs(speedValue) < this->m_RobustnessParameter ) { speedValue = 0; } // update the metric GlobalDataStruct *globalData = reinterpret_cast( gd ); if( globalData ) { globalData->m_SumOfSquaredDifference += vnl_math_sqr( speedValue ); globalData->m_NumberOfPixelsProcessed += 1; } double denominator = vnl_math_sqr( speedValue ) / m_Normalizer + gradientSquaredMagnitude; if( vnl_math_abs(speedValue) < m_IntensityDifferenceThreshold || denominator < m_DenominatorThreshold ) { for( j = 0; j < ImageDimension; j++ ) { update[j] = 0.0; } return update; } for( j = 0; j < ImageDimension; j++ ) { update[j] = speedValue * gradient[j] / denominator; if( globalData ) { globalData->m_SumOfSquaredChange += vnl_math_sqr( update[j] ); } } return update; } /* * Update the metric and release the per-thread-global data. */ template void SyNDemonsRegistrationFunction ::ReleaseGlobalDataPointer( void *gd ) const { GlobalDataStruct *globalData = reinterpret_cast( gd ); m_SumOfSquaredDifference += globalData->m_SumOfSquaredDifference; m_NumberOfPixelsProcessed += globalData->m_NumberOfPixelsProcessed; m_SumOfSquaredChange += globalData->m_SumOfSquaredChange; if( m_NumberOfPixelsProcessed ) { m_Metric = m_SumOfSquaredDifference / static_cast( m_NumberOfPixelsProcessed ); m_RMSChange = std::sqrt( m_SumOfSquaredChange / static_cast( m_NumberOfPixelsProcessed ) ); } delete globalData; } } // end namespace itk #endif ants-2.2.0/ImageRegistration/itkSyNDemonsRegistrationFunction.h000066400000000000000000000236431311104306400247370ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkSyNDemonsRegistrationFunction_h_ #define _itkSyNDemonsRegistrationFunction_h_ #include "itkPDEDeformableRegistrationFunction.h" #include "itkPoint.h" #include "itkCovariantVector.h" #include "itkInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkCentralDifferenceImageFunction.h" namespace itk { /** * \class SyNDemonsRegistrationFunction * * This class encapsulate the PDE which drives the demons registration * algorithm. It is used by SyNDemonsRegistrationFilter to compute the * output deformation field which will map a moving image onto a * a fixed image. * * Non-integer moving image values are obtained by using * interpolation. The default interpolator is of type * LinearInterpolateImageFunction. The user may set other * interpolators via method SetMovingImageInterpolator. Note that the input * interpolator must derive from baseclass InterpolateImageFunction. * * This class is templated over the fixed image type, moving image type, * and the deformation field type. * * \warning This filter assumes that the fixed image type, moving image type * and deformation field type all have the same number of dimensions. * * \sa SyNDemonsRegistrationFilter * \ingroup FiniteDifferenceFunctions */ template class SyNDemonsRegistrationFunction : public AvantsPDEDeformableRegistrationFunction { public: /** Standard class typedefs. */ typedef SyNDemonsRegistrationFunction Self; typedef PDEDeformableRegistrationFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro( SyNDemonsRegistrationFunction, PDEDeformableRegistrationFunction ); /** MovingImage image type. */ typedef typename Superclass::MovingImageType MovingImageType; typedef typename Superclass::MovingImagePointer MovingImagePointer; /** FixedImage image type. */ typedef typename Superclass::FixedImageType FixedImageType; typedef typename Superclass::FixedImagePointer FixedImagePointer; typedef typename FixedImageType::IndexType IndexType; typedef typename FixedImageType::SizeType SizeType; typedef typename FixedImageType::SpacingType SpacingType; /** Deformation field type. */ typedef typename Superclass::DisplacementFieldType DisplacementFieldType; typedef typename Superclass::DisplacementFieldTypePointer DisplacementFieldTypePointer; /** Inherit some enums from the superclass. */ itkStaticConstMacro(ImageDimension, unsigned int, Superclass::ImageDimension); /** Inherit some enums from the superclass. */ typedef typename Superclass::PixelType PixelType; typedef typename Superclass::RadiusType RadiusType; typedef typename Superclass::NeighborhoodType NeighborhoodType; typedef typename Superclass::FloatOffsetType FloatOffsetType; typedef typename Superclass::TimeStepType TimeStepType; /** Interpolator type. */ typedef double CoordRepType; typedef InterpolateImageFunction InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; typedef typename InterpolatorType::PointType PointType; typedef LinearInterpolateImageFunction DefaultInterpolatorType; /** Covariant vector type. */ typedef CovariantVector CovariantVectorType; /** Fixed image gradient calculator type. */ typedef CentralDifferenceImageFunction GradientCalculatorType; typedef typename GradientCalculatorType::Pointer GradientCalculatorPointer; /** Moving image gradient calculator type. */ typedef CentralDifferenceImageFunction MovingImageGradientCalculatorType; typedef typename MovingImageGradientCalculatorType::Pointer MovingImageGradientCalculatorPointer; /** Set the moving image interpolator. */ void SetMovingImageInterpolator( InterpolatorType * ptr ) { m_MovingImageInterpolator = ptr; } /** Get the moving image interpolator. */ InterpolatorType * GetMovingImageInterpolator(void) { return m_MovingImageInterpolator; } /** This class uses a constant timestep of 1. */ virtual TimeStepType ComputeGlobalTimeStep(void * itkNotUsed(GlobalData) ) const ITK_OVERRIDE { return m_TimeStep; } /** Return a pointer to a global data structure that is passed to * this object from the solver at each calculation. */ virtual void * GetGlobalDataPointer() const ITK_OVERRIDE { GlobalDataStruct *global = new GlobalDataStruct(); global->m_SumOfSquaredDifference = 0.0; global->m_NumberOfPixelsProcessed = 0L; global->m_SumOfSquaredChange = 0; return global; } /** Release memory for global data structure. */ virtual void ReleaseGlobalDataPointer( void *GlobalData ) const ITK_OVERRIDE; /** Set the object's state before each iteration. */ virtual void InitializeIteration() ITK_OVERRIDE; /** This method is called by a finite difference solver image filter at * each pixel that does not lie on a data set boundary */ virtual PixelType ComputeUpdate(const NeighborhoodType & neighborhood, void *globalData, const FloatOffsetType & offset = FloatOffsetType( 0.0) ) ITK_OVERRIDE; virtual PixelType ComputeUpdateInv(const NeighborhoodType & neighborhood, void *globalData, const FloatOffsetType & offset = FloatOffsetType( 0.0) ) ITK_OVERRIDE; void SetUseSSD( bool b ) { this->m_UseSSD = b; } /** Get the metric value. The metric value is the mean square difference * in intensity between the fixed image and transforming moving image * computed over the the overlapping region between the two images. */ virtual double GetMetric() const { return m_Metric; } /** Get the rms change in deformation field. */ virtual double GetRMSChange() const { return m_RMSChange; } /** Select if the fixed image or moving image gradient is used for * the computating the demon forces. The fixed image gradient is used * by default. */ virtual void SetUseMovingImageGradient( bool flag ) { m_UseMovingImageGradient = flag; } virtual bool GetUseMovingImageGradient() const { return m_UseMovingImageGradient; } /** Set/Get the threshold below which the absolute difference of * intensity yields a match. When the intensities match between a * moving and fixed image pixel, the update vector (for that * iteration) will be the zero vector. Default is 0.001. */ virtual void SetIntensityDifferenceThreshold(double); virtual double GetIntensityDifferenceThreshold() const; protected: SyNDemonsRegistrationFunction(); ~SyNDemonsRegistrationFunction() { } void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; /** FixedImage image neighborhood iterator type. */ typedef ConstNeighborhoodIterator FixedImageNeighborhoodIteratorType; /** A global data type for this class of equation. Used to store * information for computing the metric. */ struct GlobalDataStruct { double m_SumOfSquaredDifference; unsigned long m_NumberOfPixelsProcessed; double m_SumOfSquaredChange; }; private: SyNDemonsRegistrationFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** Cache fixed image information. */ SpacingType m_FixedImageSpacing; PointType m_FixedImageOrigin; double m_Normalizer; /** Function to compute derivatives of the fixed image. */ GradientCalculatorPointer m_FixedImageGradientCalculator; /** Function to compute derivatives of the moving image. */ MovingImageGradientCalculatorPointer m_MovingImageGradientCalculator; bool m_UseMovingImageGradient; /** Function to interpolate the moving image. */ InterpolatorPointer m_MovingImageInterpolator; /** The global timestep. */ TimeStepType m_TimeStep; /** Threshold below which the denominator term is considered zero. */ double m_DenominatorThreshold; /** Threshold below which two intensity value are assumed to match. */ double m_IntensityDifferenceThreshold; /** The metric value is the mean square difference in intensity between * the fixed image and transforming moving image computed over the * the overlapping region between the two images. */ mutable double m_Metric; mutable double m_SumOfSquaredDifference; mutable unsigned long m_NumberOfPixelsProcessed; mutable double m_RMSChange; mutable double m_SumOfSquaredChange; bool m_UseSSD; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkSyNDemonsRegistrationFunction.cxx" #endif #endif ants-2.2.0/ImageRegistration/itkVectorParameterizedNeighborhoodOperatorImageFilter.h000066400000000000000000000157241311104306400311140ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkVectorParameterizedNeighborhoodOperatorImageFilter_h #define __itkVectorParameterizedNeighborhoodOperatorImageFilter_h #include "itkImageToImageFilter.h" #include "itkNeighborhoodOperator.h" #include "itkImage.h" #include "itkImageBoundaryCondition.h" #include "itkGaussianOperator.h" namespace itk { /** \class VectorParameterizedNeighborhoodOperatorImageFilter * \brief Applies a single scalar NeighborhoodOperator to an * itk::Vector image region. * * This filter calculates successive inner products between a single * NeighborhoodOperator and a NeighborhoodIterator, which is swept * across every pixel in an image region. For operators that are * symmetric across their axes, the result is a fast convolution * with the image region. Apply the mirror()'d operator for * non-symmetric NeighborhoodOperators. * * This filter assumes that the input and output images have * pixels which are itk::Vectors of the same vector dimension. * The input NeighbourhoodOperator must have a scalar type * that matches the ValueType of vector pixels. * * To apply a scalar NeighborhoodOperator to a scalar image * use NeighborhoodOperatorImageFilter instead. * * \ingroup ImageFilters * * \sa Image * \sa Neighborhood * \sa NeighborhoodOperator * \sa NeighborhoodIterator * \sa NeighborhoodOperatorImageFilter */ template class VectorParameterizedNeighborhoodOperatorImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef VectorParameterizedNeighborhoodOperatorImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods) */ itkTypeMacro(VectorParameterizedNeighborhoodOperatorImageFilter, ImageToImageFilter); /** Extract some information from the image types. Dimensionality * of the two images is assumed to be the same. */ typedef typename TInputImage::Pointer InputImagePointer; typedef typename TOutputImage::Pointer OutputImagePointer; typedef typename TOutputImage::PixelType OutputPixelType; typedef typename TOutputImage::InternalPixelType OutputInternalPixelType; typedef typename TInputImage::PixelType InputPixelType; typedef typename TInputImage::InternalPixelType InputInternalPixelType; typedef typename OutputPixelType::ValueType ScalarValueType; typedef TParamImage ParameterImageType; typedef typename TParamImage::Pointer ParameterImagePointer; typedef typename TParamImage::PixelType ParameterImageTypePixelType; /** Determine image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension ); /** Image typedef support */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; /** Typedef for generic boundary condition pointer */ typedef ImageBoundaryCondition * ImageBoundaryConditionPointerType; /** Superclass typedefs. */ typedef typename Superclass::OutputImageRegionType OutputImageRegionType; typedef itk::GaussianOperator OperatorType; // Neighborhood /** Sets the operator that is used to filter the image. Note * that the operator is stored as an internal COPY (it * is not part of the pipeline). */ void SetOperator(OperatorType & p) { m_Operator = p; this->Modified(); } /** Allows a user to override the internal boundary condition. Care should be * be taken to ensure that the overriding boundary condition is a persistent * object during the time it is referenced. The overriding condition * can be of a different type than the default type as long as it is * a subclass of ImageBoundaryCondition. */ void OverrideBoundaryCondition(const ImageBoundaryConditionPointerType i) { m_BoundsCondition = i; } /** VectorParameterizedNeighborhoodOperatorImageFilter needs a larger input requested * region than the output requested region. As such, * VectorParameterizedNeighborhoodOperatorImageFilter needs to provide an implementation for * GenerateInputRequestedRegion() in order to inform the pipeline * execution model. * * \sa ProcessObject::GenerateInputRequestedRegion() */ virtual void GenerateInputRequestedRegion() throw (InvalidRequestedRegionError) ITK_OVERRIDE; void SetParameterImage( ParameterImagePointer I) { m_ParameterImage = I; } protected: VectorParameterizedNeighborhoodOperatorImageFilter() { m_ParameterImage = ITK_NULLPTR; } virtual ~VectorParameterizedNeighborhoodOperatorImageFilter() { } /** VectorParameterizedNeighborhoodOperatorImageFilter can be implemented as a * multithreaded filter. Therefore, this implementation provides a * ThreadedGenerateData() routine which is called for each * processing thread. The output image data is allocated * automatically by the superclass prior to calling * ThreadedGenerateData(). ThreadedGenerateData can only write to * the portion of the output image specified by the parameter * "outputRegionForThread" * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) ITK_OVERRIDE; void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE { Superclass::PrintSelf(os, indent); } private: VectorParameterizedNeighborhoodOperatorImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** Pointer to the internal operator used to filter the image. */ OperatorType m_Operator; /** Pointer to a persistent boundary condition object used * for the image iterator. */ ImageBoundaryConditionPointerType m_BoundsCondition; ParameterImagePointer m_ParameterImage; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkVectorParameterizedNeighborhoodOperatorImageFilter.hxx" #endif #endif ants-2.2.0/ImageRegistration/itkVectorParameterizedNeighborhoodOperatorImageFilter.hxx000066400000000000000000000130001311104306400314550ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkVectorParameterizedNeighborhoodOperatorImageFilter_hxx #define _itkVectorParameterizedNeighborhoodOperatorImageFilter_hxx #include "itkVectorParameterizedNeighborhoodOperatorImageFilter.h" #include "itkNeighborhoodAlgorithm.h" #include "itkVectorNeighborhoodInnerProduct.h" #include "itkImageRegionIterator.h" #include "itkConstNeighborhoodIterator.h" #include "itkProgressReporter.h" namespace itk { template void VectorParameterizedNeighborhoodOperatorImageFilter ::GenerateInputRequestedRegion() throw (InvalidRequestedRegionError) { // call the superclass' implementation of this method. this should // copy the output requested region to the input requested region Superclass::GenerateInputRequestedRegion(); // get pointers to the input and output InputImagePointer inputPtr = const_cast( this->GetInput() ); if( !inputPtr ) { return; } // get a copy of the input requested region (should equal the output // requested region) typename TInputImage::RegionType inputRequestedRegion; inputRequestedRegion = inputPtr->GetRequestedRegion(); // pad the input requested region by the operator radius inputRequestedRegion.PadByRadius( m_Operator.GetRadius() ); // crop the input requested region at the input's largest possible region if( inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion() ) ) { inputPtr->SetRequestedRegion( inputRequestedRegion ); return; } else { // Couldn't crop the region (requested region is outside the largest // possible region). Throw an exception. // store what we tried to request (prior to trying to crop) inputPtr->SetRequestedRegion( inputRequestedRegion ); // build an exception InvalidRequestedRegionError e(__FILE__, __LINE__); std::ostringstream msg; msg << static_cast(this->GetNameOfClass() ) << "::GenerateInputRequestedRegion()"; e.SetLocation(msg.str().c_str() ); e.SetDescription("Requested region is (at least partially) outside the largest possible region."); e.SetDataObject(inputPtr); throw e; } } template void VectorParameterizedNeighborhoodOperatorImageFilter ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId) { typedef NeighborhoodAlgorithm::ImageBoundaryFacesCalculator BFC; typedef typename BFC::FaceListType FaceListType; VectorNeighborhoodInnerProduct smartInnerProduct; BFC faceCalculator; FaceListType faceList; // Allocate output OutputImageType * output = this->GetOutput(); const InputImageType *input = this->GetInput(); // Break the input into a series of regions. The first region is free // of boundary conditions, the rest with boundary conditions. Note, // we pass in the input image and the OUTPUT requested region. We are // only concerned with centering the neighborhood operator at the // pixels that correspond to output pixels. faceList = faceCalculator(input, outputRegionForThread, m_Operator.GetRadius() ); typename FaceListType::iterator fit; // support progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels() ); ImageRegionIteratorWithIndex it; // Process non-boundary region and then each of the boundary faces. // These are N-d regions which border the edge of the buffer. ConstNeighborhoodIterator bit; for( fit = faceList.begin(); fit != faceList.end(); ++fit ) { bit = ConstNeighborhoodIterator(m_Operator.GetRadius(), input, *fit); it = ImageRegionIteratorWithIndex(output, *fit); bit.GoToBegin(); while( !bit.IsAtEnd() ) { if( m_ParameterImage ) { // float max=1.45; float param = m_ParameterImage->GetPixel(it.GetIndex() ); // if (param > 0.5 || param < 2.0 ) param = 0.0; // if (param < 1./max && param > 0 ) param = 1./max; // if (param > max && param > 0 ) param = max; // if (param < 1.0 && param > 0) param=1.0/param; // std::cout << " param " << param ; if( param <= 0 ) { it.Value() = input->GetPixel(it.GetIndex() ); } else { m_Operator.SetVariance( param ); m_Operator.CreateDirectional(); it.Value() = smartInnerProduct(bit, m_Operator); } } else { it.Value() = smartInnerProduct(bit, m_Operator); } ++bit; ++it; progress.CompletedPixel(); } } } } // end namespace itk #endif ants-2.2.0/ImageSegmentation/000077500000000000000000000000001311104306400160515ustar00rootroot00000000000000ants-2.2.0/ImageSegmentation/antsAtroposSegmentationImageFilter.h000066400000000000000000000770611311104306400252410ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsAtroposSegmentationImageFilter_h #define __antsAtroposSegmentationImageFilter_h #include "itkImageToImageFilter.h" #include "antsListSampleFunction.h" #include "antsListSampleToListSampleFilter.h" #include "itkArray.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkFixedArray.h" #include "itkListSample.h" #include "itkMersenneTwisterRandomVariateGenerator.h" #include "itkNeighborhoodIterator.h" #include "itkPointSet.h" #include "itkSymmetricSecondRankTensor.h" #include "itkVector.h" #include #include #include #include namespace itk { namespace ants { /** \class AtroposSegmentationImageFilter * \brief Atropos: A Priori Classification with Registration Initialized * Template Assistance * * This filter provides an Expectation-Maximization framework for statistical * segmentation where the intensity profile of each class is modeled as a * mixture model and spatial smoothness is enforced by an MRF prior. * * Initial labeling can be performed by otsu thresholding, kmeans clustering, * a set of user-specified prior probability images, or a prior label image. * If specified, the latter two initialization options are also used as * priors in the MRF update step. * * The assumed labeling is such that classes are assigned consecutive * indices 1, 2, 3, etc. Label 0 is reserved for the background when a * mask is specified. * */ template , class TClassifiedImage = TMaskImage> class AtroposSegmentationImageFilter : public ImageToImageFilter { public: /** Standard class typdedefs. */ typedef AtroposSegmentationImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( AtroposSegmentationImageFilter, ImageToImageFilter ); /** Dimension of the images. */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); itkStaticConstMacro( ClassifiedImageDimension, unsigned int, TClassifiedImage::ImageDimension ); itkStaticConstMacro( MaskImageDimension, unsigned int, TMaskImage::ImageDimension ); /** Typedef support of input types. */ typedef TInputImage ImageType; typedef typename ImageType::PixelType PixelType; typedef typename ImageType::IndexType IndexType; typedef typename ImageType::SizeType SizeType; typedef TMaskImage MaskImageType; typedef typename MaskImageType::PixelType MaskLabelType; typedef TClassifiedImage ClassifiedImageType; typedef typename ClassifiedImageType::Pointer ClassifiedImagePointer; typedef typename ClassifiedImageType::PixelType LabelType; /** Some convenient typedefs. */ typedef float RealType; typedef Image RealImageType; typedef typename RealImageType::Pointer RealImagePointer; typedef FixedArray ArrayType; typedef PointSet SparseImageType; typedef typename SparseImageType::Pointer SparseImagePointer; /** Mixture model component typedefs */ typedef Array MeasurementVectorType; typedef typename itk::Statistics::ListSample SampleType; typedef SmartPointer SamplePointer; typedef ants::Statistics::ListSampleFunction LikelihoodFunctionType; typedef typename LikelihoodFunctionType::Pointer LikelihoodFunctionPointer; typedef typename LikelihoodFunctionType:: ListSampleWeightArrayType WeightArrayType; typedef std::vector PartialVolumeLabelSetType; typedef std::vector PartialVolumeClassesType; /** Outlier handling typedefs */ typedef ants::Statistics:: ListSampleToListSampleFilter OutlierHandlingFilterType; /** Randomizer typedefs */ typedef itk::Statistics:: MersenneTwisterRandomVariateGenerator RandomizerType; typedef RandomizerType::IntegerType RandomizerSeedType; /** B-spline fitting typedefs */ typedef Vector ScalarType; typedef Image ScalarImageType; typedef PointSet PointSetType; typedef BSplineScatteredDataPointSetToImageFilter BSplineFilterType; typedef typename BSplineFilterType::PointDataImageType ControlPointLatticeType; typedef typename ControlPointLatticeType::Pointer ControlPointLatticePointer; typedef std::vector ControlPointLatticeContainerType; /** Initialization typedefs */ enum InitializationStrategyType { Random, KMeans, Otsu, PriorProbabilityImages, PriorLabelImage }; typedef std::pair LabelParametersType; typedef std::map LabelParameterMapType; typedef Array ParametersType; /** Posterior probability formulation typedefs */ enum PosteriorProbabilityFormulationType { Socrates, Plato, Aristotle, Sigmoid }; // ivars Set/Get functionality /** * Set the number of tissue classes which is clamped from below at 2. * Default = 3. */ itkSetClampMacro( NumberOfTissueClasses, LabelType, 2, NumericTraits::max() ); /** * Get the number of segmentation classes. */ itkGetConstMacro( NumberOfTissueClasses, unsigned int ); /** * Set the partial-volume-label set one at a time. */ void AddPartialVolumeLabelSet( PartialVolumeLabelSetType ); /** * Get the number of partial volume classes */ itkGetConstMacro( NumberOfPartialVolumeClasses, unsigned int ); /** * The user can specify whether or not to use the partial volume likelihoods, * in which case the partial volume class is considered separate from the * tissue classes. Alternatively, one can use the MRF only to handle * partial volume in which case, partial volume voxels are not considered * as separate classes. */ itkSetMacro( UsePartialVolumeLikelihoods, bool ); /** * The user can specify whether or not to use the partial volume likelihoods, * in which case the partial volume class is considered separate from the * tissue classes. Alternatively, one can use the MRF only to handle * partial volume in which case, partial volume voxels are not considered * as separate classes. */ itkGetConstMacro( UsePartialVolumeLikelihoods, bool ); /** * The user can specify whether or not to use the partial volume likelihoods, * in which case the partial volume class is considered separate from the * tissue classes. Alternatively, one can use the MRF only to handle * partial volume in which case, partial volume voxels are not considered * as separate classes. */ itkBooleanMacro( UsePartialVolumeLikelihoods ); /** * Set the maximum number of iterations. The algorithm terminates at either * the maximum number of iterations or when the convergence threshold has * been exceeded. Default = 5. */ itkSetMacro( MaximumNumberOfIterations, unsigned int ); /** * Get the maximum number of iterations. */ itkGetConstMacro( MaximumNumberOfIterations, unsigned int ); /** * Set the convergence threshold. The algorithm terminates at either * the maximum number of iterations or when the convergence threshold has * been exceeded. Default = 0.001. */ itkSetMacro( ConvergenceThreshold, RealType ); /** * Get the convergence threshold */ itkGetConstMacro( ConvergenceThreshold, RealType ); /** * Get the current convergence posterior probability for comparison with the * convergence threshold at each iteration. Also is used to report progress. */ itkGetConstMacro( CurrentPosteriorProbability, RealType ); /** * Get the current number of iterations for comparison with the maximum * number of iterations. Also is used to report progress. */ itkGetConstMacro( ElapsedIterations, unsigned int ); /** * Set the MRF smoothing parameter (sometimes designated as \beta in the * literature) which is clamped at 0.0 from below. Greater values increase * cause a greater spatial homogeneity in the final labeling. Default value * = 0.3. */ itkSetClampMacro( MRFSmoothingFactor, RealType, 0.0, NumericTraits::max() ); /** * Get the MRF smoothing parameter. */ itkGetConstMacro( MRFSmoothingFactor, RealType ); /** * Set the MRF smoothing radius. The radius can be set independently in each * dimension. Also note that the each neighbor's contribution in calculating * the MRF-based prior is weighted by its distance to the center voxel. * Default value = 1^(ImageDimension). */ itkSetMacro( MRFRadius, ArrayType ); /** * Get the MRF smoothing radius. */ itkGetConstMacro( MRFRadius, ArrayType ); /** * Set the MRF neighborhood-defining image. */ itkSetObjectMacro( MRFCoefficientImage, RealImageType ); /** * Get the MRF neighborhood-defining image. */ itkGetConstObjectMacro( MRFCoefficientImage, RealImageType ); /** * Set the annealing temperature for ICM asynchronous updating. For values * different from unity, the posterior probability is exponentiated by the * inverse of the annealing temperature, i.e. posterior probability \prop * (likelihoods * priors)^(1/T) where T is specified annealing temperature * raised to the number of elapsed iterations. Default value = 1.0. */ itkSetClampMacro( InitialAnnealingTemperature, RealType, 0.0, NumericTraits::max() ); /** * Get the initial annealing temperature. For values * different from unity, the posterior probability is exponentiated by the * inverse of the annealing temperature, i.e. posterior probability \prop * (likelihoods * priors)^(1/T) where T is specified annealing temperature * raised to the number of elapsed iterations. Default value = 1.0. */ itkGetConstMacro( InitialAnnealingTemperature, RealType ); /** * Set the minimum annealing temperature for ICM asynchronous updating. * Typically, the algorithm becomes unstable for values < 0.1. Default value * = 0.1. */ itkSetClampMacro( MinimumAnnealingTemperature, RealType, 0.0, NumericTraits::max() ); /** * Get the minimum annealing temperature for ICM asynchronous updating. * Typically, the algorithm becomes unstable for values < 0.1. Default value * = 0.1. */ itkGetConstMacro( MinimumAnnealingTemperature, RealType ); /** * Set the annealing rate for ICM asynchronous updating. For values * different from unity, the posterior probability is exponentiated by the * inverse of the annealing temperature, i.e. posterior probability \prop * (likelihoods * priors)^(1/T) where T is specified annealing temperature * raised to the number of elapsed iterations. Default value = 1.0. */ itkSetClampMacro( AnnealingRate, RealType, 0.0, 1.0 ); /** * Set the annealing rate for ICM asynchronous updating. For values * different from unity, the posterior probability is exponentiated by the * inverse of the annealing temperature, i.e. posterior probability \prop * (likelihoods * priors)^(1/T) where T is specified annealing temperature * raised to the number of elapsed iterations. Default value = 1.0. */ itkGetConstMacro( AnnealingRate, RealType ); /** * Set initialization unsigned integer for random number generator. Default is to * initialize randomly using the system clock. */ void SetRandomizerInitializationSeed( const RandomizerSeedType ); /** * Set initialization unsigned integer for random number generator. Default is to * initialize randomly using the system clock. */ itkGetConstMacro( RandomizerInitializationSeed, RandomizerSeedType ); /** * Set the initialization strategy. Initialization can occur without prior * information using kmeans or otsu thresholding or with prior information * using prior label images or prior probability images. Default is Kmeans. */ itkSetMacro( InitializationStrategy, InitializationStrategyType ); /** * Get the initialization strategy. */ itkGetConstMacro( InitializationStrategy, InitializationStrategyType ); /** * Set the initial kmeans parameters. For kmeans initialization, one can * set the initial cluster centers (for the first intensity image only). */ itkSetMacro( InitialKMeansParameters, ParametersType ); /** * Get the initial kmeans parameters. */ itkGetConstMacro( InitialKMeansParameters, ParametersType ); /** * Set the posterior probability formulation type. This flexibility is more * for developmental experimentation. Most applications will use the * default. Default = Socrates. */ itkSetMacro( PosteriorProbabilityFormulation, PosteriorProbabilityFormulationType ); /** * Get the posterior probability formulation. */ itkGetConstMacro( PosteriorProbabilityFormulation, PosteriorProbabilityFormulationType ); /** * Set whether or not to use the mixture model proportions. These proportion * parameters form part of the finite mixture formulation. Default = true. */ itkSetMacro( UseMixtureModelProportions, bool ); /** * Get the value of boolean describing whether or not the mixture model * proportions should be used. Default = true. */ itkGetConstMacro( UseMixtureModelProportions, bool ); /** * Set the value of the boolean parameter dictating whether or not memory * usage should be minimized. Memory minimization takes more time per * iteration but the resulting memory footprint allows one to perform * problems with a large number of classes. Default value = false. */ itkSetMacro( MinimizeMemoryUsage, bool ); /** * Get the value of the boolean parameter dictating whether or not memory * usage should be minimized. */ itkGetConstMacro( MinimizeMemoryUsage, bool ); /** * Set the value of the boolean parameter dictating whether or not memory * usage should be minimized. Memory minimization takes more time per * iteration but the resulting memory footprint allows one to perform * problems with a large number of classes. Default value = false. */ itkBooleanMacro( MinimizeMemoryUsage ); /** * Set the prior probability threshold value. This determines what pixel * values are included in the sparse representation of the prior probability * images. Default value = */ itkSetClampMacro( ProbabilityThreshold, RealType, 0.0, 1.0 ); /** * Get the prior probability threshold value. */ itkGetConstMacro( ProbabilityThreshold, RealType ); // The following parameters are used for adaptive smoothing of one or more of // the intensity images. /** * Set the spline order of the adaptive smoothing. Default = 3. */ itkSetMacro( SplineOrder, unsigned int ); /** * Get the spline order of the adaptive smoothing. */ itkGetConstMacro( SplineOrder, unsigned int ); /** * Set the number of fitting levels for the adaptive smoothing. Default = 6. */ itkSetMacro( NumberOfLevels, ArrayType ); /** * Get the number of fitting levels for the adaptive smoothing. */ itkGetConstMacro( NumberOfLevels, ArrayType ); /** * Set the control point grid size for the adaptive smoothing. Default = * 4^(ImageDimension) for a mesh size of 1^(ImageDimension). */ itkSetMacro( NumberOfControlPoints, ArrayType ); /** * Get the control point grid size. */ itkGetConstMacro( NumberOfControlPoints, ArrayType ); /** * Set the adaptive smoothing weight clamped between 0 and 1 which weights * between using just the intensity image (weight = 0) and using only the * full smoothed image (weight = 1). Each intensity input image uses a * seperate smoothing weight value. */ void SetAdaptiveSmoothingWeight( unsigned int idx, RealType weight ) { RealType clampedWeight = vnl_math_min( NumericTraits::OneValue(), vnl_math_max( NumericTraits::ZeroValue(), weight ) ); if( idx >= this->m_AdaptiveSmoothingWeights.size() ) { this->m_AdaptiveSmoothingWeights.resize( idx + 1 ); this->m_AdaptiveSmoothingWeights[idx] = clampedWeight; this->Modified(); } if( this->m_AdaptiveSmoothingWeights[idx] != weight ) { this->m_AdaptiveSmoothingWeights[idx] = clampedWeight; this->Modified(); } } /** * Get the adaptive smoothing weight for a specific intensity image. */ RealType GetAdaptiveSmoothingWeight( unsigned int idx ) { if( idx < this->m_AdaptiveSmoothingWeights.size() ) { return this->m_AdaptiveSmoothingWeights[idx]; } else { return 0; } } /** * Set the prior label parameters. For each class/label for label propagation * the boundary probabilty value is set and the exponential decay parameter. * The prior labeled regions are weighted linearly from the boundary to the * center of the region (as defined by either a Euclidean or Geodesic * distance) whereas outside the region, the probability value is modulated * by an exponential characterized by the decay parameter. */ void SetPriorLabelParameterMap( LabelParameterMapType m ) { this->m_PriorLabelParameterMap = m; this->Modified(); } /** * Get the prior label parameters. */ void GetPriorLabelParameterMap() { return this->m_PriorLabelParameterMap; } /** * Set the prior probability weight. Determines what percentage of the * prior probability information should be included in the posterior * probability information. */ itkSetClampMacro( PriorProbabilityWeight, RealType, 0.0, 1.e9 ); /** * Get the prior probability weight. */ itkGetConstMacro( PriorProbabilityWeight, RealType ); /** * Set a prior probability image (numbered between 1,...,numberOfClasses). */ void SetPriorProbabilityImage( unsigned int whichClass, RealImageType * prior ); /** * Get a prior probability image (numbered between 1,...,numberOfClasses). */ RealImagePointer GetPriorProbabilityImage( unsigned int whichClass ) const; /** * Set the prior label image which is assumed to have intensity values \in * {1,...,numberOfClasses} */ void SetPriorLabelImage( const ClassifiedImageType * prior ); /** * Get the prior label image. */ const ClassifiedImageType * GetPriorLabelImage() const; /** * Get the number of intensity images used during the segmentation process. */ itkGetConstMacro( NumberOfIntensityImages, unsigned int ); /** * Set the input intensity image (numbered between 1,...,numberOfClasses) */ void SetIntensityImage( unsigned int which, const ImageType * image ); /** * Get the input intensity image (numbered between 1,...,numberOfClasses) */ const ImageType * GetIntensityImage( unsigned int which ) const; /** * Set the mask image. The regional mask defines the domain of the * segmentation. */ void SetMaskImage( const MaskImageType * mask ); /** * Get the mask image. */ const MaskImageType * GetMaskImage() const; /** * Set the label propagation type. Euclidean distance uses the Maurer distance * transform to calculate the distance transform image. Otherwise the fast * marching filter is used to produce the geodesic distance. The former option * is faster but for non-Euclidean shapes (such as the cortex), it might be * more accurate to use the latter option. Default = false. */ itkSetMacro( UseEuclideanDistanceForPriorLabels, bool ); /** * Get the label propagation type. */ itkGetConstMacro( UseEuclideanDistanceForPriorLabels, bool ); /** * Set the label propagation type. Euclidean distance uses the Maurer distance * transform to calculate the distance transform image. Otherwise the fast * marching filter is used to produce the geodesic distance. The former option * is faster but for non-Euclidean shapes (such as the cortex), it might be * more accurate to use the latter option. Default = false. */ itkBooleanMacro( UseEuclideanDistanceForPriorLabels ); /** * Set the outlier handling filter. This takes the intensity samples from the * input images and modifies the sample such that the outlier effects of the * sample points are removed. Default = NULL. */ itkSetObjectMacro( OutlierHandlingFilter, OutlierHandlingFilterType ); /** * Get the outlier handling filter. */ itkGetModifiableObjectMacro( OutlierHandlingFilter, OutlierHandlingFilterType ); /** * Set the likelihood function for a specified class. These functions are * traditionally characterized as parametric, i.e. Gaussian, or nonparametric. * A likelihood function must be assigned for each class. */ void SetLikelihoodFunction( unsigned int n, LikelihoodFunctionType *prob ) { if( n < this->m_MixtureModelComponents.size() && this->m_MixtureModelComponents[n] != prob ) { this->m_MixtureModelComponents[n] = prob; this->Modified(); } else if( n >= this->m_MixtureModelComponents.size() ) { this->m_MixtureModelComponents.resize( n + 1 ); this->m_MixtureModelComponents[n] = prob; this->Modified(); } } /** * Get the likelihood function for a specified class. */ LikelihoodFunctionType * GetLikelihoodFunction( unsigned int n ) { if( n < this->m_MixtureModelComponents.size() ) { return this->m_MixtureModelComponents[n].GetPointer(); } else { return ITK_NULLPTR; } } /** * Get the likelihood image for a specified class. Note that this function * facilitates looking at the likelihood image for the user but is not used * internally during the optimization of the segmentation solution. */ RealImagePointer GetLikelihoodImage( unsigned int ); /** * Get the posterior probability image. This function provides the soft * classification results. */ RealImagePointer GetPosteriorProbabilityImage( unsigned int ); /** * Get the smooth intensity image. Available when adaptive smoothing is * enabled. */ RealImagePointer GetSmoothIntensityImageFromPriorImage( unsigned int, unsigned int ); /** * Get the distance prior probability image. Available when prior images are * used. */ RealImagePointer GetDistancePriorProbabilityImage( unsigned int ); /** * Boolean variable governing the update scheme. If set to true (default), an * asynchronous approach to updating the class labels is performed which * has theoretical convergence properties. */ itkBooleanMacro( UseAsynchronousUpdating ); /** * Boolean variable governing the update scheme. If set to true (default), an * asynchronous approach to updating the class labels is performed which * has theoretical convergence properties. */ itkSetMacro( UseAsynchronousUpdating, bool ); /** * Boolean variable governing the update scheme. If set to true (default), an * asynchronous approach to updating the class labels is performed which * has theoretical convergence properties. */ itkGetConstMacro( UseAsynchronousUpdating, bool ); /** * Set the number of maximum allowed ICM iterations. When asynchronous * updating is used, at each iteration an ICM optimization step occurs in * which the posterior is maximized. During the ICM optimization, monotonic * increase of the posterior is guaranteed during the iterations forming * the optimization which usually maximizes out between 5-10 iterations. */ itkSetMacro( MaximumNumberOfICMIterations, unsigned int ); /** * Get the number of maximum allowed ICM iterations. When asynchronous * updating is used, at each iteration an ICM optimization step occurs in * which the posterior is maximized. During the ICM optimization, monotonic * increase of the posterior is guaranteed during the iterations forming * the optimization which usually maximizes out between 5-10 iterations. */ itkGetConstMacro( MaximumNumberOfICMIterations, unsigned int ); /** * Get the ICM code image. */ ClassifiedImagePointer GetICMCodeImage() { return this->m_ICMCodeImage; }; #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro( SameDimensionCheck1, ( Concept::SameDimension ) ); itkConceptMacro( SameDimensionCheck2, ( Concept::SameDimension ) ); /** End concept checking */ #endif protected: AtroposSegmentationImageFilter(); ~AtroposSegmentationImageFilter(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void GenerateData() ITK_OVERRIDE; private: AtroposSegmentationImageFilter( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented /** * Initialize the segmentation labeling. */ void GenerateInitialClassLabeling(); /** * Initialize labeling using otsu thresholding on the first input image. */ void GenerateInitialClassLabelingWithOtsuThresholding(); /** * Initialize labeling using kmeans classification. */ void GenerateInitialClassLabelingWithKMeansClustering(); /** * Initialize labeling using prior probability images. */ void GenerateInitialClassLabelingWithPriorProbabilityImages(); /** * Update the class labeling at each iteration using asynchronous ICM updating. * and return the max posterior probability. */ RealType UpdateClassLabeling(); /** * Compute the ICM code image for asynchronous updating to ensure that the * the local MRF neighborhoods are updated independently. For more information * see notes for the bool variable m_UseAsynchronousUpdating. */ void ComputeICMCodeImage(); /** * This function returns a set of samples for each class such that each * measurement vector of the returned SampleType corresponds to a single * voxel across the set of auxiliary and input images. */ typename SampleType::Pointer GetScalarSamples(); /** * Calculate the local posterior probability. */ RealType CalculateLocalPosteriorProbability( RealType, RealType, RealType, RealType, RealType, IndexType, unsigned int ); void EvaluateMRFNeighborhoodWeights( ConstNeighborhoodIterator, Array & ); RealType PerformLocalLabelingUpdate( NeighborhoodIterator ); // ivars unsigned int m_NumberOfTissueClasses; unsigned int m_NumberOfPartialVolumeClasses; PartialVolumeClassesType m_PartialVolumeClasses; bool m_UsePartialVolumeLikelihoods; unsigned int m_NumberOfIntensityImages; unsigned int m_ElapsedIterations; unsigned int m_MaximumNumberOfIterations; RealType m_CurrentPosteriorProbability; RealType m_ConvergenceThreshold; std::vector m_MixtureModelComponents; Array m_MixtureModelProportions; InitializationStrategyType m_InitializationStrategy; ParametersType m_InitialKMeansParameters; PosteriorProbabilityFormulationType m_PosteriorProbabilityFormulation; bool m_UseMixtureModelProportions; RealType m_InitialAnnealingTemperature; RealType m_MinimumAnnealingTemperature; RealType m_AnnealingRate; typename OutlierHandlingFilterType::Pointer m_OutlierHandlingFilter; typename RandomizerType::Pointer m_Randomizer; ArrayType m_MRFRadius; RealType m_MRFSmoothingFactor; RealImagePointer m_MRFCoefficientImage; typename ClassifiedImageType::SpacingType m_ImageSpacing; unsigned int m_MaximumICMCode; ClassifiedImagePointer m_ICMCodeImage; bool m_UseAsynchronousUpdating; unsigned int m_MaximumNumberOfICMIterations; RandomizerSeedType m_RandomizerInitializationSeed; std::vector m_AdaptiveSmoothingWeights; RealType m_PriorProbabilityWeight; LabelParameterMapType m_PriorLabelParameterMap; RealType m_ProbabilityThreshold; std::vector m_PriorProbabilityImages; std::vector m_PriorProbabilitySparseImages; unsigned int m_SplineOrder; ArrayType m_NumberOfLevels; ArrayType m_NumberOfControlPoints; std::vector m_ControlPointLattices; RealImagePointer m_SumDistancePriorProbabilityImage; RealImagePointer m_SumPosteriorProbabilityImage; bool m_MinimizeMemoryUsage; bool m_UseEuclideanDistanceForPriorLabels; std::vector m_DistancePriorProbabilityImages; std::vector m_PosteriorProbabilityImages; itk::Array m_LabelVolumes; std::vector m_IntensityImages; typename ClassifiedImageType::ConstPointer m_PriorLabelImage; typename MaskImageType::ConstPointer m_MaskImage; // inline functions to help with the sparse image creation inline typename RealImageType::IndexType NumberToIndex( unsigned long number, const SizeType size ) const { IndexType k; k[0] = 1; for( unsigned int i = 1; i < ImageDimension; i++ ) { k[i] = size[i - 1] * k[i - 1]; } IndexType index; for( unsigned int i = 0; i < ImageDimension; i++ ) { index[ImageDimension - i - 1] = static_cast( number / k[ImageDimension - i - 1] ); number %= k[ImageDimension - i - 1]; } return index; } inline unsigned long IndexToNumber( const IndexType k, const SizeType size ) const { unsigned long number = k[0]; for( unsigned int i = 1; i < ImageDimension; i++ ) { unsigned long s = 1; for( unsigned int j = 0; j < i; j++ ) { s *= size[j]; } number += s * k[i]; } return number; } }; } // namespace ants } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsAtroposSegmentationImageFilter.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsAtroposSegmentationImageFilter.hxx000066400000000000000000003635571311104306400256310ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsAtroposSegmentationImageFilter_hxx #define __antsAtroposSegmentationImageFilter_hxx #include "antsAtroposSegmentationImageFilter.h" #include "antsGaussianListSampleFunction.h" #include "antsAllocImage.h" #include "itkAddImageFilter.h" #include "itkAddConstantToImageFilter.h" #include "itkBinaryContourImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkBSplineControlPointImageFilter.h" #include "itkCastImageFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkContinuousIndex.h" #include "itkDistanceToCentroidMembershipFunction.h" #include "itkFastMarchingImageFilter.h" #include "itkImageDuplicator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkIterationReporter.h" #include "itkKdTreeBasedKmeansEstimator.h" #include "itkLabelGeometryImageFilter.h" #include "itkLabelStatisticsImageFilter.h" #include "itkMinimumDecisionRule.h" #include "itkMultiplyImageFilter.h" #include "itkMaskImageFilter.h" #include "itkOtsuMultipleThresholdsCalculator.h" #include "itkSampleClassifierFilter.h" #include "itkSignedMaurerDistanceMapImageFilter.h" #include "itkVariableSizeMatrix.h" #include "itkVectorIndexSelectionCastImageFilter.h" #include "itkWeightedCentroidKdTreeGenerator.h" #include "vnl/vnl_vector.h" namespace itk { namespace ants { template AtroposSegmentationImageFilter ::AtroposSegmentationImageFilter() { this->ProcessObject::SetNumberOfRequiredInputs( 1 ); this->m_NumberOfIntensityImages = 1; this->m_NumberOfTissueClasses = 3; this->m_NumberOfPartialVolumeClasses = 0; this->m_PartialVolumeClasses.clear(); this->m_UsePartialVolumeLikelihoods = false; this->m_MaximumNumberOfIterations = 5; this->m_ElapsedIterations = 0; this->m_CurrentPosteriorProbability = 0.0; this->m_ConvergenceThreshold = 0.001; this->m_InitializationStrategy = KMeans; this->m_InitialKMeansParameters.SetSize( 0 ); this->m_PosteriorProbabilityFormulation = Socrates; this->m_UseMixtureModelProportions = true; this->m_PriorProbabilityWeight = 0.0; this->m_AdaptiveSmoothingWeights.clear(); this->m_PriorLabelParameterMap.clear(); this->m_ProbabilityThreshold = 0.0; this->m_PriorProbabilityImages.clear(); this->m_PriorProbabilitySparseImages.clear(); this->m_IntensityImages.clear(); this->m_PriorLabelImage = ITK_NULLPTR; this->m_MaskImage = ITK_NULLPTR; this->m_MRFSmoothingFactor = 0.3; this->m_MRFRadius.Fill( 1 ); this->m_SplineOrder = 3; this->m_NumberOfLevels.Fill( 6 ); this->m_NumberOfControlPoints.Fill( this->m_SplineOrder + 1 ); this->m_MinimizeMemoryUsage = false; this->m_UseEuclideanDistanceForPriorLabels = false; this->m_PosteriorProbabilityImages.clear(); this->m_DistancePriorProbabilityImages.clear(); this->m_OutlierHandlingFilter = ITK_NULLPTR; this->m_Randomizer = RandomizerType::New(); this->m_Randomizer->Initialize(); this->m_MaximumICMCode = 0; this->m_InitialAnnealingTemperature = 1.0; this->m_AnnealingRate = 1.0; this->m_MinimumAnnealingTemperature = 0.1; this->m_ICMCodeImage = ITK_NULLPTR; this->m_UseAsynchronousUpdating = true; this->m_MaximumNumberOfICMIterations = 1; } template AtroposSegmentationImageFilter ::~AtroposSegmentationImageFilter() { } template void AtroposSegmentationImageFilter ::SetRandomizerInitializationSeed( const RandomizerSeedType seed ) { this->m_Randomizer->Initialize( seed ); this->Modified(); } template void AtroposSegmentationImageFilter ::SetMaskImage( const MaskImageType * mask ) { this->SetNthInput( 1, const_cast( mask ) ); this->m_MaskImage = mask; } template const typename AtroposSegmentationImageFilter ::MaskImageType * AtroposSegmentationImageFilter ::GetMaskImage() const { // const MaskImageType * maskImage = // dynamic_cast( this->ProcessObject::GetInput( 1 ) ); // // return maskImage; return this->m_MaskImage; } template void AtroposSegmentationImageFilter ::SetPriorLabelImage( const ClassifiedImageType * prior ) { this->SetNthInput( 2, const_cast( prior ) ); this->m_PriorLabelImage = prior; } template const typename AtroposSegmentationImageFilter ::ClassifiedImageType * AtroposSegmentationImageFilter ::GetPriorLabelImage() const { // const ClassifiedImageType * prior = // dynamic_cast( // this->ProcessObject::GetInput( 2 ) ); // // return prior; return this->m_PriorLabelImage; } template void AtroposSegmentationImageFilter ::SetPriorProbabilityImage( unsigned int whichClass, RealImageType * priorImage ) { if( whichClass < 1 || whichClass > this->m_NumberOfTissueClasses ) { itkExceptionMacro( "The requested prior probability image = " << whichClass << " should be in the range [1, " << this->m_NumberOfTissueClasses << "]" ); } if( this->m_MinimizeMemoryUsage ) { // To make matters simpler, we force the index to be zero // for each priorImage image. typename RealImageType::IndexType startIndex = priorImage->GetRequestedRegion().GetIndex(); typename SparseImageType::Pointer sparsePriorImage = SparseImageType::New(); sparsePriorImage->Initialize(); unsigned long count = 0; ImageRegionConstIteratorWithIndex It( priorImage, priorImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { if( It.Get() > this->m_ProbabilityThreshold ) { typename RealImageType::IndexType index = It.GetIndex(); for( unsigned int d = 0; d < ImageDimension; d++ ) { index[d] -= startIndex[d]; } unsigned long number = this->IndexToNumber( index, priorImage->GetRequestedRegion().GetSize() ); typename SparseImageType::PointType imageNumberIndex; imageNumberIndex[0] = number; sparsePriorImage->SetPoint( count, imageNumberIndex ); sparsePriorImage->SetPointData( count, It.Get() ); count++; } } if( this->m_PriorProbabilitySparseImages.size() < whichClass ) { this->m_PriorProbabilitySparseImages.resize( whichClass ); } this->m_PriorProbabilitySparseImages[whichClass - 1] = sparsePriorImage; } else { if( this->m_PriorProbabilityImages.size() < whichClass ) { this->m_PriorProbabilityImages.resize( whichClass ); } this->m_PriorProbabilityImages[whichClass - 1] = priorImage; } } template typename AtroposSegmentationImageFilter ::RealImagePointer AtroposSegmentationImageFilter ::GetPriorProbabilityImage( unsigned int whichClass ) const { if( this->m_InitializationStrategy != PriorProbabilityImages ) { return ITK_NULLPTR; } if( this->m_NumberOfPartialVolumeClasses == 0 && whichClass > this->m_NumberOfTissueClasses ) { itkExceptionMacro( "The requested prior probability image = " << whichClass << " should be in the range [1, " << this->m_NumberOfTissueClasses << "]" ); } else if( whichClass > this->m_NumberOfTissueClasses ) { return ITK_NULLPTR; } if( ( this->m_MinimizeMemoryUsage && this->m_PriorProbabilitySparseImages.size() != this->m_NumberOfTissueClasses ) || ( !this->m_MinimizeMemoryUsage && this->m_PriorProbabilityImages.size() != this->m_NumberOfTissueClasses ) ) { itkExceptionMacro( "The number of prior probability images does not " << "equal the number of classes." ); } if( this->m_MinimizeMemoryUsage ) { // To make matters simpler, we forced the prior probability images // to all have indices of zeros. typename RealImageType::RegionType region; region.SetSize( this->GetIntensityImage( 0 )->GetRequestedRegion().GetSize() ); typename RealImageType::RegionType::IndexType index; for( unsigned int d = 0; d < ImageDimension; d++ ) { index[d] = 0; } region.SetIndex( index ); RealImagePointer priorImage = AllocImage(this->GetIntensityImage( 0 ), 0 ); typename SparseImageType::Pointer sparsePriorImage = this->m_PriorProbabilitySparseImages[whichClass - 1]; typename SparseImageType::PointsContainer::ConstIterator It = sparsePriorImage->GetPoints()->Begin(); typename SparseImageType::PointDataContainer::ConstIterator ItD = sparsePriorImage->GetPointData()->Begin(); while( It != sparsePriorImage->GetPoints()->End() ) { unsigned long number = static_cast( It.Value()[0] ); typename RealImageType::IndexType index2 = this->NumberToIndex( number, priorImage->GetRequestedRegion().GetSize() ); priorImage->SetPixel( index2, ItD.Value() ); ++It; ++ItD; } return priorImage; } else { return this->m_PriorProbabilityImages[whichClass - 1]; } } template void AtroposSegmentationImageFilter ::SetIntensityImage( unsigned int which, const ImageType * image ) { if( which == 0 ) { this->SetInput( image ); } else if( which + 1 > this->m_NumberOfIntensityImages ) { this->m_NumberOfIntensityImages = which + 1; this->SetNthInput( 2 + which, const_cast( image ) ); } // Since we need fast access to these inputs, we maintain a separate // pointer array. if( which + 1 > this->m_IntensityImages.size() ) { this->m_IntensityImages.resize( which + 1 ); } this->m_IntensityImages[which] = image; } template const typename AtroposSegmentationImageFilter ::ImageType * AtroposSegmentationImageFilter ::GetIntensityImage( unsigned int which ) const { const ImageType *image; // if( which == 0 ) // { // image = dynamic_cast( this->ProcessObject::GetInput( 0 ) ); // } // else if( which > 0 && which < this->m_NumberOfIntensityImages ) // { // image = dynamic_cast( // this->ProcessObject::GetInput( 2 + which ) ); // } if( which < this->m_NumberOfIntensityImages ) { image = dynamic_cast( this->m_IntensityImages[which] ); } else { itkExceptionMacro( "Image " << which << " is outside the range " << "[1,...,1 + m_NumberOfIntensityImages]." ); } return image; } template void AtroposSegmentationImageFilter ::AddPartialVolumeLabelSet( PartialVolumeLabelSetType labelSet ) { // // Three checks: // 1. need to see if the labels are in {1, NumberOfTissueClasses} // 2. need to determine if labelSet is a duplicate // 3. check if each label is unique // typename PartialVolumeLabelSetType::const_iterator it; for( it = labelSet.begin(); it != labelSet.end(); ++it ) { if( *it < 1 || *it > this->m_NumberOfTissueClasses ) { itkWarningMacro( "The label " << *it << " is outside the specified " << "range of the specified tissue class labels." ) return; } } typename PartialVolumeClassesType::const_iterator itp; for( itp = this->m_PartialVolumeClasses.begin(); itp != this->m_PartialVolumeClasses.end(); ++itp ) { bool isDuplicate = true; if( labelSet.size() == itp->size() ) { typename PartialVolumeLabelSetType::const_iterator itc; typename PartialVolumeLabelSetType::const_iterator itl; for( itc = itp->begin(), itl = labelSet.begin(); itc != itp->end(); ++itc, ++itl ) { if( *itl != *itc ) { isDuplicate = false; break; } } } if( isDuplicate ) { itkWarningMacro( "Duplicate label set." ); return; } } for( LabelType l = 1; l <= this->m_NumberOfTissueClasses; l++ ) { unsigned int cardinality = std::count( labelSet.begin(), labelSet.end(), l ); if( cardinality > 1 ) { itkWarningMacro( "Duplicate label " << l ); return; } } this->m_PartialVolumeClasses.push_back( labelSet ); this->Modified(); } template void AtroposSegmentationImageFilter ::GenerateData() { // // Assign Gaussian likelihood functions if mixture model components are absent // typedef ants::Statistics::GaussianListSampleFunction LikelihoodType; for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { if( !this->GetLikelihoodFunction( n ) ) { typename LikelihoodType::Pointer gaussianLikelihood = LikelihoodType::New(); this->SetLikelihoodFunction( n, gaussianLikelihood ); } } if( this->m_UsePartialVolumeLikelihoods ) { this->m_NumberOfPartialVolumeClasses = this->m_PartialVolumeClasses.size(); } else { this->m_NumberOfPartialVolumeClasses = 0; } // // Initialize the class labeling and the likelihood models // this->GenerateInitialClassLabeling(); // // Iterate until convergence or iterative exhaustion. // IterationReporter reporter( this, 0, 1 ); // // Get spacing for mrf neighborhood evaluation loop // this->m_ImageSpacing = this->GetOutput()->GetSpacing(); bool isConverged = false; this->m_CurrentPosteriorProbability = 0.0; RealType probabilityOld = NumericTraits::NonpositiveMin(); unsigned int iteration = 0; while( !isConverged && iteration++ < this->m_MaximumNumberOfIterations ) { reporter.CompletedStep(); this->m_CurrentPosteriorProbability = this->UpdateClassLabeling(); if( this->m_CurrentPosteriorProbability - probabilityOld < this->m_ConvergenceThreshold ) { isConverged = true; } probabilityOld = this->m_CurrentPosteriorProbability; itkDebugMacro( "Iteration: " << this->m_ElapsedIterations << ", " "current posterior probability = " << this->m_CurrentPosteriorProbability ); this->m_ElapsedIterations++; // // Clear the current posterior probability images to force // recalculation of the posterior probability images. // this->m_PosteriorProbabilityImages.clear(); } } template void AtroposSegmentationImageFilter ::GenerateInitialClassLabeling() { this->AllocateOutputs(); this->GetOutput()->FillBuffer( NumericTraits::ZeroValue() ); switch( this->m_InitializationStrategy ) { case Random: { ImageRegionIterator It( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { LabelType label = this->m_Randomizer->GetIntegerVariate( this->m_NumberOfTissueClasses - 1 ) + 1; It.Set( label ); } break; } case KMeans: default: { this->GenerateInitialClassLabelingWithKMeansClustering(); } break; case Otsu: { this->GenerateInitialClassLabelingWithOtsuThresholding(); } break; case PriorProbabilityImages: { this->GenerateInitialClassLabelingWithPriorProbabilityImages(); } break; case PriorLabelImage: { if( this->GetMaskImage() ) { typedef MaskImageFilter MaskerType; typename MaskerType::Pointer masker = MaskerType::New(); masker->SetInput1( this->GetPriorLabelImage() ); masker->SetInput2( this->GetMaskImage() ); masker->Update(); this->SetNthOutput( 0, masker->GetOutput() ); } else { typedef ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( this->GetPriorLabelImage() ); duplicator->Update(); this->SetNthOutput( 0, duplicator->GetModifiableOutput() ); } } break; } // // Calculate the initial parameters of the mixture model from the // initial labeling, i.e. the proportion, mean, and covariance for each label. // unsigned int totalNumberOfClasses = this->m_NumberOfTissueClasses + this->m_NumberOfPartialVolumeClasses; this->m_MixtureModelProportions.SetSize( totalNumberOfClasses ); this->m_LabelVolumes.SetSize( totalNumberOfClasses ); unsigned int totalSampleSize = 0; std::vector samples; for( unsigned int n = 0; n < totalNumberOfClasses; n++ ) { typename SampleType::Pointer sample = SampleType::New(); sample->SetMeasurementVectorSize( this->m_NumberOfIntensityImages ); samples.push_back( sample ); } // // Accumulate the sample array for all labels. Also accumulate the // prior probability weights, if applicable. // ImageRegionIteratorWithIndex ItO( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); for( ItO.GoToBegin(); !ItO.IsAtEnd(); ++ItO ) { LabelType label = ItO.Get(); if( label == 0 ) { continue; } typename SampleType::MeasurementVectorType measurement; NumericTraits::SetLength( measurement, this->m_NumberOfIntensityImages ); for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { measurement[i] = this->GetIntensityImage( i )->GetPixel( ItO.GetIndex() ); } samples[label - 1]->PushBack( measurement ); } // // Create the weight array now that we know the sample sizes. // Array count( totalNumberOfClasses ); count.Fill( 0 ); std::vector weights; for( unsigned int n = 0; n < totalNumberOfClasses; n++ ) { totalSampleSize += samples[n]->Size(); WeightArrayType weightArray( samples[n]->Size() ); weightArray.Fill( 1.0 ); weights.push_back( weightArray ); this->m_LabelVolumes[n] = samples[n]->Size(); } if( this->m_InitializationStrategy == PriorProbabilityImages ) { for( ItO.GoToBegin(); !ItO.IsAtEnd(); ++ItO ) { LabelType label = ItO.Get(); if( label == 0 || label > this->m_NumberOfTissueClasses ) { continue; } RealImagePointer priorProbabilityImage = this->GetPriorProbabilityImage( label ); weights[label - 1].SetElement( count[label - 1]++, priorProbabilityImage->GetPixel( ItO.GetIndex() ) ); } } for( unsigned int n = 0; n < totalNumberOfClasses; n++ ) { if( n < this->m_NumberOfTissueClasses ) { this->m_MixtureModelComponents[n]->SetListSampleWeights( &weights[n] ); this->m_MixtureModelComponents[n]->SetInputListSample( samples[n] ); this->m_MixtureModelComponents[n]->ClearInputListSample(); if( this->m_UseMixtureModelProportions ) { this->m_MixtureModelProportions[n] = static_cast( samples[n]->Size() ) / static_cast( totalSampleSize ); } else { this->m_MixtureModelProportions[n] = 1.0 / static_cast( totalNumberOfClasses ); } } else { PartialVolumeLabelSetType labelSet = this->m_PartialVolumeClasses[n - this->m_NumberOfTissueClasses]; for( unsigned d = 0; d < labelSet.size(); d++ ) { this->m_MixtureModelComponents[n]->SetListSampleWeights( d, &weights[labelSet[d] - 1] ); this->m_MixtureModelComponents[n]->SetIndexedInputListSample( d, samples[labelSet[d] - 1] ); this->m_MixtureModelComponents[n]->ClearInputListSample( d ); } this->m_MixtureModelProportions[n] = 0.0; } } for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { ControlPointLatticeContainerType container; this->m_ControlPointLattices.push_back( container ); for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { this->m_ControlPointLattices[i].push_back( ITK_NULLPTR ); } } } template void AtroposSegmentationImageFilter ::GenerateInitialClassLabelingWithPriorProbabilityImages() { // We first normalize prior probability images by keeping track of // 1. the sum of the prior probabilities at each pixel // 2. the max prior probability value // 3. which prior probability image corresponds to the max prior value RealImagePointer sumPriorProbabilityImage = AllocImage(this->GetInput(), 0); RealImagePointer maxPriorProbabilityImage = AllocImage(this->GetInput(), 0); for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { RealImagePointer priorProbabilityImage = this->GetPriorProbabilityImage( n + 1 ); typedef AddImageFilter AdderType; typename AdderType::Pointer adder = AdderType::New(); adder->SetInput1( sumPriorProbabilityImage ); adder->SetInput2( priorProbabilityImage ); adder->Update(); sumPriorProbabilityImage = adder->GetOutput(); ImageRegionIteratorWithIndex ItP( priorProbabilityImage, priorProbabilityImage->GetRequestedRegion() ); ImageRegionIterator ItM( maxPriorProbabilityImage, maxPriorProbabilityImage->GetRequestedRegion() ); ImageRegionIterator ItO( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); ItP.GoToBegin(); ItM.GoToBegin(); ItO.GoToBegin(); while( !ItP.IsAtEnd() ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItP.GetIndex() ) != NumericTraits::ZeroValue() ) { if( ItP.Get() > ItM.Get() ) { ItM.Set( ItP.Get() ); ItO.Set( n + 1 ); } else if( ItP.Get() == ItM.Get() ) { if( n == 0 ) { ItO.Set( 1 ); } else // if maximum probabilities are the same, randomly select one { if( this->m_Randomizer->GetIntegerVariate( 1 ) ) { ItO.Set( n + 1 ); } } } } ++ItP; ++ItM; ++ItO; } } // Now we can normalize each prior probability image by dividing by the sum for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { RealImagePointer priorProbabilityImage = this->GetPriorProbabilityImage( n + 1 ); ImageRegionIteratorWithIndex ItP( priorProbabilityImage, priorProbabilityImage->GetRequestedRegion() ); ImageRegionIterator ItS( sumPriorProbabilityImage, sumPriorProbabilityImage->GetRequestedRegion() ); ImageRegionIterator ItM( maxPriorProbabilityImage, maxPriorProbabilityImage->GetRequestedRegion() ); ImageRegionIterator ItO( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); ItP.GoToBegin(); ItS.GoToBegin(); ItM.GoToBegin(); ItO.GoToBegin(); while( !ItP.IsAtEnd() ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItP.GetIndex() ) != NumericTraits::ZeroValue() ) { if( ItM.Get() <= this->m_ProbabilityThreshold || ItS.Get() == 0.0 ) { ItO.Set( NumericTraits::ZeroValue() ); ItP.Set( NumericTraits::ZeroValue() ); } else { ItP.Set( ItP.Get() / ItS.Get() ); } } ++ItP; ++ItS; ++ItM; ++ItO; } this->SetPriorProbabilityImage( n + 1, priorProbabilityImage ); } // // Set the initial output to be the prior label image. This way we can // propogate the segmentation solution to regions of non-zero probability // but where the mask exists. // typedef ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( this->GetOutput() ); duplicator->Update(); this->SetPriorLabelImage( duplicator->GetOutput() ); } template void AtroposSegmentationImageFilter ::GenerateInitialClassLabelingWithOtsuThresholding() { RealType maxValue = NumericTraits::min(); RealType minValue = NumericTraits::max(); ImageRegionConstIteratorWithIndex ItI( this->GetInput(), this->GetInput()->GetRequestedRegion() ); for( ItI.GoToBegin(); !ItI.IsAtEnd(); ++ItI ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItI.GetIndex() ) != NumericTraits::ZeroValue() ) { if( ItI.Get() < minValue ) { minValue = ItI.Get(); } else if( ItI.Get() > maxValue ) { maxValue = ItI.Get(); } } } typedef LabelStatisticsImageFilter StatsType; typename StatsType::Pointer stats = StatsType::New(); stats->SetInput( this->GetInput() ); if( this->GetMaskImage() ) { stats->SetLabelInput( const_cast( this->GetMaskImage() ) ); } else { typename MaskImageType::Pointer maskImage = AllocImage( this->GetOutput(), NumericTraits::OneValue() ); stats->SetLabelInput( maskImage ); } stats->UseHistogramsOn(); stats->SetHistogramParameters( 200, minValue, maxValue ); stats->Update(); typedef OtsuMultipleThresholdsCalculator OtsuType; typename OtsuType::Pointer otsu = OtsuType::New(); otsu->SetInputHistogram( stats->GetHistogram( NumericTraits::OneValue() ) ); otsu->SetNumberOfThresholds( this->m_NumberOfTissueClasses - 1 ); otsu->Update(); typename OtsuType::OutputType thresholds = otsu->GetOutput(); ImageRegionIterator ItO( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); for( ItI.GoToBegin(), ItO.GoToBegin(); !ItI.IsAtEnd(); ++ItI, ++ItO ) { LabelType label = NumericTraits::ZeroValue(); if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItI.GetIndex() ) != NumericTraits::ZeroValue() ) { if( ItI.Get() < thresholds[0] ) { label = NumericTraits::OneValue(); } else { bool thresholdFound = false; for( unsigned int i = 1; i < thresholds.size(); i++ ) { if( ItI.Get() >= thresholds[i - 1] && ItI.Get() <= thresholds[i] ) { label = static_cast( i + 1 ); thresholdFound = true; break; } } if( !thresholdFound ) { label = static_cast( thresholds.size() + 1 ); } } } ItO.Set( label ); } } template void AtroposSegmentationImageFilter ::GenerateInitialClassLabelingWithKMeansClustering() { // // We first perform kmeans on the first image and use the results to // seed the second run of kmeans using all the images. // typedef LabelStatisticsImageFilter StatsType; typename StatsType::Pointer stats = StatsType::New(); stats->SetInput( this->GetInput() ); if( this->GetMaskImage() ) { stats->SetLabelInput( const_cast( this->GetMaskImage() ) ); } else { typename MaskImageType::Pointer maskImage = AllocImage( this->GetOutput(), NumericTraits::OneValue() ); stats->SetLabelInput( maskImage ); } stats->UseHistogramsOff(); stats->Update(); const RealType minValue = stats->GetMinimum( NumericTraits::OneValue() ); const RealType maxValue = stats->GetMaximum( NumericTraits::OneValue() ); // // The code below can be replaced by itkListSampleToImageFilter when we // migrate over to the Statistics classes current in the Review/ directory. // typename SampleType::Pointer sample = SampleType::New(); sample->SetMeasurementVectorSize( 1 ); ImageRegionConstIteratorWithIndex ItI( this->GetInput(), this->GetInput()->GetRequestedRegion() ); for( ItI.GoToBegin(); !ItI.IsAtEnd(); ++ItI ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItI.GetIndex() ) != NumericTraits::ZeroValue() ) { typename SampleType::MeasurementVectorType measurement; measurement.SetSize( 1 ); measurement[0] = ItI.Get(); sample->PushBack( measurement ); } } typedef itk::Statistics::WeightedCentroidKdTreeGenerator TreeGeneratorType; typename TreeGeneratorType::Pointer treeGenerator = TreeGeneratorType::New(); treeGenerator->SetSample( sample ); treeGenerator->SetBucketSize( 16 ); treeGenerator->Update(); typedef typename TreeGeneratorType::KdTreeType TreeType; typedef itk::Statistics::KdTreeBasedKmeansEstimator EstimatorType; typename EstimatorType::Pointer estimator = EstimatorType::New(); estimator->SetKdTree( treeGenerator->GetOutput() ); estimator->SetMaximumIteration( 200 ); estimator->SetCentroidPositionChangesThreshold( 0.0 ); typename EstimatorType::ParametersType initialMeans( this->m_NumberOfTissueClasses ); // // If the initial KMeans parameters are not set, guess initial class means by // dividing the dynamic range of the first image into equal intervals. // if( this->m_InitialKMeansParameters.Size() == this->m_NumberOfTissueClasses ) { for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { initialMeans[n] = this->m_InitialKMeansParameters[n]; } } else { for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { initialMeans[n] = minValue + ( maxValue - minValue ) * ( static_cast( n ) + 0.5 ) / static_cast( this->m_NumberOfTissueClasses ); } } estimator->SetParameters( initialMeans ); estimator->StartOptimization(); // // Classify the samples // typedef itk::Statistics::MinimumDecisionRule DecisionRuleType; typename DecisionRuleType::Pointer decisionRule = DecisionRuleType::New(); typedef itk::Statistics::SampleClassifierFilter ClassifierType; typename ClassifierType::Pointer classifier = ClassifierType::New(); classifier->SetDecisionRule( decisionRule ); classifier->SetInput( sample ); classifier->SetNumberOfClasses( this->m_NumberOfTissueClasses ); typename ClassifierType::ClassLabelVectorObjectType::Pointer classLabels = ClassifierType::ClassLabelVectorObjectType::New(); classifier->SetClassLabels( classLabels ); typename ClassifierType::ClassLabelVectorType & classLabelVector = classLabels->Get(); // // Order the cluster means so that the lowest mean of the input image // corresponds to label '1', the second lowest to label '2', etc. // std::vector estimatorParameters; for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { estimatorParameters.push_back( estimator->GetParameters()[n] ); } std::sort( estimatorParameters.begin(), estimatorParameters.end() ); typedef itk::Statistics::DistanceToCentroidMembershipFunction MembershipFunctionType; typename ClassifierType::MembershipFunctionVectorObjectType::Pointer membershipFunctions = ClassifierType::MembershipFunctionVectorObjectType::New(); typename ClassifierType::MembershipFunctionVectorType & membershipFunctionsVector = membershipFunctions->Get(); classifier->SetMembershipFunctions( membershipFunctions ); for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { typename MembershipFunctionType::Pointer membershipFunction = MembershipFunctionType::New(); membershipFunction->SetMeasurementVectorSize( sample->GetMeasurementVectorSize() ); typename MembershipFunctionType::CentroidType centroid; NumericTraits::SetLength( centroid, sample->GetMeasurementVectorSize() ); centroid[0] = estimatorParameters[n]; membershipFunction->SetCentroid( centroid ); membershipFunctionsVector.push_back( membershipFunction.GetPointer() ); classLabelVector.push_back( static_cast( n + 1 ) ); } classifier->Update(); // // Classify the voxels // typedef typename ClassifierType::MembershipSampleType ClassifierOutputType; typedef typename ClassifierOutputType::ConstIterator LabelIterator; ImageRegionIteratorWithIndex ItO( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); ItO.GoToBegin(); LabelIterator it = classifier->GetOutput()->Begin(); while( it != classifier->GetOutput()->End() ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItO.GetIndex() ) != NumericTraits::ZeroValue() ) { ItO.Set( it.GetClassLabel() ); ++it; } else { ItO.Set( NumericTraits::ZeroValue() ); } ++ItO; } // // If there are more than one intensity images, use the results from the first // kmeans grouping to seed a second invoking of the algorithm using both // the input image and the auxiliary images. // if( this->m_NumberOfIntensityImages > 1 ) { VariableSizeMatrix classMeanValues; classMeanValues.SetSize( this->m_NumberOfIntensityImages, this->m_NumberOfTissueClasses ); for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { typedef LabelStatisticsImageFilter ClassStatsType; typename ClassStatsType::Pointer stats2 = StatsType::New(); stats2->SetInput( this->GetIntensityImage( i ) ); stats2->SetLabelInput( this->GetOutput() ); stats2->UseHistogramsOff(); stats2->Update(); for( unsigned int j = 0; j < this->m_NumberOfTissueClasses; j++ ) { classMeanValues( i, j ) = stats->GetMean( j + 1 ); } } typename EstimatorType::Pointer estimator2 = EstimatorType::New(); typename EstimatorType::ParametersType initialMeans2( this->m_NumberOfTissueClasses * ( this->m_NumberOfIntensityImages ) ); initialMeans2.Fill( 0.0 ); for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { initialMeans2[this->m_NumberOfIntensityImages * n + i] = classMeanValues( i, n ); } } typename SampleType::Pointer sample2 = SampleType::New(); sample2->SetMeasurementVectorSize( this->m_NumberOfIntensityImages ); for( ItO.GoToBegin(); !ItO.IsAtEnd(); ++ItO ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItO.GetIndex() ) != NumericTraits::ZeroValue() ) { typename SampleType::MeasurementVectorType measurement; measurement.SetSize( this->m_NumberOfIntensityImages ); for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { measurement[i] = this->GetIntensityImage( i )->GetPixel( ItO.GetIndex() ); } sample2->PushBack( measurement ); } } typename TreeGeneratorType::Pointer treeGenerator2 = TreeGeneratorType::New(); treeGenerator2->SetSample( sample2 ); treeGenerator2->SetBucketSize( 16 ); treeGenerator2->Update(); estimator2->SetParameters( initialMeans2 ); estimator2->SetKdTree( treeGenerator2->GetOutput() ); estimator2->SetMaximumIteration( 200 ); estimator2->SetCentroidPositionChangesThreshold( 0.0 ); estimator2->StartOptimization(); // // Classify the samples // typename DecisionRuleType::Pointer decisionRule2 = DecisionRuleType::New(); typename ClassifierType::Pointer classifier2 = ClassifierType::New(); classifier2->SetDecisionRule( decisionRule2 ); classifier2->SetInput( sample2 ); classifier2->SetNumberOfClasses( this->m_NumberOfTissueClasses ); typename ClassifierType::ClassLabelVectorObjectType::Pointer classLabels2 = ClassifierType::ClassLabelVectorObjectType::New(); classifier2->SetClassLabels( classLabels2 ); typename ClassifierType::ClassLabelVectorType & classLabelVector2 = classLabels2->Get(); // // Order the cluster means so that the lowest mean of the input image // corresponds to label '1', the second lowest to label '2', etc. // std::vector estimatorParameters2; for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { estimatorParameters2.push_back( estimator2->GetParameters()[n] ); } std::sort( estimatorParameters2.begin(), estimatorParameters2.end() ); typename ClassifierType::MembershipFunctionVectorObjectType::Pointer membershipFunctions2 = ClassifierType::MembershipFunctionVectorObjectType::New(); typename ClassifierType::MembershipFunctionVectorType & membershipFunctionsVector2 = membershipFunctions2->Get(); classifier2->SetMembershipFunctions( membershipFunctions2 ); for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { typename MembershipFunctionType::Pointer membershipFunction2 = MembershipFunctionType::New(); membershipFunction2->SetMeasurementVectorSize( sample2->GetMeasurementVectorSize() ); typename MembershipFunctionType::CentroidType centroid; NumericTraits::SetLength( centroid, sample2->GetMeasurementVectorSize() ); for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { centroid[i] = estimator2->GetParameters()[ ( this->m_NumberOfIntensityImages ) * n + i]; } membershipFunction2->SetCentroid( centroid ); membershipFunctionsVector2.push_back( membershipFunction2.GetPointer() ); classLabelVector2.push_back( static_cast( n + 1 ) ); } classifier2->Update(); // // Classify the voxels // ItO.GoToBegin(); LabelIterator it2 = classifier->GetOutput()->Begin(); while( it2 != classifier->GetOutput()->End() ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItO.GetIndex() ) != NumericTraits::ZeroValue() ) { ItO.Set( it2.GetClassLabel() ); ++it2; } else { ItO.Set( NumericTraits::ZeroValue() ); } ++ItO; } } } template typename AtroposSegmentationImageFilter ::RealType AtroposSegmentationImageFilter ::UpdateClassLabeling() { RealType maxPosteriorSum = 0.0; if( this->m_UseAsynchronousUpdating ) { if( this->m_MaximumICMCode == 0 ) { this->ComputeICMCodeImage(); } typename NeighborhoodIterator::RadiusType radius; for( unsigned int d = 0; d < ImageDimension; d++ ) { radius[d] = this->m_MRFRadius[d]; } NeighborhoodIterator ItO( radius, this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); maxPosteriorSum = 0.0; RealType oldMaxPosteriorSum = -1.0; unsigned int numberOfIterations = 0; while( maxPosteriorSum > oldMaxPosteriorSum && numberOfIterations++ < this->m_MaximumNumberOfICMIterations ) { itkDebugMacro( "ICM iteration: " << numberOfIterations ); oldMaxPosteriorSum = maxPosteriorSum; maxPosteriorSum = 0.0; // Iterate randomly through the ICM codes so we don't visit the same codes // in the same order every iteration. We use the Fisher-Yates shuffle to // create a random shuffling between 1 and m_MaximumICMCode. Array icmCodeSet( this->m_MaximumICMCode ); for( unsigned int n = 1; n <= this->m_MaximumICMCode; n++ ) { icmCodeSet[n - 1] = static_cast( n ); } for( int i = icmCodeSet.Size() - 1; i > 0; i-- ) { unsigned int j = this->m_Randomizer->GetIntegerVariate( i ); LabelType tmp = icmCodeSet[i]; icmCodeSet[i] = icmCodeSet[j]; icmCodeSet[j] = tmp; } for( unsigned int n = 0; n < icmCodeSet.Size(); n++ ) { for( ItO.GoToBegin(); !ItO.IsAtEnd(); ++ItO ) { if( this->m_ICMCodeImage->GetPixel( ItO.GetIndex() ) == icmCodeSet[n] ) { maxPosteriorSum += this->PerformLocalLabelingUpdate( ItO ); } } } itkDebugMacro( "ICM posterior probability sum: " << maxPosteriorSum ); } } RealImagePointer maxPosteriorProbabilityImage = AllocImage( this->GetOutput(), NumericTraits::ZeroValue() ); typename ClassifiedImageType::Pointer maxLabels = AllocImage( this->GetOutput(), NumericTraits::ZeroValue() ); unsigned int totalNumberOfClasses = this->m_NumberOfTissueClasses + this->m_NumberOfPartialVolumeClasses; Array sumPosteriors( totalNumberOfClasses ); sumPosteriors.Fill( 0.0 ); typename SampleType::Pointer sample = SampleType::New(); sample = this->GetScalarSamples(); unsigned long totalSampleSize = sample->Size(); for( unsigned int n = 0; n < totalNumberOfClasses; n++ ) { RealImagePointer posteriorProbabilityImage = this->GetPosteriorProbabilityImage( n + 1 ); ImageRegionIteratorWithIndex ItO( maxLabels, maxLabels->GetRequestedRegion() ); ImageRegionConstIterator ItP( posteriorProbabilityImage, posteriorProbabilityImage->GetRequestedRegion() ); ImageRegionIterator ItM( maxPosteriorProbabilityImage, maxPosteriorProbabilityImage->GetRequestedRegion() ); WeightArrayType weights( totalSampleSize ); unsigned long count = 0; ItP.GoToBegin(); ItM.GoToBegin(); ItO.GoToBegin(); while( !ItP.IsAtEnd() ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItO.GetIndex() ) != NumericTraits::ZeroValue() ) { RealType posteriorProbability = ItP.Get(); weights.SetElement( count++, posteriorProbability ); // The following commented lines enforce "hard EM" as opposed to "soft EM" // which uses probabilities. // if( this->GetOutput()->GetPixel( ItP.GetIndex() ) == n + 1 ) // { // posteriorProbability = 1.0; // } // else // { // posteriorProbability = 0.0; // } if( posteriorProbability > ItM.Get() ) { ItM.Set( posteriorProbability ); ItO.Set( static_cast( n + 1 ) ); } else if( posteriorProbability == ItM.Get() ) { LabelType currentLabel = ItO.Get(); if( currentLabel >= 1 && this->m_LabelVolumes[n] < this->m_LabelVolumes[currentLabel - 1] ) { ItO.Set( static_cast( n + 1 ) ); } } sumPosteriors[n] += posteriorProbability; } ++ItP; ++ItM; ++ItO; } if( n < this->m_NumberOfTissueClasses ) { this->m_MixtureModelComponents[n]->SetListSampleWeights( &weights ); this->m_MixtureModelComponents[n]->SetInputListSample( sample ); this->m_MixtureModelComponents[n]->ClearInputListSample(); } else { PartialVolumeLabelSetType labelSet = this->m_PartialVolumeClasses[n - this->m_NumberOfTissueClasses]; for( unsigned d = 0; d < labelSet.size(); d++ ) { if( n == labelSet[d] - 1 ) { this->m_MixtureModelComponents[n]->SetListSampleWeights( d, &weights ); this->m_MixtureModelComponents[n]->SetIndexedInputListSample( d, sample ); this->m_MixtureModelComponents[n]->ClearInputListSample( d ); } } this->m_MixtureModelProportions[n] = 0.0; } if( this->m_UseMixtureModelProportions ) { this->m_MixtureModelProportions[n] = sumPosteriors[n] / static_cast( totalSampleSize ); } else { this->m_MixtureModelProportions[n] = 1.0 / static_cast( totalNumberOfClasses ); } } typedef LabelGeometryImageFilter GeometryType; typename GeometryType::Pointer geom = GeometryType::New(); geom->SetInput( maxLabels ); geom->Update(); for( unsigned int n = 0; n < totalNumberOfClasses; n++ ) { this->m_LabelVolumes[n] = geom->GetVolume( n + 1 ); } this->SetNthOutput( 0, maxLabels ); // The commented code below is used to calculate the mixture model proportions // according to the formulae given in Ashburner et al., "Unified Segmentation", // Neuroimage, 2005 Jul 1;26(3):839-51. // // if( this->m_UseMixtureModelProportions ) // { // // // // Perform the following calculation as a preprocessing step to update the // // class proportions. // // // RealImagePointer distancePriorProbabilityImage = // this->GetDistancePriorProbabilityImage( n + 1 ); // RealImagePointer priorProbabilityImage = // this->GetPriorProbabilityImage( n + 1 ); // // ImageRegionIteratorWithIndex ItW( // weightedPriorProbabilityImage, // weightedPriorProbabilityImage->GetRequestedRegion() ); // for( ItW.GoToBegin(); !ItW.IsAtEnd(); ++ItW ) // { // if( !this->GetMaskImage() || // this->GetMaskImage()->GetPixel( ItW.GetIndex() ) != NumericTraits::ZeroValue() ) // { // RealType priorProbability = 0.0; // if( this->m_InitializationStrategy == PriorLabelImage || // this->m_InitializationStrategy == PriorProbabilityImages ) // { // if( priorProbabilityImage ) // { // priorProbability = priorProbabilityImage->GetPixel( ItW.GetIndex() ); // } // if( priorProbability <= this->m_ProbabilityThreshold && // distancePriorProbabilityImage ) // { // priorProbability = // distancePriorProbabilityImage->GetPixel( ItW.GetIndex() ); // } // if( this->GetPriorLabelImage() ) // { // if( priorProbability == 0.0 ) // { // priorProbability = 1.0 / static_cast( // this->m_NumberOfTissueClasses ); // } // else // { // priorProbability = 1.0; // } // } // } // else // { // priorProbability = 1.0; // } // ItW.Set( ItW.Get() + this->m_MixtureModelProportions[n] * // priorProbability ); // } // } // } // } // this->SetNthOutput( 0, maxLabels ); // // if( this->m_UseMixtureModelProportions ) // { // // // // Update the class proportions // // // for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) // { // RealType denominator = 0.0; // // RealImagePointer distancePriorProbabilityImage = // this->GetDistancePriorProbabilityImage( n + 1 ); // RealImagePointer priorProbabilityImage = // this->GetPriorProbabilityImage( n + 1 ); // // ImageRegionIteratorWithIndex ItW( // weightedPriorProbabilityImage, // weightedPriorProbabilityImage->GetRequestedRegion() ); // for( ItW.GoToBegin(); !ItW.IsAtEnd(); ++ItW ) // { // if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItW.GetIndex() ) != NumericTraits::ZeroValue() ) // { // RealType priorProbability = 0.0; // if( this->m_InitializationStrategy == PriorLabelImage || // this->m_InitializationStrategy == PriorProbabilityImages ) // { // if( priorProbabilityImage ) // { // priorProbability = priorProbabilityImage->GetPixel( ItW.GetIndex() ); // } // if( priorProbability <= this->m_ProbabilityThreshold && // distancePriorProbabilityImage ) // { // priorProbability = // distancePriorProbabilityImage->GetPixel( ItW.GetIndex() ); // } // if( this->GetPriorLabelImage() ) // { // if( priorProbability == 0 ) // { // priorProbability= 1.0 / // static_cast( this->m_NumberOfTissueClasses ); // } // else // { // priorProbability = 1.0; // } // } // } // else // { // priorProbability = 1.0; // } // if( ItW.Get() > 0.0 ) // { // denominator += ( priorProbability / ItW.Get() ); // } // } // } // if( denominator > 0.0 ) // { // this->m_MixtureModelProportions[n] = sumPosteriors[n] / denominator; // } // else // { // this->m_MixtureModelProportions[n] = 0.0; // } // } // } // // Calculate the maximum posterior probability sum over the region of // interest. This quantity should increase at each iteration. // if( !this->m_UseAsynchronousUpdating ) { maxPosteriorSum = 0.0; ImageRegionConstIteratorWithIndex ItM( maxPosteriorProbabilityImage, maxPosteriorProbabilityImage->GetRequestedRegion() ); for( ItM.GoToBegin(); !ItM.IsAtEnd(); ++ItM ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItM.GetIndex() ) != NumericTraits::ZeroValue() ) { maxPosteriorSum += ItM.Get(); } } } return maxPosteriorSum / static_cast( totalSampleSize ); } template typename AtroposSegmentationImageFilter ::RealType AtroposSegmentationImageFilter ::PerformLocalLabelingUpdate( NeighborhoodIterator It ) { MeasurementVectorType measurement; measurement.SetSize( this->m_NumberOfIntensityImages ); for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { measurement[i] = this->GetIntensityImage( i )->GetPixel( It.GetIndex() ); } RealType mrfSmoothingFactor = this->m_MRFSmoothingFactor; if( this->m_MRFCoefficientImage ) { mrfSmoothingFactor = this->m_MRFCoefficientImage->GetPixel( It.GetIndex() ); } Array mrfNeighborhoodWeights; this->EvaluateMRFNeighborhoodWeights( It, mrfNeighborhoodWeights ); LabelType maxLabel = this->m_Randomizer->GetIntegerVariate( this->m_NumberOfTissueClasses - 1 ) + 1; RealType maxPosteriorProbability = 0.0; RealType sumPosteriorProbability = 0.0; unsigned int totalNumberOfClasses = this->m_NumberOfTissueClasses + this->m_NumberOfPartialVolumeClasses; for( unsigned int k = 0; k < totalNumberOfClasses; k++ ) { // Calculate likelihood probability RealType likelihood = this->m_MixtureModelComponents[k]->Evaluate( measurement ); // Calculate the mrf prior probability RealType mrfPriorProbability = 1.0; if( mrfSmoothingFactor > 0.0 && ( It.GetNeighborhood() ).Size() > 1 ) { RealType numerator = std::exp( -mrfSmoothingFactor * mrfNeighborhoodWeights[k] ); RealType denominator = 0.0; for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { denominator += std::exp( -mrfSmoothingFactor * mrfNeighborhoodWeights[n] ); } if( denominator > 0.0 ) { mrfPriorProbability = numerator / denominator; } } // Get the spatial prior probability RealType priorProbability = 1.0; if( this->GetPriorProbabilityImage( k + 1 ) ) { priorProbability = this->GetPriorProbabilityImage( k + 1 )->GetPixel( It.GetIndex() ); } // // Calculate the local posterior probability. Given that the // algorithm is meant to maximize the posterior probability of the // labeling configuration, this is the critical energy minimization // equation. // RealType posteriorProbability = this->CalculateLocalPosteriorProbability( this->m_MixtureModelProportions[k], priorProbability, 1, mrfPriorProbability, likelihood, It.GetIndex(), k + 1 ); if( vnl_math_isnan( posteriorProbability ) || vnl_math_isinf( posteriorProbability ) ) { posteriorProbability = 0.0; } sumPosteriorProbability += posteriorProbability; if( posteriorProbability > maxPosteriorProbability ) { maxPosteriorProbability = posteriorProbability; maxLabel = static_cast( k + 1 ); } } It.SetCenterPixel( maxLabel ); maxPosteriorProbability /= sumPosteriorProbability; if( vnl_math_isnan( maxPosteriorProbability ) || vnl_math_isinf( maxPosteriorProbability ) ) { maxPosteriorProbability = 0.0; } return maxPosteriorProbability; } template typename AtroposSegmentationImageFilter ::SamplePointer AtroposSegmentationImageFilter ::GetScalarSamples() { // // This function returns a set of samples for each class such that each // measurement vector of the returned SampleType corresponds to a single // voxel across the set of auxiliary and input images. // std::vector samples; // // Accumulate the samples in individual SampleTypes. This allows us to // "filter" the samples of each auxiliary/input image. This filtering // could including outlier winsorization, log transformation, and/or // converting vector/tensor auxiliary images to scalar data for // modeling. // for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { typename SampleType::Pointer sample = SampleType::New(); sample->SetMeasurementVectorSize( 1 ); samples.push_back( sample ); } ImageRegionIteratorWithIndex ItO( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); for( ItO.GoToBegin(); !ItO.IsAtEnd(); ++ItO ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItO.GetIndex() ) != NumericTraits::ZeroValue() ) { for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { typename SampleType::MeasurementVectorType measurement; NumericTraits::SetLength( measurement, 1 ); measurement.SetSize( 1 ); measurement[0] = this->GetIntensityImage( i )->GetPixel( ItO.GetIndex() ); samples[i]->PushBack( measurement ); } } } // // Simultaneously filter the samples and accumulate for return. // typename SampleType::Pointer scalarSamples = SampleType::New(); scalarSamples->SetMeasurementVectorSize( this->m_NumberOfIntensityImages ); for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { typename SampleType::Pointer univariateSamples = SampleType::New(); univariateSamples->SetMeasurementVectorSize( 1 ); if( this->m_OutlierHandlingFilter ) { this->m_OutlierHandlingFilter->SetInputListSample( samples[i] ); this->m_OutlierHandlingFilter->Update(); univariateSamples = this->m_OutlierHandlingFilter->GetOutput(); } else { univariateSamples = samples[i]; } if( i == 0 ) { typename SampleType::ConstIterator It = univariateSamples->Begin(); while( It != univariateSamples->End() ) { typename SampleType::MeasurementVectorType measurement; measurement.SetSize( this->m_NumberOfIntensityImages ); NumericTraits::SetLength( measurement, this->m_NumberOfIntensityImages ); measurement[0] = It.GetMeasurementVector()[0]; scalarSamples->PushBack( measurement ); ++It; } } else { typename SampleType::Iterator ItS = scalarSamples->Begin(); typename SampleType::ConstIterator It = univariateSamples->Begin(); while( ItS != scalarSamples->End() ) { scalarSamples->SetMeasurement( ItS.GetInstanceIdentifier(), i, It.GetMeasurementVector()[0] ); ++It; ++ItS; } } } return scalarSamples; } template typename AtroposSegmentationImageFilter ::RealType AtroposSegmentationImageFilter ::CalculateLocalPosteriorProbability( RealType itkNotUsed( mixtureModelProportion ), RealType spatialPriorProbability, RealType distancePriorProbability, RealType mrfPriorProbability, RealType likelihood, IndexType index, unsigned int whichClass ) { // Ensure that the probabilities are at least and epsilon > 0.0 due to numerical issues RealType probabilityEpsilon = 1.0e-10; spatialPriorProbability = vnl_math_max( static_cast( probabilityEpsilon ), spatialPriorProbability ); distancePriorProbability = vnl_math_max( static_cast( probabilityEpsilon ), distancePriorProbability ); mrfPriorProbability = vnl_math_max( static_cast( probabilityEpsilon ), mrfPriorProbability ); likelihood = vnl_math_max( static_cast( probabilityEpsilon ), likelihood ); RealType posteriorProbability = 0.0; switch( this->m_PosteriorProbabilityFormulation ) { case Socrates: default: { /** R example library(ANTsR) post<-antsImageRead('testBrainSegmentationPosteriors2.nii.gz',2) prior<-antsImageRead('testBrainSegmentationPriorWarped2.nii.gz',2) mask<-antsImageRead('testBrainExtractionMask.nii.gz',2) pwt<-rev( c(0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1) ) gz0<-mask > 0 socrates<-function( likelihood, prior , priorwt ) {likelihood^(1-priorwt) * prior^priorwt } png('atropossocrates.png',width=1280,height=1280) par( mfrow=c(3,4) ) plotANTsImage( prior ) for ( i in c(1:length(pwt)) ) { tit<-paste( "Prior-Weight = ", as.character( pwt[i] ) ) temp<-antsImageClone( prior ) temp[ gz0 ]<-socrates( post[gz0], prior[gz0], pwt[i] ) plotANTsImage( temp ) } dev.off() */ posteriorProbability = std::pow( static_cast( spatialPriorProbability ), static_cast( this->m_PriorProbabilityWeight ) ) * std::pow( static_cast( likelihood * mrfPriorProbability ), static_cast( 1.0 - this->m_PriorProbabilityWeight ) ); } break; case Plato: { if( this->m_InitializationStrategy == PriorLabelImage && this->GetPriorLabelImage() && this->GetPriorLabelImage()->GetPixel( index ) == whichClass ) { spatialPriorProbability = 1.0; likelihood = 1.0; mrfPriorProbability = 1.0; } else if( this->GetPriorProbabilityImage( whichClass ) && this->GetPriorProbabilityImage( whichClass )->GetPixel( index ) == 1.0 ) { spatialPriorProbability = 1.0; likelihood = 1.0; mrfPriorProbability = 1.0; } posteriorProbability = std::pow( static_cast( spatialPriorProbability ), static_cast( this->m_PriorProbabilityWeight ) ) * std::pow( static_cast( likelihood * mrfPriorProbability ), static_cast( 1.0 - this->m_PriorProbabilityWeight ) ); } break; case Aristotle: { posteriorProbability = std::pow( static_cast( spatialPriorProbability * distancePriorProbability ), static_cast( this->m_PriorProbabilityWeight ) ) * std::pow( static_cast( likelihood * mrfPriorProbability ), static_cast( 1.0 - this->m_PriorProbabilityWeight ) ); } break; case Sigmoid: { /** some R code to show the effect pwt<-c(0,2^c(0:7)) sig<-function( x , priorwt, offset=0.5 ) { 1.0 / ( 1 + exp(-(x-offset)*priorwt ) ) } probs<-c(0:100)/100 par( mfrow=c(3,3) ) for ( i in c(1:length(pwt)) ) { tit<-paste("Prior-Weight = ",as.character(pwt[i])) plot(main=tit,sig(probs,pwt[i]),type='l',ylim=c(0,1)); points(probs,type='l',col='red') } */ RealType totalNumberOfClasses = static_cast( this->m_NumberOfTissueClasses + this->m_NumberOfPartialVolumeClasses ); RealType offset = 1.0 / totalNumberOfClasses; spatialPriorProbability = 1.0 / ( 1.0 + std::exp( -1.0 * ( spatialPriorProbability - offset ) * this->m_PriorProbabilityWeight ) ); posteriorProbability = static_cast( spatialPriorProbability ) * static_cast( likelihood * mrfPriorProbability ); } break; } if( this->m_InitialAnnealingTemperature != 1.0 ) { RealType annealingTemperature = this->m_InitialAnnealingTemperature * std::pow( this->m_AnnealingRate, static_cast( this->m_ElapsedIterations ) ); annealingTemperature = vnl_math_max( annealingTemperature, this->m_MinimumAnnealingTemperature ); posteriorProbability = std::pow( static_cast( posteriorProbability ), static_cast( 1.0 / annealingTemperature ) ); } if( vnl_math_isnan( posteriorProbability ) || vnl_math_isinf( posteriorProbability ) ) { posteriorProbability = 0.0; } return posteriorProbability; } template void AtroposSegmentationImageFilter ::EvaluateMRFNeighborhoodWeights( ConstNeighborhoodIterator It, Array & mrfNeighborhoodWeights ) { unsigned int totalNumberOfClasses = this->m_NumberOfTissueClasses + this->m_NumberOfPartialVolumeClasses; RealType mrfSmoothingFactor = this->m_MRFSmoothingFactor; if( this->m_MRFCoefficientImage ) { mrfSmoothingFactor = this->m_MRFCoefficientImage->GetPixel( It.GetIndex() ); } mrfNeighborhoodWeights.SetSize( totalNumberOfClasses ); mrfNeighborhoodWeights.Fill( 0.0 ); unsigned int neighborhoodSize = ( It.GetNeighborhood() ).Size(); if( mrfSmoothingFactor > 0.0 && neighborhoodSize > 1 ) { for( unsigned int label = 1; label <= totalNumberOfClasses; label++ ) { for( unsigned int n = 0; n < neighborhoodSize; n++ ) { if( n == static_cast( 0.5 * neighborhoodSize ) ) { continue; } bool isInBounds = false; LabelType neighborLabel = It.GetPixel( n, isInBounds ); if( !isInBounds || neighborLabel == 0 ) { continue; } typename ClassifiedImageType::OffsetType offset = It.GetOffset( n ); RealType distance = 0.0; for( unsigned int d = 0; d < ImageDimension; d++ ) { distance += vnl_math_sqr( offset[d] * this->m_ImageSpacing[d] ); } distance = std::sqrt( distance ); RealType delta = 0.0; if( label == neighborLabel ) { if( this->m_NumberOfPartialVolumeClasses > 0 ) { delta = -2.0; } else { delta = -1.0; } } else { bool isCommonTissue = false; typename PartialVolumeClassesType::const_iterator it; for( it = this->m_PartialVolumeClasses.begin(); it != this->m_PartialVolumeClasses.end(); ++it ) { if( std::find( it->begin(), it->end(), static_cast( label ) ) != it->end() && std::find( it->begin(), it->end(), static_cast( neighborLabel ) ) != it->end() ) { isCommonTissue = true; break; } } if( isCommonTissue ) { delta = -1.0; } else if( this->m_NumberOfPartialVolumeClasses > 0 ) { delta = 1.0; } else { delta = 0.0; } } mrfNeighborhoodWeights[label - 1] += ( delta / distance ); } } } } template typename AtroposSegmentationImageFilter ::RealImagePointer AtroposSegmentationImageFilter ::GetPosteriorProbabilityImage( unsigned int whichClass ) { unsigned int totalNumberOfClasses = this->m_NumberOfTissueClasses + this->m_NumberOfPartialVolumeClasses; if( whichClass > totalNumberOfClasses ) { itkExceptionMacro( "Requested class is greater than the number of classes." ); } // // If memory minimization is turned off and if the posterior probability // images have already been calculated, simply return the probability // image for the requested class. Otherwise, calculate the probability // image. // if( whichClass <= this->m_PosteriorProbabilityImages.size() ) { return this->m_PosteriorProbabilityImages[whichClass - 1]; } else { // // Here we assume that the calling function is invoked in order such // that GetPosteriorProbabilityImage( 1 ) is called before // GetPosteriorProbabilityImage( 2 ), etc. As such, when this part of // the code is reached and the class requested is '1', we assume that // the sum of the posterior probability images needs to be calculated // for normalization purposes. This sum is then saved for subsequent calls. // RealImagePointer posteriorProbabilityImage = AllocImage( this->GetOutput(), 0 ); // // Calculate the sum of the probability images. Also, store the // posterior probability images if m_MinimizeMemoryUsage == false. // if( whichClass == 1 ) { this->m_SumPosteriorProbabilityImage = AllocImage( this->GetOutput(), 0 ); RealImagePointer sumPriorProbabilityImage = ITK_NULLPTR; if( this->m_InitializationStrategy == PriorLabelImage || this->m_InitializationStrategy == PriorProbabilityImages ) { sumPriorProbabilityImage = AllocImage( this->GetOutput(), 0 ); for( unsigned int c = 0; c < totalNumberOfClasses; c++ ) { RealImagePointer priorProbabilityImage = this->GetPriorProbabilityImage( c + 1 ); if( !priorProbabilityImage ) { continue; } ImageRegionIteratorWithIndex ItS( sumPriorProbabilityImage, sumPriorProbabilityImage->GetLargestPossibleRegion() ); for( ItS.GoToBegin(); !ItS.IsAtEnd(); ++ItS ) { RealType priorProbability = 0.0; if( priorProbabilityImage ) { priorProbability = priorProbabilityImage->GetPixel( ItS.GetIndex() ); } else if( this->GetPriorLabelImage() ) { if( priorProbability == 0 ) { priorProbability = 1.0 / static_cast( totalNumberOfClasses ); } else { priorProbability = 1.0; } } ItS.Set( ItS.Get() + this->m_MixtureModelProportions[c] * priorProbability ); } } } for( unsigned int c = 0; c < totalNumberOfClasses; c++ ) { std::vector smoothImages; if( this->m_InitializationStrategy == PriorProbabilityImages || (this->m_InitializationStrategy == PriorLabelImage && c < this->m_NumberOfTissueClasses) ) { for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { if( this->m_AdaptiveSmoothingWeights.size() > i && this->m_AdaptiveSmoothingWeights[i] > 0.0 ) { smoothImages.push_back( this->GetSmoothIntensityImageFromPriorImage( i, c + 1 ) ); } else { smoothImages.push_back( ITK_NULLPTR ); } } } RealImagePointer distancePriorProbabilityImage = this->GetDistancePriorProbabilityImage( c + 1 ); RealImagePointer priorProbabilityImage = this->GetPriorProbabilityImage( c + 1 ); typename NeighborhoodIterator::RadiusType radius; unsigned int neighborhoodSize = 1; for( unsigned int d = 0; d < ImageDimension; d++ ) { neighborhoodSize *= ( 2 * this->m_MRFRadius[d] + 1 ); radius[d] = this->m_MRFRadius[d]; } ConstNeighborhoodIterator ItO( radius, this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); ImageRegionIterator ItS( this->m_SumPosteriorProbabilityImage, this->m_SumPosteriorProbabilityImage->GetRequestedRegion() ); for( ItO.GoToBegin(), ItS.GoToBegin(); !ItO.IsAtEnd(); ++ItO, ++ItS ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItO.GetIndex() ) != NumericTraits::ZeroValue() ) { RealType mrfSmoothingFactor = this->m_MRFSmoothingFactor; if( this->m_MRFCoefficientImage ) { mrfSmoothingFactor = this->m_MRFCoefficientImage->GetPixel( ItO.GetIndex() ); } // // Perform mrf prior calculation // RealType mrfPriorProbability = 1.0; if( mrfSmoothingFactor > 0.0 && ( ItO.GetNeighborhood() ).Size() > 1 ) { Array mrfNeighborhoodWeights; this->EvaluateMRFNeighborhoodWeights( ItO, mrfNeighborhoodWeights ); RealType numerator = std::exp( -mrfSmoothingFactor * mrfNeighborhoodWeights[c] ); RealType denominator = 0.0; for( unsigned int n = 0; n < totalNumberOfClasses; n++ ) { denominator += std::exp( -mrfSmoothingFactor * mrfNeighborhoodWeights[n] ); } if( denominator > 0.0 ) { mrfPriorProbability = numerator / denominator; } } // // Perform prior calculation using both the mixing proportions // and template-based prior images (if available) // RealType priorProbability = 0.0; RealType distancePriorProbability = 0.0; if( this->m_InitializationStrategy == PriorLabelImage || this->m_InitializationStrategy == PriorProbabilityImages ) { if( distancePriorProbabilityImage ) { distancePriorProbability = distancePriorProbabilityImage->GetPixel( ItO.GetIndex() ); } if( priorProbabilityImage ) { priorProbability = priorProbabilityImage->GetPixel( ItO.GetIndex() ); } else if( this->GetPriorLabelImage() ) { if( priorProbability == 0.0 ) { priorProbability = 1.0 / static_cast( totalNumberOfClasses ); } else { priorProbability = 1.0; } } RealType sumPriorProbability = sumPriorProbabilityImage->GetPixel( ItO.GetIndex() ); if( sumPriorProbability > this->m_ProbabilityThreshold ) { priorProbability *= ( this->m_MixtureModelProportions[c] / sumPriorProbability ); } else if( distancePriorProbabilityImage ) { priorProbability = distancePriorProbability; } } MeasurementVectorType measurement; measurement.SetSize( this->m_NumberOfIntensityImages ); for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { measurement[i] = this->GetIntensityImage( i )->GetPixel( ItO.GetIndex() ); if( ( this->m_InitializationStrategy == PriorProbabilityImages || this->m_InitializationStrategy == PriorLabelImage ) && smoothImages[i] ) { measurement[i] = ( 1.0 - this->m_AdaptiveSmoothingWeights[i] ) * measurement[i] + this->m_AdaptiveSmoothingWeights[i] * smoothImages[i]->GetPixel( ItO.GetIndex() ); } } // // Calculate likelihood probability from the model // RealType likelihood = this->m_MixtureModelComponents[c]->Evaluate( measurement ); // // Calculate the local posterior probability. Given that the // algorithm is meant to maximize the posterior probability of the // labeling configuration, this is the critical energy minimization // equation. // RealType posteriorProbability = this->CalculateLocalPosteriorProbability( this->m_MixtureModelProportions[c], priorProbability, distancePriorProbability, mrfPriorProbability, likelihood, ItO.GetIndex(), c + 1 ); if( vnl_math_isnan( posteriorProbability ) || vnl_math_isinf( posteriorProbability ) ) { posteriorProbability = 0.0; } if( ( c == 0 ) || !this->m_MinimizeMemoryUsage ) { posteriorProbabilityImage->SetPixel( ItO.GetIndex(), posteriorProbability ); } // // Calculate a running total of the posterior probabilities. // ItS.Set( ItS.Get() + posteriorProbability ); } } if( !this->m_MinimizeMemoryUsage ) { typedef ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( posteriorProbabilityImage ); duplicator->Update(); this->m_PosteriorProbabilityImages.push_back( duplicator->GetModifiableOutput() ); } } // // Normalize the posterior probability image(s). // ImageRegionIterator ItS( this->m_SumPosteriorProbabilityImage, this->m_SumPosteriorProbabilityImage->GetRequestedRegion() ); if( this->m_MinimizeMemoryUsage ) { ImageRegionIterator ItP( posteriorProbabilityImage, posteriorProbabilityImage->GetRequestedRegion() ); for( ItP.GoToBegin(), ItS.GoToBegin(); !ItS.IsAtEnd(); ++ItP, ++ItS ) { if( ItS.Get() > 0 ) { ItP.Set( ItP.Get() / ItS.Get() ); } } return posteriorProbabilityImage; } else { for( unsigned int n = 0; n < totalNumberOfClasses; n++ ) { ImageRegionIterator ItP( this->m_PosteriorProbabilityImages[n], this->m_PosteriorProbabilityImages[n]->GetRequestedRegion() ); for( ItP.GoToBegin(), ItS.GoToBegin(); !ItS.IsAtEnd(); ++ItP, ++ItS ) { if( ItS.Get() > 0 ) { ItP.Set( ItP.Get() / ItS.Get() ); } } } return this->m_PosteriorProbabilityImages[0]; } } else // whichClass > 1 { RealImagePointer sumPriorProbabilityImage = ITK_NULLPTR; if( this->m_InitializationStrategy == PriorLabelImage || this->m_InitializationStrategy == PriorProbabilityImages ) { sumPriorProbabilityImage = RealImageType::New(); sumPriorProbabilityImage->CopyInformation( this->GetOutput() ); sumPriorProbabilityImage->SetRegions( this->GetOutput()->GetRequestedRegion() ); sumPriorProbabilityImage->Allocate(); sumPriorProbabilityImage->FillBuffer( 0 ); for( unsigned int c = 0; c < totalNumberOfClasses; c++ ) { RealImagePointer priorProbabilityImage = this->GetPriorProbabilityImage( c + 1 ); if( !priorProbabilityImage ) { continue; } ImageRegionIteratorWithIndex ItS( sumPriorProbabilityImage, sumPriorProbabilityImage->GetLargestPossibleRegion() ); for( ItS.GoToBegin(); !ItS.IsAtEnd(); ++ItS ) { RealType priorProbability = 0.0; if( priorProbabilityImage ) { priorProbability = priorProbabilityImage->GetPixel( ItS.GetIndex() ); } else if( this->GetPriorLabelImage() ) { if( priorProbability == 0.0 ) { priorProbability = 1.0 / static_cast( totalNumberOfClasses ); } else { priorProbability = 1.0; } } ItS.Set( ItS.Get() + this->m_MixtureModelProportions[c] * priorProbability ); } } } std::vector smoothImages; if( this->m_InitializationStrategy == PriorProbabilityImages || this->m_InitializationStrategy == PriorLabelImage ) { for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { if( this->m_AdaptiveSmoothingWeights.size() > i && this->m_AdaptiveSmoothingWeights[i] > 0.0 ) { smoothImages.push_back( this->GetSmoothIntensityImageFromPriorImage( i, whichClass ) ); } else { smoothImages.push_back( ITK_NULLPTR ); } } } RealImagePointer distancePriorProbabilityImage = this->GetDistancePriorProbabilityImage( whichClass ); RealImagePointer priorProbabilityImage = this->GetPriorProbabilityImage( whichClass ); typename NeighborhoodIterator::RadiusType radius; for( unsigned int d = 0; d < ImageDimension; d++ ) { radius[d] = this->m_MRFRadius[d]; } ConstNeighborhoodIterator ItO( radius, this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); for( ItO.GoToBegin(); !ItO.IsAtEnd(); ++ItO ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItO.GetIndex() ) != NumericTraits::ZeroValue() ) { RealType mrfSmoothingFactor = this->m_MRFSmoothingFactor; if( this->m_MRFCoefficientImage ) { mrfSmoothingFactor = this->m_MRFCoefficientImage->GetPixel( ItO.GetIndex() ); } // // Perform mrf prior calculation // RealType mrfPriorProbability = 1.0; if( mrfSmoothingFactor > 0.0 && ( ItO.GetNeighborhood() ).Size() > 1 ) { Array mrfNeighborhoodWeights; this->EvaluateMRFNeighborhoodWeights( ItO, mrfNeighborhoodWeights ); RealType numerator = std::exp( -mrfSmoothingFactor * mrfNeighborhoodWeights[whichClass - 1] ); RealType denominator = 0.0; for( unsigned int n = 0; n < totalNumberOfClasses; n++ ) { denominator += std::exp( -mrfSmoothingFactor * mrfNeighborhoodWeights[n] ); } if( denominator > 0.0 ) { mrfPriorProbability = numerator / denominator; } } // // Perform prior calculation using both the mixing proportions // and template-based prior images (if available) // RealType priorProbability = 0.0; RealType distancePriorProbability = 0.0; if( this->m_InitializationStrategy == PriorLabelImage || this->m_InitializationStrategy == PriorProbabilityImages ) { if( distancePriorProbabilityImage ) { distancePriorProbability = distancePriorProbabilityImage->GetPixel( ItO.GetIndex() ); } if( priorProbabilityImage ) { priorProbability = priorProbabilityImage->GetPixel( ItO.GetIndex() ); } RealType sumPriorProbability = sumPriorProbabilityImage->GetPixel( ItO.GetIndex() ); if( sumPriorProbability > this->m_ProbabilityThreshold ) { priorProbability *= ( this->m_MixtureModelProportions[whichClass - 1] / sumPriorProbability ); } else if( distancePriorProbabilityImage ) { priorProbability = distancePriorProbability; } } MeasurementVectorType measurement; measurement.SetSize( this->m_NumberOfIntensityImages ); for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { measurement[i] = this->GetIntensityImage( i )->GetPixel( ItO.GetIndex() ); if( ( this->m_InitializationStrategy == PriorProbabilityImages || this->m_InitializationStrategy == PriorLabelImage ) && smoothImages[i] ) { measurement[i] = ( 1.0 - this->m_AdaptiveSmoothingWeights[i] ) * measurement[i] + this->m_AdaptiveSmoothingWeights[i] * smoothImages[i]->GetPixel( ItO.GetIndex() ); } } // // Calculate likelihood probability from the model // RealType likelihood = this->m_MixtureModelComponents[whichClass - 1]->Evaluate( measurement ); // // Calculate the local posterior probability. Given that the // algorithm is meant to maximize the posterior probability of the // labeling configuration, this is the critical energy minimization // equation. // RealType posteriorProbability = this->CalculateLocalPosteriorProbability( this->m_MixtureModelProportions[whichClass - 1], priorProbability, distancePriorProbability, mrfPriorProbability, likelihood, ItO.GetIndex(), whichClass ); if( vnl_math_isnan( posteriorProbability ) || vnl_math_isinf( posteriorProbability ) ) { posteriorProbability = 0.0; } posteriorProbabilityImage->SetPixel( ItO.GetIndex(), posteriorProbability ); } } // // Normalize the posterior probability image(s). // ImageRegionIterator ItS( this->m_SumPosteriorProbabilityImage, this->m_SumPosteriorProbabilityImage->GetRequestedRegion() ); ImageRegionIterator ItP( posteriorProbabilityImage, posteriorProbabilityImage->GetRequestedRegion() ); for( ItP.GoToBegin(), ItS.GoToBegin(); !ItS.IsAtEnd(); ++ItP, ++ItS ) { if( ItS.Get() > 0 ) { ItP.Set( ItP.Get() / ItS.Get() ); } } return posteriorProbabilityImage; } } } template typename AtroposSegmentationImageFilter ::RealImagePointer AtroposSegmentationImageFilter ::GetDistancePriorProbabilityImage( unsigned int whichClass ) { unsigned int totalNumberOfClasses = this->m_NumberOfTissueClasses + this->m_NumberOfPartialVolumeClasses; if( ( this->m_InitializationStrategy != PriorLabelImage && this->m_InitializationStrategy != PriorProbabilityImages ) || ( whichClass > this->m_NumberOfTissueClasses && whichClass <= totalNumberOfClasses ) ) { return ITK_NULLPTR; } if( this->m_NumberOfPartialVolumeClasses == 0 && whichClass > this->m_NumberOfTissueClasses ) { itkExceptionMacro( "The requested distance prior probability image = " << whichClass << " should be in the range [1, " << this->m_NumberOfTissueClasses << "]" ); } else if( whichClass > this->m_NumberOfTissueClasses ) { return ITK_NULLPTR; } // // If memory minimization is turned off and if the distance prior probability // images have already been calculated, simply return the probability // image for the requested class. Otherwise, calculate the probability // image. // if( whichClass <= this->m_DistancePriorProbabilityImages.size() ) { return this->m_DistancePriorProbabilityImages[whichClass - 1]; } else { // // Here we assume that the calling function is invoked in order such // that GetDistancePriorImage( 1 ) is called before // GetDistancePriorImage( 2 ), etc. As such, when this part of // the code is reached and the class requested is '1', we assume that // the sum of the distance prior probability images needs to be calculated // for normalization purposes. This sum is then saved for subsequent calls. // RealImagePointer distancePriorProbabilityImage = ITK_NULLPTR; // // Calculate the sum of the distance probability images. Also, store the // distance probability images if m_MinimizeMemoryUsage == false. // if( whichClass == 1 ) { this->m_SumDistancePriorProbabilityImage = RealImageType::New(); this->m_SumDistancePriorProbabilityImage->CopyInformation( this->GetOutput() ); this->m_SumDistancePriorProbabilityImage->SetRegions( this->GetOutput()->GetRequestedRegion() ); this->m_SumDistancePriorProbabilityImage->Allocate(); this->m_SumDistancePriorProbabilityImage->FillBuffer( 0 ); for( unsigned int c = 0; c < this->m_NumberOfTissueClasses; c++ ) { typedef BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); if( this->m_InitializationStrategy == PriorLabelImage ) { thresholder->SetInput( const_cast( this->GetPriorLabelImage() ) ); } else { thresholder->SetInput( this->GetOutput() ); } thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); thresholder->SetLowerThreshold( static_cast( c + 1 ) ); thresholder->SetUpperThreshold( static_cast( c + 1 ) ); thresholder->Update(); RealImagePointer distanceImage = RealImageType::New(); if( this->m_UseEuclideanDistanceForPriorLabels ) { typedef SignedMaurerDistanceMapImageFilter DistancerType; typename DistancerType::Pointer distancer = DistancerType::New(); distancer->SetInput( thresholder->GetOutput() ); distancer->SetSquaredDistance( false ); distancer->SetUseImageSpacing( true ); distancer->SetInsideIsPositive( false ); distancer->Update(); distanceImage = distancer->GetOutput(); } else { typedef BinaryContourImageFilter ContourFilterType; typename ContourFilterType::Pointer contour = ContourFilterType::New(); contour->SetInput( thresholder->GetOutput() ); contour->FullyConnectedOff(); contour->SetBackgroundValue( 0 ); contour->SetForegroundValue( 1 ); contour->Update(); typedef FastMarchingImageFilter FastMarchingFilterType; typename FastMarchingFilterType::Pointer fastMarching = FastMarchingFilterType::New(); typedef CastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); if( this->GetMaskImage() ) { caster->SetInput( const_cast( this->GetMaskImage() ) ); caster->Update(); fastMarching->SetInput( caster->GetOutput() ); } else { fastMarching->SetSpeedConstant( 1.0 ); fastMarching->SetOverrideOutputInformation( true ); fastMarching->SetOutputOrigin( this->GetOutput()->GetOrigin() ); fastMarching->SetOutputSpacing( this->GetOutput()->GetSpacing() ); fastMarching->SetOutputRegion( this->GetOutput()->GetRequestedRegion() ); fastMarching->SetOutputDirection( this->GetOutput()->GetDirection() ); } typedef typename FastMarchingFilterType::NodeContainer NodeContainer; typedef typename FastMarchingFilterType::NodeType NodeType; typename NodeContainer::Pointer trialPoints = NodeContainer::New(); trialPoints->Initialize(); unsigned long trialCount = 0; ImageRegionIteratorWithIndex ItC( contour->GetOutput(), contour->GetOutput()->GetRequestedRegion() ); for( ItC.GoToBegin(); !ItC.IsAtEnd(); ++ItC ) { if( ItC.Get() == contour->GetForegroundValue() ) { NodeType node; node.SetValue( 0.0 ); node.SetIndex( ItC.GetIndex() ); trialPoints->InsertElement( trialCount++, node ); } } fastMarching->SetTrialPoints( trialPoints ); fastMarching->SetStoppingValue( NumericTraits::max() ); // fastMarching->SetTopologyCheck( FastMarchingFilterType::None ); fastMarching->Update(); ImageRegionIterator ItT( thresholder->GetOutput(), thresholder->GetOutput()->GetRequestedRegion() ); ImageRegionIterator ItF( fastMarching->GetOutput(), fastMarching->GetOutput()->GetRequestedRegion() ); for( ItT.GoToBegin(), ItF.GoToBegin(); !ItT.IsAtEnd(); ++ItT, ++ItF ) { if( ItT.Get() == 1 ) { ItF.Set( -ItF.Get() ); } } distanceImage = fastMarching->GetOutput(); } RealType maximumInteriorDistance = 0.0; ImageRegionIterator ItD( distanceImage, distanceImage->GetRequestedRegion() ); for( ItD.GoToBegin(); !ItD.IsAtEnd(); ++ItD ) { if( ItD.Get() < 0 && maximumInteriorDistance < vnl_math_abs( ItD.Get() ) ) { maximumInteriorDistance = vnl_math_abs( ItD.Get() ); } } RealType labelLambda = 0.0; RealType labelBoundaryProbability = 1.0; typename LabelParameterMapType::iterator it = this->m_PriorLabelParameterMap.find( c + 1 ); if( it != this->m_PriorLabelParameterMap.end() ) { labelLambda = ( it->second ).first; labelBoundaryProbability = ( it->second ).second; } for( ItD.GoToBegin(); !ItD.IsAtEnd(); ++ItD ) { if( labelLambda == 0 ) { if( ItD.Get() <= 0 ) { ItD.Set( labelBoundaryProbability ); } else { ItD.Set( 0.0 ); } } else if( ItD.Get() >= 0 ) { ItD.Set( labelBoundaryProbability * std::exp( -labelLambda * ItD.Get() ) ); } else if( ItD.Get() < 0 ) { ItD.Set( 1.0 - ( 1.0 - labelBoundaryProbability ) * ( maximumInteriorDistance - vnl_math_abs( ItD.Get() ) ) / ( maximumInteriorDistance ) ); } } typedef AddImageFilter AdderType; typename AdderType::Pointer adder = AdderType::New(); adder->SetInput1( this->m_SumDistancePriorProbabilityImage ); adder->SetInput2( distanceImage ); adder->Update(); this->m_SumDistancePriorProbabilityImage = adder->GetOutput(); if( ( c == 0 ) && this->m_MinimizeMemoryUsage ) { distancePriorProbabilityImage = distanceImage; } if( !this->m_MinimizeMemoryUsage ) { this->m_DistancePriorProbabilityImages.push_back( distanceImage ); } } // // Normalize the distance prior probability image(s). // ImageRegionIterator ItS( this->m_SumDistancePriorProbabilityImage, this->m_SumDistancePriorProbabilityImage->GetRequestedRegion() ); if( this->m_MinimizeMemoryUsage ) { ImageRegionIteratorWithIndex ItD( distancePriorProbabilityImage, distancePriorProbabilityImage->GetRequestedRegion() ); for( ItD.GoToBegin(), ItS.GoToBegin(); !ItS.IsAtEnd(); ++ItD, ++ItS ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItD.GetIndex() ) != NumericTraits::ZeroValue() ) { if( ItS.Get() <= this->m_ProbabilityThreshold ) { ItD.Set( NumericTraits::ZeroValue() ); } else { ItD.Set( ItD.Get() / ItS.Get() ); } } } return distancePriorProbabilityImage; } else { for( unsigned int c = 0; c < this->m_NumberOfTissueClasses; c++ ) { ImageRegionIteratorWithIndex ItD( this->m_DistancePriorProbabilityImages[c], this->m_DistancePriorProbabilityImages[c]->GetRequestedRegion() ); for( ItD.GoToBegin(), ItS.GoToBegin(); !ItS.IsAtEnd(); ++ItD, ++ItS ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItD.GetIndex() ) != NumericTraits::ZeroValue() ) { if( ItS.Get() <= this->m_ProbabilityThreshold ) { ItD.Set( NumericTraits::ZeroValue() ); } else { ItD.Set( ItD.Get() / ItS.Get() ); } } } } return this->m_DistancePriorProbabilityImages[0]; } } else // whichClass > 1 { typedef BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); if( this->m_InitializationStrategy == PriorLabelImage ) { thresholder->SetInput( const_cast( this->GetPriorLabelImage() ) ); } else { thresholder->SetInput( this->GetOutput() ); } thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); thresholder->SetLowerThreshold( static_cast( whichClass ) ); thresholder->SetUpperThreshold( static_cast( whichClass ) ); thresholder->Update(); RealImagePointer distanceImage = RealImageType::New(); if( this->m_UseEuclideanDistanceForPriorLabels ) { typedef SignedMaurerDistanceMapImageFilter DistancerType; typename DistancerType::Pointer distancer = DistancerType::New(); distancer->SetInput( thresholder->GetOutput() ); distancer->SetSquaredDistance( false ); distancer->SetUseImageSpacing( true ); distancer->SetInsideIsPositive( false ); distancer->Update(); distanceImage = distancer->GetOutput(); } else { typedef BinaryContourImageFilter ContourFilterType; typename ContourFilterType::Pointer contour = ContourFilterType::New(); contour->SetInput( thresholder->GetOutput() ); contour->FullyConnectedOff(); contour->SetBackgroundValue( 0 ); contour->SetForegroundValue( 1 ); contour->Update(); typedef FastMarchingImageFilter FastMarchingFilterType; typename FastMarchingFilterType::Pointer fastMarching = FastMarchingFilterType::New(); typedef CastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); if( this->GetMaskImage() ) { caster->SetInput( const_cast( this->GetMaskImage() ) ); caster->Update(); fastMarching->SetInput( caster->GetOutput() ); } else { fastMarching->SetSpeedConstant( 1.0 ); fastMarching->SetOverrideOutputInformation( true ); fastMarching->SetOutputOrigin( this->GetOutput()->GetOrigin() ); fastMarching->SetOutputSpacing( this->GetOutput()->GetSpacing() ); fastMarching->SetOutputRegion( this->GetOutput()->GetRequestedRegion() ); fastMarching->SetOutputDirection( this->GetOutput()->GetDirection() ); } typedef typename FastMarchingFilterType::NodeContainer NodeContainer; typedef typename FastMarchingFilterType::NodeType NodeType; typename NodeContainer::Pointer trialPoints = NodeContainer::New(); trialPoints->Initialize(); unsigned long trialCount = 0; ImageRegionIteratorWithIndex ItC( contour->GetOutput(), contour->GetOutput()->GetRequestedRegion() ); for( ItC.GoToBegin(); !ItC.IsAtEnd(); ++ItC ) { if( ItC.Get() == contour->GetForegroundValue() ) { NodeType node; node.SetValue( 0.0 ); node.SetIndex( ItC.GetIndex() ); trialPoints->InsertElement( trialCount++, node ); } } fastMarching->SetTrialPoints( trialPoints ); fastMarching->SetStoppingValue( NumericTraits::max() ); // fastMarching->SetTopologyCheck( FastMarchingFilterType::None ); fastMarching->Update(); ImageRegionIterator ItT( thresholder->GetOutput(), thresholder->GetOutput()->GetRequestedRegion() ); ImageRegionIterator ItF( fastMarching->GetOutput(), fastMarching->GetOutput()->GetRequestedRegion() ); for( ItT.GoToBegin(), ItF.GoToBegin(); !ItT.IsAtEnd(); ++ItT, ++ItF ) { if( ItT.Get() == 1 ) { ItF.Set( -ItF.Get() ); } } distanceImage = fastMarching->GetOutput(); } distancePriorProbabilityImage = distanceImage; RealType maximumInteriorDistance = 0.0; ImageRegionIterator ItD( distancePriorProbabilityImage, distancePriorProbabilityImage->GetRequestedRegion() ); for( ItD.GoToBegin(); !ItD.IsAtEnd(); ++ItD ) { if( ItD.Get() < 0 && maximumInteriorDistance < vnl_math_abs( ItD.Get() ) ) { maximumInteriorDistance = vnl_math_abs( ItD.Get() ); } } RealType labelLambda = 0.0; RealType labelBoundaryProbability = 1.0; typename LabelParameterMapType::iterator it = this->m_PriorLabelParameterMap.find( whichClass ); if( it != this->m_PriorLabelParameterMap.end() ) { labelLambda = ( it->second ).first; labelBoundaryProbability = ( it->second ).second; } for( ItD.GoToBegin(); !ItD.IsAtEnd(); ++ItD ) { if( labelLambda == 0 ) { if( ItD.Get() <= 0 ) { ItD.Set( labelBoundaryProbability ); } else { ItD.Set( 0.0 ); } } else if( ItD.Get() >= 0 ) { ItD.Set( labelBoundaryProbability * std::exp( -labelLambda * ItD.Get() ) ); } else if( ItD.Get() < 0 ) { ItD.Set( 1.0 - ( 1.0 - labelBoundaryProbability ) * ( maximumInteriorDistance - vnl_math_abs( ItD.Get() ) ) / ( maximumInteriorDistance ) ); } } // // Normalize the distance prior probability image(s). // ImageRegionIterator ItS( this->m_SumDistancePriorProbabilityImage, this->m_SumDistancePriorProbabilityImage->GetRequestedRegion() ); for( ItD.GoToBegin(), ItS.GoToBegin(); !ItS.IsAtEnd(); ++ItD, ++ItS ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItD.GetIndex() ) != NumericTraits::ZeroValue() ) { if( ItS.Get() <= this->m_ProbabilityThreshold ) { ItD.Set( NumericTraits::ZeroValue() ); } else { ItD.Set( ItD.Get() / ItS.Get() ); } } } return distancePriorProbabilityImage; } } } template typename AtroposSegmentationImageFilter ::RealImagePointer AtroposSegmentationImageFilter ::GetSmoothIntensityImageFromPriorImage( unsigned int whichImage, unsigned int whichClass ) { typename ScalarImageType::Pointer bsplineImage; if( this->m_ControlPointLattices[whichImage][whichClass - 1].GetPointer() != ITK_NULLPTR ) { typedef BSplineControlPointImageFilter BSplineReconstructorType; typename BSplineReconstructorType::Pointer bspliner = BSplineReconstructorType::New(); bspliner->SetInput( this->m_ControlPointLattices[whichImage][whichClass - 1] ); bspliner->SetSize( this->GetInput()->GetRequestedRegion().GetSize() ); bspliner->SetSpacing( this->GetInput()->GetSpacing() ); bspliner->SetOrigin( this->GetInput()->GetOrigin() ); bspliner->SetDirection( this->GetInput()->GetDirection() ); bspliner->SetSplineOrder( this->m_SplineOrder ); bspliner->Update(); bsplineImage = bspliner->GetOutput(); } else { typename PointSetType::Pointer points = PointSetType::New(); points->Initialize(); typedef typename BSplineFilterType::WeightsContainerType WeightsType; typename WeightsType::Pointer weights = WeightsType::New(); weights->Initialize(); RealImagePointer probabilityImage; if( this->m_InitializationStrategy == PriorProbabilityImages ) { probabilityImage = this->GetPriorProbabilityImage( whichClass ); } else { typedef BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( const_cast( this->GetPriorLabelImage() ) ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); thresholder->SetLowerThreshold( static_cast( whichClass ) ); thresholder->SetUpperThreshold( static_cast( whichClass ) ); thresholder->Update(); probabilityImage = thresholder->GetOutput(); } typename RealImageType::DirectionType originalDirection = probabilityImage->GetDirection(); typename RealImageType::DirectionType identity; identity.SetIdentity(); probabilityImage->SetDirection( identity ); unsigned long count = 0; ImageRegionIteratorWithIndex ItP( probabilityImage, probabilityImage->GetBufferedRegion() ); for( ItP.GoToBegin(); !ItP.IsAtEnd(); ++ItP ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItP.GetIndex() ) != NumericTraits::ZeroValue() ) { if( ItP.Get() >= 0.5 ) { typename RealImageType::PointType imagePoint; probabilityImage->TransformIndexToPhysicalPoint( ItP.GetIndex(), imagePoint ); typename PointSetType::PointType bsplinePoint; bsplinePoint.CastFrom( imagePoint ); ScalarType intensity; intensity[0] = this->GetIntensityImage( whichImage )->GetPixel( ItP.GetIndex() ); points->SetPoint( count, bsplinePoint ); points->SetPointData( count, intensity ); weights->InsertElement( count, ItP.Get() ); count++; } } } probabilityImage->SetDirection( originalDirection ); typename BSplineFilterType::ArrayType numberOfControlPoints; typename BSplineFilterType::ArrayType numberOfLevels; for( unsigned int d = 0; d < ImageDimension; d++ ) { numberOfControlPoints[d] = this->m_NumberOfControlPoints[d]; numberOfLevels[d] = this->m_NumberOfLevels[d]; } typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); bspliner->SetInput( points ); bspliner->SetPointWeights( weights ); bspliner->SetNumberOfLevels( numberOfLevels ); bspliner->SetSplineOrder( this->m_SplineOrder ); bspliner->SetNumberOfControlPoints( numberOfControlPoints ); bspliner->SetSize( this->GetOutput()->GetLargestPossibleRegion().GetSize() ); bspliner->SetOrigin( this->GetOutput()->GetOrigin() ); bspliner->SetDirection( this->GetOutput()->GetDirection() ); bspliner->SetSpacing( this->GetOutput()->GetSpacing() ); bspliner->SetGenerateOutputImage( true ); bspliner->Update(); bsplineImage = bspliner->GetOutput(); this->m_ControlPointLattices[whichImage][whichClass - 1] = bspliner->GetPhiLattice(); } typedef VectorIndexSelectionCastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput( bsplineImage ); caster->SetIndex( 0 ); caster->Update(); return caster->GetOutput(); } template typename AtroposSegmentationImageFilter ::RealImagePointer AtroposSegmentationImageFilter ::GetLikelihoodImage( unsigned int whichClass ) { RealImagePointer likelihoodImage = RealImageType::New(); likelihoodImage->CopyInformation( this->GetInput() ); likelihoodImage->SetRegions( this->GetInput()->GetRequestedRegion() ); likelihoodImage->Allocate(); likelihoodImage->FillBuffer( 0 ); std::vector smoothImages; if( this->m_InitializationStrategy == PriorProbabilityImages || this->m_InitializationStrategy == PriorLabelImage ) { for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { if( this->m_AdaptiveSmoothingWeights.size() > i && this->m_AdaptiveSmoothingWeights[i] > 0.0 ) { smoothImages.push_back( this->GetSmoothIntensityImageFromPriorImage( i, whichClass ) ); } else { smoothImages.push_back( ITK_NULLPTR ); } } } ImageRegionIteratorWithIndex It( likelihoodImage, likelihoodImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( It.GetIndex() ) != NumericTraits::ZeroValue() ) { MeasurementVectorType measurement; measurement.SetSize( this->m_NumberOfIntensityImages ); for( unsigned int i = 0; i < this->m_NumberOfIntensityImages; i++ ) { measurement[i] = this->GetIntensityImage( i )->GetPixel( It.GetIndex() ); if( ( this->m_InitializationStrategy == PriorProbabilityImages || this->m_InitializationStrategy == PriorLabelImage ) && smoothImages[i] ) { measurement[i] = ( 1.0 - this->m_AdaptiveSmoothingWeights[i] ) * measurement[i] + this->m_AdaptiveSmoothingWeights[i] * smoothImages[i]->GetPixel( It.GetIndex() ); } } RealType likelihood = this->m_MixtureModelComponents[whichClass - 1]->Evaluate( measurement ); It.Set( likelihood ); } } return likelihoodImage; } template void AtroposSegmentationImageFilter ::ComputeICMCodeImage() { if( !this->m_ICMCodeImage ) { this->m_ICMCodeImage = ClassifiedImageType::New(); this->m_ICMCodeImage->CopyInformation( this->GetInput() ); this->m_ICMCodeImage->SetRegions( this->GetInput()->GetRequestedRegion() ); this->m_ICMCodeImage->Allocate(); this->m_ICMCodeImage->FillBuffer( 0 ); } typename NeighborhoodIterator::RadiusType radius; unsigned int neighborhoodSize = 1; for( unsigned int d = 0; d < ImageDimension; d++ ) { neighborhoodSize *= ( 2 * this->m_MRFRadius[d] + 1 ); radius[d] = this->m_MRFRadius[d]; } NeighborhoodIterator It( radius, this->m_ICMCodeImage, this->m_ICMCodeImage->GetRequestedRegion() ); this->m_MaximumICMCode = 1; bool codingComplete = false; while( !codingComplete ) { codingComplete = true; It.GoToBegin(); while( !It.IsAtEnd() ) { if( this->m_ICMCodeImage->GetPixel( It.GetIndex() ) == 0 && ( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( It.GetIndex() ) != NumericTraits::ZeroValue() ) ) { bool hasCode = false; for( unsigned int n = 0; n < neighborhoodSize; n++ ) { bool isInBounds = false; LabelType label = It.GetPixel( n, isInBounds ); if( isInBounds && label == this->m_MaximumICMCode ) { hasCode = true; break; } } if( !hasCode ) { It.SetCenterPixel( this->m_MaximumICMCode ); codingComplete = false; } } ++It; } if( codingComplete == false ) { this->m_MaximumICMCode++; } } this->m_MaximumICMCode--; } template void AtroposSegmentationImageFilter ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf( os, indent ); os << indent << "Maximum number of iterations: " << this->m_MaximumNumberOfIterations << std::endl; os << indent << "Convergence threshold: " << this->m_ConvergenceThreshold << std::endl; os << indent << "Number of tissue classes: " << this->m_NumberOfTissueClasses << std::endl; os << indent << "Number of partial volume classes: " << this->m_NumberOfPartialVolumeClasses << std::endl; os << indent << "Minimize memory usage:"; if( this->m_MinimizeMemoryUsage ) { os << " true"; if( this->m_MinimizeMemoryUsage && this->m_InitializationStrategy == PriorProbabilityImages ) { os << " (prior probability threshold = " << this->m_ProbabilityThreshold << ")" << std::endl; } os << std::endl; } else { os << " false" << std::endl; } os << indent << "Initialization strategy: "; switch( this->m_InitializationStrategy ) { case Random: { os << "Random" << std::endl; } break; case KMeans: { os << "K means clustering" << std::endl; } break; case Otsu: { os << "Otsu thresholding" << std::endl; } break; case PriorProbabilityImages: { os << "Prior probability images" << std::endl; os << indent << " Use Euclidean distance for prior labels:"; if( this->m_UseEuclideanDistanceForPriorLabels ) { os << " true" << std::endl; } else { os << " false" << std::endl; } if( this->m_PriorLabelParameterMap.size() > 0 ) { os << indent << " Specified prior label parameters:" << std::endl; typename LabelParameterMapType::const_iterator it; for( it = this->m_PriorLabelParameterMap.begin(); it != this->m_PriorLabelParameterMap.end(); ++it ) { RealType label = it->first; RealType lambda = ( it->second ).first; RealType boundaryProbability = ( it->second ).second; os << indent << " Class " << label << ": lambda = " << lambda << ", boundary probability = " << boundaryProbability << std::endl; } } } break; case PriorLabelImage: { os << "Prior label image" << std::endl; os << indent << " Use Euclidean distance for prior labels:"; if( this->m_UseEuclideanDistanceForPriorLabels ) { os << " true" << std::endl; } else { os << " false" << std::endl; } os << indent << " Specified prior label parameters:" << std::endl; typename LabelParameterMapType::const_iterator it; for( it = this->m_PriorLabelParameterMap.begin(); it != this->m_PriorLabelParameterMap.end(); ++it ) { RealType label = it->first; RealType lambda = ( it->second ).first; RealType boundaryProbability = ( it->second ).second; os << indent << " Class " << label << ": lambda = " << lambda << ", boundary probability = " << boundaryProbability << std::endl; } } break; } os << indent << "Posterior probability formulation: "; switch( this->m_PosteriorProbabilityFormulation ) { case Socrates: { os << "Socrates" << std::endl; } break; case Plato: { os << "Plato" << std::endl; } break; case Aristotle: { os << "Aristotle" << std::endl; } break; case Sigmoid: { os << "Sigmoid" << std::endl; } break; } os << indent << " initial annealing temperature = " << this->m_InitialAnnealingTemperature << std::endl; os << indent << " annealing rate = " << this->m_AnnealingRate << std::endl; os << indent << " minimum annealing temperature = " << this->m_MinimumAnnealingTemperature << std::endl; os << indent << "MRF parameters" << std::endl; if( this->m_MRFCoefficientImage ) { os << indent << " Using MRF coefficient image" << std::endl; } else { os << indent << " MRF smoothing factor = " << this->m_MRFSmoothingFactor << std::endl; } os << indent << " MRF radius = " << this->m_MRFRadius << std::endl; if( this->m_UseAsynchronousUpdating ) { os << indent << "Use asynchronous updating of the labels." << std::endl; os << indent << " ICM parameters" << std::endl; os << indent << " maximum ICM code = " << this->m_MaximumICMCode << std::endl; os << indent << " maximum number of ICM iterations = " << this->m_MaximumNumberOfICMIterations << std::endl; } else { os << indent << "Use synchronous updating of the labels." << std::endl; } if( this->m_OutlierHandlingFilter ) { os << indent << "Outlier handling " << std::endl; this->m_OutlierHandlingFilter->Print( os, indent.GetNextIndent() ); } else { os << indent << "No outlier handling." << std::endl; } if( ( this->m_InitializationStrategy == PriorProbabilityImages || this->m_InitializationStrategy == PriorLabelImage ) && this->m_AdaptiveSmoothingWeights.size() > 0 ) { os << indent << "Adaptive smoothing weights: ["; for( unsigned int i = 0; i < this->m_AdaptiveSmoothingWeights.size() - 1; i++ ) { os << this->m_AdaptiveSmoothingWeights[i] << ", "; } os << this->m_AdaptiveSmoothingWeights[ this->m_AdaptiveSmoothingWeights.size() - 1] << "]" << std::endl; os << indent << "B-spline smoothing" << std::endl; os << indent << " spline order = " << this->m_SplineOrder << std::endl; os << indent << " number of levels = " << this->m_NumberOfLevels << std::endl; os << indent << " number of initial control points = " << this->m_NumberOfControlPoints << std::endl; } for( unsigned int n = 0; n < this->m_NumberOfTissueClasses; n++ ) { if( this->m_MixtureModelProportions.size() > n ) { os << indent << "Tissue class " << n + 1 << ": proportion = " << this->m_MixtureModelProportions[n] << std::endl; } if( this->m_MixtureModelComponents.size() > n ) { this->m_MixtureModelComponents[n]->Print( os, indent.GetNextIndent() ); } } if( this->m_UsePartialVolumeLikelihoods ) { unsigned int n = this->m_NumberOfTissueClasses; typename PartialVolumeClassesType::const_iterator it; for( it = this->m_PartialVolumeClasses.begin(); it != this->m_PartialVolumeClasses.end(); ++it ) { os << indent << "Partial volume class " << n + 1 << " [labels: "; for( unsigned int l = 0; l < it->size() - 1; l++ ) { os << ( *it )[l] << 'x'; } os << ( *it )[it->size() - 1] << "]: proportion = " << this->m_MixtureModelProportions[n] << std::endl; this->m_MixtureModelComponents[n++]->Print( os, indent.GetNextIndent() ); } } } } // namespace ants } // namespace itk #endif ants-2.2.0/ImageSegmentation/antsBoxPlotQuantileListSampleFilter.h000066400000000000000000000073231311104306400253530ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsBoxPlotQuantileListSampleFilter_h #define __antsBoxPlotQuantileListSampleFilter_h #include "antsListSampleToListSampleFilter.h" #include namespace itk { namespace ants { namespace Statistics { /** \class BoxPlotQuantileListSampleFilter * \brief Base class of filters intended to generate scalar samples from * intensity samples. * */ template class BoxPlotQuantileListSampleFilter : public ListSampleToListSampleFilter { public: /** * Standard class typedefs. */ typedef BoxPlotQuantileListSampleFilter Self; typedef ListSampleToListSampleFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** * Standard macros */ itkTypeMacro( BoxPlotQuantileListSampleFilter, ListSampleToScalarListSampleFilter ); /** * Method for creation through the object factory. */ itkNewMacro( Self ); /** * Conveneient typedefs */ typedef double RealType; typedef TScalarListSample ScalarListSampleType; typedef typename ScalarListSampleType ::MeasurementVectorType MeasurementVectorType; typedef typename ScalarListSampleType ::InstanceIdentifier InstanceIdentifierType; typedef std::vector InstanceIdentifierContainerType; enum OutlierHandlingType { None, Trim, Winsorize }; itkSetMacro( OutlierHandling, OutlierHandlingType ); itkGetConstMacro( OutlierHandling, OutlierHandlingType ); itkSetMacro( WhiskerScalingFactor, RealType ); itkGetConstMacro( WhiskerScalingFactor, RealType ); itkSetClampMacro( UpperPercentile, RealType, 0, 1 ); itkGetConstMacro( UpperPercentile, RealType ); itkSetClampMacro( LowerPercentile, RealType, 0, 1 ); itkGetConstMacro( LowerPercentile, RealType ); InstanceIdentifierContainerType GetOutlierInstanceIdentifiers() { return this->m_OutlierInstanceIdentifiers; } // itkGetConstMacro( Outliers, InstanceIdentifierContainerType ); protected: BoxPlotQuantileListSampleFilter(); virtual ~BoxPlotQuantileListSampleFilter(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; virtual void GenerateData() ITK_OVERRIDE; private: BoxPlotQuantileListSampleFilter( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented InstanceIdentifierType FindMaximumNonOutlierDeviationValue( RealType, RealType ); bool IsMeasurementAnOutlier( RealType, RealType, RealType, unsigned long ); OutlierHandlingType m_OutlierHandling; InstanceIdentifierContainerType m_OutlierInstanceIdentifiers; RealType m_WhiskerScalingFactor; RealType m_LowerPercentile; RealType m_UpperPercentile; }; // end of class } // end of namespace Statistics } // end of namespace ants4443 } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsBoxPlotQuantileListSampleFilter.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsBoxPlotQuantileListSampleFilter.hxx000066400000000000000000000137041311104306400257330ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsBoxPlotQuantileListSampleFilter_hxx #define __antsBoxPlotQuantileListSampleFilter_hxx #include "antsBoxPlotQuantileListSampleFilter.h" #include "itkDenseFrequencyContainer2.h" #include "itkHistogram.h" #include "itkSampleToHistogramFilter.h" namespace itk { namespace ants { namespace Statistics { template BoxPlotQuantileListSampleFilter ::BoxPlotQuantileListSampleFilter() { this->AllocateOutput(); this->GetOutput()->SetMeasurementVectorSize( 1 ); this->m_OutlierHandling = Winsorize; this->m_WhiskerScalingFactor = 1.5; this->m_LowerPercentile = 0.25; this->m_UpperPercentile = 0.75; } template BoxPlotQuantileListSampleFilter ::~BoxPlotQuantileListSampleFilter() { } template void BoxPlotQuantileListSampleFilter ::GenerateData() { if( this->GetInput()->GetMeasurementVectorSize() != 1 ) { itkExceptionMacro( "The input sample must be univariate." ); } if( this->m_LowerPercentile >= this->m_UpperPercentile ) { itkExceptionMacro( "Lower percentile must be less than upper percentile." ); } const unsigned int scalarMeasurementVectorSize = this->GetInput()->GetMeasurementVectorSize(); this->GetOutput()->SetMeasurementVectorSize( scalarMeasurementVectorSize ); /** * Initialize the histogram in preparation */ typedef itk::Statistics:: Histogram HistogramType; typedef itk::Statistics:: SampleToHistogramFilter SampleFilterType; typename SampleFilterType::HistogramSizeType histogramSize( 1 ); histogramSize.Fill( 200 ); typename SampleFilterType::Pointer sampleFilter = SampleFilterType::New(); sampleFilter->SetInput( this->GetInput() ); sampleFilter->SetHistogramSize( histogramSize ); sampleFilter->Update(); RealType lowerQuantile = sampleFilter->GetOutput()-> Quantile( 0, this->m_LowerPercentile ); RealType upperQuantile = sampleFilter->GetOutput()-> Quantile( 0, this->m_UpperPercentile ); RealType upperBound = upperQuantile + this->m_WhiskerScalingFactor * ( upperQuantile - lowerQuantile ); RealType lowerBound = lowerQuantile - this->m_WhiskerScalingFactor * ( upperQuantile - lowerQuantile ); typename ScalarListSampleType::ConstIterator It = this->GetInput()->Begin(); It = this->GetInput()->Begin(); while( It != this->GetInput()->End() ) { MeasurementVectorType inputMeasurement = It.GetMeasurementVector(); typename ScalarListSampleType::MeasurementVectorType outputMeasurement; outputMeasurement.SetSize( scalarMeasurementVectorSize ); if( inputMeasurement[0] < lowerBound || inputMeasurement[0] > upperBound ) { this->m_OutlierInstanceIdentifiers.push_back( It.GetInstanceIdentifier() ); if( this->m_OutlierHandling == None ) { outputMeasurement[0] = inputMeasurement[0]; this->GetOutput()->PushBack( outputMeasurement ); } // else trim from the output } else { outputMeasurement[0] = inputMeasurement[0]; this->GetOutput()->PushBack( outputMeasurement ); } ++It; } if( this->m_OutlierHandling == Winsorize ) { /** Retabulate the histogram with the outliers removed */ typename SampleFilterType::Pointer sampleFilter2 = SampleFilterType::New(); sampleFilter2->SetInput( this->GetOutput() ); sampleFilter2->SetHistogramSize( histogramSize ); sampleFilter2->Update(); RealType lowerQuantile2 = sampleFilter2->GetOutput()-> Quantile( 0, this->m_LowerPercentile ); RealType upperQuantile2 = sampleFilter2->GetOutput()-> Quantile( 0, this->m_UpperPercentile ); RealType upperBound2 = upperQuantile2 + this->m_WhiskerScalingFactor * ( upperQuantile2 - lowerQuantile2 ); RealType lowerBound2 = lowerQuantile2 - this->m_WhiskerScalingFactor * ( upperQuantile2 - lowerQuantile2 ); this->GetOutput()->Clear(); It = this->GetInput()->Begin(); while( It != this->GetInput()->End() ) { MeasurementVectorType inputMeasurement = It.GetMeasurementVector(); typename ScalarListSampleType::MeasurementVectorType outputMeasurement; outputMeasurement.SetSize( scalarMeasurementVectorSize ); outputMeasurement[0] = inputMeasurement[0]; if( inputMeasurement[0] < lowerBound ) { outputMeasurement[0] = lowerBound2; } else if( inputMeasurement[0] > upperBound ) { outputMeasurement[0] = upperBound2; } this->GetOutput()->PushBack( outputMeasurement ); ++It; } } } template void BoxPlotQuantileListSampleFilter ::PrintSelf( std::ostream& os, Indent indent ) const { os << indent << "Percentile Bounds: [" << this->m_LowerPercentile << ", " << this->m_UpperPercentile << "]" << std::endl; os << indent << "Whisker scaling factor: " << this->m_WhiskerScalingFactor << std::endl; os << indent << "Outlier handling: "; if( this->m_OutlierHandling == None ) { os << "None" << std::endl; } if( this->m_OutlierHandling == Trim ) { os << "Trim" << std::endl; } if( this->m_OutlierHandling == Winsorize ) { os << "Winsorize" << std::endl; } } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsGaussianListSampleFunction.h000066400000000000000000000055531311104306400243760ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsGaussianListSampleFunction_h #define __antsGaussianListSampleFunction_h #include "antsListSampleFunction.h" #include "itkGaussianMembershipFunction.h" namespace itk { namespace ants { namespace Statistics { /** \class GaussianListSampleFunction.h * \brief point set filter. */ template class GaussianListSampleFunction : public ListSampleFunction { public: typedef GaussianListSampleFunction Self; typedef ListSampleFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( GaussianListSampleFunction, ListSampleFunction ); typedef typename Superclass::InputListSampleType InputListSampleType; typedef typename Superclass::InputMeasurementVectorType InputMeasurementVectorType; typedef typename Superclass::InputMeasurementType InputMeasurementType; /** List sample typedef support. */ typedef TListSample ListSampleType; typedef typename Superclass::ListSampleWeightArrayType ListSampleWeightArrayType; /** Gaussian typedefs */ typedef typename itk::Statistics::GaussianMembershipFunction GaussianType; /** Other typedef */ typedef TOutput RealType; typedef TOutput OutputType; /** Helper functions */ virtual void SetInputListSample( const InputListSampleType * ptr ) ITK_OVERRIDE; virtual TOutput Evaluate( const InputMeasurementVectorType& measurement ) const ITK_OVERRIDE; protected: GaussianListSampleFunction(); virtual ~GaussianListSampleFunction(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void GenerateData(); private: // purposely not implemented GaussianListSampleFunction( const Self & ); void operator=( const Self & ); typename GaussianType::Pointer m_Gaussian; }; } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsGaussianListSampleFunction.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsGaussianListSampleFunction.hxx000066400000000000000000000130001311104306400247400ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsGaussianListSampleFunction_hxx #define __antsGaussianListSampleFunction_hxx #include "antsGaussianListSampleFunction.h" #include "itkCovarianceSampleFilter.h" #include "itkMeanSampleFilter.h" #include "itkWeightedCovarianceSampleFilter.h" #include "itkWeightedMeanSampleFilter.h" namespace itk { namespace ants { namespace Statistics { template GaussianListSampleFunction ::GaussianListSampleFunction() { this->m_Gaussian = GaussianType::New(); } template GaussianListSampleFunction ::~GaussianListSampleFunction() { } template void GaussianListSampleFunction ::SetInputListSample( const InputListSampleType * ptr ) { Superclass::SetInputListSample( ptr ); if( !this->GetInputListSample() ) { return; } if( this->GetInputListSample()->Size() > 1 ) { if( this->GetListSampleWeights()->Size() == this->GetInputListSample()->Size() ) { typedef typename itk::Statistics:: WeightedCovarianceSampleFilter CovarianceCalculatorType; typename CovarianceCalculatorType::Pointer covarianceCalculator = CovarianceCalculatorType::New(); covarianceCalculator->SetWeights( *this->GetListSampleWeights() ); covarianceCalculator->SetInput( this->GetInputListSample() ); covarianceCalculator->Update(); typename GaussianType::MeanVectorType mean; NumericTraits::SetLength( mean, this->GetInputListSample()-> GetMeasurementVectorSize() ); for( unsigned int d = 0; d < this->GetInputListSample()-> GetMeasurementVectorSize(); d++ ) { mean[d] = covarianceCalculator->GetMean()[d]; } this->m_Gaussian->SetMean( mean ); this->m_Gaussian->SetCovariance( covarianceCalculator->GetCovarianceMatrix() ); } else { typedef itk::Statistics::CovarianceSampleFilter CovarianceCalculatorType; typename CovarianceCalculatorType::Pointer covarianceCalculator = CovarianceCalculatorType::New(); covarianceCalculator->SetInput( this->GetInputListSample() ); covarianceCalculator->Update(); typename GaussianType::MeanVectorType mean; NumericTraits::SetLength( mean, this->GetInputListSample()-> GetMeasurementVectorSize() ); for( unsigned int d = 0; d < this->GetInputListSample()-> GetMeasurementVectorSize(); d++ ) { mean[d] = covarianceCalculator->GetMean()[d]; } this->m_Gaussian->SetMean( mean ); this->m_Gaussian->SetCovariance( covarianceCalculator->GetCovarianceMatrix() ); } // Check to see if the covariance matrix is nonsingular vnl_matrix_inverse inv_cov( ( this->m_Gaussian->GetCovariance() ).GetVnlMatrix() ); double det = inv_cov.determinant_magnitude(); if( det < 0.0 ) { itkExceptionMacro( "Determinant of the covariance < 0" ); } else if( det < 1.0e-6 ) { itkExceptionMacro( "Covariance is singular (determinant = " << det << " < 1.0e-6)" ); } } else { itkWarningMacro( "The input list sample has <= 1 element. " << "Function evaluations will be equal to 0." ); } } template TOutput GaussianListSampleFunction ::Evaluate( const InputMeasurementVectorType & measurement ) const { try { return this->m_Gaussian->Evaluate( measurement ); } catch( ... ) { return 0.0; } } /** * Standard "PrintSelf" method */ template void GaussianListSampleFunction ::PrintSelf( std::ostream& os, Indent indent) const { os << indent << "mean = " << this->m_Gaussian->GetMean() << ", "; typename GaussianType::CovarianceMatrixType covariance = this->m_Gaussian->GetCovariance(); os << "covariance = ["; for( unsigned int r = 0; r < covariance.Rows(); r++ ) { for( unsigned int c = 0; c < covariance.Cols() - 1; c++ ) { os << covariance( r, c ) << ", "; } if( r == covariance.Rows() - 1 ) { os << covariance( r, covariance.Cols() - 1 ) << "]" << std::endl; } else { os << covariance( r, covariance.Cols() - 1 ) << "; "; } } } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsGrubbsRosnerListSampleFilter.h000066400000000000000000000067771311104306400247120ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsGrubbsRosnerListSampleFilter_h #define __antsGrubbsRosnerListSampleFilter_h #include "antsListSampleToListSampleFilter.h" #include namespace itk { namespace ants { namespace Statistics { /** \class GrubbsRosnerListSampleFilter * \brief Base class of filters intended to generate scalar samples from * intensity samples. * */ template class GrubbsRosnerListSampleFilter : public ListSampleToListSampleFilter { public: /** * Standard class typedefs. */ typedef GrubbsRosnerListSampleFilter Self; typedef ListSampleToListSampleFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** * Standard macros */ itkTypeMacro( GrubbsRosnerListSampleFilter, ListSampleToScalarListSampleFilter ); /** * Method for creation through the object factory. */ itkNewMacro( Self ); /** * Conveneient typedefs */ typedef double RealType; typedef TScalarListSample ScalarListSampleType; typedef typename ScalarListSampleType ::MeasurementVectorType MeasurementVectorType; typedef typename ScalarListSampleType ::InstanceIdentifier InstanceIdentifierType; typedef std::vector InstanceIdentifierContainerType; enum OutlierHandlingType { None, Trim, Winsorize }; itkSetMacro( OutlierHandling, OutlierHandlingType ); itkGetConstMacro( OutlierHandling, OutlierHandlingType ); itkSetMacro( WinsorizingLevel, RealType ); itkGetConstMacro( WinsorizingLevel, RealType ); itkSetMacro( SignificanceLevel, RealType ); itkGetConstMacro( SignificanceLevel, RealType ); InstanceIdentifierContainerType GetOutlierInstanceIdentifiers() { return this->m_OutlierInstanceIdentifiers; } // itkGetConstMacro( Outliers, InstanceIdentifierContainerType ); protected: GrubbsRosnerListSampleFilter(); virtual ~GrubbsRosnerListSampleFilter(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; virtual void GenerateData() ITK_OVERRIDE; private: GrubbsRosnerListSampleFilter( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented InstanceIdentifierType FindMaximumNonOutlierDeviationValue( RealType, RealType ); bool IsMeasurementAnOutlier( RealType, RealType, RealType, unsigned long ); OutlierHandlingType m_OutlierHandling; RealType m_WinsorizingLevel; InstanceIdentifierContainerType m_OutlierInstanceIdentifiers; RealType m_SignificanceLevel; }; // end of class } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsGrubbsRosnerListSampleFilter.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsGrubbsRosnerListSampleFilter.hxx000066400000000000000000000211611311104306400252520ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsGrubbsRosnerListSampleFilter_hxx #define __antsGrubbsRosnerListSampleFilter_hxx #include "antsGrubbsRosnerListSampleFilter.h" #include "itkTDistribution.h" namespace itk { namespace ants { namespace Statistics { template GrubbsRosnerListSampleFilter ::GrubbsRosnerListSampleFilter() { this->AllocateOutput(); this->GetOutput()->SetMeasurementVectorSize( 1 ); this->m_OutlierHandling = Winsorize; this->m_WinsorizingLevel = 0.10; this->m_SignificanceLevel = 0.05; } template GrubbsRosnerListSampleFilter ::~GrubbsRosnerListSampleFilter() { } template void GrubbsRosnerListSampleFilter ::GenerateData() { if( this->GetInput()->GetMeasurementVectorSize() != 1 ) { itkExceptionMacro( "The input sample must be univariate." ); } const unsigned int scalarMeasurementVectorSize = this->GetOutput()->GetMeasurementVectorSize(); this->GetOutput()->SetMeasurementVectorSize( scalarMeasurementVectorSize ); /** * A common hueristic is that Grubbs-Rosner outlier removal does not work for * sample sizes less than or equal to 6. */ if( this->GetInput()->Size() <= 6 ) { typename ScalarListSampleType::ConstIterator It = this->GetInput()->Begin(); while( It != this->GetInput()->End() ) { MeasurementVectorType inputMeasurement = It.GetMeasurementVector(); MeasurementVectorType outputMeasurement; outputMeasurement.SetSize( scalarMeasurementVectorSize ); for( unsigned int d = 0; d < scalarMeasurementVectorSize; d++ ) { outputMeasurement[d] = inputMeasurement[d]; } this->GetOutput()->PushBack( outputMeasurement ); ++It; } return; } /** * Otherwise, iterate through the input list, removing t */ RealType mean = 0.0; RealType variance = 0.0; RealType count = 0.0; typename ScalarListSampleType::ConstIterator It = this->GetInput()->Begin(); while( It != this->GetInput()->End() ) { MeasurementVectorType inputMeasurement = It.GetMeasurementVector(); count += 1.0; variance += ( count - 1.0 ) * vnl_math_sqr( inputMeasurement[0] - mean ) / count; mean = mean + ( inputMeasurement[0] - mean ) / count; ++It; } variance /= ( count - 1.0 ); bool outlierFound = true; this->m_OutlierInstanceIdentifiers.clear(); while( outlierFound == true && ( this->GetInput()->Size() - this->m_OutlierInstanceIdentifiers.size() > 6 ) ) { outlierFound = false; InstanceIdentifierType id = this->FindMaximumNonOutlierDeviationValue( mean, variance ); if( this->GetInput()->GetFrequency( id ) > 0 ) { MeasurementVectorType measurement = this->GetInput()->GetMeasurementVector( id ); outlierFound = this->IsMeasurementAnOutlier( measurement[0], mean, variance, this->GetInput()->Size() - this->m_OutlierInstanceIdentifiers.size() ); if( outlierFound ) { /** Retabulate the variance and mean by removing the previous estimate */ RealType count2 = this->GetInput()->Size() - this->m_OutlierInstanceIdentifiers.size(); mean = ( mean * count2 - measurement[0] ) / ( count2 - 1.0 ); variance = ( count2 - 1.0 ) * variance - ( count2 - 1.0 ) * vnl_math_sqr( measurement[0] - mean ) / count2; variance /= ( count2 - 2.0 ); this->m_OutlierInstanceIdentifiers.push_back( id ); } } } RealType lowerWinsorBound = 0.0; RealType upperWinsorBound = 0.0; if( this->m_OutlierHandling == Winsorize ) { typename itk::Statistics::TDistribution::Pointer tdistribution = itk::Statistics::TDistribution::New(); RealType t = tdistribution->EvaluateInverseCDF( 1.0 - 0.5 * this->m_WinsorizingLevel, this->GetInput()->Size() - this->m_OutlierInstanceIdentifiers.size() ); lowerWinsorBound = mean - t * std::sqrt( variance ); upperWinsorBound = mean + t * std::sqrt( variance ); } It = this->GetInput()->Begin(); while( It != this->GetInput()->End() ) { MeasurementVectorType inputMeasurement = It.GetMeasurementVector(); MeasurementVectorType outputMeasurement; outputMeasurement.SetSize( scalarMeasurementVectorSize ); if( this->m_OutlierHandling == None || std::find( this->m_OutlierInstanceIdentifiers.begin(), this->m_OutlierInstanceIdentifiers.end(), It.GetInstanceIdentifier() ) == this->m_OutlierInstanceIdentifiers.end() ) { outputMeasurement[0] = inputMeasurement[0]; this->GetOutput()->PushBack( outputMeasurement ); } else if( this->m_OutlierHandling == Winsorize ) { if( inputMeasurement[0] < lowerWinsorBound ) { outputMeasurement[0] = lowerWinsorBound; } else { outputMeasurement[0] = upperWinsorBound; } this->GetOutput()->PushBack( outputMeasurement ); } ++It; } } template typename GrubbsRosnerListSampleFilter ::InstanceIdentifierType GrubbsRosnerListSampleFilter ::FindMaximumNonOutlierDeviationValue( RealType mean, RealType itkNotUsed( variance ) ) { RealType maximumDeviation = 0.0; InstanceIdentifierType maximumID = NumericTraits::max(); typename ScalarListSampleType::ConstIterator It = this->GetInput()->Begin(); while( It != this->GetInput()->End() ) { MeasurementVectorType inputMeasurement = It.GetMeasurementVector(); InstanceIdentifierType inputID = It.GetInstanceIdentifier(); if( std::find( this->m_OutlierInstanceIdentifiers.begin(), this->m_OutlierInstanceIdentifiers.end(), inputID ) == this->m_OutlierInstanceIdentifiers.end() ) { if( vnl_math_abs( inputMeasurement[0] - mean ) > maximumDeviation ) { maximumDeviation = vnl_math_abs( inputMeasurement[0] - mean ); maximumID = inputID; } } ++It; } return maximumID; } template bool GrubbsRosnerListSampleFilter ::IsMeasurementAnOutlier( RealType x, RealType mean, RealType variance, unsigned long N ) { /** * The Grubb critical two-sided value is defined to be * (N-1)/sqrt(N)*sqrt( t*t / (N-2+t*t) ) where t is at the * (alpha / (2N)) signficance level with N-2 degrees of freedom. */ RealType sig = this->m_SignificanceLevel / ( 2.0 * static_cast( N ) ); typename itk::Statistics::TDistribution::Pointer tdistribution = itk::Statistics::TDistribution::New(); RealType t = tdistribution->EvaluateInverseCDF( 1.0 - sig, N - 2 ); RealType nu = static_cast( N - 1 ); RealType g = nu / std::sqrt( nu + 1.0 ) * std::sqrt( t * t / ( nu - 1 + t * t ) ); return g < ( vnl_math_abs( x - mean ) / std::sqrt( variance ) ); } template void GrubbsRosnerListSampleFilter ::PrintSelf( std::ostream& os, Indent indent ) const { os << indent << "Significance level: " << this->m_SignificanceLevel << std::endl; os << indent << "Outlier handling: "; if( this->m_OutlierHandling == None ) { os << "None" << std::endl; } if( this->m_OutlierHandling == Trim ) { os << "Trim" << std::endl; } if( this->m_OutlierHandling == Winsorize ) { os << "Winsorize"; os << " (level = " << this->m_WinsorizingLevel << ")" << std::endl; } if( this->m_OutlierInstanceIdentifiers.size() > 0 ) { os << indent << "Outlier Identifiers: " << std::endl; for( unsigned int d = 0; d < this->m_OutlierInstanceIdentifiers.size(); d++ ) { os << indent << " " << this->m_OutlierInstanceIdentifiers[d] << std::endl; } } else { os << indent << "There are no outliers." << std::endl; } } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsHistogramParzenWindowsListSampleFunction.h000066400000000000000000000067501311104306400273140ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsHistogramParzenWindowsListSampleFunction_h #define __antsHistogramParzenWindowsListSampleFunction_h #include "antsListSampleFunction.h" #include "itkImage.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" namespace itk { namespace ants { namespace Statistics { /** \class HistogramParzenWindowsListSampleFunction.h * \brief point set filter. */ template class HistogramParzenWindowsListSampleFunction : public ListSampleFunction { public: typedef HistogramParzenWindowsListSampleFunction Self; typedef ListSampleFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( HistogramParzenWindowsListSampleFunction, ListSampleFunction ); typedef typename Superclass::InputListSampleType InputListSampleType; typedef typename Superclass::InputMeasurementVectorType InputMeasurementVectorType; typedef typename Superclass::InputMeasurementType InputMeasurementType; /** List sample typedef support. */ typedef TListSample ListSampleType; /** Other typedef */ typedef TOutput RealType; typedef TOutput OutputType; typedef Image HistogramImageType; typedef BSplineInterpolateImageFunction InterpolatorType; typedef LinearInterpolateImageFunction LInterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; /** Helper functions */ itkSetMacro( Sigma, RealType ); itkGetConstMacro( Sigma, RealType ); itkSetMacro( NumberOfHistogramBins, unsigned int ); itkGetConstMacro( NumberOfHistogramBins, unsigned int ); virtual void SetInputListSample( const InputListSampleType * ptr ) ITK_OVERRIDE; virtual TOutput Evaluate( const InputMeasurementVectorType& measurement ) const ITK_OVERRIDE; protected: HistogramParzenWindowsListSampleFunction(); virtual ~HistogramParzenWindowsListSampleFunction(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void GenerateData(); private: // purposely not implemented HistogramParzenWindowsListSampleFunction( const Self & ); void operator=( const Self & ); unsigned int m_NumberOfHistogramBins; RealType m_Sigma; InterpolatorPointer m_Interpolator; std::vector m_HistogramImages; }; } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsHistogramParzenWindowsListSampleFunction.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsHistogramParzenWindowsListSampleFunction.hxx000066400000000000000000000172431311104306400276730ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsHistogramParzenWindowsListSampleFunction_hxx #define __antsHistogramParzenWindowsListSampleFunction_hxx #include "antsHistogramParzenWindowsListSampleFunction.h" #include "itkArray.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkContinuousIndex.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkDivideByConstantImageFilter.h" #include "itkStatisticsImageFilter.h" namespace itk { namespace ants { namespace Statistics { template HistogramParzenWindowsListSampleFunction ::HistogramParzenWindowsListSampleFunction() { this->m_Interpolator = InterpolatorType::New(); this->m_Interpolator->SetSplineOrder( 3 ); this->m_NumberOfHistogramBins = 32; this->m_Sigma = 1.0; } template HistogramParzenWindowsListSampleFunction ::~HistogramParzenWindowsListSampleFunction() { } template void HistogramParzenWindowsListSampleFunction ::SetInputListSample( const InputListSampleType * ptr ) { Superclass::SetInputListSample( ptr ); if( !this->GetInputListSample() ) { return; } if( this->GetInputListSample()->Size() <= 1 ) { itkWarningMacro( "The input list sample has <= 1 element." << "Function evaluations will be equal to 0." ); return; } const unsigned int Dimension = this->GetInputListSample()->GetMeasurementVectorSize(); /** * Find the min/max values to define the histogram domain */ Array minValues( Dimension ); minValues.Fill( NumericTraits::max() ); Array maxValues( Dimension ); maxValues.Fill( NumericTraits::NonpositiveMin() ); typename InputListSampleType::ConstIterator It = this->GetInputListSample()->Begin(); while( It != this->GetInputListSample()->End() ) { InputMeasurementVectorType inputMeasurement = It.GetMeasurementVector(); for( unsigned int d = 0; d < Dimension; d++ ) { if( inputMeasurement[d] < minValues[d] ) { minValues[d] = inputMeasurement[d]; } if( inputMeasurement[d] > maxValues[d] ) { maxValues[d] = inputMeasurement[d]; } } ++It; } this->m_HistogramImages.clear(); for( unsigned int d = 0; d < Dimension; d++ ) { this->m_HistogramImages.push_back( HistogramImageType::New() ); typename HistogramImageType::SpacingType spacing; spacing[0] = ( maxValues[d] - minValues[d] ) / static_cast( this->m_NumberOfHistogramBins - 1 ); typename HistogramImageType::PointType origin; origin[0] = minValues[d] - 3.0 * ( this->m_Sigma * spacing[0] ); typename HistogramImageType::SizeType size; size[0] = static_cast( std::ceil( ( maxValues[d] + 3.0 * ( this->m_Sigma * spacing[0] ) - ( minValues[d] - 3.0 * ( this->m_Sigma * spacing[0] ) ) ) / spacing[0] ) ); this->m_HistogramImages[d]->SetOrigin( origin ); this->m_HistogramImages[d]->SetSpacing( spacing ); this->m_HistogramImages[d]->SetRegions( size ); this->m_HistogramImages[d]->Allocate(); this->m_HistogramImages[d]->FillBuffer( 0 ); } unsigned long count = 0; It = this->GetInputListSample()->Begin(); while( It != this->GetInputListSample()->End() ) { InputMeasurementVectorType inputMeasurement = It.GetMeasurementVector(); RealType newWeight = 1.0; if( this->GetListSampleWeights()->Size() == this->GetInputListSample()->Size() ) { newWeight = ( *this->GetListSampleWeights() )[count]; } for( unsigned int d = 0; d < Dimension; d++ ) { typename HistogramImageType::PointType point; point[0] = inputMeasurement[d]; ContinuousIndex cidx; this->m_HistogramImages[d]->TransformPhysicalPointToContinuousIndex( point, cidx ); typename HistogramImageType::IndexType idx; idx[0] = static_cast( std::floor( cidx[0] ) ); if( this->m_HistogramImages[d]->GetLargestPossibleRegion().IsInside( idx ) ) { RealType oldWeight = this->m_HistogramImages[d]->GetPixel( idx ); this->m_HistogramImages[d]->SetPixel( idx, ( 1.0 - ( cidx[0] - idx[0] ) ) * newWeight + oldWeight ); } idx[0]++; if( this->m_HistogramImages[d]->GetLargestPossibleRegion().IsInside( idx ) ) { RealType oldWeight = this->m_HistogramImages[d]->GetPixel( idx ); this->m_HistogramImages[d]->SetPixel( idx, ( 1.0 - ( idx[0] - cidx[0] ) ) * newWeight + oldWeight ); } } ++count; ++It; } for( unsigned int d = 0; d < Dimension; d++ ) { typedef DiscreteGaussianImageFilter GaussianFilterType; typename GaussianFilterType::Pointer gaussian = GaussianFilterType::New(); gaussian->SetInput( this->m_HistogramImages[d] ); gaussian->SetVariance( this->m_Sigma * this->m_Sigma ); gaussian->SetMaximumError( 0.01 ); gaussian->SetUseImageSpacing( false ); gaussian->Update(); typedef StatisticsImageFilter StatsFilterType; typename StatsFilterType::Pointer stats = StatsFilterType::New(); stats->SetInput( gaussian->GetOutput() ); stats->Update(); typedef DivideByConstantImageFilter DividerType; typename DividerType::Pointer divider = DividerType::New(); divider->SetInput( gaussian->GetOutput() ); divider->SetConstant( stats->GetSum() ); divider->Update(); this->m_HistogramImages[d] = divider->GetOutput(); } } template TOutput HistogramParzenWindowsListSampleFunction ::Evaluate( const InputMeasurementVectorType & measurement ) const { try { RealType probability = 1.0; for( unsigned int d = 0; d < this->m_HistogramImages.size(); d++ ) { typename HistogramImageType::PointType point; point[0] = measurement[d]; this->m_Interpolator->SetInputImage( this->m_HistogramImages[d] ); if( this->m_Interpolator->IsInsideBuffer( point ) ) { probability *= this->m_Interpolator->Evaluate( point ); } else { return 0; } } return probability; } catch( ... ) { return 0; } } /** * Standard "PrintSelf" method */ template void HistogramParzenWindowsListSampleFunction ::PrintSelf( std::ostream& os, Indent indent) const { os << indent << "Sigma: " << this->m_Sigma << std::endl; os << indent << "Number of histogram bins: " << this->m_NumberOfHistogramBins << std::endl; } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsJointHistogramParzenShapeAndOrientationListSampleFunction.h000066400000000000000000000113001311104306400325500ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsJointHistogramParzenShapeAndOrientationListSampleFunction_h #define __antsJointHistogramParzenShapeAndOrientationListSampleFunction_h #include "antsListSampleFunction.h" #include "itkImage.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" namespace itk { namespace ants { namespace Statistics { /** \class JointHistogramParzenShapeAndOrientationListSampleFunction.h * \brief point set filter. */ template class JointHistogramParzenShapeAndOrientationListSampleFunction : public ListSampleFunction { public: typedef JointHistogramParzenShapeAndOrientationListSampleFunction Self; typedef ListSampleFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( JointHistogramParzenShapeAndOrientationListSampleFunction, ListSampleFunction ); typedef typename Superclass::InputListSampleType InputListSampleType; typedef typename Superclass::InputMeasurementVectorType InputMeasurementVectorType; typedef typename Superclass::InputMeasurementType InputMeasurementType; /** List sample typedef support. */ typedef TListSample ListSampleType; /** Other typedef */ typedef TOutput RealType; typedef TOutput OutputType; typedef Image JointHistogramImageType; typedef typename JointHistogramImageType::Pointer JointHistogramImagePointer; typedef Vector ThetaPsiType; typedef typename JointHistogramImageType::IndexType IndexType; typedef typename IndexType::IndexValueType IndexValueType; typedef BSplineInterpolateImageFunction InterpolatorType; typedef LinearInterpolateImageFunction LInterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; /** Helper functions */ virtual void SetInputListSample( const InputListSampleType * ptr ) ITK_OVERRIDE; itkSetMacro( ShapeSigma, RealType ); itkGetConstMacro( ShapeSigma, RealType ); itkSetMacro( OrientationSigma, RealType ); itkGetConstMacro( OrientationSigma, RealType ); itkSetMacro( NumberOfShapeJointHistogramBins, unsigned int ); itkGetConstMacro( NumberOfShapeJointHistogramBins, unsigned int ); itkSetMacro( NumberOfOrientationJointHistogramBins, unsigned int ); itkGetConstMacro( NumberOfOrientationJointHistogramBins, unsigned int ); virtual TOutput Evaluate( const InputMeasurementVectorType & ) const ITK_OVERRIDE; protected: JointHistogramParzenShapeAndOrientationListSampleFunction(); virtual ~JointHistogramParzenShapeAndOrientationListSampleFunction(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void GenerateData(); void IncrementJointHistogramForShape( RealType, RealType ); void IncrementJointHistogramForOrientation( RealType, RealType, RealType, unsigned int ); private: // purposely not implemented JointHistogramParzenShapeAndOrientationListSampleFunction( const Self & ); void operator=( const Self & ); unsigned int m_NumberOfShapeJointHistogramBins; unsigned int m_NumberOfOrientationJointHistogramBins; RealType m_ShapeSigma; RealType m_OrientationSigma; RealType m_MaximumEigenvalue1; RealType m_MaximumEigenvalue2; RealType m_MinimumEigenvalue1; RealType m_MinimumEigenvalue2; JointHistogramImagePointer m_JointHistogramImages[3]; InterpolatorPointer m_Interpolator; bool m_UseNearestNeighborIncrements; }; } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsJointHistogramParzenShapeAndOrientationListSampleFunction.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsJointHistogramParzenShapeAndOrientationListSampleFunction.hxx000066400000000000000000000613061311104306400331430ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsJointHistogramParzenShapeAndOrientationListSampleFunction_hxx #define __antsJointHistogramParzenShapeAndOrientationListSampleFunction_hxx #include "antsJointHistogramParzenShapeAndOrientationListSampleFunction.h" #include "itkArray.h" #include "itkContinuousIndex.h" #include "itkDecomposeTensorFunction.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkDivideByConstantImageFilter.h" #include "itkStatisticsImageFilter.h" namespace itk { namespace ants { namespace Statistics { template JointHistogramParzenShapeAndOrientationListSampleFunction ::JointHistogramParzenShapeAndOrientationListSampleFunction() { this->m_NumberOfShapeJointHistogramBins = 32; this->m_NumberOfOrientationJointHistogramBins = 64; this->m_ShapeSigma = 1.0; this->m_OrientationSigma = 2.0; this->m_UseNearestNeighborIncrements = true; this->m_MaximumEigenvalue1 = 0; this->m_MaximumEigenvalue2 = 0; this->m_MinimumEigenvalue1 = 1; this->m_MinimumEigenvalue2 = 1; this->m_Interpolator = InterpolatorType::New(); this->m_Interpolator->SetSplineOrder( 3 ); this->m_JointHistogramImages[0] = ITK_NULLPTR; this->m_JointHistogramImages[1] = ITK_NULLPTR; this->m_JointHistogramImages[2] = ITK_NULLPTR; } template JointHistogramParzenShapeAndOrientationListSampleFunction ::~JointHistogramParzenShapeAndOrientationListSampleFunction() { } template void JointHistogramParzenShapeAndOrientationListSampleFunction ::IncrementJointHistogramForShape( RealType eigenvalue1, RealType eigenvalue2 ) { RealType newWeight = 1.0; // now define two joint histograms, one for shape, one for orientation. // first, the shape histogram --- 0,0 origin and spacing of 1 if( !this->m_JointHistogramImages[0] ) { typename JointHistogramImageType::SpacingType spacing; spacing.Fill( 1 ); typename JointHistogramImageType::PointType origin; origin.Fill( 0 ); typename JointHistogramImageType::SizeType size; size.Fill( this->m_NumberOfShapeJointHistogramBins ); this->m_JointHistogramImages[0] = AllocImage(size); this->m_JointHistogramImages[0]->SetOrigin( origin ); this->m_JointHistogramImages[0]->SetSpacing( spacing ); this->m_JointHistogramImages[0]->FillBuffer( 0 ); } typename JointHistogramImageType::PointType shapePoint; if( eigenvalue1 > 1.0 ) { eigenvalue1 = 1.0; } if( eigenvalue2 > 1.0 ) { eigenvalue2 = 1.0; } if( eigenvalue1 < 0.0 ) { eigenvalue1 = 0.0; } if( eigenvalue2 < 0 ) { eigenvalue2 = 0.0; } shapePoint[0] = eigenvalue1 * ( this->m_NumberOfShapeJointHistogramBins - 1 ); shapePoint[1] = eigenvalue2 * ( this->m_NumberOfShapeJointHistogramBins - 1 ); ContinuousIndex shapeCidx; this->m_JointHistogramImages[0]->TransformPhysicalPointToContinuousIndex( shapePoint, shapeCidx ); typedef typename JointHistogramImageType::IndexType JointHistogramImageIndexType; JointHistogramImageIndexType shapeIdx; /** Nearest neighbor increment to JH */ if( this->m_UseNearestNeighborIncrements ) { shapeIdx[0] = static_cast( std::floor( shapeCidx[0] + 0.5 ) ); shapeIdx[1] = static_cast( std::floor( shapeCidx[1] + 0.5 ) ); if( this->m_JointHistogramImages[0]-> GetLargestPossibleRegion().IsInside( shapeIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[0]->GetPixel( shapeIdx ); this->m_JointHistogramImages[0]->SetPixel( shapeIdx, 1 + oldWeight ); } } else { /** linear addition */ shapeIdx[0] = static_cast( std::floor( shapeCidx[0] ) ); shapeIdx[1] = static_cast( std::floor( shapeCidx[1] ) ); RealType distance1 = std::sqrt( vnl_math_sqr( shapeCidx[0] - shapeIdx[0] ) + vnl_math_sqr( shapeCidx[1] - shapeIdx[1] ) ); shapeIdx[0]++; RealType distance2 = std::sqrt( vnl_math_sqr( shapeCidx[0] - shapeIdx[0] ) + vnl_math_sqr( shapeCidx[1] - shapeIdx[1] ) ); shapeIdx[1]++; RealType distance3 = std::sqrt( vnl_math_sqr( shapeCidx[0] - shapeIdx[0] ) + vnl_math_sqr( shapeCidx[1] - shapeIdx[1] ) ); shapeIdx[0]--; RealType distance4 = std::sqrt( vnl_math_sqr( shapeCidx[0] - shapeIdx[0] ) + vnl_math_sqr( shapeCidx[1] - shapeIdx[1] ) ); RealType sumDistance = distance1 + distance2 + distance3 + distance4; distance1 /= sumDistance; distance2 /= sumDistance; distance3 /= sumDistance; distance4 /= sumDistance; unsigned int whichHistogram = 0; shapeIdx[0] = static_cast( std::floor( shapeCidx[0] ) ); shapeIdx[1] = static_cast( std::floor( shapeCidx[1] ) ); if( this->m_JointHistogramImages[whichHistogram]-> GetLargestPossibleRegion().IsInside( shapeIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[whichHistogram]->GetPixel( shapeIdx ); this->m_JointHistogramImages[whichHistogram]->SetPixel( shapeIdx, ( 1.0 - distance1 ) * newWeight + oldWeight ); } shapeIdx[0]++; if( this->m_JointHistogramImages[whichHistogram]-> GetLargestPossibleRegion().IsInside( shapeIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[whichHistogram]->GetPixel( shapeIdx ); this->m_JointHistogramImages[whichHistogram]->SetPixel( shapeIdx, ( 1.0 - distance2 ) * newWeight + oldWeight ); } shapeIdx[1]++; if( this->m_JointHistogramImages[whichHistogram]-> GetLargestPossibleRegion().IsInside( shapeIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[whichHistogram]->GetPixel( shapeIdx ); this->m_JointHistogramImages[whichHistogram]->SetPixel( shapeIdx, ( 1.0 - distance3 ) * newWeight + oldWeight ); } shapeIdx[0]--; if( this->m_JointHistogramImages[whichHistogram]-> GetLargestPossibleRegion().IsInside( shapeIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[whichHistogram]->GetPixel( shapeIdx ); this->m_JointHistogramImages[whichHistogram]->SetPixel( shapeIdx, ( 1.0 - distance4) * newWeight + oldWeight ); } } return; } template void JointHistogramParzenShapeAndOrientationListSampleFunction ::IncrementJointHistogramForOrientation( RealType x, RealType y, RealType z, unsigned int whichHistogram ) { RealType newWeight = 1.0; // 2nd, the orientation histogram. origin 0,0. spacing of 1,1. // need to be careful for wrap around in the 0 to 2*pi case. if( !this->m_JointHistogramImages[whichHistogram] ) { typename JointHistogramImageType::SpacingType spacing2; spacing2.Fill(1); typename JointHistogramImageType::PointType origin2; origin2.Fill(0); typename JointHistogramImageType::SizeType size2; size2.Fill( this->m_NumberOfOrientationJointHistogramBins ); size2[0] = size2[0] + 2; this->m_JointHistogramImages[whichHistogram] = AllocImage(size2); this->m_JointHistogramImages[whichHistogram]->SetOrigin( origin2 ); this->m_JointHistogramImages[whichHistogram]->SetSpacing( spacing2 ); this->m_JointHistogramImages[whichHistogram]->FillBuffer( 0 ); } typename JointHistogramImageType::PointType orientPoint; RealType tp[2]; tp[1] = 0.0; // If eigenvector has negative x, we reflect it about the origin. // We do this to avoid redundancy in representation of the eigenvectors, // because they are all redundant. if( x < 0 ) { x *= -1; y *= -1; z *= -1; } tp[0] = std::acos( z ); // phi goes from 0.0 (+x axis) and goes to -pi/2 and pi/2. // theta goes from 0.0 (+z axis) and wraps at PI // if x and y are 0.0 or very close, return phi == 0 // we do this to eliminate redundancy in the distribution of orientations. if( vnl_math_abs( x ) + vnl_math_abs( y ) < 1e-9 ) { tp[1] = 0.0; } else { if( y == 0.0 ) { if( x > 0.0 ) { tp[1] = 0.0; } else { tp[1] = vnl_math::pi; } } else if( x == 0.0 ) { // avoid div by zero if( y > 0 ) { tp[1] = vnl_math::pi_over_2; } else { tp[1] = -vnl_math::pi_over_2; } } else if( x > 0.0 && y > 0.0 ) { // first quadrant tp[1] = std::atan( y / x ); } else if( x < 0.0 && y > 0.0 ) { // second quadrant tp[1] = vnl_math::pi + std::atan( y / x ); } else if( x < 0.0 && y < 0.0 ) { // third quadrant tp[1] = vnl_math::pi + atan( y / x ); } else { // fourth quadrant tp[1] = atan( y / x ); } } RealType psi = tp[0]; RealType theta = tp[1]; // note, if a point maps to 0 or 2*pi then it should contribute to both bins -- pretty much only difference between this // function and matlab code is the next 15 or so lines, as far as we see orientPoint[0] = psi / (vnl_math::pi ) * ( this->m_NumberOfOrientationJointHistogramBins - 1) + 1; orientPoint[1] = ( theta + vnl_math::pi_over_2 ) / vnl_math::pi * ( this->m_NumberOfOrientationJointHistogramBins - 1 ); ContinuousIndex orientCidx; this->m_JointHistogramImages[whichHistogram]-> TransformPhysicalPointToContinuousIndex( orientPoint, orientCidx ); typedef typename JointHistogramImageType::IndexType JointHistogramImageIndexType; JointHistogramImageIndexType orientIdx; /** Nearest neighbor interpolation */ if( this->m_UseNearestNeighborIncrements ) { orientIdx[0] = static_cast( std::floor( orientCidx[0] + 0.5 ) ); orientIdx[1] = static_cast( std::floor( orientCidx[1] + 0.5 ) ); if( this->m_JointHistogramImages[whichHistogram]-> GetLargestPossibleRegion().IsInside( orientIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[whichHistogram]->GetPixel( orientIdx ); this->m_JointHistogramImages[whichHistogram]-> SetPixel( orientIdx, 1 + oldWeight ); } } else { orientIdx[0] = static_cast( std::floor( orientCidx[0] ) ); orientIdx[1] = static_cast( std::floor( orientCidx[1] ) ); RealType distance1 = std::sqrt( vnl_math_sqr( orientCidx[0] - orientIdx[0] ) + vnl_math_sqr( orientCidx[1] - orientIdx[1] ) ); orientIdx[0]++; RealType distance2 = std::sqrt( vnl_math_sqr( orientCidx[0] - orientIdx[0] ) + vnl_math_sqr( orientCidx[1] - orientIdx[1] ) ); orientIdx[1]++; RealType distance3 = std::sqrt( vnl_math_sqr( orientCidx[0] - orientIdx[0] ) + vnl_math_sqr( orientCidx[1] - orientIdx[1] ) ); orientIdx[0]--; RealType distance4 = std::sqrt( vnl_math_sqr( orientCidx[0] - orientIdx[0] ) + vnl_math_sqr( orientCidx[1] - orientIdx[1] ) ); RealType sumDistance = distance1 + distance2 + distance3 + distance4; distance1 /= sumDistance; distance2 /= sumDistance; distance3 /= sumDistance; distance4 /= sumDistance; orientIdx[0] = static_cast( std::floor( orientCidx[0] ) ); orientIdx[1] = static_cast( std::floor( orientCidx[1] ) ); if( this->m_JointHistogramImages[whichHistogram]-> GetLargestPossibleRegion().IsInside( orientIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[whichHistogram]->GetPixel( orientIdx ); this->m_JointHistogramImages[whichHistogram]->SetPixel( orientIdx, ( 1.0 - distance1 ) * newWeight + oldWeight ); } orientIdx[0]++; if( this->m_JointHistogramImages[whichHistogram]-> GetLargestPossibleRegion().IsInside( orientIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[whichHistogram]->GetPixel( orientIdx ); this->m_JointHistogramImages[whichHistogram]->SetPixel( orientIdx, ( 1.0 - distance2 ) * newWeight + oldWeight ); } orientIdx[1]++; if( this->m_JointHistogramImages[whichHistogram]-> GetLargestPossibleRegion().IsInside( orientIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[whichHistogram]->GetPixel( orientIdx ); this->m_JointHistogramImages[whichHistogram]->SetPixel( orientIdx, ( 1.0 - distance3 ) * newWeight + oldWeight ); } orientIdx[0]--; if( this->m_JointHistogramImages[whichHistogram]-> GetLargestPossibleRegion().IsInside( orientIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[whichHistogram]->GetPixel(orientIdx ); this->m_JointHistogramImages[whichHistogram]->SetPixel( orientIdx, ( 1.0 - distance4) * newWeight + oldWeight ); } } // The last thing we do is copy the [1,] column to the [NBins+1,] column and // the [NBins,] column to the [0,] column --- circular boundary conditions. typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator tIter( this->m_JointHistogramImages[whichHistogram], this->m_JointHistogramImages[whichHistogram]->GetBufferedRegion() ); for( tIter.GoToBegin(); !tIter.IsAtEnd(); ++tIter ) { IndexType index = tIter.GetIndex(); IndexType index2 = tIter.GetIndex(); if( index[0] == 0 ) { index2[0] = this->m_NumberOfOrientationJointHistogramBins; index2[1] = index[1]; tIter.Set( this->m_JointHistogramImages[whichHistogram]->GetPixel( index2 ) ); } if( index[0] == static_cast (this->m_NumberOfOrientationJointHistogramBins + 1) ) { index2[0] = 1; index2[1] = index[1]; tIter.Set( this->m_JointHistogramImages[whichHistogram]->GetPixel( index2 ) ); } } return; } template void JointHistogramParzenShapeAndOrientationListSampleFunction ::SetInputListSample( const InputListSampleType * ptr ) { Superclass::SetInputListSample( ptr ); if( !this->GetInputListSample() ) { return; } if( this->GetInputListSample()->Size() <= 1 ) { itkWarningMacro( "The input list sample has <= 1 element." << "Function evaluations will be equal to 0." ); return; } const unsigned int Dimension = this->GetInputListSample()->GetMeasurementVectorSize(); /** * Find the min/max values to define the histogram domain */ Array minValues( Dimension ); minValues.Fill( NumericTraits::max() ); Array maxValues( Dimension ); maxValues.Fill( NumericTraits::NonpositiveMin() ); typename InputListSampleType::ConstIterator It = this->GetInputListSample()->Begin(); while( It != this->GetInputListSample()->End() ) { InputMeasurementVectorType inputMeasurement = It.GetMeasurementVector(); for( unsigned int d = 0; d < Dimension; d++ ) { if( inputMeasurement[d] < minValues[d] ) { minValues[d] = inputMeasurement[d]; } if( inputMeasurement[d] > maxValues[d] ) { maxValues[d] = inputMeasurement[d]; } } ++It; } for( unsigned int d = 0; d < 3; d++ ) { this->m_JointHistogramImages[d] = ITK_NULLPTR; } RealType L = static_cast( this->GetInputListSample()->GetMeasurementVectorSize() ); unsigned int D = static_cast( 0.5 * ( -1 + std::sqrt( 1.0 + 8.0 * L ) ) ); It = this->GetInputListSample()->Begin(); while( It != this->GetInputListSample()->End() ) { InputMeasurementVectorType inputMeasurement = It.GetMeasurementVector(); // convert to a tensor then get its shape and primary orientation vector typedef VariableSizeMatrix TensorType; TensorType T( D, D ); T.Fill( 0.0 ); unsigned int index = 0; for( unsigned int i = 0; i < D; i++ ) { for( unsigned int j = i; j < D; j++ ) { T(i, j) = inputMeasurement( index++ ); T(j, i) = T(i, j); } } // now decompose T into shape and orientation TensorType V; TensorType W; TensorType Tc = T; typedef DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); decomposer->EvaluateSymmetricEigenDecomposition( Tc, W, V ); // now W holds the eigenvalues ( shape ) // for each tensor sample, we add its content to the relevant histogram. RealType eigenvalue1 = W(2, 2); RealType eigenvalue2 = ( W(1, 1) ); // + W(0, 0) ) * 0.5; if( eigenvalue1 > this->m_MaximumEigenvalue1 ) { this->m_MaximumEigenvalue1 = eigenvalue1; } if( eigenvalue2 > this->m_MaximumEigenvalue2 ) { this->m_MaximumEigenvalue2 = eigenvalue2; } if( eigenvalue1 < this->m_MinimumEigenvalue1 ) { this->m_MinimumEigenvalue1 = eigenvalue1; } if( eigenvalue2 < this->m_MinimumEigenvalue2 ) { this->m_MinimumEigenvalue2 = eigenvalue2; } ++It; } It = this->GetInputListSample()->Begin(); while( It != this->GetInputListSample()->End() ) { InputMeasurementVectorType inputMeasurement = It.GetMeasurementVector(); // convert to a tensor then get its shape and primary orientation vector typedef VariableSizeMatrix TensorType; TensorType T( D, D ); T.Fill( 0.0 ); unsigned int index = 0; for( unsigned int i = 0; i < D; i++ ) { for( unsigned int j = i; j < D; j++ ) { T(i, j) = inputMeasurement( index++ ); T(j, i) = T(i, j); } } // now decompose T into shape and orientation TensorType V; TensorType W; TensorType Tc = T; typedef DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); decomposer->EvaluateSymmetricEigenDecomposition( Tc, W, V ); // now W holds the eigenvalues ( shape ) // for each tensor sample, we add its content to the relevant histogram. RealType eigenvalue1 = W(2, 2); RealType eigenvalue2 = W(1, 1); eigenvalue1 /= ( this->m_MaximumEigenvalue1 - this->m_MinimumEigenvalue1 ); eigenvalue2 /= ( this->m_MaximumEigenvalue2 - this->m_MinimumEigenvalue2 ); // std::cout << " ev1 " << eigenvalue1 << " oev1 " << W(2,2) << " ev2 " << eigenvalue2 << " oev2 " << // W(1,1) << // std::endl; /** joint-hist model for the eigenvalues */ this->IncrementJointHistogramForShape( eigenvalue1, eigenvalue2 ); RealType x = V(0, 2); RealType y = V(1, 2); RealType z = V(2, 2); /** joint-hist model for the principal eigenvector */ this->IncrementJointHistogramForOrientation( x, y, z, 1 ); x = V(0, 1); y = V(1, 1); z = V(2, 1); /** joint-hist model for the second eigenvector */ this->IncrementJointHistogramForOrientation( x, y, z, 2 ); ++It; } for( unsigned int d = 0; d < 3; d++ ) { typedef DiscreteGaussianImageFilter GaussianFilterType; typename GaussianFilterType::Pointer gaussian = GaussianFilterType::New(); gaussian->SetInput( this->m_JointHistogramImages[d] ); if( d == 0 ) // Shape { gaussian->SetVariance( this->m_ShapeSigma * this->m_ShapeSigma ); } else if( d == 1 ) // Orientation of 1st eigenvector { gaussian->SetVariance(this->m_OrientationSigma * this->m_OrientationSigma ); } else if( d == 2 ) // Orientation of 2nd eigenvector { gaussian->SetVariance( this->m_ShapeSigma * this->m_OrientationSigma ); } gaussian->SetMaximumError( 0.01 ); gaussian->SetUseImageSpacing( false ); gaussian->Update(); typedef StatisticsImageFilter StatsFilterType; typename StatsFilterType::Pointer stats = StatsFilterType::New(); stats->SetInput( gaussian->GetOutput() ); stats->Update(); typedef DivideByConstantImageFilter DividerType; typename DividerType::Pointer divider = DividerType::New(); divider->SetInput( gaussian->GetOutput() ); divider->SetConstant( stats->GetSum() ); divider->Update(); this->m_JointHistogramImages[d] = divider->GetOutput(); } /* write out histograms--for debugging static int which_class=0; which_class++; std::string string; std::stringstream outstring; outstring< WriterType; typename WriterType::Pointer writer = WriterType::New(); std::string output( "output_shape"+string+".nii.gz" ); writer->SetFileName( output.c_str() ); writer->SetInput(this->m_JointHistogramImages[0] ); writer->Update(); typedef ImageFileWriter< JointHistogramImageType > WriterType2; typename WriterType2::Pointer writer2 = WriterType::New(); std::string output2( "output_orientation"+string+".nii.gz" ); writer2->SetFileName( output2.c_str() ); writer2->SetInput(this->m_JointHistogramImages[1] ); writer2->Update(); std::cout << "Writing output of histograms." << std::endl; */ } template TOutput JointHistogramParzenShapeAndOrientationListSampleFunction ::Evaluate( const InputMeasurementVectorType & measurement ) const { try { RealType probability = 1.0; for( unsigned int d = 0; d < 2; d++ ) { typename JointHistogramImageType::PointType point; point[0] = measurement[d]; this->m_Interpolator->SetInputImage( this->m_JointHistogramImages[d] ); if( this->m_Interpolator->IsInsideBuffer( point ) ) { probability *= this->m_Interpolator->Evaluate( point ); } else { return 0; } } return probability; } catch( ... ) { return 0; } } /** * Standard "PrintSelf" method */ template void JointHistogramParzenShapeAndOrientationListSampleFunction ::PrintSelf( std::ostream& os, Indent indent) const { os << indent << "Shape Sigma: " << this->m_ShapeSigma << std::endl; os << indent << "Number of shape histogram bins: " << this->m_NumberOfShapeJointHistogramBins << std::endl; os << indent << "Orientation Sigma: " << this->m_OrientationSigma << std::endl; os << indent << "Number of orientation histogram bins: " << this->m_NumberOfOrientationJointHistogramBins << std::endl; os << indent << "Minimum eigenvalue 1: " << this->m_MinimumEigenvalue1; os << indent << "Minimum eigenvalue 2: " << this->m_MinimumEigenvalue2; os << indent << "Maximum eigenvalue 1: " << this->m_MaximumEigenvalue1; os << indent << "Maximum eigenvalue 2: " << this->m_MaximumEigenvalue2; if( this->m_UseNearestNeighborIncrements ) { os << indent << "Use nearest neighbor increments." << std::endl; } else { os << indent << "Use linear interpolation for increments." << std::endl; } } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsJointHistogramParzenWindowsListSampleFunction.h000066400000000000000000000065371311104306400303230ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsJointHistogramParzenWindowsListSampleFunction_h #define __antsJointHistogramParzenWindowsListSampleFunction_h #include "antsListSampleFunction.h" #include "itkImage.h" namespace itk { namespace ants { namespace Statistics { /** \class JointHistogramParzenWindowsListSampleFunction.h * \brief */ template class JointHistogramParzenWindowsListSampleFunction : public ListSampleFunction { public: typedef JointHistogramParzenWindowsListSampleFunction Self; typedef ListSampleFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( JointHistogramParzenWindowsListSampleFunction, ListSampleFunction ); typedef typename Superclass::InputListSampleType InputListSampleType; typedef typename Superclass::InputMeasurementVectorType InputMeasurementVectorType; typedef typename Superclass::InputMeasurementType InputMeasurementType; /** List sample typedef support. */ typedef TListSample ListSampleType; /** Other typedef */ typedef TOutput RealType; typedef TOutput OutputType; typedef Image JointHistogramImageType; typedef Vector ThetaPsiType; /** Helper functions */ itkSetMacro( Sigma, RealType ); itkGetConstMacro( Sigma, RealType ); itkSetMacro( NumberOfJointHistogramBins, unsigned int ); itkGetConstMacro( NumberOfJointHistogramBins, unsigned int ); virtual void SetInputListSample( const InputListSampleType * ptr ); virtual TOutput Evaluate( const InputMeasurementVectorType& measurement ) const; protected: JointHistogramParzenWindowsListSampleFunction(); virtual ~JointHistogramParzenWindowsListSampleFunction(); void PrintSelf( std::ostream& os, Indent indent ) const; void GenerateData(); void IncrementJointHistogram(RealType e1, RealType e2, unsigned int which); private: // purposely not implemented JointHistogramParzenWindowsListSampleFunction( const Self & ); void operator=( const Self & ); unsigned int m_NumberOfJointHistogramBins; RealType m_Sigma; bool m_UseNNforJointHistIncrements; std::vector m_JointHistogramImages; }; } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsJointHistogramParzenWindowsListSampleFunction.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsJointHistogramParzenWindowsListSampleFunction.hxx000066400000000000000000000266211311104306400306770ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsJointHistogramParzenWindowsListSampleFunction_hxx #define __antsJointHistogramParzenWindowsListSampleFunction_hxx #include "antsJointHistogramParzenWindowsListSampleFunction.h" #include "itkArray.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkContinuousIndex.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkDivideByConstantImageFilter.h" #include "itkStatisticsImageFilter.h" namespace itk { namespace ants { namespace Statistics { template JointHistogramParzenWindowsListSampleFunction ::JointHistogramParzenWindowsListSampleFunction() { this->m_NumberOfJointHistogramBins = 32; this->m_Sigma = 1.0; this->m_UseNNforJointHistIncrements = true; } template JointHistogramParzenWindowsListSampleFunction ::~JointHistogramParzenWindowsListSampleFunction() { } template void JointHistogramParzenWindowsListSampleFunction ::IncrementJointHistogram(RealType eigenvalue1, RealType eigenvalue2, unsigned int which_hist) { RealType newWeight = 1.0; // now define two joint histograms, one for shape, one for orientation. // first, the shape histogram --- 0,0 origin and spacing of 1 if( this->m_JointHistogramImages.size() == which_hist ) { typename JointHistogramImageType::SpacingType spacing; spacing.Fill(1); typename JointHistogramImageType::PointType origin; origin.Fill(0); typename JointHistogramImageType::SizeType size; size.Fill(this->m_NumberOfJointHistogramBins); typename JointHistogramImageType::DirectionType direction; direction.SetIdentity(); typename JointHistogramImageType::Pointer curJHI = AllocImage(size, spacing, origin, regions, 0); this->m_JointHistogramImages.push_back( curJHI); } typename JointHistogramImageType::PointType shapePoint; if( eigenvalue1 > 1 ) { eigenvalue1 = 1; } if( eigenvalue2 > 1 ) { eigenvalue2 = 1; } if( eigenvalue1 < 0 ) { eigenvalue1 = 0; } if( eigenvalue2 < 0 ) { eigenvalue2 = 0; } shapePoint[0] = eigenvalue1 * (this->m_NumberOfJointHistogramBins - 1); shapePoint[1] = eigenvalue2 * (this->m_NumberOfJointHistogramBins - 1); ContinuousIndex shapeCidx; this->m_JointHistogramImages[which_hist]->TransformPhysicalPointToContinuousIndex( shapePoint, shapeCidx ); typename JointHistogramImageType::IndexType shapeIdx; /** Nearest neighbor increment to JH */ if( this->m_UseNNforJointHistIncrements ) { shapeIdx[0] = std::floor( shapeCidx[0] + 0.5); shapeIdx[1] = std::floor( shapeCidx[1] + 0.5 ); if( this->m_JointHistogramImages[which_hist]->GetLargestPossibleRegion().IsInside( shapeIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[which_hist]->GetPixel( shapeIdx ); this->m_JointHistogramImages[which_hist]->SetPixel(shapeIdx, 1 + oldWeight ); } } else { /** linear addition */ shapeIdx[0] = static_cast( std::floor( shapeCidx[0] ) ); shapeIdx[1] = static_cast( std::floor( shapeCidx[1] ) ); RealType dist1 = sqrt( (shapeCidx[0] - shapeIdx[0]) * (shapeCidx[0] - shapeIdx[0]) + (shapeCidx[1] - shapeIdx[1]) * (shapeCidx[1] - shapeIdx[1]) ); shapeIdx[0]++; RealType dist2 = sqrt( (shapeCidx[0] - shapeIdx[0]) * (shapeCidx[0] - shapeIdx[0]) + (shapeCidx[1] - shapeIdx[1]) * (shapeCidx[1] - shapeIdx[1]) ); shapeIdx[1]++; RealType dist3 = sqrt( (shapeCidx[0] - shapeIdx[0]) * (shapeCidx[0] - shapeIdx[0]) + (shapeCidx[1] - shapeIdx[1]) * (shapeCidx[1] - shapeIdx[1]) ); shapeIdx[0]--; RealType dist4 = sqrt( (shapeCidx[0] - shapeIdx[0]) * (shapeCidx[0] - shapeIdx[0]) + (shapeCidx[1] - shapeIdx[1]) * (shapeCidx[1] - shapeIdx[1]) ); RealType distsum = dist1 + dist2 + dist3 + dist4; dist1 /= distsum; dist2 /= distsum; dist3 /= distsum; dist4 /= distsum; shapeIdx[0] = static_cast( std::floor( shapeCidx[0] ) ); shapeIdx[1] = static_cast( std::floor( shapeCidx[1] ) ); if( this->m_JointHistogramImages[which_hist]->GetLargestPossibleRegion().IsInside( shapeIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[which_hist]->GetPixel( shapeIdx ); this->m_JointHistogramImages[which_hist]->SetPixel(shapeIdx, ( 1.0 - dist1 ) * newWeight + oldWeight ); } shapeIdx[0]++; if( this->m_JointHistogramImages[which_hist]->GetLargestPossibleRegion().IsInside( shapeIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[which_hist]->GetPixel(shapeIdx ); this->m_JointHistogramImages[which_hist]->SetPixel(shapeIdx, ( 1.0 - dist2 ) * newWeight + oldWeight ); } shapeIdx[1]++; if( this->m_JointHistogramImages[which_hist]->GetLargestPossibleRegion().IsInside( shapeIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[which_hist]->GetPixel(shapeIdx ); this->m_JointHistogramImages[which_hist]->SetPixel(shapeIdx, ( 1.0 - dist3 ) * newWeight + oldWeight ); } shapeIdx[0]--; if( this->m_JointHistogramImages[which_hist]->GetLargestPossibleRegion().IsInside( shapeIdx ) ) { RealType oldWeight = this->m_JointHistogramImages[which_hist]->GetPixel(shapeIdx ); this->m_JointHistogramImages[which_hist]->SetPixel(shapeIdx, ( 1.0 - dist4) * newWeight + oldWeight ); } } return; } template void JointHistogramParzenWindowsListSampleFunction ::SetInputListSample( const InputListSampleType * ptr ) { this->m_ListSample = ptr; this->m_JointHistogramImages.clear(); if( !this->m_ListSample ) { return; } if( this->m_ListSample->Size() <= 1 ) { itkWarningMacro( "The input list sample has <= 1 element." << "Function evaluations will be equal to 0." ); return; } typename InputListSampleType::ConstIterator It = this->m_ListSample->Begin(); InputMeasurementVectorType inputMeasurement = It.GetMeasurementVector(); unsigned int Dimension = inputMeasurement.Size(); if( ( Dimension % 2) != 0 ) { itkWarningMacro( "The input list should contain 2*N images where N > 0." ); return; } /** * Find the min/max values to define the histogram domain */ Array minValues( Dimension ); minValues.Fill( NumericTraits::max() ); Array maxValues( Dimension ); maxValues.Fill( NumericTraits::NonpositiveMin() ); It = this->m_ListSample->Begin(); while( It != this->m_ListSample->End() ) { InputMeasurementVectorType inputMeasurement = It.GetMeasurementVector(); for( unsigned int d = 0; d < Dimension; d++ ) { if( inputMeasurement[d] < minValues[d] ) { minValues[d] = inputMeasurement[d]; } if( inputMeasurement[d] > maxValues[d] ) { maxValues[d] = inputMeasurement[d]; } } ++It; } It = this->m_ListSample->Begin(); while( It != this->m_ListSample->End() ) { InputMeasurementVectorType inputMeasurement = It.GetMeasurementVector(); /** joint-hist model for the eigenvalues */ unsigned int jhcount = 0; for( unsigned int d = 0; d < Dimension; d = d + 2 ) { RealType value1 = (inputMeasurement[d] - minValues[d]) / (maxValues[d] - minValues[d]); RealType value2 = (inputMeasurement[d + 1] - minValues[d + 1]) / (maxValues[d + 1] - minValues[d + 1]); this->IncrementJointHistogram(value1, value2, jhcount); jhcount++; } ++It; } for( unsigned int d = 0; d < this->m_JointHistogramImages.size(); d++ ) { typedef DiscreteGaussianImageFilter GaussianFilterType; typename GaussianFilterType::Pointer gaussian = GaussianFilterType::New(); gaussian->SetInput( this->m_JointHistogramImages[d] ); gaussian->SetVariance( this->m_Sigma * this->m_Sigma ); gaussian->SetMaximumError( 0.01 ); gaussian->SetUseImageSpacing( false ); gaussian->Update(); typedef StatisticsImageFilter StatsFilterType; typename StatsFilterType::Pointer stats = StatsFilterType::New(); stats->SetInput( gaussian->GetOutput() ); stats->Update(); typedef DivideByConstantImageFilter DividerType; typename DividerType::Pointer divider = DividerType::New(); divider->SetInput( gaussian->GetOutput() ); divider->SetConstant( stats->GetSum() ); divider->Update(); this->m_JointHistogramImages[d] = divider->GetOutput(); } } template TOutput JointHistogramParzenWindowsListSampleFunction ::Evaluate( const InputMeasurementVectorType & measurement ) const { try { typedef BSplineInterpolateImageFunction InterpolatorType; RealType probability = 1.0; for( unsigned int d = 0; d < this->m_JointHistogramImages.size(); d++ ) { typename JointHistogramImageType::PointType point; point[0] = measurement[d]; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); interpolator->SetSplineOrder( 3 ); interpolator->SetInputImage( this->m_JointHistogramImages[d] ); if( interpolator->IsInsideBuffer( point ) ) { probability *= interpolator->Evaluate( point ); } else { return 0; } } return probability; } catch( ... ) { return 0; } } /** * Standard "PrintSelf" method */ template void JointHistogramParzenWindowsListSampleFunction ::PrintSelf( std::ostream& os, Indent indent) const { os << indent << "Sigma: " << this->m_Sigma << std::endl; os << indent << "Number of histogram bins: " << this->m_NumberOfJointHistogramBins << std::endl; } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsListSampleFunction.h000066400000000000000000000112311311104306400226710ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsListSampleFunction_h #define __antsListSampleFunction_h #include "itkFunctionBase.h" #include "itkArray.h" namespace itk { namespace ants { namespace Statistics { /** \class ListSampleFunction * \brief Evaluates a function of an image at specified position. * * ListSampleFunction is a baseclass for all objects that evaluates * a function of a list sample at a measurement * This class is templated over the input list type, the type * of the function output and the coordinate representation type * (e.g. float or double). * * The input list sample is set via method SetInputListSample(). * The methods Evaluate() evaluates the function at a measurement vector. * * \ingroup ListSampleFunctions */ template class ListSampleFunction : public FunctionBase { public: /** Standard class typedefs. */ typedef ListSampleFunction Self; typedef FunctionBase Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro( ListSampleFunction, FunctionBase ); /** InputListSampleType typedef support. */ typedef TInputListSample InputListSampleType; /** Array typedef for weights */ typedef Array ListSampleWeightArrayType; /** InputPixel typedef support */ typedef typename InputListSampleType::MeasurementVectorType InputMeasurementVectorType; typedef typename InputListSampleType::MeasurementType InputMeasurementType; /** OutputType typedef support. */ typedef TOutput OutputType; /** CoordRepType typedef support. */ typedef TCoordRep CoordRepType; /** Set the input point set. * \warning this method caches BufferedRegion information. * If the BufferedRegion has changed, user must call * SetInputListSample again to update cached values. */ virtual void SetInputListSample( const InputListSampleType * ptr ) { this->SetIndexedInputListSample( 0, ptr ); } virtual void SetIndexedInputListSample( const unsigned int d, const InputListSampleType * ptr ); /** Sets the weights using an array */ virtual void SetListSampleWeights( ListSampleWeightArrayType *array ) { this->SetListSampleWeights( 0, array ); } virtual void SetListSampleWeights( const unsigned int, ListSampleWeightArrayType * ); /** Get the input image. */ virtual const InputListSampleType * GetInputListSample( const unsigned int idx = 0 ) const; /** Clear the input list sample to free memory */ virtual void ClearInputListSample( const unsigned int idx = 0 ) { this->SetIndexedInputListSample( idx, ITK_NULLPTR ); } /** Gets the weights array */ virtual ListSampleWeightArrayType * GetListSampleWeights( const unsigned int idx = 0 ); /** Evaluate the function at specified Point position. * Subclasses must provide this method. */ virtual TOutput Evaluate( const InputMeasurementVectorType& measurement ) const ITK_OVERRIDE = 0; protected: ListSampleFunction(); ~ListSampleFunction() { } void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; /** Const pointer to the input image. */ std::vector m_ListSamples; std::vector m_ListSampleWeights; private: ListSampleFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end of namespace Statistics } // end of namespace ants } // end of namespace itk // Define instantiation macro for this template. #define ITK_TEMPLATE_ListSampleFunction(_, EXPORT, x, y) namespace itk { \ _(3 (class EXPORT ListSampleFunction ) ) \ namespace Templates { typedef ListSampleFunction ListSampleFunction##y; } \ } #if ITK_TEMPLATE_EXPLICIT #include "Templates/antsListSampleFunction+-.h" #endif #ifndef ITK_MANUAL_INSTANTIATION #include "antsListSampleFunction.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsListSampleFunction.hxx000066400000000000000000000064141311104306400232600ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsListSampleFunction_hxx #define __antsListSampleFunction_hxx #include "antsListSampleFunction.h" namespace itk { namespace ants { namespace Statistics { /** * Constructor */ template ListSampleFunction ::ListSampleFunction() { this->m_ListSamples.clear(); this->m_ListSampleWeights.clear(); } /** * Standard "PrintSelf" method */ template void ListSampleFunction ::PrintSelf( std::ostream& os, Indent indent) const { for( unsigned int d = 0; d < this->m_ListSamples.size(); d++ ) { os << indent << "InputListSample: " << this->m_ListSamples[d] << std::endl; } } template void ListSampleFunction ::SetListSampleWeights( const unsigned int idx, ListSampleWeightArrayType* array ) { if( idx >= this->m_ListSampleWeights.size() ) { this->m_ListSampleWeights.resize( idx + 1 ); this->m_ListSampleWeights[idx] = array; this->Modified(); } if( this->m_ListSampleWeights[idx] != array ) { this->m_ListSampleWeights[idx] = array; this->Modified(); } } template typename ListSampleFunction::ListSampleWeightArrayType * ListSampleFunction ::GetListSampleWeights( const unsigned int idx ) { if( idx < this->m_ListSampleWeights.size() ) { return this->m_ListSampleWeights[idx]; } else { return ITK_NULLPTR; } } /** * Initialize by setting the input point set */ template void ListSampleFunction ::SetIndexedInputListSample( const unsigned int idx, const InputListSampleType * ptr ) { if( idx >= this->m_ListSamples.size() ) { this->m_ListSamples.resize( idx + 1 ); this->m_ListSamples[idx] = ptr; this->Modified(); } if( this->m_ListSamples[idx] != ptr ) { this->m_ListSamples[idx] = ptr; this->Modified(); } } template const typename ListSampleFunction::InputListSampleType * ListSampleFunction ::GetInputListSample( const unsigned int idx ) const { if( idx < this->m_ListSamples.size() ) { return this->m_ListSamples[idx]; } else { return ITK_NULLPTR; } } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsListSampleToListSampleFilter.h000066400000000000000000000054621311104306400246430ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Date: $$ Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsListSampleToListSampleFilter_h #define __antsListSampleToListSampleFilter_h #include "itkProcessObject.h" namespace itk { namespace ants { namespace Statistics { /** \class ListSampleToListSampleFilter * \brief Base class for filters that take a list sample as an input and output * another list sample. * * ListSampleToListSampleFilter is the base class for all process objects that output * list sample data, and require list sample data as input. Specifically, this class * defines the SetInput() method for defining the input to a filter. * * \ingroup ListSampleFilters * */ template class ListSampleToListSampleFilter : public ProcessObject { public: /** Standard class typedefs. */ typedef ListSampleToListSampleFilter Self; typedef ProcessObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro( ListSampleToListSampleFilter, ProcessObject ); /** Some convenient typedefs. */ typedef TInputListSample InputListSampleType; typedef TOutputListSample OutputListSampleType; /** Set the list sample input of this object. */ virtual void SetInputListSample( const InputListSampleType *input ); /** Get the list sample input of this object. */ InputListSampleType * GetInput(); /** Get the list sample output of this object. */ OutputListSampleType * GetOutput(); virtual void Update() ITK_OVERRIDE { this->GenerateData(); } protected: ListSampleToListSampleFilter(); ~ListSampleToListSampleFilter() { }; virtual void GenerateData() ITK_OVERRIDE = 0; void AllocateOutput(); private: ListSampleToListSampleFilter( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented // typename InputListSampleType::ConstPointer m_InputListSample; // typename OutputListSampleType::Pointer m_OutputListSample; }; } // end namespace Statistics } // end namespace ants } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsListSampleToListSampleFilter.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsListSampleToListSampleFilter.hxx000066400000000000000000000057701311104306400252250ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkListSampleToListSampleFilter_hxx #define _itkListSampleToListSampleFilter_hxx #include "antsListSampleToListSampleFilter.h" namespace itk { namespace ants { namespace Statistics { /** * */ template ListSampleToListSampleFilter ::ListSampleToListSampleFilter() { // Modify superclass default values, can be overridden by subclasses this->SetNumberOfRequiredInputs( 1 ); this->SetNumberOfRequiredOutputs( 1 ); } template void ListSampleToListSampleFilter ::SetInputListSample( const TInputListSample *input ) { // this->m_InputListSample = const_cast( input ); this->ProcessObject::SetNthInput( 0, reinterpret_cast( const_cast( input ) ) ); } template void ListSampleToListSampleFilter ::AllocateOutput() { typename DataObject::Pointer obj = reinterpret_cast(TOutputListSample::New().GetPointer() ); // typename TOutputListSample::Pointer output // = reinterpret_cast(obj.GetPointer()); this->ProcessObject::SetNumberOfRequiredOutputs( 1 ); this->ProcessObject::SetNthOutput( 0, obj.GetPointer() ); // this->m_OutputListSample = OutputListSampleType::New(); } /** * */ template typename ListSampleToListSampleFilter::InputListSampleType * ListSampleToListSampleFilter ::GetInput() { return reinterpret_cast( this->ProcessObject::GetInput( 0 ) ); } template typename ListSampleToListSampleFilter::OutputListSampleType * ListSampleToListSampleFilter ::GetOutput() { if( this->GetNumberOfOutputs() < 1 ) { return ITK_NULLPTR; } // we assume that the first output is of the templated type return reinterpret_cast( this->ProcessObject::GetOutput( 0 ) ); // return this->m_OutputListSample.GetPointer(); } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsLogEuclideanGaussianListSampleFunction.h000066400000000000000000000056311311104306400266470ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsLogEuclideanGaussianListSampleFunction_h #define __antsLogEuclideanGaussianListSampleFunction_h #include "antsListSampleFunction.h" #include "itkVariableSizeMatrix.h" namespace itk { namespace ants { namespace Statistics { /** \class LogEuclideanGaussianListSampleFunction.h * \brief */ template class LogEuclideanGaussianListSampleFunction : public ListSampleFunction { public: typedef LogEuclideanGaussianListSampleFunction Self; typedef ListSampleFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( LogEuclideanGaussianListSampleFunction, ListSampleFunction ); typedef typename Superclass::InputListSampleType InputListSampleType; typedef typename Superclass::InputMeasurementVectorType InputMeasurementVectorType; typedef typename Superclass::InputMeasurementType InputMeasurementType; /** Other typedef */ typedef TOutput RealType; typedef TOutput OutputType; typedef VariableSizeMatrix TensorType; /** Helper functions */ virtual void SetInputListSample( const InputListSampleType * ptr ) ITK_OVERRIDE; virtual TOutput Evaluate( const InputMeasurementVectorType& measurement ) const ITK_OVERRIDE; protected: LogEuclideanGaussianListSampleFunction(); virtual ~LogEuclideanGaussianListSampleFunction(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void GenerateData(); TensorType LogTensorTransform( const TensorType & ) const; TensorType ExpTensorTransform( const TensorType & ) const; RealType CalculateTensorDistance( const TensorType &, const TensorType & ) const; TensorType m_MeanTensor; RealType m_Dispersion; private: // purposely not implemented LogEuclideanGaussianListSampleFunction( const Self & ); void operator=( const Self & ); }; } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsLogEuclideanGaussianListSampleFunction.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsLogEuclideanGaussianListSampleFunction.hxx000066400000000000000000000174671311104306400272410ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsLogEuclideanGaussianListSampleFunction_hxx #define __antsLogEuclideanGaussianListSampleFunction_hxx #include "antsLogEuclideanGaussianListSampleFunction.h" #include "itkDecomposeTensorFunction.h" #include "vnl/vnl_trace.h" namespace itk { namespace ants { namespace Statistics { template LogEuclideanGaussianListSampleFunction ::LogEuclideanGaussianListSampleFunction() { } template LogEuclideanGaussianListSampleFunction ::~LogEuclideanGaussianListSampleFunction() { } template void LogEuclideanGaussianListSampleFunction ::SetInputListSample( const InputListSampleType * ptr ) { Superclass::SetInputListSample( ptr ); if( !this->GetInputListSample() ) { return; } if( this->GetInputListSample()->Size() > 1 ) { RealType L = static_cast( this->GetInputListSample()->GetMeasurementVectorSize() ); unsigned int D = static_cast( 0.5 * ( -1 + std::sqrt( 1.0 + 8.0 * L ) ) ); this->m_MeanTensor.SetSize( D, D ); this->m_MeanTensor.Fill( 0.0 ); unsigned long N = 0; RealType totalWeight = 0.0; typename InputListSampleType::ConstIterator It = this->GetInputListSample()->Begin(); while( It != this->GetInputListSample()->End() ) { InputMeasurementVectorType measurement = It.GetMeasurementVector(); TensorType T( D, D ); unsigned int index = 0; for( unsigned int i = 0; i < D; i++ ) { for( unsigned int j = i; j < D; j++ ) { T( i, j ) = measurement( index++ ); T( j, i ) = T( i, j ); } } T = this->LogTensorTransform( T ); RealType weight = 1.0; if( this->GetListSampleWeights()->Size() == this->GetInputListSample()->Size() ) { weight = ( *this->GetListSampleWeights() )[N++]; } totalWeight += weight; this->m_MeanTensor += ( T * weight ); ++It; } if( totalWeight > 0.0 ) { this->m_MeanTensor /= totalWeight; } this->m_MeanTensor = this->ExpTensorTransform( this->m_MeanTensor ); /** * Now calculate the dispersion (i.e. variance) */ this->m_Dispersion = 0.0; N = 0; It = this->GetInputListSample()->Begin(); while( It != this->GetInputListSample()->End() ) { InputMeasurementVectorType measurement = It.GetMeasurementVector(); TensorType T( D, D ); unsigned int index = 0; for( unsigned int i = 0; i < D; i++ ) { for( unsigned int j = i; j < D; j++ ) { T( i, j ) = measurement( index++ ); T( j, i ) = T( i, j ); } } RealType distance = this->CalculateTensorDistance( T, this->m_MeanTensor ); RealType weight = 1.0; if( this->GetListSampleWeights()->Size() == this->GetInputListSample()->Size() ) { weight = ( *this->GetListSampleWeights() )[N++]; } this->m_Dispersion += ( weight * vnl_math_sqr( distance ) ); ++It; } this->m_Dispersion /= static_cast( N ); } else { itkWarningMacro( "The input list sample has <= 1 element." << "Function evaluations will be equal to 0." ); } } template typename LogEuclideanGaussianListSampleFunction ::TensorType LogEuclideanGaussianListSampleFunction ::LogTensorTransform( const TensorType & T ) const { TensorType V; TensorType W; TensorType Tc = T; typedef DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); decomposer->EvaluateSymmetricEigenDecomposition( Tc, W, V ); for( unsigned int i = 0; i < W.Rows(); i++ ) { if( W( i, i ) > 0.0 ) { W( i, i ) = std::log( W( i, i ) ); } else { W( i, i ) = 0.0; } } W *= V.GetTranspose(); TensorType logT = V * W; return logT; } template typename LogEuclideanGaussianListSampleFunction ::TensorType LogEuclideanGaussianListSampleFunction ::ExpTensorTransform( const TensorType & T ) const { TensorType V; TensorType W; TensorType Tc = T; typedef DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); decomposer->EvaluateSymmetricEigenDecomposition( Tc, W, V ); for( unsigned int i = 0; i < W.Rows(); i++ ) { W( i, i ) = std::exp( W( i, i ) ); } W *= V.GetTranspose(); TensorType expT = V * W; return expT; } template typename LogEuclideanGaussianListSampleFunction ::RealType LogEuclideanGaussianListSampleFunction ::CalculateTensorDistance( const TensorType & S, const TensorType & T ) const { TensorType logS = this->LogTensorTransform( S ); TensorType logT = this->LogTensorTransform( T ); TensorType diff = logS - logT; TensorType diffSq = diff * diff; RealType distance = std::sqrt( vnl_trace( ( diffSq ).GetVnlMatrix() ) ); // RealType distance = ( ( logS - logT ).GetVnlMatrix() ).frobenius_norm(); return distance; } template TOutput LogEuclideanGaussianListSampleFunction ::Evaluate( const InputMeasurementVectorType & measurement ) const { unsigned int D = this->m_MeanTensor.Rows(); TensorType T( D, D ); unsigned int index = 0; for( unsigned int i = 0; i < D; i++ ) { for( unsigned int j = i; j < D; j++ ) { T( i, j ) = measurement( index++ ); T( j, i ) = T( i, j ); } } RealType distance = this->CalculateTensorDistance( T, this->m_MeanTensor ); RealType preFactor = 1.0 / ( std::sqrt( 2.0 * vnl_math::pi * this->m_Dispersion ) ); RealType probability = preFactor * std::exp( -0.5 * vnl_math_sqr( distance ) / this->m_Dispersion ); return probability; } /** * Standard "PrintSelf" method */ template void LogEuclideanGaussianListSampleFunction ::PrintSelf( std::ostream& os, Indent indent) const { os << indent << "Mean tensor = ["; for( unsigned int r = 0; r < this->m_MeanTensor.Rows(); r++ ) { for( unsigned int c = 0; c < this->m_MeanTensor.Cols() - 1; c++ ) { os << this->m_MeanTensor( r, c ) << ", "; } if( r == this->m_MeanTensor.Rows() - 1 ) { os << this->m_MeanTensor( r, this->m_MeanTensor.Cols() - 1 ) << "], "; } else { os << this->m_MeanTensor( r, this->m_MeanTensor.Cols() - 1 ) << "; "; } } os << "Dispersion (variance) = " << this->m_Dispersion << std::endl; } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsManifoldParzenWindowsListSampleFunction.h000066400000000000000000000100341311104306400270760ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsManifoldParzenWindowsListSampleFunction_h #define __antsManifoldParzenWindowsListSampleFunction_h #include "antsListSampleFunction.h" #include "itkGaussianMembershipFunction.h" #include "itkWeightedCentroidKdTreeGenerator.h" #include namespace itk { namespace ants { namespace Statistics { /** \class ManifoldParzenWindowsListSampleFunction.h * \brief point set filter. */ template class ManifoldParzenWindowsListSampleFunction : public ListSampleFunction { public: typedef ManifoldParzenWindowsListSampleFunction Self; typedef ListSampleFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( ManifoldParzenWindowsListSampleFunction, ListSampleFunction ); typedef typename Superclass::InputListSampleType InputListSampleType; typedef typename Superclass::InputMeasurementVectorType InputMeasurementVectorType; typedef typename Superclass::InputMeasurementType InputMeasurementType; /** List sample typedef support. */ typedef TListSample ListSampleType; /** Kd tree typedefs */ typedef typename itk::Statistics:: WeightedCentroidKdTreeGenerator TreeGeneratorType; typedef typename TreeGeneratorType::KdTreeType KdTreeType; typedef typename KdTreeType ::InstanceIdentifierVectorType NeighborhoodIdentifierType; /** Other typedef */ typedef TOutput RealType; typedef TOutput OutputType; typedef typename itk::Statistics::GaussianMembershipFunction GaussianType; typedef std::vector GaussianContainerType; typedef typename GaussianType::CovarianceMatrixType CovarianceMatrixType; /** Helper functions */ itkSetMacro( EvaluationKNeighborhood, unsigned int ); itkGetConstMacro( EvaluationKNeighborhood, unsigned int ); itkSetMacro( RegularizationSigma, RealType ); itkGetConstMacro( RegularizationSigma, RealType ); itkSetMacro( CovarianceKNeighborhood, unsigned int ); itkGetConstMacro( CovarianceKNeighborhood, unsigned int ); itkSetMacro( KernelSigma, RealType ); itkGetConstMacro( KernelSigma, RealType ); virtual void SetInputListSample( const InputListSampleType * ptr ) ITK_OVERRIDE; virtual TOutput Evaluate( const InputMeasurementVectorType& measurement ) const ITK_OVERRIDE; protected: ManifoldParzenWindowsListSampleFunction(); virtual ~ManifoldParzenWindowsListSampleFunction(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void GenerateData(); private: // purposely not implemented ManifoldParzenWindowsListSampleFunction( const Self & ); void operator=( const Self & ); unsigned int m_CovarianceKNeighborhood; unsigned int m_EvaluationKNeighborhood; RealType m_RegularizationSigma; RealType m_KernelSigma; RealType m_NormalizationFactor; typename TreeGeneratorType::Pointer m_KdTreeGenerator; GaussianContainerType m_Gaussians; }; } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsManifoldParzenWindowsListSampleFunction.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsManifoldParzenWindowsListSampleFunction.hxx000066400000000000000000000171301311104306400274620ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsManifoldParzenWindowsListSampleFunction_hxx #define __antsManifoldParzenWindowsListSampleFunction_hxx #include "antsManifoldParzenWindowsListSampleFunction.h" namespace itk { namespace ants { namespace Statistics { template ManifoldParzenWindowsListSampleFunction ::ManifoldParzenWindowsListSampleFunction() { this->m_KdTreeGenerator = ITK_NULLPTR; this->m_EvaluationKNeighborhood = 50; this->m_RegularizationSigma = 1.0; this->m_CovarianceKNeighborhood = 0; this->m_KernelSigma = 0.0; } template ManifoldParzenWindowsListSampleFunction ::~ManifoldParzenWindowsListSampleFunction() { } template void ManifoldParzenWindowsListSampleFunction ::SetInputListSample( const InputListSampleType * ptr ) { Superclass::SetInputListSample( ptr ); if( !this->GetInputListSample() ) { return; } if( this->GetInputListSample()->Size() <= 1 ) { itkWarningMacro( "The input list sample has <= 1 element." << "Function evaluations will be equal to 0." ); return; } /** * Generate KdTree and create set of gaussians from input point set */ this->m_KdTreeGenerator = TreeGeneratorType::New(); this->m_KdTreeGenerator->SetSample( const_cast( this->GetInputListSample() ) ); this->m_KdTreeGenerator->SetBucketSize( 16 ); this->m_KdTreeGenerator->Update(); /** * Calculate covariance matrices */ this->m_Gaussians.resize( this->GetInputListSample()->Size() ); const unsigned int Dimension = this->GetInputListSample()->GetMeasurementVectorSize(); unsigned long count = 0; typename InputListSampleType::ConstIterator It = this->GetInputListSample()->Begin(); while( It != this->GetInputListSample()->End() ) { InputMeasurementVectorType inputMeasurement = It.GetMeasurementVector(); typename GaussianType::MeanVectorType mean( Dimension ); for( unsigned int d = 0; d < Dimension; d++ ) { mean[d] = inputMeasurement[d]; } this->m_Gaussians[count] = GaussianType::New(); this->m_Gaussians[count]->SetMean( mean ); if( this->m_CovarianceKNeighborhood > 0 ) { /** Temporarily set the covariance */ CovarianceMatrixType Cov( Dimension, Dimension ); Cov.SetIdentity(); Cov *= this->m_KernelSigma; this->m_Gaussians[count]->SetCovariance( Cov ); Cov.Fill( 0 ); typename TreeGeneratorType::KdTreeType ::InstanceIdentifierVectorType neighbors; unsigned int numberOfNeighbors = vnl_math_min( this->m_CovarianceKNeighborhood, static_cast( this->GetInputListSample()->Size() ) ); this->m_KdTreeGenerator->GetOutput()->Search( inputMeasurement, numberOfNeighbors, neighbors ); RealType denominator = 0.0; for( unsigned int j = 0; j < numberOfNeighbors; j++ ) { if( neighbors[j] != count && neighbors[j] < this->GetInputListSample()->Size() ) { InputMeasurementVectorType neighbor = this->m_KdTreeGenerator->GetOutput()->GetMeasurementVector( neighbors[j] ); RealType kernelValue = this->m_Gaussians[count]->Evaluate( neighbor ); if( this->GetListSampleWeights()->Size() == this->m_Gaussians.size() ) { kernelValue *= ( *this->GetListSampleWeights() )[count]; } denominator += kernelValue; if( kernelValue > 0.0 ) { for( unsigned int m = 0; m < Dimension; m++ ) { for( unsigned int n = m; n < Dimension; n++ ) { RealType covariance = kernelValue * ( neighbor[m] - inputMeasurement[m] ) * ( neighbor[n] - inputMeasurement[n] ); Cov( m, n ) += covariance; Cov( n, m ) += covariance; } } } } } if( denominator > 0.0 ) { Cov /= denominator; } for( unsigned int m = 0; m < Dimension; m++ ) { Cov( m, m ) += ( this->m_RegularizationSigma * this->m_RegularizationSigma ); } this->m_Gaussians[count]->SetCovariance( Cov ); } else { CovarianceMatrixType Cov( Dimension, Dimension ); Cov.SetIdentity(); Cov *= this->m_RegularizationSigma; this->m_Gaussians[count]->SetCovariance( Cov ); } ++It; ++count; } /** * Calculate normalization factor */ this->m_NormalizationFactor = 0.0; for( unsigned int i = 0; i < this->m_Gaussians.size(); i++ ) { if( this->GetListSampleWeights()->Size() == this->m_Gaussians.size() ) { this->m_NormalizationFactor += ( *this->GetListSampleWeights() )[i]; } else { this->m_NormalizationFactor += 1.0; } } } template TOutput ManifoldParzenWindowsListSampleFunction ::Evaluate( const InputMeasurementVectorType & measurement ) const { try { unsigned int numberOfNeighbors = vnl_math_min( this->m_EvaluationKNeighborhood, static_cast( this->m_Gaussians.size() ) ); OutputType sum = 0.0; if( numberOfNeighbors == this->m_Gaussians.size() ) { for( unsigned int j = 0; j < this->m_Gaussians.size(); j++ ) { sum += static_cast( this->m_Gaussians[j]->Evaluate( measurement ) ); } } else { typename TreeGeneratorType::KdTreeType ::InstanceIdentifierVectorType neighbors; this->m_KdTreeGenerator->GetOutput()->Search( measurement, numberOfNeighbors, neighbors ); for( unsigned int j = 0; j < numberOfNeighbors; j++ ) { sum += static_cast( this->m_Gaussians[neighbors[j]]->Evaluate( measurement ) ); } } return static_cast( sum / this->m_NormalizationFactor ); } catch( ... ) { return 0; } } /** * Standard "PrintSelf" method */ template void ManifoldParzenWindowsListSampleFunction ::PrintSelf( std::ostream& os, Indent indent) const { os << indent << "Regularization sigma: " << this->m_RegularizationSigma << std::endl; os << indent << "Evaluation K neighborhood: " << this->m_EvaluationKNeighborhood << std::endl; if( this->m_CovarianceKNeighborhood > 0 ) { os << indent << "Covariance K neighborhood: " << this->m_CovarianceKNeighborhood << std::endl; os << indent << "Kernel sigma: " << this->m_KernelSigma << std::endl; } } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsPartialVolumeGaussianListSampleFunction.h000066400000000000000000000066111311104306400270770ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsPartialVolumeGaussianListSampleFunction_h #define __antsPartialVolumeGaussianListSampleFunction_h #include "antsListSampleFunction.h" #include "itkGaussianMembershipFunction.h" namespace itk { namespace ants { namespace Statistics { /** \class PartialVolumeGaussianListSampleFunction.h * \brief point set filter. */ template class PartialVolumeGaussianListSampleFunction : public ListSampleFunction { public: typedef PartialVolumeGaussianListSampleFunction Self; typedef ListSampleFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( PartialVolumeGaussianListSampleFunction, ListSampleFunction ); typedef typename Superclass::InputListSampleType InputListSampleType; typedef typename Superclass::InputMeasurementVectorType InputMeasurementVectorType; typedef typename Superclass::InputMeasurementType InputMeasurementType; typedef typename Superclass::ListSampleWeightArrayType ListSampleWeightArrayType; /** Gaussian typedefs */ typedef typename itk::Statistics::GaussianMembershipFunction GaussianType; typedef typename GaussianType::MeanVectorType MeanType; typedef typename GaussianType::CovarianceMatrixType CovarianceType; /** List sample typedef support. */ typedef TListSample ListSampleType; /** Other typedef */ typedef TOutput RealType; typedef TOutput OutputType; virtual void SetIndexedInputListSample(unsigned int d, const InputListSampleType * ptr ) ITK_OVERRIDE; virtual TOutput Evaluate( const InputMeasurementVectorType& measurement ) const ITK_OVERRIDE; protected: PartialVolumeGaussianListSampleFunction(); virtual ~PartialVolumeGaussianListSampleFunction(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void GenerateData(); private: // purposely not implemented PartialVolumeGaussianListSampleFunction( const Self & ); void operator=( const Self & ); void CalculateGaussianParametersFromListSample( const InputListSampleType *, const ListSampleWeightArrayType *, MeanType & ); void CalculateGaussianParameters(); MeanType m_Mean[2]; bool m_IsCalculated[2]; typename GaussianType::Pointer m_Gaussian; }; } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsPartialVolumeGaussianListSampleFunction.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsPartialVolumeGaussianListSampleFunction.hxx000066400000000000000000000137421311104306400274620ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsPartialVolumeGaussianListSampleFunction_hxx #define __antsPartialVolumeGaussianListSampleFunction_hxx #include "antsPartialVolumeGaussianListSampleFunction.h" #include "itkMeanSampleFilter.h" #include "itkWeightedMeanSampleFilter.h" namespace itk { namespace ants { namespace Statistics { template PartialVolumeGaussianListSampleFunction ::PartialVolumeGaussianListSampleFunction() { this->m_Gaussian = GaussianType::New(); this->m_IsCalculated[0] = false; this->m_IsCalculated[1] = false; } template PartialVolumeGaussianListSampleFunction ::~PartialVolumeGaussianListSampleFunction() { } template void PartialVolumeGaussianListSampleFunction ::SetIndexedInputListSample( const unsigned int d, const InputListSampleType * ptr ) { Superclass::SetIndexedInputListSample( d, ptr ); if( d > 1 ) { itkExceptionMacro( "This class only requires two input list samples." ); } if( !this->GetInputListSample( d ) ) { return; } else { this->CalculateGaussianParametersFromListSample( this->GetInputListSample( d ), this->GetListSampleWeights( d ), this->m_Mean[d] ); this->m_IsCalculated[d] = true; } if( this->m_IsCalculated[0] && this->m_IsCalculated[1] ) { this->CalculateGaussianParameters(); } } template void PartialVolumeGaussianListSampleFunction ::CalculateGaussianParametersFromListSample( const InputListSampleType *listSample, const ListSampleWeightArrayType *weights, MeanType & mean ) { if( !listSample ) { return; } if( listSample->Size() > 1 ) { if( weights->Size() == listSample->Size() ) { typedef typename itk::Statistics:: WeightedMeanSampleFilter MeanCalculatorType; typename MeanCalculatorType::Pointer meanCalculator = MeanCalculatorType::New(); meanCalculator->SetWeights( *weights ); meanCalculator->SetInput( listSample ); meanCalculator->Update(); NumericTraits::SetLength( mean, listSample->GetMeasurementVectorSize() ); for( unsigned int d = 0; d < listSample->GetMeasurementVectorSize(); d++ ) { mean[d] = meanCalculator->GetMean()[d]; } } else { typedef itk::Statistics::MeanSampleFilter MeanCalculatorType; typename MeanCalculatorType::Pointer meanCalculator = MeanCalculatorType::New(); meanCalculator->SetInput( listSample ); meanCalculator->Update(); NumericTraits::SetLength( mean, listSample->GetMeasurementVectorSize() ); for( unsigned int d = 0; d < listSample->GetMeasurementVectorSize(); d++ ) { mean[d] = meanCalculator->GetMean()[d]; } } } else { itkWarningMacro( "The input list sample has <= 1 element." ); } } template void PartialVolumeGaussianListSampleFunction ::CalculateGaussianParameters() { if( this->m_Mean[0].Size() != this->m_Mean[1].Size() ) { itkExceptionMacro( "Mean sizes are unequal." ); } MeanType mean; NumericTraits::SetLength( mean, this->m_Mean[0].Size() ); CovarianceType covariance; covariance.SetSize( mean.Size(), mean.Size() ); covariance.SetIdentity(); for( unsigned int d = 0; d < mean.Size(); d++ ) { mean[d] = 0.5 * ( this->m_Mean[0][d] + this->m_Mean[1][d] ); covariance( d, d ) = 1.0 / 12.0 * vnl_math_sqr( this->m_Mean[0][d] ) + -1.0 / 6.0 * this->m_Mean[0][d] * this->m_Mean[1][d] + 1.0 / 12.0 * vnl_math_sqr( this->m_Mean[1][d] ); } this->m_Gaussian->SetMean( mean ); this->m_Gaussian->SetCovariance( covariance ); } template TOutput PartialVolumeGaussianListSampleFunction ::Evaluate( const InputMeasurementVectorType & measurement ) const { if( this->m_IsCalculated[0] && this->m_IsCalculated[1] ) { try { return this->m_Gaussian->Evaluate( measurement ); } catch( ... ) { return 0.0; } } else { return 0.0; } } /** * Standard "PrintSelf" method */ template void PartialVolumeGaussianListSampleFunction ::PrintSelf( std::ostream& os, Indent indent) const { os << indent << "mean = " << this->m_Gaussian->GetMean() << ", "; CovarianceType covariance = this->m_Gaussian->GetCovariance(); os << "covariance = ["; for( unsigned int r = 0; r < covariance.Rows(); r++ ) { for( unsigned int c = 0; c < covariance.Cols() - 1; c++ ) { os << covariance( r, c ) << ", "; } if( r == covariance.Rows() - 1 ) { os << covariance( r, covariance.Cols() - 1 ) << "]" << std::endl; } else { os << covariance( r, covariance.Cols() - 1 ) << "; "; } } } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/antsPassThroughListSampleFilter.h000066400000000000000000000046761311104306400245400ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsPassThroughListSampleFilter_h #define __antsPassThroughListSampleFilter_h #include "antsListSampleToListSampleFilter.h" namespace itk { namespace ants { namespace Statistics { /** \class PassThroughListSampleFilter * \brief Simple class which pass the input to the output. * */ template class PassThroughListSampleFilter : public ListSampleToListSampleFilter { public: /** * Standard class typedefs. */ typedef PassThroughListSampleFilter Self; typedef ListSampleToListSampleFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** * Standard macros */ itkTypeMacro( PassThroughListSampleFilter, ListSampleToScalarListSampleFilter ); /** * Method for creation through the object factory. */ itkNewMacro( Self ); /** * Conveneient typedefs */ typedef TListSample ListSampleType; typedef ListSampleType InputType; typedef typename ListSampleType::MeasurementVectorType MeasurementVectorType; typedef typename ListSampleType::MeasurementType MeasurementType; protected: PassThroughListSampleFilter(); virtual ~PassThroughListSampleFilter(); void PrintSelf( std::ostream& os, Indent indent ) const; virtual void GenerateData(); private: PassThroughListSampleFilter( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented }; // end of class } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsPassThroughListSampleFilter.hxx" #endif #endif ants-2.2.0/ImageSegmentation/antsPassThroughListSampleFilter.hxx000066400000000000000000000032721311104306400251070ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsPassThroughListSampleFilter_hxx #define __antsPassThroughListSampleFilter_hxx #include "antsPassThroughListSampleFilter.h" namespace itk { namespace ants { namespace Statistics { template PassThroughListSampleFilter ::PassThroughListSampleFilter() { this->AllocateOutput(); this->GetOutput()->SetMeasurementVectorSize( this->GetInput()->GetMeasurementVectorSize() ); } template PassThroughListSampleFilter ::~PassThroughListSampleFilter() { } template void PassThroughListSampleFilter ::GenerateData() { /** * Simply pass the input to the output. */ typename ListSampleType::ConstIterator It = this->GetInput()->Begin(); while( It != this->GetInput()->End() ) { this->GetOutput()->PushBack( It.GetMeasurementVector() ); ++It; } } template void PassThroughListSampleFilter ::PrintSelf( std::ostream& os, Indent indent ) const { this->Superclass::PrintSelf( os, indent ); } } // end of namespace Statistics } // end of namespace ants } // end of namespace itk #endif ants-2.2.0/ImageSegmentation/itkWeightedVotingFusionImageFilter.h000066400000000000000000000335571311104306400251730ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkWeightedVotingFusionImageFilter_h #define __itkWeightedVotingFusionImageFilter_h #include "itkNonLocalPatchBasedImageFilter.h" #include "itkConstNeighborhoodIterator.h" #include #include #include #include #include namespace itk { /** \class WeightedVotingFusionImageFilter * \brief Implementation of the joint label fusion and joint intensity fusion algorithm * * \author Paul Yushkevich with modifications by Brian Avants and Nick Tustison * * \par REFERENCE * * H. Wang, J. W. Suh, S. Das, J. Pluta, C. Craige, P. Yushkevich, * "Multi-atlas segmentation with joint label fusion," IEEE Trans. * on Pattern Analysis and Machine Intelligence, 35(3), 611-623, 2013. * * H. Wang and P. A. Yushkevich, "Multi-atlas segmentation with joint * label fusion and corrective learning--an open source implementation," * Front. Neuroinform., 2013. * * \ingroup ImageSegmentation */ template class WeightedVotingFusionImageFilter : public NonLocalPatchBasedImageFilter { public: /** Standard class typedefs. */ typedef WeightedVotingFusionImageFilter Self; typedef NonLocalPatchBasedImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro( WeightedVotingFusionImageFilter, NonLocalPatchBasedImageFilter ); itkNewMacro( Self ); /** ImageDimension constants */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); /** Some convenient typedefs. */ typedef TInputImage InputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename InputImageType::ConstPointer InputImageConstPointer; typedef typename InputImageType::PixelType InputImagePixelType; typedef typename Superclass::InputImageList InputImageList; typedef typename Superclass::InputImageSetList InputImageSetList; typedef typename Superclass::InputImagePixelVectorType InputImagePixelVectorType; typedef TOutputImage OutputImageType; typedef typename OutputImageType::PixelType LabelType; typedef std::set LabelSetType; typedef Image LabelImageType; typedef typename LabelImageType::Pointer LabelImagePointer; typedef std::vector LabelImageList; typedef Image CountImageType; typedef LabelImageType MaskImageType; typedef typename MaskImageType::Pointer MaskImagePointer; typedef typename InputImageType::RegionType RegionType; typedef typename InputImageType::SizeType SizeType; typedef typename InputImageType::IndexType IndexType; typedef Image ProbabilityImageType; typedef typename ProbabilityImageType::Pointer ProbabilityImagePointer; typedef double RealType; typedef std::vector OffsetList; typedef vnl_matrix MatrixType; typedef vnl_vector VectorType; typedef std::map LabelPosteriorProbabilityMap; typedef std::map LabelExclusionMap; typedef std::vector VotingWeightImageList; typedef typename Superclass::ConstNeighborhoodIteratorType ConstNeighborhoodIteratorType; typedef typename Superclass::NeighborhoodRadiusType NeighborhoodRadiusType; typedef typename Superclass::NeighborhoodOffsetType NeighborhoodOffsetType; typedef typename Superclass::NeighborhoodOffsetListType NeighborhoodOffsetListType; typedef typename SizeType::SizeValueType RadiusValueType; typedef Image RadiusImageType; typedef typename RadiusImageType::Pointer RadiusImagePointer; /** * Set the multimodal target image */ void SetTargetImage( InputImageList imageList ) { this->m_TargetImage = imageList; this->UpdateInputs(); } /** * Add an atlas (multi-modal image + segmentation) */ void AddAtlas( InputImageList imageList, LabelImageType *segmentation = ITK_NULLPTR ) { for( unsigned int i = 0; i < imageList.size(); i++ ) { this->m_AtlasImages.push_back( imageList ); } if( this->m_NumberOfAtlasModalities == 0 ) { itkDebugMacro( "Setting the number of modalities to " << this->m_NumberOfAtlasModalities ); this->m_NumberOfAtlasModalities = imageList.size(); } else if( this->m_NumberOfAtlasModalities != imageList.size() ) { itkExceptionMacro( "The number of atlas multimodal images is not equal to " << this->m_NumberOfAtlasModalities ); } this->m_NumberOfAtlases++; if( segmentation != ITK_NULLPTR ) { this->m_AtlasSegmentations.push_back( segmentation ); this->m_NumberOfAtlasSegmentations++; } this->UpdateInputs(); } /** * Set mask image function. If a binary mask image is specified, only * those input image voxels corresponding with mask image values equal * to one are used. */ void SetMaskImage( MaskImageType *mask ) { this->m_MaskImage = mask; this->UpdateInputs(); } /** * Add a label exclusion map */ void AddLabelExclusionImage( LabelType label, LabelImageType *exclusionImage ) { this->m_LabelExclusionImages[label] = exclusionImage; this->UpdateInputs(); } /** * Get the number of modalities used in determining the optimal label fusion * or optimal fused image. */ itkGetConstMacro( NumberOfAtlasModalities, unsigned int ); /** * Get the label set. */ itkGetConstMacro( LabelSet, LabelSetType ); /** * Set/Get the local search neighborhood radius image. */ void SetNeighborhoodSearchRadiusImage( RadiusImageType *image ) { this->m_NeighborhoodSearchRadiusImage = image; } /** * Set/Get the Alpha parameter---the regularization weight added to the matrix Mx for * the inverse. Default = 0.1. */ itkSetMacro( Alpha, RealType ); itkGetConstMacro( Alpha, RealType ); /** * Set/Get the Beta parameter---exponent for mapping intensity difference to joint error. * Default = 2.0. */ itkSetMacro( Beta, RealType ); itkGetConstMacro( Beta, RealType ); /** Set the requested region */ void GenerateInputRequestedRegion() ITK_OVERRIDE; /** * Boolean for retaining the posterior images. This can have a negative effect * on memory use, so it should only be done if one wishes to save the posterior * maps. The posterior maps (n = number of labels) give the probability of each * voxel in the target image belonging to each label. Default = false. */ itkSetMacro( RetainLabelPosteriorProbabilityImages, bool ); itkGetConstMacro( RetainLabelPosteriorProbabilityImages, bool ); itkBooleanMacro( RetainLabelPosteriorProbabilityImages ); /** * Boolean for retaining the voting weights images. This can have a negative effect * on memory use, so it should only be done if one wishes to save the voting weight * maps. The voting weight maps (n = number of atlases) gives the contribution of * a particular atlas to the final label/intensity fusion. */ itkSetMacro( RetainAtlasVotingWeightImages, bool ); itkGetConstMacro( RetainAtlasVotingWeightImages, bool ); itkBooleanMacro( RetainAtlasVotingWeightImages ); /** * Boolean for constraining the weights to be positive and sum to 1. We use * an implementation of the algorithm based on the algorithm by Lawson, Charles L.; * Hanson, Richard J. (1995). Solving Least Squares Problems. SIAM. */ itkSetMacro( ConstrainSolutionToNonnegativeWeights, bool ); itkGetConstMacro( ConstrainSolutionToNonnegativeWeights, bool ); itkBooleanMacro( ConstrainSolutionToNonnegativeWeights ); /** * Get the current state for progress reporting. */ itkGetConstMacro( IsWeightedAveragingComplete, bool ); /** * Get the posterior probability image corresponding to a label. */ const ProbabilityImagePointer GetLabelPosteriorProbabilityImage( LabelType label ) { if( this->m_RetainLabelPosteriorProbabilityImages ) { if( std::find( this->m_LabelSet.begin(), this->m_LabelSet.end(), label ) != this->m_LabelSet.end() ) { return this->m_LabelPosteriorProbabilityImages[label]; } else { itkDebugMacro( "Not returning a label posterior probability image. Requested label not found." ); return ITK_NULLPTR; } } else { itkDebugMacro( "Not returning a label posterior probability image. These images were not saved." ); return ITK_NULLPTR; } } /** * Get the voting weight image corresponding to an atlas. */ const ProbabilityImagePointer GetAtlasVotingWeightImage( unsigned int n ) { if( this->m_RetainAtlasVotingWeightImages ) { if( n < this->m_NumberOfAtlases ) { return this->m_AtlasVotingWeightImages[n]; } else { itkDebugMacro( "Not returning a voting weight image. Requested index is greater than the number of atlases." ); return ITK_NULLPTR; } } else { itkDebugMacro( "Not returning a voting weight image. These images were not saved." ); return ITK_NULLPTR; } } /** * Get the joint intensity fusion output image */ const ProbabilityImagePointer GetJointIntensityFusionImage( unsigned int n ) { if( n < this->m_NumberOfAtlasModalities ) { return this->m_JointIntensityFusionImage[n]; } else { itkDebugMacro( "Not returning a joint intensity fusion image. Requested index is greater than the number of modalities." ); return ITK_NULLPTR; } } protected: WeightedVotingFusionImageFilter(); ~WeightedVotingFusionImageFilter() {} void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void ThreadedGenerateData( const RegionType &, ThreadIdType ) ITK_OVERRIDE; void BeforeThreadedGenerateData() ITK_OVERRIDE; void AfterThreadedGenerateData() ITK_OVERRIDE; void GenerateData() ITK_OVERRIDE; private: void ThreadedGenerateDataForWeightedAveraging( const RegionType &, ThreadIdType ); void ThreadedGenerateDataForReconstruction( const RegionType &, ThreadIdType ); VectorType NonNegativeLeastSquares( const MatrixType, const VectorType, const RealType ); void UpdateInputs(); typedef std::pair DistanceIndexType; typedef std::vector DistanceIndexVectorType; struct DistanceIndexComparator { bool operator () ( const DistanceIndexType& left, const DistanceIndexType& right ) { return left.second < right.second; } }; bool m_IsWeightedAveragingComplete; /** Input variables */ InputImageList m_TargetImage; InputImageSetList m_AtlasImages; LabelImageList m_AtlasSegmentations; LabelExclusionMap m_LabelExclusionImages; MaskImagePointer m_MaskImage; typename CountImageType::Pointer m_CountImage; LabelSetType m_LabelSet; SizeValueType m_NumberOfAtlases; SizeValueType m_NumberOfAtlasSegmentations; SizeValueType m_NumberOfAtlasModalities; std::map m_NeighborhoodSearchOffsetSetsMap; RealType m_Alpha; RealType m_Beta; bool m_RetainLabelPosteriorProbabilityImages; bool m_RetainAtlasVotingWeightImages; bool m_ConstrainSolutionToNonnegativeWeights; ProbabilityImagePointer m_WeightSumImage; RadiusImagePointer m_NeighborhoodSearchRadiusImage; /** Output variables */ LabelPosteriorProbabilityMap m_LabelPosteriorProbabilityImages; VotingWeightImageList m_AtlasVotingWeightImages; InputImageList m_JointIntensityFusionImage; }; } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkWeightedVotingFusionImageFilter.hxx" #endif #endif ants-2.2.0/ImageSegmentation/itkWeightedVotingFusionImageFilter.hxx000066400000000000000000001023111311104306400255340ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkWeightedVotingFusionImageFilter_hxx #define itkWeightedVotingFusionImageFilter_hxx #include "itkWeightedVotingFusionImageFilter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkProgressReporter.h" #include #include #include #include #include namespace itk { template WeightedVotingFusionImageFilter ::WeightedVotingFusionImageFilter() : m_IsWeightedAveragingComplete( false ), m_NumberOfAtlases( 0 ), m_NumberOfAtlasSegmentations( 0 ), m_NumberOfAtlasModalities( 0 ), m_Alpha( 0.1 ), m_Beta( 2.0 ), m_RetainLabelPosteriorProbabilityImages( false ), m_RetainAtlasVotingWeightImages( false ), m_ConstrainSolutionToNonnegativeWeights( false ) { this->m_MaskImage = ITK_NULLPTR; this->m_CountImage = ITK_NULLPTR; this->m_NeighborhoodSearchRadiusImage = ITK_NULLPTR; this->SetSimilarityMetric( Superclass::PEARSON_CORRELATION ); } template void WeightedVotingFusionImageFilter ::UpdateInputs() { // Set all the inputs this->SetNumberOfIndexedInputs( this->m_NumberOfAtlases * this->m_NumberOfAtlasModalities + this->m_NumberOfAtlasSegmentations + this->m_TargetImage.size() + this->m_LabelExclusionImages.size() ); SizeValueType nthInput = 0; for( SizeValueType i = 0; i < this->m_TargetImage.size(); i++ ) { this->SetNthInput( nthInput++, this->m_TargetImage[i] ); } for( SizeValueType i = 0; i < this->m_NumberOfAtlases; i++ ) { for( SizeValueType j = 0; j < this->m_NumberOfAtlasModalities; j++ ) { this->SetNthInput( nthInput++, this->m_AtlasImages[i][j] ); } } for( SizeValueType i = 0; i < this->m_NumberOfAtlasSegmentations; i++ ) { this->SetNthInput( nthInput++, this->m_AtlasSegmentations[i] ); } typename LabelExclusionMap::const_iterator it; for( it = m_LabelExclusionImages.begin(); it != m_LabelExclusionImages.end(); ++it ) { this->SetNthInput( nthInput++, it->second ); } if( this->m_MaskImage.IsNotNull() ) { this->SetNthInput( nthInput++, this->m_MaskImage ); } this->Modified(); } template void WeightedVotingFusionImageFilter ::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); // Get the output requested region RegionType outRegion = this->GetOutput()->GetRequestedRegion(); // Pad this region by the search window and patch size if( this->m_NeighborhoodSearchRadiusImage.IsNull() ) { outRegion.PadByRadius( this->GetNeighborhoodSearchRadius() ); } else { NeighborhoodRadiusType maxNeighborhoodSearchRadius; maxNeighborhoodSearchRadius.Fill( 0 ); ImageRegionConstIterator ItR( this->m_NeighborhoodSearchRadiusImage, this->m_NeighborhoodSearchRadiusImage->GetRequestedRegion() ); for( ItR.GoToBegin(); !ItR.IsAtEnd(); ++ItR ) { RadiusValueType localSearchRadius = ItR.Get(); if( localSearchRadius > maxNeighborhoodSearchRadius[0] ) { maxNeighborhoodSearchRadius.Fill( localSearchRadius ); } } outRegion.PadByRadius( maxNeighborhoodSearchRadius ); } outRegion.PadByRadius( this->GetNeighborhoodPatchRadius() ); // Iterate over all the inputs to this filter for( SizeValueType i = 0; i < this->m_TargetImage.size(); i++ ) { InputImageType *input = this->m_TargetImage[i]; if( i == 0 ) { this->SetTargetImageRegion( input->GetRequestedRegion() ); } RegionType region = outRegion; region.Crop( input->GetLargestPossibleRegion() ); input->SetRequestedRegion( region ); } for( SizeValueType i = 0; i < this->m_NumberOfAtlases; i++ ) { for( SizeValueType j = 0; j < this->m_NumberOfAtlasModalities; j++ ) { InputImageType *input = this->m_AtlasImages[i][j]; RegionType region = outRegion; region.Crop( input->GetLargestPossibleRegion() ); input->SetRequestedRegion( region ); } } for( SizeValueType i = 0; i < this->m_NumberOfAtlasSegmentations; i++ ) { LabelImageType *input = this->m_AtlasSegmentations[i]; RegionType region = outRegion; region.Crop( input->GetLargestPossibleRegion() ); input->SetRequestedRegion( region ); } typename LabelExclusionMap::const_iterator it; for( it = m_LabelExclusionImages.begin(); it != m_LabelExclusionImages.end(); ++it ) { LabelImageType *input = it->second; RegionType region = outRegion; region.Crop( input->GetLargestPossibleRegion() ); input->SetRequestedRegion( region ); } if( this->m_MaskImage.IsNotNull() ) { MaskImageType *input = this->m_MaskImage; RegionType region = outRegion; region.Crop( input->GetLargestPossibleRegion() ); input->SetRequestedRegion( region ); } } template void WeightedVotingFusionImageFilter ::GenerateData() { this->BeforeThreadedGenerateData(); /** * Multithread processing for the weighted averaging */ typename ImageSource::ThreadStruct str1; str1.Filter = this; this->GetMultiThreader()->SetNumberOfThreads( this->GetNumberOfThreads() ); this->GetMultiThreader()->SetSingleMethod( this->ThreaderCallback, &str1 ); this->GetMultiThreader()->SingleMethodExecute(); this->m_IsWeightedAveragingComplete = true; /** * Multithread processing for the image(s) reconstruction */ typename ImageSource::ThreadStruct str2; str2.Filter = this; this->GetMultiThreader()->SetNumberOfThreads( this->GetNumberOfThreads() ); this->GetMultiThreader()->SetSingleMethod( this->ThreaderCallback, &str2 ); this->GetMultiThreader()->SingleMethodExecute(); this->AfterThreadedGenerateData(); } template void WeightedVotingFusionImageFilter ::BeforeThreadedGenerateData() { Superclass::BeforeThreadedGenerateData(); if( this->m_NumberOfAtlasSegmentations != this->m_NumberOfAtlases ) { // Set the number of atlas segmentations to 0 since we're just going to // doing joint intensity fusion this->m_NumberOfAtlasSegmentations = 0; } // Check to see if the number of target images is equal to 1 or equal to the number // of atlas modalities if( this->m_TargetImage.size() != 1 && this->m_TargetImage.size() != this->m_NumberOfAtlasModalities ) { itkExceptionMacro( "The number of target images must be 1 or must be the number of atlas modalities." ); } // Find all the unique labels in the atlas segmentations this->m_LabelSet.clear(); for( unsigned int i = 0; i < this->m_NumberOfAtlasSegmentations; i++ ) { ImageRegionConstIteratorWithIndex It( this->m_AtlasSegmentations[i], this->m_AtlasSegmentations[i]->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { if( !this->m_MaskImage || this->m_MaskImage->GetPixel( It.GetIndex() ) != NumericTraits::ZeroValue() ) { this->m_LabelSet.insert( It.Get() ); } } } // Initialize the posterior maps this->m_LabelPosteriorProbabilityImages.clear(); typename LabelSetType::const_iterator labelIt; for( labelIt = this->m_LabelSet.begin(); labelIt != this->m_LabelSet.end(); ++labelIt ) { typename ProbabilityImageType::Pointer labelProbabilityImage = ProbabilityImageType::New(); labelProbabilityImage->CopyInformation( this->m_TargetImage[0] ); labelProbabilityImage->SetRegions( this->m_TargetImage[0]->GetRequestedRegion() ); labelProbabilityImage->SetLargestPossibleRegion( this->m_TargetImage[0]->GetLargestPossibleRegion() ); labelProbabilityImage->Allocate( true ); this->m_LabelPosteriorProbabilityImages.insert( std::pair( *labelIt, labelProbabilityImage ) ); } // Initialize the atlas voting weight images if( this->m_RetainAtlasVotingWeightImages ) { this->m_AtlasVotingWeightImages.clear(); this->m_AtlasVotingWeightImages.resize( this->m_NumberOfAtlases ); for( SizeValueType i = 0; i < this->m_NumberOfAtlases; i++ ) { this->m_AtlasVotingWeightImages[i] = ProbabilityImageType::New(); this->m_AtlasVotingWeightImages[i]->CopyInformation( this->m_TargetImage[0] ); this->m_AtlasVotingWeightImages[i]->SetRegions( this->m_TargetImage[0]->GetRequestedRegion() ); this->m_AtlasVotingWeightImages[i]->SetLargestPossibleRegion( this->m_TargetImage[0]->GetLargestPossibleRegion() ); this->m_AtlasVotingWeightImages[i]->Allocate( true ); } } // Do the joint intensity fusion this->m_JointIntensityFusionImage.clear(); this->m_JointIntensityFusionImage.resize( this->m_NumberOfAtlasModalities ); for( SizeValueType i = 0; i < this->m_NumberOfAtlasModalities; i++ ) { this->m_JointIntensityFusionImage[i] = InputImageType::New(); this->m_JointIntensityFusionImage[i]->CopyInformation( this->m_TargetImage[0] ); this->m_JointIntensityFusionImage[i]->SetRegions( this->m_TargetImage[0]->GetRequestedRegion() ); this->m_JointIntensityFusionImage[i]->SetLargestPossibleRegion( this->m_TargetImage[0]->GetLargestPossibleRegion() ); this->m_JointIntensityFusionImage[i]->Allocate( true ); } // Initialize the weight sum image this->m_WeightSumImage = ProbabilityImageType::New(); this->m_WeightSumImage->CopyInformation( this->m_TargetImage[0] ); this->m_WeightSumImage->SetRegions( this->m_TargetImage[0]->GetRequestedRegion() ); this->m_WeightSumImage->SetLargestPossibleRegion( this->m_TargetImage[0]->GetLargestPossibleRegion() ); this->m_WeightSumImage->Allocate( true ); // Initialize the count image this->m_CountImage = CountImageType::New(); this->m_CountImage->CopyInformation( this->m_TargetImage[0] ); this->m_CountImage->SetRegions( this->m_TargetImage[0]->GetRequestedRegion() ); this->m_CountImage->SetLargestPossibleRegion( this->m_TargetImage[0]->GetLargestPossibleRegion() ); this->m_CountImage->Allocate( true ); // Determine the ordered search offset list (or map if an search radius image is specified) typename InputImageType::SpacingType spacing = this->m_TargetImage[0]->GetSpacing(); NeighborhoodOffsetListType orderedNeighborhoodSearchOffsetList; orderedNeighborhoodSearchOffsetList.clear(); this->m_NeighborhoodSearchOffsetSetsMap.clear(); if( this->m_NeighborhoodSearchRadiusImage.IsNull() ) { ConstNeighborhoodIterator It( this->GetNeighborhoodSearchRadius(), this->GetInput(), this->GetInput()->GetRequestedRegion() ); DistanceIndexVectorType squaredDistances; squaredDistances.resize( this->GetNeighborhoodSearchSize() ); for( unsigned int n = 0; n < this->GetNeighborhoodSearchSize(); n++ ) { NeighborhoodOffsetType offset = ( It.GetNeighborhood() ).GetOffset( n ); squaredDistances[n].first = n; squaredDistances[n].second = 0.0; for( unsigned int d = 0; d < ImageDimension; d++ ) { squaredDistances[n].second += vnl_math_sqr( offset[d] * spacing[d] ); } } std::sort( squaredDistances.begin(), squaredDistances.end(), DistanceIndexComparator() ); for( unsigned int n = 0; n < this->GetNeighborhoodSearchSize(); n++ ) { orderedNeighborhoodSearchOffsetList.push_back( ( It.GetNeighborhood() ).GetOffset( squaredDistances[n].first ) ); } this->SetNeighborhoodSearchOffsetList( orderedNeighborhoodSearchOffsetList ); } else { ImageRegionConstIterator ItR( this->m_NeighborhoodSearchRadiusImage, this->m_NeighborhoodSearchRadiusImage->GetRequestedRegion() ); for( ItR.GoToBegin(); !ItR.IsAtEnd(); ++ItR ) { RadiusValueType localSearchRadius = ItR.Get(); if( localSearchRadius > 0 && this->m_NeighborhoodSearchOffsetSetsMap.find( localSearchRadius ) == this->m_NeighborhoodSearchOffsetSetsMap.end() ) { NeighborhoodRadiusType localNeighborhoodSearchRadius; localNeighborhoodSearchRadius.Fill( localSearchRadius ); std::vector localNeighborhoodSearchOffsetList; ConstNeighborhoodIterator It( localNeighborhoodSearchRadius, this->GetInput(), this->GetInput()->GetRequestedRegion() ); RadiusValueType localNeighborhoodSearchSize = ( It.GetNeighborhood() ).Size(); DistanceIndexVectorType squaredDistances; squaredDistances.resize( localNeighborhoodSearchSize ); for( unsigned int n = 0; n < localNeighborhoodSearchSize; n++ ) { NeighborhoodOffsetType offset = ( It.GetNeighborhood() ).GetOffset( n ); squaredDistances[n].first = n; squaredDistances[n].second = 0.0; for( unsigned int d = 0; d < ImageDimension; d++ ) { squaredDistances[n].second += vnl_math_sqr( offset[d] * spacing[d] ); } } std::sort( squaredDistances.begin(), squaredDistances.end(), DistanceIndexComparator() ); for( unsigned int n = 0; n < localNeighborhoodSearchSize; n++ ) { localNeighborhoodSearchOffsetList.push_back( ( It.GetNeighborhood() ).GetOffset( squaredDistances[n].first ) ); } this->m_NeighborhoodSearchOffsetSetsMap[localSearchRadius] = localNeighborhoodSearchOffsetList; } } } this->AllocateOutputs(); } template void WeightedVotingFusionImageFilter ::ThreadedGenerateData( const RegionType ®ion, ThreadIdType threadId ) { if( !this->m_IsWeightedAveragingComplete ) { this->ThreadedGenerateDataForWeightedAveraging( region, threadId ); } else { this->ThreadedGenerateDataForReconstruction( region, threadId ); } } template void WeightedVotingFusionImageFilter ::ThreadedGenerateDataForWeightedAveraging( const RegionType & region, ThreadIdType threadId ) { ProgressReporter progress( this, threadId, region.GetNumberOfPixels(), 100 ); typename OutputImageType::Pointer output = this->GetOutput(); SizeValueType numberOfTargetModalities = this->m_TargetImage.size(); MatrixType absoluteAtlasPatchDifferences( this->m_NumberOfAtlases, this->GetNeighborhoodPatchSize() * numberOfTargetModalities ); MatrixType originalAtlasPatchIntensities( this->m_NumberOfAtlases, this->GetNeighborhoodPatchSize() * this->m_NumberOfAtlasModalities ); std::vector minimumAtlasOffsetIndices( this->m_NumberOfAtlases ); bool useOnlyFirstAtlasImage = true; if( numberOfTargetModalities == this->m_NumberOfAtlasModalities ) { useOnlyFirstAtlasImage = false; } // Iterate over the input region ConstNeighborhoodIteratorType ItN( this->GetNeighborhoodPatchRadius(), this->m_TargetImage[0], region ); for( ItN.GoToBegin(); !ItN.IsAtEnd(); ++ItN ) { progress.CompletedPixel(); IndexType currentCenterIndex = ItN.GetIndex(); if( this->m_MaskImage && this->m_MaskImage->GetPixel( currentCenterIndex ) == NumericTraits::ZeroValue() ) { continue; } // Do not do the following check from Paul's original code. Since we're incorporating // joint intensity fusion, we want to calculate at every voxel (except outside of a // possible mask) even if there are no segmentation labels at that voxel. // // Check to see if there are any non-zero labels // if( this->m_NumberOfAtlasSegmentations > 0 ) // { // bool nonBackgroundLabelExistAtThisVoxel = false; // for( SizeValueType i = 0; i < this->m_NumberOfAtlasSegmentations; i++ ) // { // if( this->m_AtlasSegmentations[i]->GetPixel( currentCenterIndex ) > 0 ) // { // nonBackgroundLabelExistAtThisVoxel = true; // break; // } // } // if( ! nonBackgroundLabelExistAtThisVoxel ) // { // continue; // } // } // Determine the search neighborhood offset list for the current center voxel std::vector searchNeighborhoodOffsetList; if( this->m_NeighborhoodSearchRadiusImage.IsNull() ) { searchNeighborhoodOffsetList = this->GetNeighborhoodSearchOffsetList(); } else { RadiusValueType localSearchRadius = this->m_NeighborhoodSearchRadiusImage->GetPixel( currentCenterIndex ); if( localSearchRadius <= 0 ) { continue; } searchNeighborhoodOffsetList = this->m_NeighborhoodSearchOffsetSetsMap[localSearchRadius]; } SizeValueType searchNeighborhoodSize = searchNeighborhoodOffsetList.size(); InputImagePixelVectorType normalizedTargetPatch = this->VectorizeImageListPatch( this->m_TargetImage, currentCenterIndex, true ); absoluteAtlasPatchDifferences.fill( 0.0 ); originalAtlasPatchIntensities.fill( 0.0 ); // In each atlas, search for a patch that matches the target patch for( SizeValueType i = 0; i < this->m_NumberOfAtlases; i++ ) { RealType minimumPatchSimilarity = NumericTraits::max(); SizeValueType minimumPatchOffsetIndex = 0; for( SizeValueType j = 0; j < searchNeighborhoodSize; j++ ) { IndexType searchIndex = currentCenterIndex + searchNeighborhoodOffsetList[j]; if( !output->GetRequestedRegion().IsInside( searchIndex ) ) { continue; } RealType patchSimilarity = this->ComputeNeighborhoodPatchSimilarity( this->m_AtlasImages[i], searchIndex, normalizedTargetPatch, useOnlyFirstAtlasImage ); if( patchSimilarity < minimumPatchSimilarity ) { minimumPatchSimilarity = patchSimilarity; minimumPatchOffsetIndex = j; } } // Once the patch has been found, normalize it and then compute the absolute // difference with target patch IndexType minimumIndex = currentCenterIndex + searchNeighborhoodOffsetList[minimumPatchOffsetIndex]; InputImagePixelVectorType normalizedMinimumAtlasPatch; if( numberOfTargetModalities == this->m_NumberOfAtlasModalities ) { normalizedMinimumAtlasPatch = this->VectorizeImageListPatch( this->m_AtlasImages[i], minimumIndex, true ); } else { normalizedMinimumAtlasPatch = this->VectorizeImagePatch( this->m_AtlasImages[i][0], minimumIndex, true ); } typename InputImagePixelVectorType::const_iterator itA = normalizedMinimumAtlasPatch.begin(); typename InputImagePixelVectorType::const_iterator itT = normalizedTargetPatch.begin(); while( itA != normalizedMinimumAtlasPatch.end() ) { RealType value = std::fabs( *itA - *itT ); absoluteAtlasPatchDifferences(i, itA - normalizedMinimumAtlasPatch.begin()) = value; ++itA; ++itT; } InputImagePixelVectorType originalMinimumAtlasPatch = this->VectorizeImageListPatch( this->m_AtlasImages[i], minimumIndex, false ); typename InputImagePixelVectorType::const_iterator itO = originalMinimumAtlasPatch.begin(); while( itO != originalMinimumAtlasPatch.end() ) { originalAtlasPatchIntensities(i, itO - originalMinimumAtlasPatch.begin()) = *itO; ++itO; } minimumAtlasOffsetIndices[i] = minimumPatchOffsetIndex; } // Allocate Mx MatrixType Mx( this->m_NumberOfAtlases, this->m_NumberOfAtlases ); // Compute Mx values for( SizeValueType i = 0; i < this->m_NumberOfAtlases; i++ ) { for( SizeValueType j = 0; j <= i; j++ ) { RealType mxValue = 0.0; for( unsigned int k = 0; k < this->GetNeighborhoodPatchSize() * numberOfTargetModalities; k++ ) { mxValue += absoluteAtlasPatchDifferences[i][k] * absoluteAtlasPatchDifferences[j][k]; } mxValue /= static_cast( this->GetNeighborhoodPatchSize() - 1 ); if( this->m_Beta == 2.0 ) { mxValue *= mxValue; } else { mxValue = std::pow( mxValue, this->m_Beta ); } if( !std::isfinite( mxValue ) ) { mxValue = 0.0; } Mx(i, j) = Mx(j, i) = mxValue; } } // Compute the weights by solving for the inverse of Mx MatrixType MxBar( this->m_NumberOfAtlases, this->m_NumberOfAtlases, 0.0 ); MxBar.fill_diagonal( this->m_Alpha ); MxBar += Mx; // Define a vector of all ones VectorType ones( this->m_NumberOfAtlases, 1.0 ); VectorType W( this->m_NumberOfAtlases, 1.0 ); if( this->m_ConstrainSolutionToNonnegativeWeights ) { W = this->NonNegativeLeastSquares( MxBar, ones, 1e-6 ); } else { vnl_cholesky cholesky( MxBar, vnl_cholesky::estimate_condition ); if( cholesky.rcond() > vnl_math::sqrteps ) { // well-conditioned matrix W = cholesky.solve( ones ); } else { // ill-conditioned matrix W = vnl_svd( MxBar ).solve( ones ); } for( SizeValueType i = 0; i < W.size(); i++ ) { if( W[i] < 0.0 ) { W[i] = 0.0; } } } // Normalize the weights W *= 1.0 / dot_product( W, ones ); // Do joint intensity fusion VectorType estimatedNeighborhoodIntensities = W; estimatedNeighborhoodIntensities.post_multiply( originalAtlasPatchIntensities ); for( SizeValueType i = 0; i < this->m_NumberOfAtlasModalities; i++ ) { for( SizeValueType j = 0; j < this->GetNeighborhoodPatchSize(); j++ ) { IndexType neighborhoodIndex = ItN.GetIndex( j ); if( !output->GetRequestedRegion().IsInside( neighborhoodIndex ) ) { continue; } if( this->m_MaskImage && this->m_MaskImage->GetPixel( neighborhoodIndex ) == NumericTraits::ZeroValue() ) { continue; } RealType estimatedValue = ( estimatedNeighborhoodIntensities[i * this->GetNeighborhoodPatchSize() + j] + this->m_JointIntensityFusionImage[i]->GetPixel( neighborhoodIndex ) ); if( !std::isfinite( estimatedValue ) ) { estimatedValue = 0.0; } this->m_JointIntensityFusionImage[i]->SetPixel( neighborhoodIndex, static_cast( estimatedValue ) ); if( i == 0 ) { this->m_CountImage->SetPixel( neighborhoodIndex, this->m_CountImage->GetPixel( neighborhoodIndex ) + 1 ); } } } if( this->m_NumberOfAtlasSegmentations > 0 ) { // Perform voting using Hongzhi's averaging scheme. Iterate over all segmentation patches for( SizeValueType n = 0; n < this->GetNeighborhoodPatchSize(); n++ ) { IndexType neighborhoodIndex = ItN.GetIndex( n ); if( !output->GetRequestedRegion().IsInside( neighborhoodIndex ) ) { continue; } for( SizeValueType i = 0; i < this->m_NumberOfAtlasSegmentations; i++ ) { // The segmentation at the corresponding patch location in atlas i IndexType minimumIndex = neighborhoodIndex + searchNeighborhoodOffsetList[minimumAtlasOffsetIndices[i]]; if( !output->GetRequestedRegion().IsInside( minimumIndex ) ) { continue; } LabelType label = this->m_AtlasSegmentations[i]->GetPixel( minimumIndex ); if( this->m_LabelSet.find( label ) == this->m_LabelSet.end() ) { continue; } // Add that weight the posterior map for voxel at idx this->m_LabelPosteriorProbabilityImages[label]->SetPixel( neighborhoodIndex, this->m_LabelPosteriorProbabilityImages[label]->GetPixel( neighborhoodIndex ) + W[i] ); this->m_WeightSumImage->SetPixel( neighborhoodIndex, this->m_WeightSumImage->GetPixel( neighborhoodIndex ) + W[i] ); if( this->m_RetainAtlasVotingWeightImages ) { this->m_AtlasVotingWeightImages[i]->SetPixel( neighborhoodIndex, this->m_AtlasVotingWeightImages[i]->GetPixel( neighborhoodIndex ) + W[i] ); } } } } } } template void WeightedVotingFusionImageFilter ::ThreadedGenerateDataForReconstruction( const RegionType ®ion, ThreadIdType threadId ) { ProgressReporter progress( this, threadId, 2 * region.GetNumberOfPixels(), 100 ); typename OutputImageType::Pointer output = this->GetOutput(); // Perform voting at each voxel ImageRegionIteratorWithIndex It( output, region ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { progress.CompletedPixel(); IndexType index = It.GetIndex(); if( this->m_MaskImage && this->m_MaskImage->GetPixel( It.GetIndex() ) == NumericTraits::ZeroValue() ) { continue; } RealType maxPosteriorProbability = 0.0; LabelType winningLabel = NumericTraits::ZeroValue(); typename LabelSetType::const_iterator labelIt; for( labelIt = this->m_LabelSet.begin(); labelIt != this->m_LabelSet.end(); ++labelIt ) { // check if the label is excluded typename LabelExclusionMap::const_iterator xIt = this->m_LabelExclusionImages.find( *labelIt ); bool isLabelExcluded = ( xIt != m_LabelExclusionImages.end() && xIt->second->GetPixel( index ) != 0 ); if( !isLabelExcluded ) { typename ProbabilityImageType::PixelType posteriorProbability = this->m_LabelPosteriorProbabilityImages[*labelIt]->GetPixel( index ); // Vote! if( maxPosteriorProbability < posteriorProbability ) { maxPosteriorProbability = posteriorProbability; winningLabel = *labelIt; } } } It.Set( winningLabel ); } ImageRegionIteratorWithIndex ItW( this->m_WeightSumImage, region ); for( ItW.GoToBegin(); !ItW.IsAtEnd(); ++ItW ) { progress.CompletedPixel(); typename ProbabilityImageType::PixelType weightSum = ItW.Get(); IndexType index = ItW.GetIndex(); if( weightSum < 0.1 ) { continue; } if( this->m_RetainLabelPosteriorProbabilityImages ) { typename LabelSetType::const_iterator labelIt; for( labelIt = this->m_LabelSet.begin(); labelIt != this->m_LabelSet.end(); ++labelIt ) { typename ProbabilityImageType::PixelType labelProbability = this->m_LabelPosteriorProbabilityImages[*labelIt]->GetPixel( index ); this->m_LabelPosteriorProbabilityImages[*labelIt]->SetPixel( index, labelProbability / weightSum ); } } if( this->m_RetainAtlasVotingWeightImages ) { for( SizeValueType i = 0; i < this->m_NumberOfAtlases; i++ ) { typename ProbabilityImageType::PixelType votingWeight = this->m_AtlasVotingWeightImages[i]->GetPixel( index ); this->m_AtlasVotingWeightImages[i]->SetPixel( index, votingWeight / weightSum ); } } } } template void WeightedVotingFusionImageFilter ::AfterThreadedGenerateData() { // Clear posterior maps if not kept if( !this->m_RetainLabelPosteriorProbabilityImages ) { this->m_LabelPosteriorProbabilityImages.clear(); } // Normalize the joint intensity fusion images. for( SizeValueType i = 0; i < this->m_NumberOfAtlasModalities; i++ ) { ImageRegionIterator ItJ( this->m_JointIntensityFusionImage[i], this->m_JointIntensityFusionImage[i]->GetRequestedRegion() ); ImageRegionIterator ItC( this->m_CountImage, this->m_CountImage->GetRequestedRegion() ); for( ItJ.GoToBegin(), ItC.GoToBegin(); !ItJ.IsAtEnd(); ++ItJ, ++ItC ) { typename CountImageType::PixelType count = ItC.Get(); if( count > 0 ) { ItJ.Set( ItJ.Get() / static_cast( count ) ); } } } } template typename WeightedVotingFusionImageFilter::VectorType WeightedVotingFusionImageFilter ::NonNegativeLeastSquares( const MatrixType A, const VectorType y, const RealType tolerance ) { // Algorithm based on // Lawson, Charles L.; Hanson, Richard J. (1995). Solving Least Squares Problems. SIAM. // cf https://en.wikipedia.org/wiki/Non-negative_least_squares SizeValueType m = A.rows(); SizeValueType n = A.cols(); // This fortran implementation sets a maximum iteration number of 3 times the // number of columns: // http://www.netlib.org/lawson-hanson/all const SizeValueType maximumNumberOfIterations = 3 * n; // Initialization VectorType P( n, 0 ); VectorType R( n, 1 ); VectorType x( n, 0 ); VectorType s( n, 0 ); VectorType w = A.transpose() * ( y - A * x ); RealType wMaxValue = w.max_value(); SizeValueType maxIndex = NumericTraits::max(); wMaxValue = NumericTraits::NonpositiveMin(); for( SizeValueType i = 0; i < n; i++ ) { if( R[i] == 1 && wMaxValue < w[i] ) { maxIndex = i; wMaxValue = w[i]; } } // Outer loop SizeValueType numberOfIterations = 0; while( R.sum() > 0 && wMaxValue > tolerance && numberOfIterations++ < maximumNumberOfIterations ) { P[maxIndex] = 1; R[maxIndex] = 0; SizeValueType sizeP = P.sum(); MatrixType AP( m, sizeP, 0 ); SizeValueType jIndex = 0; for( SizeValueType j = 0; j < n; j++ ) { if( P[j] == 1 ) { AP.set_column( jIndex++, A.get_column( j ) ); } } VectorType sP = vnl_svd( AP ).pinverse() * y; SizeValueType iIndex = 0; for( SizeValueType i = 0; i < n; i++ ) { if( R[i] != 0 ) { s[i] = 0; } else { s[i] = sP[iIndex++]; } } // Inner loop while( sP.min_value() <= tolerance && sizeP > 0 ) { RealType alpha = NumericTraits::max(); for( SizeValueType i = 0; i < n; i++ ) { if( P[i] == 1 && s[i] <= tolerance ) { RealType value = x[i] / ( x[i] - s[i] ); if( value < alpha ) { alpha = value; } } } x += alpha * ( s - x ); for( SizeValueType i = 0; i < n; i++ ) { if( P[i] == 1 && std::fabs( x[i] ) < tolerance ) { P[i] = 0; R[i] = 1; } } sizeP = P.sum(); if( sizeP == 0 ) { break; } AP.set_size( m, sizeP ); jIndex = 0; for( SizeValueType j = 0; j < n; j++ ) { if( P[j] == 1 ) { AP.set_column( jIndex++, A.get_column( j ) ); } } sP = vnl_svd( AP ).pinverse() * y; iIndex = 0; for( SizeValueType i = 0; i < n; i++ ) { if( R[i] != 0 ) { s[i] = 0; } else { s[i] = sP[iIndex++]; } } } x = s; w = A.transpose() * ( y - A * x ); maxIndex = NumericTraits::max(); wMaxValue = NumericTraits::NonpositiveMin(); for( SizeValueType i = 0; i < n; i++ ) { if( R[i] == 1 && wMaxValue < w[i] ) { maxIndex = i; wMaxValue = w[i]; } } } return x; } template void WeightedVotingFusionImageFilter ::PrintSelf( std::ostream &os, Indent indent ) const { Superclass::PrintSelf( os, indent ); os << "Number of atlases = " << this->m_NumberOfAtlases << std::endl; os << "Number of atlas segmentations = " << this->m_NumberOfAtlasSegmentations << std::endl; os << "Number of atlas modalities = " << this->m_NumberOfAtlasModalities << std::endl; os << "Alpha = " << this->m_Alpha << std::endl; os << "Beta = " << this->m_Beta << std::endl; if( this->m_ConstrainSolutionToNonnegativeWeights ) { os << "Constrain solution to positive weights using NNLS." << std::endl; } os << "Label set: "; typename LabelSetType::const_iterator labelIt; for( labelIt = this->m_LabelSet.begin(); labelIt != this->m_LabelSet.end(); ++labelIt ) { os << *labelIt << " "; } os << std::endl; } } // end namespace itk #endif ants-2.2.0/README.md000066400000000000000000000362701311104306400137400ustar00rootroot00000000000000 ============================ [Advanced Normalization Tools](https://imgur.com/a/kySGi) ============================ [![Build Status](https://travis-ci.org/stnava/ANTs.svg?branch=master)](https://travis-ci.org/stnava/ANTs) ANTs computes high-dimensional mappings to capture the statistics of brain structure and function. See the [FAQ page](https://github.com/stnava/ANTsTutorial/blob/master/handout/antsGithubExamples.Rmd). ![ants template](http://i.imgur.com/mLZ71Ai.png) ANTs allows one to organize, visualize and statistically explore large biomedical image sets. ![ants render](http://i.imgur.com/hMW6fjB.png) ANTs integrates imaging modalities and related information in space and time. ![ants render](http://i.imgur.com/oIMrnpY.png) ANTs works across species or organ systems with minimal customization. ![ants primate](http://i.imgur.com/Dfrifgg.png) ANTs and related tools have won several international and unbiased competitions. ![ants competes](http://i.imgur.com/HE0j7IC.png) [ANTsR](http://stnava.github.io/ANTsR/) is the underlying statistical workhorse. Questions: [Discussion Site](http://sourceforge.net/p/advants/discussion/) or *new* [ANTsDoc](http://stnava.github.io/ANTsDoc/) or try [this version](http://issuu.com/brianavants/docs/ants2) ... also read our [guide to evaluation strategies and addressing new problems with ANTs or other software](http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3766821/). *New* [ANTs handout](https://github.com/stnava/ANTsTutorial/raw/master/handout/antsHandout.pdf), part of forthcoming [ANTs tutorial]() material. [ANTsTalk - subject to change at any moment](http://stnava.github.io/ANTsTalk/) [ANTsRegistrationTalk - subject to change at any moment](http://stnava.github.io/ANTsRegistrationTalk/) Install ANTs via pre-built: [Packages @ github](https://github.com/stnava/ANTs/releases) older versions [@ sourceforge](http://sourceforge.net/projects/advants/files/ANTS/) ... also, [Github Releases are here](https://github.com/stnava/ANTs/releases) thanks to Arman Eshaghi. Build [ANTs](https://github.com/stnava/ANTs) from: [Source-Code](http://brianavants.wordpress.com/2012/04/13/updated-ants-compile-instructions-april-12-2012/) (recommended) [ANTs Dashboard](https://travis-ci.org/stnava/ANTs/) thanks to Arman Eshaghi and Hans J. Johnson ANTs extracts information from complex datasets that include imaging ([Word Cloud](http://brianavants.files.wordpress.com/2013/05/avants_wordcloud.jpg)). Paired with [ANTsR](http://stnava.github.io/software/2014/01/08/antsr/) (answer), ANTs is useful for managing, interpreting and visualizing multidimensional data. ANTs is [popularly](https://sourceforge.net/projects/advants/files/ANTS/stats/timeline?dates=2010-07-19+to+2099-05-25) considered a state-of-the-art medical image registration and segmentation toolkit. ANTsR is an emerging tool supporting standardized multimodality image analysis. ANTs depends on the Insight ToolKit [(ITK)](http://www.itk.org), a widely used medical image processing library to which ANTs developers contribute. A summary of some ANTs findings and tutorial material (most of which is on this page) is [here](http://rpubs.com/stnava/ANTsTut). Authors ------- ### Brian B. Avants - UPENN **Role:** Creator, Algorithm Design, Implementation, [more](http://stnava.github.io/Resume/) ### Nicholas J. Tustison - UVA **Role:** Compeller, Algorithm Design, Implementation Guru, [more](http://ntustison.github.io/CV/) ### Hans J. Johnson - UIowa **Role:** Large-Scale Application, Testing, Software design ### Team Members **Core:** Gang Song (Originator), Philip A. Cook, Jeffrey T. Duda (DTI), Ben M. Kandel (Perfusion, multivariate analysis) Image Registration ------------------ Diffeomorphisms: [SyN](http://www.ncbi.nlm.nih.gov/pubmed/17659998), Independent Evaluation: [Klein](http://www.ncbi.nlm.nih.gov/pubmed/19195496), [Murphy](http://www.ncbi.nlm.nih.gov/pubmed/21632295), Template Construction [(2004)](http://www.ncbi.nlm.nih.gov/pubmed/15501083)[(2010)](http://www.ncbi.nlm.nih.gov/pubmed/19818860), [Similarity Metrics](http://www.ncbi.nlm.nih.gov/pubmed/20851191), [Multivariate registration](http://www.ncbi.nlm.nih.gov/pubmed/18995188), [Multiple modality analysis and statistical bias](http://www.ncbi.nlm.nih.gov/pubmed/23151955) Image Segmentation ------------------ Atropos Multivar-EM Segmentation [(link)](http://www.ncbi.nlm.nih.gov/pubmed/21373993), Multi-atlas methods [(link)](https://scholar.google.com/scholar?q=joint+label+fusion+yushkevich&btnG=&hl=en&as_sdt=0%2C31) and [JLF](http://journal.frontiersin.org/article/10.3389/fninf.2013.00027/full), Bias Correction [(link)](http://www.ncbi.nlm.nih.gov/pubmed/20378467), DiReCT cortical thickness [(link)](http://www.ncbi.nlm.nih.gov/pubmed/19150502), DiReCT in [chimpanzees](http://www.ncbi.nlm.nih.gov/pubmed/23516289) Multivariate Analysis Eigenanatomy [(1)](http://www.ncbi.nlm.nih.gov/pubmed/23286132) [(2)](http://www.ncbi.nlm.nih.gov/pubmed/23475817) ---------------------------------------------------------------------------------------------------------------------------------------- Prior-Based Eigenanatomy [(in prep)](http://www.ncbi.nlm.nih.gov/pubmed/?), Sparse CCA [(1)](http://www.ncbi.nlm.nih.gov/pubmed/20083207), [(2)](http://www.ncbi.nlm.nih.gov/pubmed/20879247), Sparse Regression [(link)](http://link.springer.com/chapter/10.1007%2F978-3-642-38868-2_8) ImageMath Useful! ----------------- morphology, GetLargestComponent, CCA, FillHoles ... much more! Application Domains ------------------- ### Frontotemporal degeneration [PENN FTD center](http://ftd.med.upenn.edu) ### Multimodality Neuroimaging - [Structural MRI](http://jeffduda.github.io/NeuroBattery/) - Functional MRI - Network Analysis ### Lung Imaging - Structure - Perfusion MRI - Branching ### Multiple sclerosis (lesion filling) [example](https://github.com/armaneshaghi/LesionFilling_example) Background & Theory ---------------------------------------------------------- - The [SyN](http://www.ncbi.nlm.nih.gov/pubmed/?term=%22SyN%22+AND+%22Avants+B%22) and [N4 bias correction](http://www.ncbi.nlm.nih.gov/pubmed/?term=%22N4%22+AND+%22Tustison+N4ITK%22) papers and other relevant references in [Pubmed](http://www.ncbi.nlm.nih.gov/pubmed/?term=%22Tustison+N%22+AND+%22Avants+B%22) - Visualization: e.g. [a gource of ANTs development](http://vimeo.com/66781467) - [DiReCT](http://www.ncbi.nlm.nih.gov/pubmed/?term=%22DIRECT%22+AND+%22Avants%22+AND+DAS) cortical thickness [papers](http://www.ncbi.nlm.nih.gov/pubmed/?term=%22Cortical+Thickness%22+AND+%22Avants%22) - A [folder](https://sourceforge.net/projects/advants/files/Documentation/) of relevant docs: [segmentation](http://sourceforge.net/projects/advants/files/Documentation/atropos.pdf/download), [registration](http://sourceforge.net/projects/advants/files/Documentation/antstheory.pdf/download), [usage(old)](http://sourceforge.net/projects/advants/files/Documentation/ants.pdf/download), [for clinical apps](http://sourceforge.net/projects/advants/files/Documentation/ANTSMethodologySummary.docx/download) - ANTs redesigned for generality, automation, multi-core computation with ITKv4 - Dev'd ITKv4 with Kitware, GE, Natl. Lib of Medicine & Academia ANTs has won several unbiased & international competitions ---------------------------------------------------------- - ANTs finished in 1st rank in [Klein 2009 intl. brain mapping competition](http://www.ncbi.nlm.nih.gov/pubmed/19195496) - ANTs finished 1st overall in [EMPIRE10 intl. lung mapping competition](http://www.ncbi.nlm.nih.gov/pubmed/21632295) - ANTs is the standard registration for [MICCAI-2013](http://www.miccai2013.org/) segmentation competitions - Conducting ANTs-based R tutorial @ MICCAI-2013 - ITK-focused Frontiers in Neuroinformatics research topic [here](http://www.frontiersin.org/neuroinformatics/researchtopics/neuroinformatics_with_the_insi/1580) - Won the [BRATS 2013 challenge](http://martinos.org/qtim/miccai2013/) with [ANTsR](http://stnava.github.io/ANTsR/) - Won the best paper award at the [STACOM 2014 challenge](http://www.cardiacatlas.org/web/stacom2014/home) Learning about ANTs (examples, etc.) ---------------------------------------------------------- ### General * **antsRegistration** [bash example](https://github.com/stnava/ANTs/blob/master/Scripts/newAntsExample.sh) * **antsRegistration with mask** [example](https://github.com/ntustison/antsRegistrationWithMaskExample) * **ANTs and ITK** [paper](http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4009425/) * **Large deformation** [example](http://stnava.github.io/C/) * **Automobile** [example](http://stnava.github.io/cars/) * **Asymmetry** [example](http://stnava.github.io/asymmetry/) * **Point-set** [mapping](http://stnava.github.io/chicken/) which includes the PSE metric and affine and deformable registration with (labeled) pointsets or iterative closest point * **Feature matching** [example](http://stnava.github.io/featureMatching/) ... not up to date ... * **Global optimization** [example](http://stnava.github.io/butterfly/) * **Patch-based super resolution** [example](https://github.com/ntustison/NonLocalSuperResolutionExample) * **Image denoising** [example](https://github.com/ntustison/DenoiseImageExample) * **Visualization** [example](https://github.com/ntustison/antsVisualizationExamples) * **Morphing** [example](http://stnava.github.io/Morpheus/) * **Bibliography** [bibtex of ANTs-related papers](https://github.com/stnava/ANTsBibliography) * **ANTs** [google scholar page](http://scholar.google.com/citations?user=ox-mhOkAAAAJ&hl=en) ### Neuro * **Basic Brain Mapping** [example](http://stnava.github.io/BasicBrainMapping/) * **Template construction** [example](http://ntustison.github.io/TemplateBuildingExample/) * **Single subject template construction** [example](https://github.com/ntustison/SingleSubjectTemplateExample) * **Pre-built ANTs templates with spatial priors** [download](http://figshare.com/articles/ANTs_ANTsR_Brain_Templates/915436) * **Brain extraction** [example](https://github.com/ntustison/antsBrainExtractionExample) * **N4 bias correction <-> segmentation** [example](https://github.com/ntustison/antsAtroposN4Example) * **Cortical thickness** [example](https://github.com/ntustison/antsCorticalThicknessExample) * **"Cooking" tissue priors for templates** [example](https://github.com/ntustison/antsCookTemplatePriorsExample) (after you build your template) * **Multi-atlas joint label/intensity fusion examples** [example 1](https://github.com/ntustison/MalfLabelingExample) [example 2](https://github.com/qureai/Multi-Atlas-Segmentation) (thanks to @chsasank) * **The ANTs Cortical Thickness Pipeline** [example](https://github.com/ntustison/KapowskiChronicles/blob/master/paper2.pdf?raw=true) * **Chimpanzee cortical thickness** [example](https://github.com/stnava/WHopkinsNHP/) * **Brain tumor segmentation** [example](https://github.com/ntustison/BRATS2013/SimpleExample/) * **Eigenanatomy** for [multivariate neuroimage analysis](http://www.ncbi.nlm.nih.gov/pubmed/23269595) via [PCA](http://www.ncbi.nlm.nih.gov/pubmed/23286132) & [CCA](http://www.ncbi.nlm.nih.gov/pubmed/20083207) * **fMRI or Motion Correction** [example](http://stnava.github.io/fMRIANTs/) * **fMRI reproducibility** [example](http://stnava.github.io/RfMRI/) * **fMRI prediction** [example](http://stnava.github.io/Haxby2001/) ... WIP ... ### Lung * **CT lung registration** [example](https://github.com/ntustison/antsCtLungRegistrationExample) * **Lung mask registration** [example](https://github.com/ntustison/ProtonCtLungMaskRegistration) * **Lung and lobe estimation** [example](https://github.com/ntustison/LungAndLobeEstimationExample) * **Lung ventilation-based segmentation** [example](https://github.com/ntustison/LungVentilationSegmentationExample) ### Cardiac * **Cardiac** [example](http://stnava.github.io/LabelMyHeart) ### Misc. Presentations: e.g. [a Prezi about ANTs](http://prezi.com/mwrmcm-h9-w4/ants/?kw=view-mwrmcm-h9-w4&rc=ref-40024395) (WIP) Reproducible science as a teaching tool: e.g. [compilable ANTs tutorial](https://github.com/stnava/ANTS_MultiModality) (WIP) Other examples [slideshow](http://brianavants.wordpress.com) Landmark-based mapping for e.g. hippocampus [discussed here](https://sourceforge.net/p/advants/discussion/840261/thread/1cb7b165/?limit=50) Brief ANTs segmentation [video](http://vimeo.com/67814201) **Benchmarks** for expected memory and computation time: [results](https://github.com/gdevenyi/antsRegistration-benchmarking). These results are, of course, system and data dependent. References ---------------------------------------------------------- [Google Scholar](http://scholar.google.com/scholar?q=Advanced+Normalization+Tools+%22ANTs%22+-ant&hl=en&as_sdt=1%2C39&as_ylo=2008&as_yhi=) [Pubmed](http://www.ncbi.nlm.nih.gov/pubmed?term=%22Avants%20B%22%20OR%20%22Tustison%20N%22) Boilerplate ANTs ------------------ Here is some boilerplate regarding ants image processing: We will analyze multiple modality neuroimaging data with Advanced Normalization Tools (ANTs) version >= 2.1 [1] (http://stnava.github.io/ANTs/). ANTs has proven performance in lifespan analyses of brain morphology [1] and function [2] in both adult [1] and pediatric brain data [2,5,6] including infants [7]. ANTs employs both probabilistic tissue segmentation (via Atropos [3]) and machine learning methods based on expert labeled data (via joint label fusion [4]) in order to maximize reliability and consistency of multiple modality image segmentation. These methods allow detailed extraction of critical image-based biomarkers such as volumes (e.g. hippocampus and amygdala), cortical thickness and area and connectivity metrics derived from structural white matter [13] or functional connectivity [12]. Critically, all ANTs components are capable of leveraging multivariate image features as well as expert knowledge in order to learn the best segmentation strategy available for each individual image [3,4]. This flexibility in segmentation and the underlying high-performance normalization methods have been validated by winning several internationally recognized medical image processing challenges conducted within the premier conferences within the field and published in several accompanying articles [8][9][10][11]. References [1] http://www.ncbi.nlm.nih.gov/pubmed/24879923 [2] http://www.ncbi.nlm.nih.gov/pubmed/24817849 [3] http://www.ncbi.nlm.nih.gov/pubmed/21373993 [4] http://www.ncbi.nlm.nih.gov/pubmed/21237273 [5] http://www.ncbi.nlm.nih.gov/pubmed/22517961 [6] http://www.ncbi.nlm.nih.gov/pubmed/24033570 [7] http://www.ncbi.nlm.nih.gov/pubmed/24139564 [8] http://www.ncbi.nlm.nih.gov/pubmed/21632295 [9] http://www.ncbi.nlm.nih.gov/pubmed/19195496 [10] http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3837555/ [11] http://nmr.mgh.harvard.edu/~koen/MenzeTMI2014.pdf [12] http://www.ncbi.nlm.nih.gov/pubmed/23813017 [13] http://www.ncbi.nlm.nih.gov/pubmed/24830834 ANTs was supported by: R01-EB006266-01 and by K01-ES025432-01 ![ants chimp](http://i.imgur.com/4tPvy05.png) ants-2.2.0/README.txt000066400000000000000000000326701311104306400141570ustar00rootroot00000000000000See the *new* ants [website](http://stnava.github.io/ANTs/ "ANTs") [Installation/Compilation](http://brianavants.wordpress.com/2012/04/13/updated-ants-compile-instructions-april-12-2012/ "Build") [Old Homepage](http://www.picsl.upenn.edu/ANTS/) Experimental transition to github for development: Wed Jan 23 10:46:13 EST 2013 Release 1.9.x --- final svn release = 1781, now moved to git .... Introduction -- ANTS is a tool for computational neuroanatomy based on medical images. ANTS reads any image type that can be read by ITK (www.itk.org), that is, jpg, tiff, hdr, nii, nii.gz, mha/d and more image types as well. For the most part, ANTS will output float images which you can convert to other types with the ANTS ConvertImagePixelType tool. ImageMath has a bunch of basic utilities such as multiplication, inversion and many more advanced tools such as computation of the Lipschitz norm of a deformation field. ANTS programs may be called from the command line on almost any platform .... you can compile the code yourself or use the precompiled binaries for Windows (Vista), OSX (Darwin) or linux (32 bit or 64 bit). Download the binaries that are correct for you. If you are a naive user (not from a medical imaging background) then you might still find the tools here useful. Many of the operations available, for instance, in PhotoShop are available in ANTS and many more are available as well. For instance, ANTS tools may be useful in face mapping / morphing and also in generating animations from two different images, for instance, interpolating between frames in a movie. But, mainly, ANTS is useful for brain mapping, segmentation, measuring cortical thickness and in generating automated or semi-automated labeling of three-dimensional imagery (e.g. labeling hippocampus or cortical regions or lobes of the lung). Many prior-based segmentation possibilities are available in the Atropos tool, including three tissue segmentation, structure-specific segmentation and brain extracton. The ants.pdf file has more details and examples. New Stuff 1.9.x : Requires git-itk. must be compiled with USE_REVIEW on. official release of atropos segmentation tool. checked compilation on windows os. we now save vector nii.gz files and do not store component images. improves ImageMath tensor functions. many operations may be performed on vector images. some enhancements include: you can warp a vector field represented as a nii.gz via WarpImageMultiTransform MeasureMinMaxMean and MultiplyImages support vector images. the only functionality that we "lost" is the ability to use a bspline interpolator with WIMT. the BSplineInterpolateImageFunction doesnt support vector valued pixels. Atropos updated and validated. New Stuff 1.9.2 : New atropos interface + ROIStatistics in ImageMath New Stuff 1.9.1 : Atropos refactored , vtk dependencies allowed , additional tools for surface-based mapping (not much tested), augmented warping for vtk files Must compile ITK with USE_REVIEW_STATISTICS ON if you want Atropos functionality Should compile ITK with USE_REVIEW ON Should compile ITK with USE_OPTIMIZED_REGISTRATION ON Should have ITK v 3.20 or greater. New Stuff 1.9 : Atropos revisions + various utilities. New Stuff 1.8 : Sped up CC metric -- comparable to PR but faster. Fixed the MI metric -- fast and functional. WarpTimeSeries --- for deforming 4D or vector images. New Stuff 1.7 : Now using SymmetricSecondOrderPixelType -- fixes some DT bugs with Nifti I/O etc. This means our nii tensors have SYMMATRIX as intent (as with standard) Update in parameters for convergence and inversion -- aids performance. TensorToVector coloring --- also preliminary integration of vector field Speed up number one for the CC metric (number two coming later). Atropos ! new tool for segmentation. Nick's N4 bias correction tool. Updates to buildtemplateparallel that allow parallel use on multicore machines. Various utilities and a few improvements in usage. New Stuff 1.6 : Check DT tensor ordering in DTI Read/Write HistogramMatching in ImageMath ConvertImagePixelType utility Affine averaging in buildtemplateparallel Updated time-dependent diffeomorphic mapping (option --geodesic 1 / 2 ). Updated with a greedy exponential mapping diffeomorphic approach akin to DiffeomorphicDemons. Bug fix in checking ANTS convergence. Other miscellaneous including minor Apocrita changes and allowing spaces in command line interface. # directory guide: Documentation -- pdf / tex describing ANTS Examples -- the executable programs and test data in Examples/Data Scripts -- user-friendly scripts for template building and running studies Utilities --- basic utilities ImageRegistration -- base code for ImageRegistration Temporary -- where temporary code lives Tensor -- base code for diffusion tensor operations Use cmake (cmake.org) to set up compilation. To build ANTS, do the following: 1. get ANTS svn checkout https://advants.svn.sourceforge.net/svnroot/advants ANTS 2. get itk : cvs -d :pserver:anoncvs@www.itk.org:/cvsroot/Insight co Insight 3. compile itk and ANTS -- link ANTS to itk build directory ccmake ANTS/Examples/ 4. call ctest in the compile directory and verify that the tests pass 5. in general, to perform a mapping : # include the mask, if desired. mask is inclusive. ANTS 3 -m PR[tp22_s1.nii,template.nii.gz,1,4] -i 50x20x10 -o tp22map -t SyN[0.25] -x mask.nii.gz -r Gauss[3,0] # The ANTS executable reflects the variational optimization problem # which balances regularization of the transformation model's parameters # and the quality of matchins as driven by a similarity (or data) term # # explanation : -m PR -- the similarity metric => PR[fixed.nii,moving,nii,weight,metric-radius] # : -i 50x20 -- the number of iterations and number of resolution levels # : -o tp22map -- the output naming convention (can add an extension) # : -t SyN/Elast/Exp/Syn[time] --- transformation model # : -r Gauss/Bspline -- the regularization models # Gauss[gradient-regularize,deformation-regularize] # : -x mask -- an inclusive mask -- dictates what information to use in registration # -- defined in the fixed domain but works on both domains # : -m other metrics : PSE MSQ MI etc -- some are label-image (or point-set) metrics # and some are intensity metrics # # Call ANTS with no params to get detailed help # # warp the tp22 to template image WarpImageMultiTransform 3 tp22_s1.nii tp22totemplate.nii -R template.nii.gz -i tp22mapAffine.txt tp22mapInverseWarp.nii # warp the template image to tp22 -- note reversal of order from above WarpImageMultiTransform 3 template.nii.gz templatetotp22.nii -R tp22_s1.nii tp22mapWarp.nii tp22mapAffine.txt # or call ants.sh for a standard approach. # # use CreateJacobianDeterminantImage to get log-Jacobian (volumetric change) images # and programs StudentsTestOnImages or GLM to peform a statistical study # one might also use SurfaceCurvature to do a curvature study # or LaplacianThickness to do a thickness study # References: ANTs registration Principal references http://www.ncbi.nlm.nih.gov/pubmed/20851191 Avants BB, Tustison NJ, Song G, Cook PA, Klein A, Gee JC. A reproducible evaluation of ANTs similarity metric performance in brain image registration. Neuroimage. 2011 Feb 1;54(3):2033-44. doi: 10.1016/j.neuroimage.2010.09.025. http://link.springer.com/chapter/10.1007/978-3-642-31340-0_28 Brian B. Avants, Nicholas J. Tustison, Gang Song, Baohua Wu, Michael Stauffer, Matthew McCormick, Hans J. Johnson, James C. Gee. A Unified Image Registration Framework for ITK. Proceedings of the Fifth Workshop on Biomedical Image Registration 2012:266-275. Symmetric Normalization (SyN) http://www.ncbi.nlm.nih.gov/pubmed/17659998 Avants BB, Epstein CL, Grossman M, Gee JC. Symmetric diffeomorphic image registration with cross-correlation: evaluating automated labeling of elderly and neurodegenerative brain. Med Image Anal. 2008 Feb;12(1):26-41. B-spline-based http://www.ncbi.nlm.nih.gov/pubmed/19171516 Tustison NJ, Avants BB, Gee JC. Directly manipulated free-form deformation image registration. IEEE Trans Image Process. 2009 Mar;18(3):624-35. doi: 10.1109/TIP.2008.2010072. http://link.springer.com/chapter/10.1007%2F978-3-642-31340-0_4 Nicholas J. Tustison, Brian B. Avants: Diffeomorphic Directly Manipulated Free-Form Deformation Image Registration via Vector Field Flows. Proceedings of the Fifth Workshop on Biomedical Image Registration 2012:31-39. Point set registration Point-set expectation (PSE) http://www.ncbi.nlm.nih.gov/pubmed/19437413 Pluta J, Avants BB, Glynn S, Awate S, Gee JC, Detre JA. Appearance and incomplete label matching for diffeomorphic template based hippocampus segmentation. Hippocampus. 2009 Jun;19(6):565-71. doi: 10.1002/hipo.20619. Havrda-Charvat-Tsallis (JTB) http://www.ncbi.nlm.nih.gov/pubmed/20937578 Tustison NJ, Awate SP, Song G, Cook TS, Gee JC. Point set registration using Havrda-Charvat-Tsallis entropy measures. IEEE Trans Med Imaging. 2011 Feb;30(2):451-60. doi: 10.1109/TMI.2010.2086065. Template construction http://www.ncbi.nlm.nih.gov/pubmed/15501083 Avants B, Gee JC. Geodesic estimation for large deformation anatomical shape averaging and interpolation. Neuroimage. 2004;23 Suppl 1:S139-50. http://www.ncbi.nlm.nih.gov/pubmed/19818860 Avants BB, Yushkevich P, Pluta J, Minkoff D, Korczykowski M, Detre J, Gee JC. The optimal template effect in hippocampus studies of diseased populations. Neuroimage. 2010 Feb 1;49(3):2457-66. doi: 10.1016/j.neuroimage.2009.09.062. http://www.ncbi.nlm.nih.gov/pubmed/18995188 Avants B, Duda JT, Kim J, Zhang H, Pluta J, Gee JC, Whyte J. Multivariate analysis of structural and diffusion imaging in traumatic brain injury. Acad Radiol. 2008 Nov;15(11):1360-75. doi: 10.1016/j.acra.2008.07.007. Atropos (n-tissue multivariate segmentation) http://www.ncbi.nlm.nih.gov/pubmed/21373993 Avants BB, Tustison NJ, Wu J, Cook PA, Gee JC. An open source multivariate framework for n-tissue segmentation with evaluation on public data. Neuroinformatics. 2011 Dec;9(4):381-400. doi: 10.1007/s12021-011-9109-y. N4 bias correction http://www.ncbi.nlm.nih.gov/pubmed/20378467 Tustison NJ, Avants BB, Cook PA, Zheng Y, Egan A, Yushkevich PA, Gee JC. N4ITK: improved N3 bias correction. IEEE Trans Med Imaging. 2010 Jun;29(6):1310-20. doi: 10.1109/TMI.2010.2046908. DiReCT aka KellyKapowski/KellySlater http://www.ncbi.nlm.nih.gov/pubmed/19150502 Das SR, Avants BB, Grossman M, Gee JC. Registration based cortical thickness measurement. Neuroimage. 2009 Apr 15;45(3):867-79. doi: 10.1016/j.neuroimage.2008.12.016. SCCAN http://www.ncbi.nlm.nih.gov/pubmed/20083207 Avants BB, Cook PA, Ungar L, Gee JC, Grossman M. Dementia induces correlated reductions in white matter integrity and cortical thickness: a multivariate neuroimaging study with sparse canonical correlation analysis. Neuroimage. 2010 Apr 15;50(3):1004-16. doi: 10.1016/j.neuroimage.2010.01.041. SurfaceCurvature http://www.ncbi.nlm.nih.gov/pubmed/15344450 Avants B, Gee J. The shape operator for differential analysis of images. Inf Process Med Imaging. 2003 Jul;18:101-13. Topological well-composedness http://www.ncbi.nlm.nih.gov/pubmed/21118779 Tustison NJ, Avants BB, Siqueira M, Gee JC. Topological well-composedness and glamorous glue: a digital gluing algorithm for topologically constrained front propagation. IEEE Trans Image Process. 2011 Jun;20(6):1756-61. doi: 10.1109/TIP.2010.2095021. ANTs-related Studies http://www.ncbi.nlm.nih.gov/pubmed/15948659 Avants BB, Schoenemann PT, Gee JC. Lagrangian frame diffeomorphic image registration: Morphometric comparison of human and chimpanzee cortex. Med Image Anal. 2006 Jun;10(3):397-412. Epub 2005 Jun 3. http://www.ncbi.nlm.nih.gov/pubmed/19195496 Klein A, Andersson J, Ardekani BA, Ashburner J, Avants B, Chiang MC, Christensen GE, Collins DL, Gee J, Hellier P, Song JH, Jenkinson M, Lepage C, Rueckert D, Thompson P, Vercauteren T, Woods RP, Mann JJ, Parsey RV. Evaluation of 14 nonlinear deformation algorithms applied to human brain MRI registration. Neuroimage. 2009 Jul 1;46(3):786-802. doi: 10.1016/j.neuroimage.2008.12.037. http://www.ncbi.nlm.nih.gov/pubmed/20123029 Klein A, Ghosh SS, Avants B, Yeo BT, Fischl B, Ardekani B, Gee JC, Mann JJ, Parsey RV. Evaluation of volume-based and surface-based brain image registration methods. Neuroimage. 2010 May 15;51(1):214-20. doi: 10.1016/j.neuroimage.2010.01.091. http://www.ncbi.nlm.nih.gov/pubmed/23151955 Tustison NJ, Avants BB, Cook PA, Kim J, Whyte J, Gee JC, Stone JR. Logical circularity in voxel-based analysis: Normalization strategy may induce statistical bias. Hum Brain Mapp. 2012 Nov 14. doi: 10.1002/hbm.22211. http://www.ncbi.nlm.nih.gov/pubmed/17999940 Kim J, Avants B, Patel S, Whyte J, Coslett BH, Pluta J, Detre JA, Gee JC. Structural consequences of diffuse traumatic brain injury: a large deformation tensor-based morphometry study. Neuroimage. 2008 Feb 1;39(3):1014-26. Epub 2007 Oct 13. # gource visualization gource ./ -s 0.05 --stop-at-end --output-ppm-stream ants.ppm ffmpeg -y -f image2pipe -vcodec ppm -i ants.ppm -vcodec mpeg4 -preset slow -crf 2 -b:v 4M ./ants_gource.mp4 ants-2.2.0/Scripts/000077500000000000000000000000001311104306400141005ustar00rootroot00000000000000ants-2.2.0/Scripts/ANTSAverage2DAffine.sh000077500000000000000000000043131311104306400177770ustar00rootroot00000000000000#!/bin/bash NUMPARAM=$# if [ $NUMPARAM -lt 2 ] ; then echo " Usage " echo " $0 OUTNameAffine.txt *Affine.txt " echo " assumes close to idetity affine transforms " exit fi OUTNM=$1 shift 1 FLIST=$* NFILES=0 PARAM1=0 PARAM2=0 PARAM3=0 PARAM4=0 PARAM5=0 PARAM6=0 PARAM7=0 PARAM8=0 LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 2 ` for x in $LL ; do PARAM1=` awk -v a=$PARAM1 -v b=$x 'BEGIN{print (a + b)}' ` ; let NFILES=$NFILES+1 ; done PARAM1=` awk -v a=$PARAM1 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 3 ` for x in $LL ; do PARAM2=` awk -v a=$PARAM2 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM2=` awk -v a=$PARAM2 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 4 ` for x in $LL ; do PARAM3=` awk -v a=$PARAM3 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM3=` awk -v a=$PARAM3 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 5 ` for x in $LL ; do PARAM4=` awk -v a=$PARAM4 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM4=` awk -v a=$PARAM4 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 6 ` for x in $LL ; do PARAM5=` awk -v a=$PARAM5 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM5=` awk -v a=$PARAM5 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 7 ` for x in $LL ; do PARAM6=` awk -v a=$PARAM6 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM6=` awk -v a=$PARAM6 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` cat $FLIST | grep FixedParamet | cut -d ' ' -f 2 ` for x in $LL ; do PARAM7=` awk -v a=$PARAM7 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM7=` awk -v a=$PARAM7 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` cat $FLIST | grep FixedParamet | cut -d ' ' -f 3 ` for x in $LL ; do PARAM8=` awk -v a=$PARAM8 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM8=` awk -v a=$PARAM8 -v b=$NFILES 'BEGIN{print (a / b)}' ` echo "#Insight Transform File V1.0 " > $OUTNM echo "# Transform 0 " >> $OUTNM echo "Transform: MatrixOffsetTransformBase_double_2_2 " >> $OUTNM echo "Parameters: $PARAM1 $PARAM2 $PARAM3 $PARAM4 $PARAM5 $PARAM6 " >> $OUTNM echo "FixedParameters: $PARAM7 $PARAM8 " >> $OUTNM exit ants-2.2.0/Scripts/ANTSAverage3DAffine.sh000077500000000000000000000074731311104306400200120ustar00rootroot00000000000000#!/bin/bash NUMPARAM=$# if [ $NUMPARAM -lt 2 ] ; then echo " Usage " echo " $0 OUTNameAffine.txt *Affine.txt " echo " assumes close to idetity affine transforms " exit fi OUTNM=$1 shift 1 FLIST=$* NFILES=0 PARAM1=0 PARAM2=0 PARAM3=0 PARAM4=0 PARAM5=0 PARAM6=0 PARAM7=0 PARAM8=0 PARAM9=0 PARAM10=0 PARAM11=0 PARAM12=0 PARAM13=0 PARAM14=0 PARAM15=0 LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 2 ` for x in $LL ; do PARAM1=` awk -v a=$PARAM1 -v b=$x 'BEGIN{print (a + b)}' ` ; let NFILES=$NFILES+1 ; done PARAM1=` awk -v a=$PARAM1 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 3 ` for x in $LL ; do PARAM2=` awk -v a=$PARAM2 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM2=` awk -v a=$PARAM2 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 4 ` for x in $LL ; do PARAM3=` awk -v a=$PARAM3 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM3=` awk -v a=$PARAM3 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 5 ` for x in $LL ; do PARAM4=` awk -v a=$PARAM4 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM4=` awk -v a=$PARAM4 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 6 ` for x in $LL ; do PARAM5=` awk -v a=$PARAM5 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM5=` awk -v a=$PARAM5 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 7 ` for x in $LL ; do PARAM6=` awk -v a=$PARAM6 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM6=` awk -v a=$PARAM6 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 8 ` for x in $LL ; do PARAM7=` awk -v a=$PARAM7 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM7=` awk -v a=$PARAM7 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 9 ` for x in $LL ; do PARAM8=` awk -v a=$PARAM8 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM8=` awk -v a=$PARAM8 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 10 ` for x in $LL ; do PARAM9=` awk -v a=$PARAM9 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM9=` awk -v a=$PARAM9 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 11 ` for x in $LL ; do PARAM10=` awk -v a=$PARAM10 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM10=` awk -v a=$PARAM10 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 12 ` for x in $LL ; do PARAM11=` awk -v a=$PARAM11 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM11=` awk -v a=$PARAM11 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 13 ` for x in $LL ; do PARAM12=` awk -v a=$PARAM12 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM12=` awk -v a=$PARAM12 -v b=$NFILES 'BEGIN{print (a / b)}' ` # translation params below LL=` cat $FLIST | grep FixedParamet | cut -d ' ' -f 2 ` for x in $LL ; do PARAM13=` awk -v a=$PARAM13 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM13=` awk -v a=$PARAM13 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` cat $FLIST | grep FixedParamet | cut -d ' ' -f 3 ` for x in $LL ; do PARAM14=` awk -v a=$PARAM14 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM14=` awk -v a=$PARAM14 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` cat $FLIST | grep FixedParamet | cut -d ' ' -f 4 ` for x in $LL ; do PARAM15=` awk -v a=$PARAM15 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM15=` awk -v a=$PARAM15 -v b=$NFILES 'BEGIN{print (a / b)}' ` echo "#Insight Transform File V1.0 " > $OUTNM echo "# Transform 0 " >> $OUTNM echo "Transform: MatrixOffsetTransformBase_double_3_3 " >> $OUTNM echo "Parameters: $PARAM1 $PARAM2 $PARAM3 $PARAM4 $PARAM5 $PARAM6 $PARAM7 $PARAM8 $PARAM9 $PARAM10 $PARAM11 $PARAM12 " >> $OUTNM echo "FixedParameters: $PARAM13 $PARAM14 $PARAM15 " >> $OUTNM exit ants-2.2.0/Scripts/ANTSpexec.sh000077500000000000000000000104111311104306400162260ustar00rootroot00000000000000#!/bin/bash VERSION="0.0.1" function Usage { cat <&2 fi while getopts j:rh OPT; do # "j:" waits for an argument "h" doesnt case $OPT in h) Help ;; j) MAX_NPROC=$OPTARG ;; r) REPLACE_CMD=1 ;; \?) Usage >&2 ;; esac done # Main program echo Using max $MAX_NPROC parallel threads if [ $MAX_NPROC -eq 1 ] ; then echo " Dont use pexec to run 1 process at a time. " echo " In this case, just run in series. " exit fi shift `expr $OPTIND - 1` # shift input args, ignore processed args COMMAND=$1 shift # keep list of started processes echo "#!/bin/bash" >> ${here}/killme.sh chmod +x ${here}/killme.sh for INS in $* # for the rest of the arguments do # DEFINE COMMAND if [ $REPLACE_CMD -eq 1 ]; then CMD=${COMMAND//"*"/$INS} else CMD="$COMMAND $INS" #append args fi echo "Running $CMD" eval "$CMD &" # DEFINE COMMAND END PID=$! echo "kill $PID" >> ${here}/killme.sh queue $PID osmac=0 osmac="` uname -a | grep Darwin `" oslin=0 oslin="`uname -a | grep Linux`" if [ ${#osmac} -ne 0 ] then while [ $NUM -ge $MAX_NPROC ]; do checkqueuemac sleep 0.5 done elif [ ${#oslin} -ne 0 ] then while [ $NUM -ge $MAX_NPROC ]; do checkqueuelinux sleep 0.5 done fi done wait # wait for all processes to finish before exit rm ${here}/killme.sh exit 0 ants-2.2.0/Scripts/ants.sh000077500000000000000000000124461311104306400154130ustar00rootroot00000000000000#!/bin/bash NUMPARAMS=$# MAXITERATIONS=30x90x20 if [ ${#ANTSPATH} -le 3 ] ; then echo we guess at your ants path export ANTSPATH=${ANTSPATH:="/ipldev/scratch/aghayoor/ANTS/release/bin"} # EDIT THIS fi if [ ! -s ${ANTSPATH}/ANTS ] ; then echo we cant find the ANTS program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi if [ $NUMPARAMS -lt 3 ] then echo " USAGE :: " echo " sh ants.sh ImageDimension fixed.ext moving.ext OPTIONAL-OUTPREFIX OPTIONAL-max-iterations OPTIONAL-Labels-In-Fixed-Image-Space-To-Deform-To-Moving-Image Option-DoANTSQC " echo " be sure to set ANTSPATH environment variable " echo " Max-Iterations in form : JxKxL where " echo " J = max iterations at coarsest resolution (here, reduce by power of 2^2) " echo " K = middle resolution iterations ( here, reduce by power of 2 ) " echo " L = fine resolution iterations ( here, full resolution ) -- this level takes much more time per iteration " echo " an extra Ix before JxKxL would add another level " echo " Default ierations is $MAXITERATIONS -- you can often get away with fewer for many apps " echo " Other parameters are updates of the defaults used in the A. Klein evaluation paper in Neuroimage, 2009 " exit fi #ANTSPATH=YOURANTSPATH if [ ${#ANTSPATH} -le 0 ] then echo " Please set ANTSPATH=LocationOfYourAntsBinaries " echo " Either set this in your profile or directly, here in the script. " echo " For example : " echo " ANTSPATH=/home/yourname/bin/ants/ " exit else echo " ANTSPATH is $ANTSPATH " fi #initialization, here, is unbiased DIM=$1 if [ ${#DIM} -gt 1 ] then echo " Problem with specified ImageDimension => User Specified Value = $DIM " exit fi FIXED=$2 if [ ${#FIXED} -lt 1 -o ! -f $FIXED ] then echo " Problem with specified Fixed Image => User Specified Value = $FIXED " exit fi MOVING=$3 if [ ${#MOVING} -lt 1 -o ! -f $MOVING ] then echo " Problem with specified Moving Image => User Specified Value = $MOVING " exit fi OUTPUTNAME=` echo $MOVING | cut -d '.' -f 1 ` if [ $NUMPARAMS -gt 4 ] then MAXITERATIONS=$5 fi LABELIMAGE=0 if [ $NUMPARAMS -gt 5 ] then LABELIMAGE=$6 fi DoANTSQC=0 if [ $NUMPARAMS -gt 6 ] then DoANTSQC=$7 fi if [ $NUMPARAMS -gt 3 ] then OUTPUTNAME=${4} fi # Mapping Parameters TRANSFORMATION=SyN[0.25] ITERATLEVEL=(`echo $MAXITERATIONS | tr 'x' ' '`) NUMLEVELS=${#ITERATLEVEL[@]} echo $NUMLEVELS REGULARIZATION=Gauss[3,0] METRIC=CC[ METRICPARAMS=1,4] # echo " $METRICPARAMS & $METRIC " # exit echo " ANTSPATH is $ANTSPATH " echo " Mapping Parameters :: " echo " Transformation is: $TRANSFORMATION " echo " MaxIterations : $MAXITERATIONS " echo " Number Of MultiResolution Levels $NUMLEVELS " echo " Regularization : $REGULARIZATION " echo " Metric : ${METRIC} " echo " OutputName : $OUTPUTNAME " echo " " echo " if the files and parameters are all ok then uncomment the exit call below this line " echo " " #exit if [[ ! -s ${OUTPUTNAME}repaired.nii.gz ]] ; then ${ANTSPATH}/N4BiasFieldCorrection -d $DIM -i $MOVING -o ${OUTPUTNAME}repaired.nii.gz -s 2 -c [50x50x50x50,0.000001] -b [200] # ${ANTSPATH}/N3BiasFieldCorrection $DIM $MOVING ${OUTPUTNAME}repaired.nii.gz 4 fi exe=" ${ANTSPATH}/ANTS $DIM -m ${METRIC}${FIXED},${OUTPUTNAME}repaired.nii.gz,${METRICPARAMS} -t $TRANSFORMATION -r $REGULARIZATION -o ${OUTPUTNAME} -i $MAXITERATIONS --use-Histogram-Matching --number-of-affine-iterations 10000x10000x10000x10000x10000 --MI-option 32x16000 " echo " $exe " $exe #below, some affine options #--MI-option 16x8000 #-a InitAffine.txt --continue-affine 0 ${ANTSPATH}/WarpImageMultiTransform $DIM ${OUTPUTNAME}repaired.nii.gz ${OUTPUTNAME}deformed.nii.gz ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt -R ${FIXED} if [ ${#LABELIMAGE} -gt 3 ] then ${ANTSPATH}/WarpImageMultiTransform $DIM $LABELIMAGE ${OUTPUTNAME}labeled.nii.gz -i ${OUTPUTNAME}Affine.txt ${OUTPUTNAME}InverseWarp.nii.gz -R ${MOVING} --use-NN fi exit if [ $DoANTSQC -eq 1 ] ; then # measure image similarity for SIM in 0 1 2 ; do ${ANTSPATH}/MeasureImageSimilarity $DIM $SIM $FIXED ${OUTPUTNAME}deformed.nii.gz done # measure dice overlap and mds ${ANTSPATH}/ThresholdImage $DIM $FIXED ${OUTPUTNAME}fixthresh.nii.gz Otsu 4 ${ANTSPATH}/ThresholdImage $DIM $MOVING ${OUTPUTNAME}movthresh.nii.gz Otsu 4 ${ANTSPATH}/WarpImageMultiTransform $DIM ${OUTPUTNAME}movthresh.nii.gz ${OUTPUTNAME}defthresh.nii.gz ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt -R ${FIXED} --use-NN ${ANTSPATH}/ImageMath $DIM ${OUTPUTNAME}dicestats.txt DiceAndMinDistSum ${OUTPUTNAME}fixthresh.nii.gz ${OUTPUTNAME}movthresh.nii.gz ${OUTPUTNAME}mindistsum.nii.gz # labelstats for jacobian wrt segmenation # below, to compose # ${ANTSPATH}ComposeMultiTransform $DIM ${OUTPUTNAME}CompWarp.nii.gz -R $FIXED ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt # ${ANTSPATH}CreateJacobianDeterminantImage $DIM ${OUTPUTNAME}CompWarp.nii.gz ${OUTPUTNAME}jacobian.nii.gz 0 # ${ANTSPATH}ImageMath $DIM ${OUTPUTNAME}movlabstat.txt LabelStats ${OUTPUTNAME}movthresh.nii.gz ${OUTPUTNAME}movthresh.nii.gz # ${ANTSPATH}ImageMath $DIM ${OUTPUTNAME}jaclabstat.txt LabelStats ${OUTPUTNAME}defthresh.nii.gz ${OUTPUTNAME}jacobian.nii.gz # we compare the output of these last two lines: # the Volume of the movlabstat computation vs. the mass of the jaclabstat fi ants-2.2.0/Scripts/antsASLProcessing.R000077500000000000000000000265761311104306400176100ustar00rootroot00000000000000#!/usr/bin/env Rscript library(ANTsR) library(tools) if(!usePkg('optparse') | !usePkg('ANTsR')){ stop("optparse and ANTsR packages required.") } optlist <- list( make_option(c('-s', '--pCASL'), default='', help=' raw pCASL image'), make_option(c('-o', '--outputpre'), default='CBF_', help='output prefix (defaults to %default)'), make_option(c('-a', '--antsCorticalThicknessPrefix'), default='', help='prefix of antsCorticalThickness output'), make_option(c('-l', '--labelSet'), default='', help='label set in template space to warp to ASL'), make_option(c('-t', '--template'), default='', help='Template to warp output to'), make_option(c('-c', '--paramFile'), default='', help='parameter file containing ASL acquisition parameters'), make_option(c('-x', '--smoothingFWHM'), default=0, help='Full width half max for smoothing'), make_option(c('-m', '--method'), default='regression', help=paste(' method for perfusion calculation. \n\t\tOne of:', '"regression", "subtraction", "bayesian",', '"RobustRegression", "BayesianRegression", "LocalBayesianRegression."')), make_option(c('-d', '--denoising'), default='CompCorMotion', help=paste('denoising method.', 'Options are: "CompCor", "Motion", "Detrending",', '\n\t\t"Cross-Validation", "OutlierRejection".', 'Multiple options can be specified', '(e.g., "CompCorMotion" is legal). Default is %default.')), make_option(c('-g', '--debug'), default=0, help=paste('Save debugging information, including motion', 'correction and nuisance variables')), make_option(c('-b', '--bloodT1'), default=0.67, help='blood T1 value (defaults to %default s^-1)'), make_option(c('-r', '--robustness'), default=0.95, help='robustness parameter (defaults to %default).'), make_option(c('-n', '--bootstrapNumber'), default=20, help=' number of bootstrap samples (defaults to %default)'), make_option(c('-e', '--bootstrapPercent'), default=0.70, help='percent to sample per bootstrap run (defaults to %default)'), make_option(c('-k', '--keepTmp'), default=0, help=paste('keep tmp files, including warps', '(defaults to %default--takes lots of space to save)')), make_option(c('-f', '--bootstrapReplace'), default=0, help=paste('bootstrap with replacement? takes arguments', '0 or 1; defaults to 0.')), make_option(c('-v', '--verbose'), default=0, help='verbose output.')) usage <- OptionParser(option_list=optlist, usage='Usage: %prog [otlcxmdgbrnekfv]') opt <- parse_args(usage) ## debug #opt <- data.frame( # pCASL=paste('/data/jag/BD2K01/ASL_pipeline/data/AddictionCenter/ABART/imgs/', # '../processed/ABART_Bac_106/ASL/ABART_Bac_106_pCASL.nii.gz', sep=''), # outputpre=paste('/data/jag/BD2K01/ASL_pipeline/data/AddictionCenter/ABART/imgs', # '/../processed/ABART_Bac_106/ASL/ABART_Bac_106_', sep=''), # antsCorticalThicknessPrefix=paste('/data/jag/BD2K01/ASL_pipeline/', # 'data/AddictionCenter/ABART/imgs/../processed/ABART_Bac_106', # '/ASL/../Anatomy/ABART_Bac_106_', sep=''), # labelSet=paste('/data/jag/BD2K01/ASL_pipeline/templates/', # 'HarvardOxford/ABART_rois.nii.gz', sep=''), # template=paste('/data/jag/BD2K01/ASL_pipeline/templates/', # 'HarvardOxford/MNI152_T1_2mm.nii.gz', sep='')) # pCASL='data/101_pcasl.nii.gz', # out='test') if(!file.exists(as.character(opt$pCASL))) { stop(paste('pCASL image', opt$pCASL, 'does not exist.')) } if(opt$verbose) { cat('Running antsASLProcessing.R with the following options:\n') for(option in names(opt)){ cat(paste(option, ': ', opt[option], '\n', sep='')) } } if(length(grep(.Platform$file.sep, opt$outputpre)) > 0) { outdir <- dirname(opt$outputpre) if(!file.exists(outdir)) dir.create(outdir) } pcasl <- tryCatch({ antsImageRead(as.character(opt$pCASL), 4) }, error = function(e) { stop(paste('pCASL image', as.character(opt$pCASL), 'does not exist.')) }) if(length(opt$paramFile) > 0){ if(file.exists(as.character(opt$paramFile))) { config <- read.csv(opt$paramFile) } else { config <- data.frame(tagFirst=T, sequence='pcasl') } } if (opt$smoothingFWHM > 0) { mysmoother <- c(rep(opt$smoothingFWHM, 3), 0) pcasl <- smoothImage(pcasl, mysmoother, FWHM=TRUE) } avg <- getAverageOfTimeSeries(pcasl) avg <- n3BiasFieldCorrection(avg, 2) avg <- n3BiasFieldCorrection(avg, 2) mask <- getMask(avg, mean(avg), Inf, 2) avg[mask==0] <- 0 moco <- antsrMotionCalculation(pcasl, fixed=avg, mask=mask) tag.first <- config$tagFirst ts <- timeseries2matrix(moco$moco_img, moco$moco_mask) if (!tag.first) { tc <- (rep(c(1, 0), dim(ts)[1])[1:dim(ts)[1]] - 0.5) # control minus tag } else { tc <- (rep(c(0, 1), dim(ts)[1])[1:dim(ts)[1]] - 0.5) # tag minus control } nuisance <- getASLNoisePredictors(ts, tc, polydegree='loess') noise.all <- cbind(moco$moco_params, moco$fd$MeanDisplacement, nuisance) noise.combined <- as.matrix(combineNuisancePredictors(ts, tc, noise.all)) onlypairs <- FALSE if (opt$method == 'subtract') { onlypairs <- TRUE } censored <- aslCensoring(pcasl, mask, nuis=noise.combined, method='robust', reject.pairs=onlypairs) if (length(censored$which.outliers) > 0) { tc <- tc[-censored$which.outliers] noise.censored <- noise.combined[-censored$which.outliers, ] } else { noise.censored <- noise.combined } if (opt$debug) { mean.ts <- apply(ts, 1, mean) dat.debug <- cbind(data.frame(MeanTimeSeries=mean.ts), noise.all) write.csv(dat.debug, file=paste(opt$outputpre, 'TimeSeriesData.csv', sep=''), row.names=as.character(1:nrow(ts))) write.csv(data.frame(Outliers=censored$which.outliers), file=paste(opt$outputpre, 'OutlierTimepoints.csv', sep='')) } if (opt$method == 'regression') { perf <- aslAveraging(censored$asl.inlier, mask=moco$moco_mask, tc=tc, nuisance=noise.censored, method='regression') } else if (opt$method == 'bayesian') { if (length(opt$antsCorticalThicknessPrefix) == 0) { stop("For Bayesian regression, segmentations are required.") } act <- as.character(opt$antsCorticalThicknessPrefix) braint1 <- tryCatch({ antsImageRead(paste(act, "ExtractedBrain0N4.nii.gz", sep="")) }, error = function(e) { print(paste('T1 brain image', paste(act, "ExtractedBrain0N4.nii.gz", sep=""), 'does not exist.')) }) segmentation <- tryCatch({ antsImageRead(paste(act, "BrainSegmentation.nii.gz", sep="")) }, error = function(e) { stop(paste('Segmentation image', paste(act, "BrainSegmentation.nii.gz", sep=""), 'does not exist.')) }) postnames <- list.files(path=dirname(act), glob2rx("*BrainSegmentationPosteriors*.nii.gz"), full.names=TRUE) tissuelist <- tryCatch({ imageFileNames2ImageList(postnames) }, error = function(e) { stop(paste("Probability images", postnames, "cannot be loaded.")) }) reg.t12asl <- antsRegistration(fixed=avg, moving=braint1, typeofTransform="SyNBold", outprefix=as.character(opt$outputpre)) seg.asl <- antsApplyTransforms(avg, segmentation, reg.t12asl$fwdtransforms, "MultiLabel") for (ii in 1:length(tissuelist)) { tissuelist[[ii]] <- antsApplyTransforms(avg, tissuelist[[ii]], reg.t12asl$fwdtransforms, "Linear") } perf <- aslAveraging(censored$asl.inlier, mask=moco$moco_mask, tc=tc, nuisance=noise.censored, method='bayesian', segmentation=seg.asl, tissuelist=tissuelist) } else if(opt$method == 'subtract'){ perf <- aslAveraging(censored$asl.inlier, mask=moco$moco_mask, tc=tc, method='cubicSubtract') } mvals2 <- apply(ts[tc == 0.5, ], 2, mean) mvals1 <- apply(ts[tc == -0.5, ], 2, mean) # mean control should exceed mean tag if (mean(mvals2) > mean(mvals1)) { m0vals<-mvals2 m1vals<-mvals1 } else { m0vals<-mvals1 m1vals<-mvals2 } m0 <- antsImageClone(moco$moco_mask) m0[moco$moco_mask == 0] <- 0 m0[moco$moco_mask == 1] <- m0vals m0 <- n3BiasFieldCorrection(m0,4) m0 <- n3BiasFieldCorrection(m0,2) if (length(opt$config > 0)) { tryCatch({ config <- read.csv(opt$config, row.names=1) }, error = function(e){ print(paste("Configuration file", opt$config, "does not exist.")) }) parameters <- c(list(m0=antsImageClone(m0)), config) } else { parameters = list(sequence="pcasl", m0=antsImageClone(m0)) } if (opt$debug) { antsImageWrite(perf, paste(opt$outputpre, 'Perfusion.nii.gz', sep='')) antsImageWrite(m0, paste(opt$outputpre, 'M0.nii.gz', sep='')) } cbf <- quantifyCBF(perf, mask=moco$moco_mask, parameters=parameters) antsImageWrite(cbf$meancbf, paste(opt$outputpre, "CBF.nii.gz", sep="")) if (nchar(opt$antsCorticalThicknessPrefix) > 0){ act <- as.character(opt$antsCorticalThicknessPrefix) braint1 <- tryCatch({ antsImageRead(paste(act, "ExtractedBrain0N4.nii.gz", sep="")) }, error = function(e) { print(paste('T1 brain image', paste(act, "ExtractedBrain0N4.nii.gz", sep=""), 'does not exist.')) }) seg <- tryCatch({ antsImageRead(paste(act, "BrainSegmentation.nii.gz", sep="")) }, error = function(e) { print(paste('Segmentation image', paste(act, "BrainSegmentation.nii.gz", sep=""), 'does not exist.')) }) reg.t12asl <- antsRegistration(fixed=avg, moving=braint1, typeofTransform="SyNBold" ) seg.asl <- antsApplyTransforms(avg, seg, reg.t12asl$fwdtransforms, "MultiLabel") antsImageWrite(seg.asl, paste(opt$outputpre, "SegmentationWarpedToASL.nii.gz", sep='')) segstats <- labelStats(cbf$meancbf, seg.asl) write.csv(segstats, paste(opt$outputpre, 'TissueStats.csv', sep=''), row.names=FALSE) tx.template2t1 <- c(paste(act, "TemplateToSubject0Warp.nii.gz", sep=""), paste(act, "TemplateToSubject1GenericAffine.mat", sep="")) tx.t12template <- c(paste(act, "SubjectToTemplate1Warp.nii.gz", sep=""), paste(act, "SubjectToTemplate0GenericAffine.mat", sep="")) tx.asl2template <- c(reg.t12asl$invtransforms, tx.t12template) if (length(opt$template) > 0) { template <- tryCatch({ antsImageRead(as.character(opt$template)) }, error = function(e) { print(paste("Template image", template, "does not exist.")) }) asl.warped2template <- antsApplyTransforms(template, cbf$meancbf, tx.asl2template, whichtoinvert=c(F, F, F, F)) antsImageWrite(asl.warped2template, paste(opt$outputpre, "CBFWarpedToTemplate.nii.gz", sep='')) } tx.template2asl <- c(tx.template2t1, reg.t12asl$fwdtransforms) if (nchar(as.character(opt$labelSet)) > 0) { label <- tryCatch({ antsImageRead(as.character(opt$labelSet)) }, error = function(e) { print(paste("Label image", opt$labelSet, "does not exist.")) }) label.asl <- antsApplyTransforms(avg, label, tx.template2asl, "MultiLabel") antsImageWrite(label.asl, paste(opt$outputpre, 'LabelWarpedToASL.nii.gz', sep='')) labelstats.cbf <- labelStats(cbf$meancbf, label.asl) write.csv(labelstats.cbf, paste(opt$outputpre, 'LabelStats.csv', sep=''), row.names=FALSE) } } ants-2.2.0/Scripts/antsASLProcessing.sh000077500000000000000000000244211311104306400200040ustar00rootroot00000000000000#!/bin/bash if [[ ! -s ${ANTSPATH}/antsRegistration ]] then echo "Cannot find antsRegistration. Please \(re\)define \$ANTSPATH in your environment." fi if [[ ! -s ${ANTSPATH}/antsApplyTransforms ]] then echo "Cannot find antsApplyTransforms. Please \(re\)define \$ANTSPATH in your environment." fi if [[ ! -s ${ANTSPATH}/antsIntermodalityIntrasubject.sh ]] then echo "Cannot find antsIntermodalityIntrasubject.sh script. Please \(re\)define \$ANTSPATH in your environemnt." fi function Usage { cat <>>>>>>>>>>>>>>>>>>>" echo $cmd $cmd echo "END <<<<<<<<<<<<<<<<<<<<" echo echo } KEEP_TMP_FILES=false USE_INVERSE_WARPS=false if [[ $# -lt 3 ]] then Usage >&2 exit 1 else while getopts "a:s:e:p:t:o:x:l:b:r:g:n:c:f:k:i:h" OPT do case $OPT in a) #anatomical t1 image ANATOMICAL_IMAGE=$OPTARG ;; s) # raw pCASL image PCASL=$OPTARG ;; e) # brain template TEMPLATE=$OPTARG ;; p) # segmentation probabilities SEGMENTATION_PROB=$OPTARG ;; g) # hard seg SEGMENTATION=$OPTARG ;; t) # transform prefix TRANSFORM_PREFIX=$OPTARG ;; o) # output prefix OUTNAME=$OPTARG ;; x) # mask BRAINMASK=$OPTARG ;; l) # labels LABELS=$OPTARG ;; b) # blood t1 BLOODT1=$OPTARG ;; r) # robustness ROBUST=$OPTARG ;; n) # number of bootstrap runs NBOOTSTRAP=$OPTARG ;; c) # pct to sample per bootstrap PCTBOOTSTRAP=$OPTARG ;; f) # sample with replacement? SAMPLE_WITH_REPLACEMENT=$OPTARG ;; k) # keep tmp files KEEP_TMP_FILES=true ;; i) # use inverse warps USE_INVERSE_WARPS=true ;; h) # help Usage >&2 exit 0 ;; *) echo "ERROR: unrecognized option -$OPT $OPTARG" exit 1 ;; esac done fi if [[ -z $BLOODT1 ]] then BLOODT1=0.67 fi if [[ -z $ROBUST ]] then ROBUST=0.95 fi if [[ -z $NBOOTSTRAP ]] then NBOOTSTRAP=20 fi if [[ -z $PCTBOOTSTRAP ]] then PCTBOOTSTRAP=0.70 fi if [[ -z $SAMPLE_WITH_REPLACEMENT ]] then SAMPLE_WITH_REPLACEMENT=false fi echoParameters >&2 # parse prior syntax FORMAT=${SEGMENTATION_PROB} PREFORMAT=${FORMAT%%\%*} POSTFORMAT=${FORMAT##*d} FORMAT=${FORMAT#*\%} FORMAT=${FORMAT%%d*} REPCHARACTER='' TOTAL_LENGTH=0 if [ ${#FORMAT} -eq 2 ] then REPCHARACTER=${FORMAT:0:1} TOTAL_LENGTH=${FORMAT:1:1} fi MAXNUMBER=1000 PRIOR_IMAGE_FILENAMES=() for (( i = 1; i < $MAXNUMBER; i++ )) do NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#i} )) ROOT=''; for(( j=0; j < $NUMBER_OF_REPS; j++ )) do ROOT=${ROOT}${REPCHARACTER} done FILENAME=${PREFORMAT}${ROOT}${i}${POSTFORMAT} if [[ -f $FILENAME ]]; then PRIOR_IMAGE_FILENAMES=( ${PRIOR_IMAGE_FILENAMES[@]} $FILENAME ) else break 1 fi done echo "Warning: antsASLProcessing.sh is being deprecated. Use antsASLProcessing.R instead." # check for existence of all images if [[ ! -f ${ANATOMICAL_IMAGE} ]] then echo "ERROR: Anatomical image ${ANATOMICAL_IMAGE} does not exist." exit 1 fi if [[ ! -f $BRAINMASK ]] then echo "ERROR: Brain mask $BRAINMASK does not exist." exit 1 fi if [[ ! -f $PCASL ]] then echo "ERROR: pCASL image $PCASL does not exist." exit 1 fi if [[ ! -f $TEMPLATE ]] then echo "ERROR: template image $TEMPLATE does not exist." exit 1 fi if [[ ! -f $LABELS ]] then echo "ERROR: Template label image $LABELS does not exist." fi if [[ ! -f $SEGMENTATION ]] then echo "ERROR: Segmentation image $SEGMENTATION does not exist." exit 1 fi if [[ ! -f ${TRANSFORM_PREFIX}SubjectToTemplate1Warp.nii.gz ]] then echo "ERROR: Warp ${TRANSFORM_PREFIX}SubjectToTemplate1Warp.nii.gz does not exist." exit 1 fi if [[ ${#PRIOR_IMAGE_FILENAMES[@]} -lt 3 ]] then echo "ERROR: Fewer than 3 prior images specified." echo " Check that you defined prior file names correctly." fi time_start=`date +%s` # Do processing. if [[ ! -d `dirname $OUTNAME` ]] then mkdir -p `dirname $OUTNAME` fi if [[ ! -s ${OUTNAME}/AveragePCASL.nii.gz ]] ; then logCmd ${ANTSPATH}/antsMotionCorr -d 3 -a $PCASL -o ${OUTNAME}AveragePCASL.nii.gz fi logCmd ${ANTSPATH}/ThresholdImage 3 ${OUTNAME}AveragePCASL.nii.gz ${OUTNAME}tmp.nii.gz 600 999999 logCmd ${ANTSPATH}/ImageMath 3 ${OUTNAME}tmp.nii.gz ME ${OUTNAME}tmp.nii.gz 2 logCmd ${ANTSPATH}/ImageMath 3 ${OUTNAME}tmp.nii.gz GetLargestComponent ${OUTNAME}tmp.nii.gz logCmd ${ANTSPATH}/ImageMath 3 ${OUTNAME}tmp.nii.gz MD ${OUTNAME}tmp.nii.gz 3 logCmd ${ANTSPATH}/ImageMath 3 ${OUTNAME}pCASLBrain.nii.gz m ${OUTNAME}AveragePCASL.nii.gz ${OUTNAME}tmp.nii.gz INTERSUBJECT_PARAMS=" -d 3 -i ${OUTNAME}pCASLBrain.nii.gz -r $ANATOMICAL_IMAGE -x $BRAINMASK -w ${TRANSFORM_PREFIX}SubjectToTemplate -t 2 -o $OUTNAME " if [[ -n $LABELS ]] then INTERSUBJECT_PARAMS=" ${INTERSUBJECT_PARAMS} -l $LABELS " fi logCmd ${ANTSPATH}/antsIntermodalityIntrasubject.sh $INTERSUBJECT_PARAMS logCmd ${ANTSPATH}/N4BiasFieldCorrection -d 3 -i ${OUTNAME}AveragePCASL.nii.gz -o ${OUTNAME}AveragePCASL.nii.gz -r 1 -s 4 logCmd ${ANTSPATH}/N4BiasFieldCorrection -d 3 -i ${OUTNAME}AveragePCASL.nii.gz -o ${OUTNAME}AveragePCASL.nii.gz -r 1 -s 2 logCmd ${ANTSPATH}/N4BiasFieldCorrection -d 3 -i ${OUTNAME}AveragePCASL.nii.gz -o ${OUTNAME}AveragePCASL.nii.gz -r 1 -s 1 logCmd ${ANTSPATH}/ThresholdImage 3 ${OUTNAME}AveragePCASL.nii.gz ${OUTNAME}OtsuMask.nii.gz Otsu 4 logCmd ${ANTSPATH}/ThresholdImage 3 ${OUTNAME}OtsuMask.nii.gz ${OUTNAME}OtsuMask.nii.gz 2 4 logCmd ${ANTSPATH}/ImageMath 3 ${OUTNAME}OtsuMask.nii.gz ME ${OUTNAME}OtsuMask.nii.gz 1 logCmd ${ANTSPATH}/ImageMath 3 ${OUTNAME}OtsuMask.nii.gz MD ${OUTNAME}OtsuMask.nii.gz 1 logCmd ${ANTSPATH}/ThresholdImage 3 ${OUTNAME}brainmask.nii.gz ${OUTNAME}BrainThresh.nii.gz 1 999 logCmd ${ANTSPATH}/MultiplyImages 3 ${OUTNAME}OtsuMask.nii.gz ${OUTNAME}BrainThresh.nii.gz ${OUTNAME}OtsuMask.nii.gz if [ ! -f ${OUTNAME}_kcbf.nii.gz ]; then logCmd ${ANTSPATH}/antsNetworkAnalysis.R \ -o $OUTNAME \ --freq 0.01x0.1 \ --mask ${OUTNAME}OtsuMask.nii.gz \ --labels ${OUTNAME}labels.nii.gz \ --fmri $PCASL \ --modality ASLCBF \ --bloodt1 $BLOODT1 \ --robust $ROBUST \ --nboot $NBOOTSTRAP \ --pctboot $PCTBOOTSTRAP \ --replace $SAMPLE_WITH_REPLACEMENT fi logCmd ${ANTSPATH}/antsApplyTransforms -d 3 \ -i ${OUTNAME}_kcbf.nii.gz \ -r $TEMPLATE \ -o ${OUTNAME}MeanCBFWarpedToTemplate.nii.gz \ -n Linear \ -t ${TRANSFORM_PREFIX}SubjectToTemplate0GenericAffine.mat \ -t ${TRANSFORM_PREFIX}SubjectToTemplate1Warp.nii.gz \ -t ${OUTNAME}1Warp.nii.gz \ -t ${OUTNAME}0GenericAffine.mat logCmd ${ANTSPATH}/antsApplyTransforms -d 3 \ -i $LABELS \ -r ${OUTNAME}AveragePCASL.nii.gz \ -o ${OUTNAME}LabelsWarpedToPCASL.nii.gz \ -n MultiLabel \ -t ${TRANSFORM_PREFIX}TemplateToSubject0Warp.nii.gz \ -t ${TRANSFORM_PREFIX}TemplateToSubject1GenericAffine.mat \ -t [${OUTNAME}0GenericAffine.mat,1] \ -t ${OUTNAME}1InverseWarp.nii.gz logCmd ${ANTSPATH}/antsApplyTransforms -d 3 \ -i ${OUTNAME}_kcbf.nii.gz \ -r $ANATOMICAL_IMAGE \ -o ${OUTNAME}MeanCBFWarpedToT1.nii.gz \ -n Linear \ -t ${OUTNAME}1Warp.nii.gz \ -t ${OUTNAME}0GenericAffine.mat \ logCmd ${ANTSPATH}/antsApplyTransforms -d 3 \ -i $SEGMENTATION \ -r ${OUTNAME}AveragePCASL.nii.gz \ -o ${OUTNAME}SegmentationWarpedToPCASL.nii.gz \ -n MultiLabel \ -t [${OUTNAME}0GenericAffine.mat,1] \ -t ${OUTNAME}1InverseWarp.nii.gz \ if ! $KEEP_TMP_FILES then for FILE in 0GenericAffine.mat 1Warp.nii.gz 1InverseWarp.nii.gz anatomical.nii.gz template.nii.gz tmp.nii.gz do logCmd rm ${OUTNAME}${FILE} done fi time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo echo "--------------------------------------------------------------------------------------" echo " Done with ANTs ASL processing pipeline." echo " Script executed in $time_elapsed seconds." echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" echo "--------------------------------------------------------------------------------------" exit 0 ants-2.2.0/Scripts/antsAtroposN4.sh000077500000000000000000000571731311104306400171730ustar00rootroot00000000000000#!/bin/bash VERSION="0.0" if [[ ! -s ${ANTSPATH}/N4BiasFieldCorrection ]]; then echo we cant find the N4 program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi if [[ ! -s ${ANTSPATH}/Atropos ]]; then echo we cant find the Atropos program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi function Usage { cat < Atropos to improve segmentation results. Usage: `basename $0` -d imageDimension -a inputImage -x maskImage -m n4AtroposIterations -n atroposIterations -c numberOfClasses -l posteriorLabelForN4Mask -o outputPrefix Example: bash $0 -d 3 -a t1.nii.gz -x mask.nii.gz -c 4 -p segmentationPriors%d.nii.gz -o output Required arguments: -d: image dimension 2 or 3 (for 2- or 3-dimensional image) -a: input image Anatomical image, typically T1. If more than one anatomical image is specified, subsequently specified images are used during the segmentation process. -x: mask image Binary mask defining the region of interest. -c: number of segmentation classes Number of classes defining the segmentation -o: output prefix The following images are created: * ${OUTPUT_PREFIX}N4Corrected.${OUTPUT_SUFFIX} * ${OUTPUT_PREFIX}Segmentation.${OUTPUT_SUFFIX} * ${OUTPUT_PREFIX}SegmentationPosteriors.${OUTPUT_SUFFIX} Optional arguments: -m: max. N4 <-> Atropos iterations Maximum number of (outer loop) iterations between N4 <-> Atropos. -n: max. Atropos iterations Maximum number of (inner loop) iterations in Atropos. -p: segmentation priors Prior probability images initializing the segmentation. Specified using c-style formatting, e.g. -p labelsPriors%02d.nii.gz. -r: mrf Specifies MRF prior (of the form '[weight,neighborhood]', e.g. '[0.1,1x1x1]' which is default). -g: denoise anatomical images Denoise anatomical images (default = 0). -b: posterior formulation Posterior formulation and whether or not to use mixture model proportions. e.g 'Socrates[1]' (default) or 'Aristotle[1]'. Choose the latter if you want use the distance priors (see also the -l option for label propagation control). -l: label propagation Incorporate a distance prior one the posterior formulation. Should be of the form 'label[lambda,boundaryProbability]' where label is a value of 1,2,3,... denoting label ID. The label probability for anything outside the current label = boundaryProbability * exp( -lambda * distanceFromBoundary ) Intuitively, smaller lambda values will increase the spatial capture range of the distance prior. To apply to all label values, simply omit specifying the label, i.e. -l [lambda,boundaryProbability]. -y: posterior label for N4 weight mask Which posterior probability image should be used to define the N4 weight mask. Can also specify multiple posteriors in which case the chosen posteriors are combined. -s: image file suffix Any of the standard ITK IO formats e.g. nrrd, nii.gz (default), mhd -k: keep temporary files Keep temporary files on disk (default = 0). -u: use random seeding Use random number generated from system clock in Atropos (default = 1) -w: Atropos prior segmentation weight Atropos spatial prior probability weight for the segmentation (default = 0) -z: Test / debug mode If > 0, attempts to continue after errors. USAGE exit 1 } echoParameters() { cat <Atropos iters. = ${ATROPOS_SEGMENTATION_NUMBER_OF_ITERATIONS} use clock random seed = ${USE_RANDOM_SEEDING} PARAMETERS } # Echos a command to stdout, then runs it # Will immediately exit on error unless you set debug flag DEBUG_MODE=0 function logCmd() { cmd="$*" echo "BEGIN >>>>>>>>>>>>>>>>>>>>" echo $cmd exec 5>&1 logCmdOutput=$( $cmd | tee >(cat - >&5) ) cmdExit=${PIPESTATUS[0]} if [[ $cmdExit -gt 0 ]]; then echo "ERROR: command exited with nonzero status $cmdExit" echo "Command: $cmd" echo if [[ ! $DEBUG_MODE -gt 0 ]]; then exit 1 fi fi echo "END <<<<<<<<<<<<<<<<<<<<" echo echo return $cmdExit } ################################################################################ # # Main routine # ################################################################################ HOSTNAME=`hostname` DATE=`date` CURRENT_DIR=`pwd`/ OUTPUT_DIR=${CURRENT_DIR}/tmp$RANDOM/ OUTPUT_PREFIX=${OUTPUT_DIR}/tmp OUTPUT_SUFFIX="nii.gz" KEEP_TMP_IMAGES=0 USE_RANDOM_SEEDING=1 DENOISE_ANATOMICAL_IMAGES=0 DIMENSION=3 ANATOMICAL_IMAGES=() ATROPOS_SEGMENTATION_PRIORS="" ################################################################################ # # Programs and their parameters # ################################################################################ N4_ATROPOS_NUMBER_OF_ITERATIONS=15 N4=${ANTSPATH}/N4BiasFieldCorrection N4_CONVERGENCE="[50x50x50x50,0.0000000001]" N4_SHRINK_FACTOR=2 N4_BSPLINE_PARAMS="[200]" N4_WEIGHT_MASK_POSTERIOR_LABELS=() ATROPOS=${ANTSPATH}/Atropos ATROPOS_SEGMENTATION_PRIOR_WEIGHT=0.0 ATROPOS_SEGMENTATION_LIKELIHOOD="Gaussian" ATROPOS_SEGMENTATION_POSTERIOR_FORMULATION="Socrates[1]" ATROPOS_SEGMENTATION_MASK='' ATROPOS_SEGMENTATION_NUMBER_OF_ITERATIONS=5 ATROPOS_SEGMENTATION_NUMBER_OF_CLASSES=3 ATROPOS_SEGMENTATION_MRF='' ATROPOS_SEGMENTATION_LABEL_PROPAGATION=() if [[ $# -lt 3 ]] ; then Usage >&2 exit 1 else while getopts "a:b:c:d:g:h:k:l:m:n:o:p:r:s:t:u:w:x:y:z:" OPT do case $OPT in c) #number of segmentation classes ATROPOS_SEGMENTATION_NUMBER_OF_CLASSES=$OPTARG ;; d) #dimensions DIMENSION=$OPTARG if [[ ${DIMENSION} -gt 4 || ${DIMENSION} -lt 2 ]]; then echo " Error: ImageDimension must be 2, 3, or 4 " exit 1 fi ;; h) #help Usage >&2 exit 0 ;; a) #anatomical t1 image ANATOMICAL_IMAGES[${#ANATOMICAL_IMAGES[@]}]=$OPTARG ;; b) #atropos prior weight ATROPOS_SEGMENTATION_POSTERIOR_FORMULATION=$OPTARG ;; g) # denoise anatomical images DENOISE_ANATOMICAL_IMAGES=$OPTARG ;; k) #keep tmp images KEEP_TMP_IMAGES=$OPTARG ;; l) ATROPOS_SEGMENTATION_LABEL_PROPAGATION[${#ATROPOS_SEGMENTATION_LABEL_PROPAGATION[@]}]=$OPTARG ;; m) #atropos segmentation iterations N4_ATROPOS_NUMBER_OF_ITERATIONS=$OPTARG ;; n) #atropos segmentation iterations ATROPOS_SEGMENTATION_NUMBER_OF_ITERATIONS=$OPTARG ;; o) #output prefix OUTPUT_PREFIX=$OPTARG ;; p) # segmentation label prior image ATROPOS_SEGMENTATION_PRIORS=$OPTARG ;; r) #mrf ATROPOS_SEGMENTATION_MRF=$OPTARG ;; s) #output suffix OUTPUT_SUFFIX=$OPTARG ;; t) #n4 convergence N4_CONVERGENCE=$OPTARG ;; u) #use random seeding USE_RANDOM_SEEDING=$OPTARG ;; w) #atropos prior weight ATROPOS_SEGMENTATION_PRIOR_WEIGHT=$OPTARG ;; x) #atropos segmentation mask ATROPOS_SEGMENTATION_MASK=$OPTARG ;; y) # N4_WEIGHT_MASK_POSTERIOR_LABELS[${#N4_WEIGHT_MASK_POSTERIOR_LABELS[@]}]=$OPTARG ;; z) #debug mode DEBUG_MODE=$OPTARG ;; *) # getopts issues an error message echo "ERROR: unrecognized option -$OPT $OPTARG" exit 1 ;; esac done fi if [[ -z "$ATROPOS_SEGMENTATION_MRF" ]]; then ATROPOS_SEGMENTATION_MRF="[0.1,1x1x1]"; if [[ DIMENSION -eq 2 ]]; then ATROPOS_SEGMENTATION_MRF="[0.1,1x1]" fi fi ATROPOS_SEGMENTATION_CONVERGENCE="[${ATROPOS_SEGMENTATION_NUMBER_OF_ITERATIONS},0.0]" ################################################################################ # # Preliminaries: # 1. Check existence of inputs # 2. Figure out output directory and mkdir if necessary # ################################################################################ for (( i = 0; i < ${#ANATOMICAL_IMAGES[@]}; i++ )) do if [[ ! -f ${ANATOMICAL_IMAGES[$i]} ]]; then echo "The specified image \"${ANATOMICAL_IMAGES[$i]}\" does not exist." exit 1 fi done FORMAT=${ATROPOS_SEGMENTATION_PRIORS} PREFORMAT=${FORMAT%%\%*} POSTFORMAT=${FORMAT##*d} FORMAT=${FORMAT#*\%} FORMAT=${FORMAT%%d*} REPCHARACTER='' TOTAL_LENGTH=0 if [ ${#FORMAT} -eq 2 ] then REPCHARACTER=${FORMAT:0:1} TOTAL_LENGTH=${FORMAT:1:1} fi # MAXNUMBER=$(( 10 ** $TOTAL_LENGTH )) MAXNUMBER=1000 PRIOR_IMAGE_FILENAMES=() POSTERIOR_IMAGE_FILENAMES=() POSTERIOR_IMAGE_FILENAMES_PREVIOUS_ITERATION=() for (( i = 1; i <= $ATROPOS_SEGMENTATION_NUMBER_OF_CLASSES; i++ )) do NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#i} )) ROOT=''; for(( j=0; j < $NUMBER_OF_REPS; j++ )) do ROOT=${ROOT}${REPCHARACTER} done PRIOR_FILENAME=${PREFORMAT}${ROOT}${i}${POSTFORMAT} POSTERIOR_FILENAME=${OUTPUT_PREFIX}SegmentationPosteriors${ROOT}${i}.${OUTPUT_SUFFIX} POSTERIOR_FILENAME_PREVIOUS_ITERATION=${OUTPUT_PREFIX}SegmentationPosteriorsPreviousIteration${ROOT}${i}.${OUTPUT_SUFFIX} POSTERIOR_IMAGE_FILENAMES=( ${POSTERIOR_IMAGE_FILENAMES[@]} $POSTERIOR_FILENAME ) POSTERIOR_IMAGE_FILENAMES_PREVIOUS_ITERATION=( ${POSTERIOR_IMAGE_FILENAMES_PREVIOUS_ITERATION[@]} $POSTERIOR_FILENAME_PREVIOUS_ITERATION ) if [[ -f $PRIOR_FILENAME ]]; then PRIOR_IMAGE_FILENAMES=( ${PRIOR_IMAGE_FILENAMES[@]} $PRIOR_FILENAME ) fi done NUMBER_OF_PRIOR_IMAGES=${#PRIOR_IMAGE_FILENAMES[*]} INITIALIZE_WITH_KMEANS=0 if [[ ${NUMBER_OF_PRIOR_IMAGES} -eq 0 ]]; then echo "Initializing with kmeans segmentation." INITIALIZE_WITH_KMEANS=1 elif [[ ${ATROPOS_SEGMENTATION_NUMBER_OF_CLASSES} -ne ${NUMBER_OF_PRIOR_IMAGES} ]]; then echo "Expected ${ATROPOS_SEGMENTATION_NUMBER_OF_CLASSES} prior images (${NUMBER_OF_PRIOR_IMAGES} are specified). Check the command line specification." exit 1 fi for(( j=0; j < $NUMBER_OF_PRIOR_IMAGES; j++ )) do if [[ ! -f ${PRIOR_IMAGE_FILENAMES[$j]} ]]; then echo "Prior image $j ${PRIOR_IMAGE_FILENAMES[$j]} does not exist." exit 1 fi done OUTPUT_DIR=${OUTPUT_PREFIX%\/*} if [[ ! -d $OUTPUT_DIR ]]; then echo "The output directory \"$OUTPUT_DIR\" does not exist. Making it." mkdir -p $OUTPUT_DIR fi echoParameters >&2 echo "--------------------- Running `basename $0` on $HOSTNAME ---------------------" time_start=`date +%s` ################################################################################ # # Output images # ################################################################################ ATROPOS_SEGMENTATION_OUTPUT=${OUTPUT_PREFIX}Segmentation ATROPOS_SEGMENTATION=${ATROPOS_SEGMENTATION_OUTPUT}.${OUTPUT_SUFFIX} ATROPOS_SEGMENTATION_POSTERIORS=${ATROPOS_SEGMENTATION_OUTPUT}Posteriors%${FORMAT}d.${OUTPUT_SUFFIX} ################################################################################ # # Preprocess anatomical images # 1. Denoise input images (if requested) # ################################################################################ PREPROCESSED_ANATOMICAL_IMAGES=() if [[ ${DENOISE_ANATOMICAL_IMAGES} -ne 0 ]]; then if [[ ! -s ${ANTSPATH}/DenoiseImage ]]; then echo "Error: we can't find the DenoiseImage program." echo "Perhaps you need to \(re\)define \$ANTSPATH in your environment or update your repository." exit fi fi ################################################################################ # # Segmentation # ################################################################################ SEGMENTATION_WEIGHT_MASK=${OUTPUT_PREFIX}SegmentationWeightMask.nii.gz SEGMENTATION_CONVERGENCE_FILE=${OUTPUT_PREFIX}SegmentationConvergence.txt SEGMENTATION_PREVIOUS_ITERATION=${OUTPUT_PREFIX}SegmentationPreviousIteration.${OUTPUT_SUFFIX} N4_WEIGHT_MASK_POSTERIOR_IDXS=() for (( i = 0; i < ${#N4_WEIGHT_MASK_POSTERIOR_LABELS[@]}; i++ )) do N4_WEIGHT_MASK_POSTERIOR_IDXS[$i]=$((N4_WEIGHT_MASK_POSTERIOR_LABELS[$i]-1)) done time_start_segmentation=`date +%s` if [[ $INITIALIZE_WITH_KMEANS -eq 0 ]] then N4_WEIGHT_MASK_IMAGES=() for (( i = 0; i < ${#N4_WEIGHT_MASK_POSTERIOR_LABELS[@]}; i++ )) do N4_WEIGHT_MASK_IMAGES=( ${N4_WEIGHT_MASK_IMAGES[@]} ${PRIOR_IMAGE_FILENAMES[${N4_WEIGHT_MASK_POSTERIOR_IDXS[$i]}]} ) done if [[ ${#N4_WEIGHT_MASK_IMAGES[@]} -gt 0 ]]; then logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SEGMENTATION_WEIGHT_MASK} PureTissueN4WeightMask ${N4_WEIGHT_MASK_IMAGES[@]} fi fi if [[ -f ${SEGMENTATION_CONVERGENCE_FILE} ]]; then logCmd rm -f ${SEGMENTATION_CONVERGENCE_FILE} fi POSTERIOR_PROBABILITY_CONVERGED=0 for (( i = 0; i < ${N4_ATROPOS_NUMBER_OF_ITERATIONS}; i++ )) do SEGMENTATION_N4_IMAGES=() for(( j = 0; j < ${#ANATOMICAL_IMAGES[@]}; j++ )) do SEGMENTATION_N4_IMAGES=( ${SEGMENTATION_N4_IMAGES[@]} ${ATROPOS_SEGMENTATION_OUTPUT}${j}N4.${OUTPUT_SUFFIX} ) if [[ $j == 0 ]]; then # BA edit - forcing the image to copy physical space due to ITK bug images do not occupy same space logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SEGMENTATION_N4_IMAGES[$j]} TruncateImageIntensity ${ANATOMICAL_IMAGES[$j]} 0.025 0.995 256 ${ATROPOS_SEGMENTATION_MASK} 1 if [[ ${DENOISE_ANATOMICAL_IMAGES} -ne 0 ]]; then logCmd ${ANTSPATH}/DenoiseImage -d ${DIMENSION} -i ${SEGMENTATION_N4_IMAGES[$j]} -o ${SEGMENTATION_N4_IMAGES[$j]} --verbose 1 fi else cp ${ANATOMICAL_IMAGES[$j]} ${SEGMENTATION_N4_IMAGES[$j]} fi exe_n4_correction="${N4} -d ${DIMENSION} -i ${SEGMENTATION_N4_IMAGES[$j]} -x ${ATROPOS_SEGMENTATION_MASK} -s ${N4_SHRINK_FACTOR} -c ${N4_CONVERGENCE} -b ${N4_BSPLINE_PARAMS} -o ${SEGMENTATION_N4_IMAGES[$j]} --verbose 1" if [[ -f ${SEGMENTATION_WEIGHT_MASK} ]]; then exe_n4_correction="${exe_n4_correction} -w ${SEGMENTATION_WEIGHT_MASK}" fi logCmd $exe_n4_correction logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SEGMENTATION_N4_IMAGES[$j]} Normalize ${SEGMENTATION_N4_IMAGES[$j]} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SEGMENTATION_N4_IMAGES[$j]} m ${SEGMENTATION_N4_IMAGES[$j]} 1000 done ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE='' for (( j = 0; j < ${#ANATOMICAL_IMAGES[@]}; j++ )) do ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE="${ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE} -a ${SEGMENTATION_N4_IMAGES[$j]}" done INITIALIZATION="PriorProbabilityImages[${ATROPOS_SEGMENTATION_NUMBER_OF_CLASSES},${ATROPOS_SEGMENTATION_PRIORS},${ATROPOS_SEGMENTATION_PRIOR_WEIGHT}]" if [[ INITIALIZE_WITH_KMEANS -eq 1 ]]; then if [[ $i -eq 0 ]]; then INITIALIZATION="kmeans[${ATROPOS_SEGMENTATION_NUMBER_OF_CLASSES}]" else INITIALIZATION="PriorProbabilityImages[${ATROPOS_SEGMENTATION_NUMBER_OF_CLASSES},${ATROPOS_SEGMENTATION_POSTERIORS},${ATROPOS_SEGMENTATION_PRIOR_WEIGHT}]" fi fi ATROPOS_LABEL_PROPAGATION_COMMAND_LINE='' for (( j = 0; j < ${#ATROPOS_SEGMENTATION_LABEL_PROPAGATION[@]}; j++ )) do ATROPOS_LABEL_PROPAGATION_COMMAND_LINE="${ATROPOS_LABEL_PROPAGATION_COMMAND_LINE} -l ${ATROPOS_SEGMENTATION_LABEL_PROPAGATION[$j]}"; done exe_segmentation="${ATROPOS} -d ${DIMENSION} -x ${ATROPOS_SEGMENTATION_MASK} -c ${ATROPOS_SEGMENTATION_CONVERGENCE} ${ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE} ${ATROPOS_LABEL_PROPAGATION_COMMAND_LINE} --verbose 1" exe_segmentation="${exe_segmentation} -i ${INITIALIZATION} -k ${ATROPOS_SEGMENTATION_LIKELIHOOD} -m ${ATROPOS_SEGMENTATION_MRF} -o [${ATROPOS_SEGMENTATION},${ATROPOS_SEGMENTATION_POSTERIORS}] -r ${USE_RANDOM_SEEDING}" if [[ $i -eq 0 ]]; then exe_segmentation="${exe_segmentation} -p Socrates[0]" else exe_segmentation="${exe_segmentation} -p ${ATROPOS_SEGMENTATION_POSTERIOR_FORMULATION}" logCmd cp -f ${ATROPOS_SEGMENTATION} ${SEGMENTATION_PREVIOUS_ITERATION} for (( j = 0; j < ${#POSTERIOR_IMAGE_FILENAMES[@]}; j++ )) do logCmd cp -f ${POSTERIOR_IMAGE_FILENAMES[$j]} ${POSTERIOR_IMAGE_FILENAMES_PREVIOUS_ITERATION[$j]} done for (( j = 0; j < ${#ANATOMICAL_IMAGES[@]}; j++ )) do ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE="${ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE} -a ${SEGMENTATION_N4_IMAGES[$j]}"; done fi logCmd $exe_segmentation if [[ $i -eq 0 ]]; then if [[ ! -f ${SEGMENTATION_CONVERGENCE_FILE} ]]; then echo "Iteration,Posterior" > ${SEGMENTATION_CONVERGENCE_FILE} fi POSTERIOR_PROBABILITY=0 while read line; do tokens=( $line ) if [[ ${tokens[0]} == "Iteration" ]]; then POSTERIOR_PROBABILITY=${tokens[7]} fi done <<< "$logCmdOutput" echo "${i},${POSTERIOR_PROBABILITY}" >> ${SEGMENTATION_CONVERGENCE_FILE} fi if [[ $i -gt 0 && -f ${SEGMENTATION_PREVIOUS_ITERATION} ]]; then POSTERIOR_PROBABILITY_PREVIOUS_ITERATION=$POSTERIOR_PROBABILITY POSTERIOR_PROBABILITY=0 while read line; do tokens=( $line ) if [[ ${tokens[0]} == "Iteration" ]]; then POSTERIOR_PROBABILITY=${tokens[7]} fi done <<< "$logCmdOutput" if [[ $( echo "${POSTERIOR_PROBABILITY} < ${POSTERIOR_PROBABILITY_PREVIOUS_ITERATION}"|bc ) -eq 1 ]]; then POSTERIOR_PROBABILITY_CONVERGED=1 POSTERIOR_PROBABILITY=${POSTERIOR_PROBABILITY_PREVIOUS_ITERATION} logCmd cp -f ${SEGMENTATION_PREVIOUS_ITERATION} ${ATROPOS_SEGMENTATION} for (( j = 0; j < ${#POSTERIOR_IMAGE_FILENAMES[@]}; j++ )) do logCmd cp -f ${POSTERIOR_IMAGE_FILENAMES_PREVIOUS_ITERATION[$j]} ${POSTERIOR_IMAGE_FILENAMES[$j]} done break else echo "${i},${POSTERIOR_PROBABILITY}" >> ${SEGMENTATION_CONVERGENCE_FILE} fi fi N4_WEIGHT_MASK_IMAGES=() for (( j = 0; j < ${#N4_WEIGHT_MASK_POSTERIOR_LABELS[@]}; j++ )) do N4_WEIGHT_MASK_IMAGES=( ${N4_WEIGHT_MASK_IMAGES[@]} ${POSTERIOR_IMAGE_FILENAMES[${N4_WEIGHT_MASK_POSTERIOR_IDXS[$j]}]} ) done if [[ ${#N4_WEIGHT_MASK_IMAGES[@]} -gt 0 ]]; then logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SEGMENTATION_WEIGHT_MASK} PureTissueN4WeightMask ${N4_WEIGHT_MASK_IMAGES[@]} fi done TMP_FILES=( $SEGMENTATION_WEIGHT_MASK ${POSTERIOR_IMAGE_FILENAMES_PREVIOUS_ITERATION[@]} ${SEGMENTATION_PREVIOUS_ITERATION} ) if [[ $KEEP_TMP_IMAGES -eq 0 ]]; then for f in ${TMP_FILES[@]} do if [[ -e $f ]]; then logCmd rm $f else echo "WARNING: expected temp file doesn't exist: $f" fi done fi time_end_segmentation=`date +%s` time_elapsed_segmentation=$((time_end_segmentation - time_start_segmentation)) echo echo "--------------------------------------------------------------------------------------" if [[ POSTERIOR_PROBABILITY_CONVERGED -eq 1 ]]; then echo " Done with segmentation (posterior prob. converged): $(( time_elapsed_segmentation / 3600 ))h $(( time_elapsed_segmentation %3600 / 60 ))m $(( time_elapsed_segmentation % 60 ))s" else echo " Done with segmentation (exceeded max. iterations): $(( time_elapsed_segmentation / 3600 ))h $(( time_elapsed_segmentation %3600 / 60 ))m $(( time_elapsed_segmentation % 60 ))s" fi echo "--------------------------------------------------------------------------------------" echo ################################################################################ # # End of main routine # ################################################################################ time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo echo "--------------------------------------------------------------------------------------" echo " Done with N4 <-> Atropos processing" echo " Script executed in $time_elapsed seconds" echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" echo "--------------------------------------------------------------------------------------" SEGMENTATION_CONVERGENCE_SCRIPT=${ATROPOS_SEGMENTATION_OUTPUT}Convergence.R SEGMENTATION_CONVERGENCE_PLOT=${ATROPOS_SEGMENTATION_OUTPUT}Convergence.pdf if [[ `type -p RScript` > /dev/null ]]; then echo "library( ggplot2 )" > $SEGMENTATION_CONVERGENCE_SCRIPT echo "conv <- read.csv( \"${SEGMENTATION_CONVERGENCE_FILE}\" )" >> $SEGMENTATION_CONVERGENCE_SCRIPT echo "myPlot <- ggplot( conv, aes( x = Iteration, y = Posterior ) ) +" >> $SEGMENTATION_CONVERGENCE_SCRIPT echo " geom_point( data = conv, aes( colour = Iteration ), size = 4 ) +" >> $SEGMENTATION_CONVERGENCE_SCRIPT echo " scale_y_continuous( breaks = seq( 0.8 , 1, by = 0.025 ), labels = seq( 0.8, 1, by = 0.025 ), limits = c( 0.8, 1 ) ) +" >> $SEGMENTATION_CONVERGENCE_SCRIPT echo " theme( legend.position = \"none\" )" >> $SEGMENTATION_CONVERGENCE_SCRIPT echo "ggsave( filename = \"$SEGMENTATION_CONVERGENCE_PLOT\", plot = myPlot, width = 4, height = 3, units = 'in' )" >> $SEGMENTATION_CONVERGENCE_SCRIPT `RScript $SEGMENTATION_CONVERGENCE_SCRIPT` rm -f $SEGMENTATION_CONVERGENCE_SCRIPT fi exit 0 ants-2.2.0/Scripts/antsBOLDNetworkAnalysis.R000077500000000000000000000326731311104306400207250ustar00rootroot00000000000000#!/usr/bin/env Rscript myscale <- function(x,doscale=F) { if ( doscale ) return( scale(x) ) return( x ) } dotest<-F options(digits=3) Args <- commandArgs() self<-Args[4] self<-substring(self,8,nchar(as.character(self))) getPckg <- function(pckg) install.packages(pckg, repos = "http://cran.r-project.org") pckg = try(require(getopt)) if(!pckg) { cat("Installing 'getopt' from CRAN\n") getPckg("getopt") require("getopt") } pckg = try(require(igraph)) if(!pckg) { getPckg("igraph") } pckg = try(require(psych)) if(!pckg) { getPckg("psych") } pckg = try(require(glasso)) if(!pckg) { getPckg("glasso") } library(glasso) library(igraph) library(ANTsR) spec = c( 'labels' , 'l', "0", "character" ," name of (aal) label image ", 'thresh' , 't', "1x100000000", "character" ," lower upper thresh for label image ", 'motion' , 'm', "0", "character" ," name of brain motion csv ", 'mask' , 'x', "0", "character" ," name of brain mask image ", 'fmri' , 'f', "0", "character" ," name of BOLD fmri", 'freq' , 'q', "0.01x0.1", "character" ," low x high frequency for filtering", 'gdens' , 'g', 0.25, "numeric","graph density", 'winsortrim' , 'w', 0.01, "numeric","winsorizing value e.g. 0.05 = 5%", 'glass' , 'a', NA, "numeric","graphical lasso parameter", 'help' , 'h', 0, "logical" ," print the help ", 'output' , 'o', "1", "character"," the output prefix ") # ............................................. # spec=matrix(spec,ncol=5,byrow=TRUE) # get the options opt = getopt(spec) # ............................................. # #help was asked for. if ( !is.null(opt$help) || length(opt) == 1 ) { #print a friendly message and exit with a non-zero error code cat("\n") cat(paste(self,"\n")) for ( x in 1:nrow(spec) ) { cat("\n") longopt<-paste("--",spec[x,1],sep='') shortopt<-paste("-",spec[x,2],sep='') hlist<-paste(shortopt,"|",longopt,spec[x,5],"\n \n") # print(hlist,quote=F) cat(format(hlist, width=40, justify = c("left"))) } cat(format("Example: \n", width=40, justify = c("left"))) ex<-paste(self," --output myoutput --mask mask.nii.gz --labels labels.nii.gz --fmri bold.nii.gz --freq 0.01x0.1 --gdens 0.1 --motion motion.csv \n \n ") ex<-format(ex, width=length(ex), justify = c("left")) cat("\n") cat(ex) if ( !dotest ) q(status=1); } # for ( myfn in c( opt$mask, opt$fmri, opt$labels , opt$motion ) ) { if ( !file.exists(myfn) ) { print(paste("input file",myfn,"does not exist. Exiting.")) q(status=1) } # else print(paste(" have input file",myfn)) } freqLo<-0.02 freqHi<-0.1 threshLo<-1 threshHi<-10000000 throwaway<-10 if ( is.null( opt$winsortrim ) ) opt$winsortrim<-0 if ( is.null( opt$gdens ) ) opt$gdens<-0.25 if ( is.null( opt$glass ) ) opt$glass<-NA if ( ! is.null( opt$freq ) ) { freqLo<-as.numeric( strsplit(opt$freq,"x")[[1]][1] ) freqHi<-as.numeric( strsplit(opt$freq,"x")[[1]][2] ) } if ( ! is.null( opt$thresh ) ) { threshLo<-as.numeric( strsplit(opt$thresh,"x")[[1]][1] ) threshHi<-as.numeric( strsplit(opt$thresh,"x")[[1]][2] ) } if ( dotest ) { subjid<-"SZ017_20050928" subjid<-"NC805_20050906" # subjid<-"PEDS045_20110208" # subjid<-"PEDS008_20101120" # subjid<-"PEDS104_20120817" subjid<-"PEDS007_20110903" print(paste("start test",subjid,freqHi,freqLo)) opt$motion<-paste("moco/",subjid,"_MOCOparams.csv",sep='') opt$fmri<-paste("bold/",subjid,"_bold.nii.gz",sep='') opt$labels<-paste("regions/",subjid,"_AAL.nii.gz",sep='') opt$mask<-paste("mask/",subjid,"_Mask.nii.gz",sep='') opt$output<-"test/TEST" opt$gdens<-0.25 opt$glass<-0.01 threshLo<-1 threshHi<-90 freqLo<-0.01 freqHi<-0.1 } else print(paste("start subject",opt$output,freqHi,freqLo)) mask<-antsImageRead(opt$mask,3) aalm<-antsImageRead(opt$labels,3) bold<-antsImageRead(opt$fmri,4) mytimes<-dim(bold)[4] motion<-read.csv(opt$motion) aalmask<-antsImageClone( aalm ) mylog<-( aalm >= threshLo & aalm <= threshHi ) aalmask[ mylog ]<-1 aalmask[!mylog ]<-0 aalm[!mylog]<-0 print(paste("You are using:",length( unique( aalm[aalmask>0] ) ) ,"unique labels.")) omat<-myscale( timeseries2matrix( bold, aalmask ) ) templateFD<-rep(0,nrow(motion)) DVARS<-rep(0,nrow(motion)) for ( i in 2:nrow(motion) ) { mparams1<-c( motion[i,3:14] ) tmat1<-matrix( as.numeric(mparams1[1:9]), ncol = 3, nrow = 3) mparams2<-c( motion[i-1,3:14] ) tmat2<-matrix( as.numeric(mparams2[1:9]), ncol = 3, nrow = 3) pt<-t( matrix( rep(10,3), nrow=1) ) newpt1<-data.matrix(tmat1) %*% data.matrix( pt )+as.numeric(mparams1[10:12]) newpt2<-data.matrix(tmat2) %*% data.matrix( pt )+as.numeric(mparams1[10:12]) templateFD[i]<-sum(abs(newpt2-newpt1)) DVARS[i]<-sqrt( mean( ( omat[i,] - omat[i-1,] )^2 ) ) } # question - should this be a constant of 0.2 as recommended in Yan Craddock He Milham? keepinds<-which( templateFD < ( mean(templateFD) + 2*sd(templateFD)) & ( (1:mytimes) > throwaway ) ) keepinds<-c(throwaway,keepinds) throwinds<-which( templateFD > ( mean(templateFD) + 2*sd(templateFD)) & ( (1:mytimes) > throwaway ) ) if ( dotest ) { plot( templateFD , type='l' ); print(paste(sum( templateFD > 0.2 ),length(throwinds))) } doimpute<-TRUE if ( length( throwinds ) > 0 & doimpute ) for ( i in throwinds ) { previ <- max( keepinds[ keepinds < i ] ) nexti <- min( keepinds[ keepinds > i ] ) wt1 <- 1-abs( i - previ )/(nexti-previ) wt2 <- 1-abs( i - nexti )/(nexti-previ) omat[i,] <- wt1 * omat[previ,] + omat[nexti,] * wt2 DVARS[i] <- wt1 * DVARS[previ] + DVARS[nexti] * wt2 templateFD[i] <- wt1 * templateFD[previ] + templateFD[nexti] * wt2 motion[i,] <- wt1 * motion[previ,] + motion[nexti,] * wt2 } keepinds<-throwaway:mytimes usemotiondirectly<-TRUE if ( ! usemotiondirectly ) { msvd<-svd( as.matrix(motion[keepinds,3:ncol(motion)] ) ) mysum<-cumsum(msvd$d)/sum(msvd$d) nsvdcomp<-which( mysum > 0.999 )[1] motionnuis <- (msvd$u[, 1:nsvdcomp]) print(paste(" % var of motion ", mysum[nsvdcomp],'with',nsvdcomp ) ) } if ( usemotiondirectly ) motionnuis <- as.matrix(motion[keepinds,3:ncol(motion)] ) colnames(motionnuis)<-paste("mot",1:ncol(motionnuis),sep='') bkgd<-4 if ( bkgd > 0 ) { negmask<-antsImageClone( mask ) backgroundvoxels <- negmask == 0 neginds<-which( backgroundvoxels ) negmask[ negmask >= 0 ] <- 0 backgroundvoxels[ ]<-FALSE backgroundvoxels[ neginds ]<-TRUE negmask[ backgroundvoxels ]<-1 ImageMath(3,negmask,"ME",negmask,1) tempmat<-myscale( timeseries2matrix( bold, negmask )[keepinds,] ) # tempmat<-myscale( timeseries2matrix( bold, mask )[keepinds,] ) # tempmat<-tempmat-ashift(tempmat,c(1,0)) bgsvd<-svd( tempmat ) mysum<-cumsum(bgsvd$d)/sum(bgsvd$d) newnuisv<-min( c( bkgd, which( mysum > 0.8 )[1] ) ) print(paste(newnuisv," % var of bgd ",mysum[newnuisv] ) ) bgdnuis<-bgsvd$u[, 1:newnuisv] colnames(bgdnuis)<-paste("bgdNuis",1:newnuisv,sep='') } print(paste("winsorizing with trim",opt$winsortrim)) if ( opt$winsortrim > 0 ) omat<-winsor(omat,trim=opt$winsortrim) omat<-omat[keepinds,] ################################################## classiccompcor<-compcor(omat,mask=mask,ncompcor = 4 ) omotionnuis<-as.matrix(motion[keepinds,3:ncol(motion)] ) motnuisshift<-ashift(omotionnuis,c(1,0)) motmag<-apply( omotionnuis, FUN=mean,MARGIN=2) matmag<-sqrt( sum(motmag[1:9]*motmag[1:9]) ) tranmag<-sqrt( sum(motmag[10:12]*motmag[10:12]) ) motsd<-apply( omotionnuis-motnuisshift, FUN=mean,MARGIN=2) matsd<-sqrt( sum(motsd[1:9]*motsd[1:9]) ) transd<-sqrt( sum(motsd[10:12]*motsd[10:12]) ) dmatrix<-(omotionnuis-motnuisshift)[,1:9] dtran<-(omotionnuis-motnuisshift)[,10:12] dmatrixm<-apply( dmatrix * dmatrix , FUN=sum, MARGIN=1 ) dtranm<-apply( dtran * dtran , FUN=sum, MARGIN=1 ) if ( bkgd ) mynuis<-cbind(scale(dmatrixm)[,1],scale(dtranm)[,1],classiccompcor, bgdnuis, templateFD[keepinds], DVARS[keepinds] ) else mynuis<-cbind(scale(dmatrixm)[,1],scale(dtranm)[,1],classiccompcor, templateFD[keepinds], DVARS[keepinds] ) colnames(mynuis)[1:2]<-c("dmatrix","dtran") colnames(mynuis)[(length(colnames(mynuis))-1):length(colnames(mynuis))]<-c("FD","DVARS") print("My nuisance variables are:") print( colnames(mynuis) ) mytimes<-dim(omat)[1] mat<-myscale( residuals( lm( omat ~ mynuis ) ) , doscale = TRUE ) flo<-freqLo fhi<-freqHi mytimeshalf<-mytimes/2 # 1st half mat1<-omat[1:mytimeshalf,] mynuis1<-mynuis[1:mytimeshalf,] mat1<-residuals( lm( mat1 ~ mynuis1 ) ) locmotnuis<-cbind( mynuis, motionnuis-ashift(motionnuis,c(1,0)) ) mynetwork1<-filterfMRIforNetworkAnalysis( mat1 , tr=antsGetSpacing(bold)[4], mask=aalmask ,cbfnetwork = "BOLD", labels= aalm , graphdensity = as.numeric(opt$gdens), freqLo = flo, freqHi = fhi , useglasso = opt$glass, usesvd=FALSE ) # , nuisancein = locmotnuis[1:mytimeshalf,]) # 2nd half mat2<-omat[mytimeshalf:mytimes,] mynuis2<-mynuis[mytimeshalf:mytimes,] mat2<-residuals( lm( mat2 ~ mynuis2 ) ) mynetwork2<-filterfMRIforNetworkAnalysis( mat2 , tr=antsGetSpacing(bold)[4], mask=aalmask ,cbfnetwork = "BOLD", labels= aalm , graphdensity = as.numeric(opt$gdens), freqLo = flo, freqHi = fhi, usesvd=FALSE ) # , nuisancein = locmotnuis[mytimeshalf:mytimes,] ) if ( TRUE ) { print( cor.test(mynetwork1$graph$degree,mynetwork2$graph$degree) ) print( cor.test(mynetwork1$graph$centrality,mynetwork2$graph$centrality) ) print( cor.test(mynetwork1$graph$closeness,mynetwork2$graph$closeness) ) print( cor.test(mynetwork1$graph$pagerank,mynetwork2$graph$pagerank) ) print( cor.test(mynetwork1$graph$betweeness,mynetwork2$graph$betweeness) ) print( cor.test(mynetwork1$graph$localtransitivity,mynetwork2$graph$localtransitivity) ) } cor1<-cor(t(mynetwork1$network)) cor2<-cor(t(mynetwork2$network)) cor1t<-cor1[upper.tri(cor1)] cor2t<-cor2[upper.tri(cor2)] # print(cor.test(abs(cor1t),abs(cor2t))) # print( cor.test(mynetwork1$graph$degree,mynetwork2$graph$degree) ) mynetwork<-filterfMRIforNetworkAnalysis( mat , tr=antsGetSpacing(bold)[4], mask=aalmask ,cbfnetwork = "BOLD", labels= aalm , graphdensity = as.numeric(opt$gdens), freqLo = flo, freqHi = fhi , usesvd=FALSE ) # , nuisancein = locmotnuis ) if ( FALSE ) { par(mfrow=c(1,3)) pdf(paste(opt$output,'boldrepro.pdf',sep=''),width=5,height=5) plot( abs(cor1t),abs(cor2t)) # plot(mynetwork1$graph$pagerank,mynetwork2$graph$pagerank) plot(mynetwork1$graph$degree,mynetwork2$graph$degree) plot(mynetwork1$graph$centrality,mynetwork2$graph$centrality) dev.off() } ################################################## ############## now finalize it all ############### ################################################## reproval<-cor.test(mynetwork1$graph$degree,mynetwork2$graph$degree)$est reproval2<-cor.test(mynetwork1$graph$localtransitivity,mynetwork2$graph$localtransitivity)$est names(reproval)<-"boldReproval" names(reproval2)<-"boldReproval2" # return network values on full dataset names(matmag)<-"MatrixMotion" names(tranmag)<-"TransMotion" names(matsd)<-"DMatrixMotion" names(transd)<-"DTransMotion" mydeg<-mynetwork$graph$degree names(mydeg)<-paste("Deg",1:length(mynetwork$graph$degree),sep='') mypr<-mynetwork$graph$pagerank names(mypr)<-paste("PR",1:length(mynetwork$graph$degree),sep='') mycent<-mynetwork$graph$centrality names(mycent)<-paste("Cent",1:length(mynetwork$graph$degree),sep='') mytrans<-mynetwork$graph$localtransitivity names(mytrans)<-paste("Trans",1:length(mynetwork$graph$degree),sep='') myclose<-mynetwork$graph$closeness names(myclose)<-paste("Close",1:length(mynetwork$graph$degree),sep='') mybtwn<-mynetwork$graph$betweeness names(mybtwn)<-paste("Btwn",1:length(mynetwork$graph$degree),sep='') mytimes<-length(keepinds) names(mytimes)<-"NTimePoints" meanFD<-mean(templateFD) names(meanFD)<-"meanFD" meanDVARS<-mean(DVARS) names(meanDVARS)<-"meanDVARS" myc<-c( matmag, tranmag, matsd, transd, reproval , mydeg, mypr, mycent, mytrans, myclose , mybtwn, reproval2, mytimes, meanFD, meanDVARS ) outmat<-matrix(myc,nrow=1) colnames(outmat)<-names(myc) write.csv(outmat,paste(opt$output,'boldout.csv',sep=''),row.names=F) write.csv(mynetwork$graph$adjacencyMatrix,paste(opt$output,'adjacencymatrix.csv',sep=''),row.names=F) write.csv(mynetwork$corrmat,paste(opt$output,'pearson_corrmat.csv',sep=''),row.names=F) write.csv(mynetwork$partialcorrmat,paste(opt$output,'partial_corrmat.csv',sep=''),row.names=F) write.csv(mynetwork$glassocormat,paste(opt$output,'glasso_corrmat.csv',sep=''),row.names=F) write.csv(mynetwork$rcormat,paste(opt$output,'r_corrmat.csv',sep=''),row.names=F) if ( TRUE ) { require(gridExtra) require(ggplot2) dat<-data.frame(deg1=mynetwork1$graph$degree,deg2=mynetwork2$graph$degree) plot1<-ggplot(dat, aes(x=deg1, y=deg2)) + geom_point(shape=1) + geom_smooth(method=lm) dat<-data.frame(btwn1=mynetwork1$graph$betweeness,btwn2=mynetwork2$graph$betweeness) plot2<-ggplot(dat, aes(x=btwn1, y=btwn2)) + geom_point(shape=1) + geom_smooth(method=lm) dat<-data.frame(clos1=mynetwork1$graph$closeness,clos2=mynetwork2$graph$closeness) plot3<-ggplot(dat, aes(x=clos1, y=clos2)) + geom_point(shape=1) + geom_smooth(method=lm) dat<-data.frame(pr1=mynetwork1$graph$pagerank,pr2=mynetwork2$graph$pagerank) plot4<-ggplot(dat, aes(x=pr1, y=pr2)) + geom_point(shape=1) + geom_smooth(method=lm) dat<-data.frame(LT1=mynetwork1$graph$localtransitivity,LT2=mynetwork2$graph$localtransitivity) plot5<-ggplot(dat, aes(x=LT1, y=LT2)) + geom_point(shape=1) + geom_smooth(method=lm) dat<-data.frame(cent1=mynetwork1$graph$centrality,cent2=mynetwork2$graph$centrality) plot6<-ggplot(dat, aes(x=cent1, y=cent2)) + geom_point(shape=1) + geom_smooth(method=lm) pdf(paste(opt$output,"reproplot.pdf",sep=''),width=8,height=4) grid.arrange(plot1, plot2,plot3, plot4,plot5, plot6, ncol=3) dev.off() } ################################################## ants-2.2.0/Scripts/antsBrainExtraction.sh000077500000000000000000000617231311104306400204320ustar00rootroot00000000000000#!/bin/bash VERSION="0.0" if [[ ! -s ${ANTSPATH}/N4BiasFieldCorrection ]]; then echo we cant find the N4 program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi if [[ ! -s ${ANTSPATH}/Atropos ]]; then echo we cant find the Atropos program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi if [[ ! -s ${ANTSPATH}/antsRegistration ]]; then echo we cant find the antsRegistration program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi if [[ ! -s ${ANTSPATH}/antsApplyTransforms ]]; then echo we cant find the antsApplyTransforms program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi function Usage { cat < -o outputPrefix Example: bash $0 -d 3 -a t1.nii.gz -e brainWithSkullTemplate.nii.gz -m brainPrior.nii.gz -o output Required arguments: -d: Image dimension 2 or 3 (for 2- or 3-dimensional image) -a: Anatomical image Structural image, typically T1. If more than one anatomical image is specified, subsequently specified images are used during the segmentation process. However, only the first image is used in the registration of priors. Our suggestion would be to specify the T1 as the first image. -e: Brain extraction template Anatomical template created using e.g. LPBA40 data set with buildtemplateparallel.sh in ANTs. -m: Brain extraction probability mask Brain probability mask created using e.g. LPBA40 data set which have brain masks defined, and warped to anatomical template and averaged resulting in a probability image. -o: Output prefix Output directory + file prefix Optional arguments: -c: Tissue classification A k-means segmentation is run to find gray or white matter around the edge of the initial brain mask warped from the template. This produces a segmentation image with K classes, ordered by mean intensity in increasing order. With this option, you can control K and tell the script which classes represent CSF, gray and white matter. Format (K, csfLabel, gmLabel, wmLabel) Examples: -c 3,1,2,3 for T1 with K=3, CSF=1, GM=2, WM=3 (default) -c 3,3,2,1 for T2 with K=3, CSF=3, GM=2, WM=1 -c 3,1,3,2 for FLAIR with K=3, CSF=1 GM=3, WM=2 -c 4,4,2,3 uses K=4, CSF=4, GM=2, WM=3 -f: Brain extraction registration mask Mask used for registration to limit the metric computation to a specific region. -s: image file suffix Any of the standard ITK IO formats e.g. nrrd, nii.gz (default), mhd -u: use random seeding Use random number generated from system clock in Atropos (default = 1) -k: keep temporary files Keep brain extraction/segmentation warps, etc (default = false). -q: use floating point precision Use antsRegistration with floating point precision. -z: Test / debug mode If > 0, runs a faster version of the script. Only for debugging, results will not be good. USAGE exit 1 } echoParameters() { cat <>>>>>>>>>>>>>>>>>>>" echo $cmd $cmd cmdExit=$? if [[ $cmdExit -gt 0 ]]; then echo "ERROR: command exited with nonzero status $cmdExit" echo "Command: $cmd" echo if [[ ! $DEBUG_MODE -gt 0 ]]; then exit 1 fi fi echo "END <<<<<<<<<<<<<<<<<<<<" echo echo return $cmdExit } ################################################################################ # # Main routine # ################################################################################ HOSTNAME=`hostname` DATE=`date` CURRENT_DIR=`pwd`/ OUTPUT_DIR=${CURRENT_DIR}/tmp$RANDOM/ OUTPUT_PREFIX=${OUTPUT_DIR}/tmp OUTPUT_SUFFIX="nii.gz" KEEP_TMP_IMAGES=0 USE_RANDOM_SEEDING=1 DIMENSION=3 ANATOMICAL_IMAGES=() ################################################################################ # # Programs and their parameters # ################################################################################ ATROPOS=${ANTSPATH}/Atropos ATROPOS_NUM_CLASSES=3 ATROPOS_CSF_CLASS_LABEL=1 ATROPOS_GM_CLASS_LABEL=2 ATROPOS_WM_CLASS_LABEL=3 ATROPOS_BRAIN_EXTRACTION_INITIALIZATION="kmeans[${ATROPOS_NUM_CLASSES}]" ATROPOS_BRAIN_EXTRACTION_LIKELIHOOD="Gaussian" ATROPOS_BRAIN_EXTRACTION_CONVERGENCE="[3,0.0]" ANTS=${ANTSPATH}/antsRegistration ANTS_MAX_ITERATIONS="100x100x70x20" ANTS_TRANSFORMATION="SyN[0.1,3,0]" ANTS_LINEAR_METRIC_PARAMS="1,32,Regular,0.25" ANTS_LINEAR_CONVERGENCE="[1000x500x250x100,1e-8,10]" ANTS_METRIC="CC" ANTS_METRIC_PARAMS="1,4" WARP=${ANTSPATH}/antsApplyTransforms N4=${ANTSPATH}/N4BiasFieldCorrection N4_CONVERGENCE_1="[50x50x50x50,0.0000001]" N4_CONVERGENCE_2="[50x50x50x50,0.0000001]" N4_SHRINK_FACTOR_1=4 N4_SHRINK_FACTOR_2=2 N4_BSPLINE_PARAMS="[200]" USE_FLOAT_PRECISION=0 if [[ $# -lt 3 ]] ; then Usage >&2 exit 1 else while getopts "a:c:d:e:f:h:k:m:o:q:s:u:z:" OPT do case $OPT in d) #dimensions DIMENSION=$OPTARG if [[ ${DIMENSION} -gt 4 || ${DIMENSION} -lt 2 ]]; then echo " Error: ImageDimension must be 2, 3, or 4 " exit 1 fi ;; h) #help Usage >&2 exit 0 ;; a) #anatomical t1 image ANATOMICAL_IMAGES[${#ANATOMICAL_IMAGES[@]}]=$OPTARG ;; c) #k-means segmentation params kmeansParamsArr=(${OPTARG//,/ }) ATROPOS_NUM_CLASSES=${kmeansParamsArr[0]} ATROPOS_BRAIN_EXTRACTION_INITIALIZATION="kmeans[${ATROPOS_NUM_CLASSES}]" ATROPOS_CSF_CLASS_LABEL=${kmeansParamsArr[1]} ATROPOS_GM_CLASS_LABEL=${kmeansParamsArr[2]} ATROPOS_WM_CLASS_LABEL=${kmeansParamsArr[3]} ;; k) #keep tmp images KEEP_TMP_IMAGES=$OPTARG ;; e) #brain extraction anatomical image EXTRACTION_TEMPLATE=$OPTARG ;; f) #brain extraction registration mask EXTRACTION_REGISTRATION_MASK=$OPTARG ;; m) #brain extraction prior probability mask EXTRACTION_PRIOR=$OPTARG ;; o) #output prefix OUTPUT_PREFIX=$OPTARG ;; q) USE_FLOAT_PRECISION=$OPTARG ;; s) #output suffix OUTPUT_SUFFIX=$OPTARG ;; u) #use random seeding USE_RANDOM_SEEDING=$OPTARG ;; z) #debug mode DEBUG_MODE=$OPTARG ;; *) # getopts issues an error message echo "ERROR: unrecognized option -$OPT $OPTARG" exit 1 ;; esac done fi ATROPOS_BRAIN_EXTRACTION_MRF="[0.1,1x1x1]" if [[ DIMENSION -eq 2 ]]; then ATROPOS_BRAIN_EXTRACTION_MRF="[0.1,1x1]" fi if [[ -z "$ATROPOS_SEGMENTATION_MRF" ]]; then ATROPOS_SEGMENTATION_MRF="[0.1,1x1x1]"; if [[ DIMENSION -eq 2 ]]; then ATROPOS_SEGMENTATION_MRF="[0.1,1x1]" fi fi echo " Will run Atropos segmentation with K=${ATROPOS_NUM_CLASSES}. Classes labeled in order of mean intensity. Assuming CSF=${ATROPOS_CSF_CLASS_LABEL}, GM=${ATROPOS_GM_CLASS_LABEL}, WM=${ATROPOS_WM_CLASS_LABEL} " ################################################################################ # # Preliminaries: # 1. Check existence of inputs # 2. Figure out output directory and mkdir if necessary # ################################################################################ for (( i = 0; i < ${#ANATOMICAL_IMAGES[@]}; i++ )) do if [[ ! -f ${ANATOMICAL_IMAGES[$i]} ]]; then echo "The specified image \"${ANATOMICAL_IMAGES[$i]}\" does not exist." exit 1 fi done OUTPUT_DIR=${OUTPUT_PREFIX%\/*} if [[ ! -d $OUTPUT_DIR ]]; then echo "The output directory \"$OUTPUT_DIR\" does not exist. Making it." mkdir -p $OUTPUT_DIR fi if [[ $DEBUG_MODE -gt 0 ]]; then echo " WARNING - Running in test / debug mode. Results will be suboptimal " # Speed up by doing fewer its. Careful about changing this because # certain things are hard coded elsewhere, eg number of levels ANTS_MAX_ITERATIONS="40x40x20x0" ANTS_LINEAR_CONVERGENCE="[100x100x50x10,1e-8,10]" # Leave N4 / Atropos alone because they're pretty fast fi echoParameters >&2 echo "--------------------- Running `basename $0` on $HOSTNAME ---------------------" time_start=`date +%s` ################################################################################ # # Output image # ################################################################################ BRAIN_EXTRACTION_MASK=${OUTPUT_PREFIX}BrainExtractionMask.${OUTPUT_SUFFIX} ################################################################################ # # Brain extraction # ################################################################################ N4_CORRECTED_IMAGES=() BRAIN_EXTRACTION_OUTPUT=${OUTPUT_PREFIX}BrainExtraction EXTRACTION_WARP_OUTPUT_PREFIX=${BRAIN_EXTRACTION_OUTPUT}Prior EXTRACTION_WARP=${EXTRACTION_WARP_OUTPUT_PREFIX}1Warp.nii.gz EXTRACTION_INVERSE_WARP=${EXTRACTION_WARP_OUTPUT_PREFIX}1InverseWarp.nii.gz EXTRACTION_GENERIC_AFFINE=${EXTRACTION_WARP_OUTPUT_PREFIX}0GenericAffine.mat EXTRACTION_MASK_PRIOR_WARPED=${EXTRACTION_WARP_OUTPUT_PREFIX}Warped.${OUTPUT_SUFFIX} EXTRACTION_MASK=$BRAIN_EXTRACTION_MASK EXTRACTION_SEGMENTATION=${BRAIN_EXTRACTION_OUTPUT}Segmentation.${OUTPUT_SUFFIX} EXTRACTION_BRAIN=${BRAIN_EXTRACTION_OUTPUT}Brain.${OUTPUT_SUFFIX} EXTRACTION_WM=${BRAIN_EXTRACTION_OUTPUT}WM.${OUTPUT_SUFFIX} EXTRACTION_GM=${BRAIN_EXTRACTION_OUTPUT}GM.${OUTPUT_SUFFIX} EXTRACTION_CSF=${BRAIN_EXTRACTION_OUTPUT}CSF.${OUTPUT_SUFFIX} EXTRACTION_TMP=${BRAIN_EXTRACTION_OUTPUT}Tmp.${OUTPUT_SUFFIX} EXTRACTION_INITIAL_AFFINE=${BRAIN_EXTRACTION_OUTPUT}InitialAffine.mat EXTRACTION_INITIAL_AFFINE_FIXED=${BRAIN_EXTRACTION_OUTPUT}InitialAffineFixed.${OUTPUT_SUFFIX} EXTRACTION_INITIAL_AFFINE_MOVING=${BRAIN_EXTRACTION_OUTPUT}InitialAffineMoving.${OUTPUT_SUFFIX} EXTRACTION_LAPLACIAN=${BRAIN_EXTRACTION_OUTPUT}Laplacian.${OUTPUT_SUFFIX} EXTRACTION_TEMPLATE_LAPLACIAN=${BRAIN_EXTRACTION_OUTPUT}TemplateLaplacian.${OUTPUT_SUFFIX} TMP_FILES=( $EXTRACTION_MASK_PRIOR_WARPED $EXTRACTION_WARP $EXTRACTION_INVERSE_WARP $EXTRACTION_TMP $EXTRACTION_GM $EXTRACTION_CSF $EXTRACTION_SEGMENTATION $EXTRACTION_INITIAL_AFFINE $EXTRACTION_INITIAL_AFFINE_MOVING $EXTRACTION_INITIAL_AFFINE_FIXED $EXTRACTION_LAPLACIAN $EXTRACTION_TEMPLATE_LAPLACIAN $EXTRACTION_WM ) if [[ ! -f ${EXTRACTION_MASK} || ! -f ${EXTRACTION_WM} ]]; then time_start_brain_extraction=`date +%s` ################################################################################ # # N4 Correction (pre brain extraction) # ################################################################################ echo echo "--------------------------------------------------------------------------------------" echo " Bias correction of anatomical images (pre brain extraction)" echo " 1) pre-process by truncating the image intensities" echo " 2) run N4" echo "--------------------------------------------------------------------------------------" echo time_start_n4_correction=`date +%s` for (( i = 0; i < ${#ANATOMICAL_IMAGES[@]}; i++ )) do N4_TRUNCATED_IMAGE=${OUTPUT_PREFIX}N4Truncated${i}.${OUTPUT_SUFFIX} N4_CORRECTED_IMAGE=${OUTPUT_PREFIX}N4Corrected${i}.${OUTPUT_SUFFIX} TMP_FILES=( ${TMP_FILES[@]} $N4_TRUNCATED_IMAGE $N4_CORRECTED_IMAGE ) N4_CORRECTED_IMAGES=( ${N4_CORRECTED_IMAGES[@]} ${N4_CORRECTED_IMAGE} ) if [[ ! -f ${N4_CORRECTED_IMAGE} ]]; then logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${N4_TRUNCATED_IMAGE} TruncateImageIntensity ${ANATOMICAL_IMAGES[$i]} 0.01 0.999 256 exe_n4_correction="${N4} -d ${DIMENSION} -i ${N4_TRUNCATED_IMAGE} -s ${N4_SHRINK_FACTOR_1} -c ${N4_CONVERGENCE_1} -b ${N4_BSPLINE_PARAMS} -o ${N4_CORRECTED_IMAGE} --verbose 1" logCmd $exe_n4_correction fi done time_end_n4_correction=`date +%s` time_elapsed_n4_correction=$((time_end_n4_correction - time_start_n4_correction)) ## check if output was produced if [[ ! -f ${N4_CORRECTED_IMAGES[0]} ]]; then echo "Expected output was not produce. The N4 corrected image doesn't exist:" echo " ${N4_CORRECTED_IMAGES[0]}" exit 1 fi echo echo "--------------------------------------------------------------------------------------" echo " Done with N4 correction (pre brain extraction): $(( time_elapsed_n4_correction / 3600 ))h $(( time_elapsed_n4_correction %3600 / 60 ))m $(( time_elapsed_n4_correction % 60 ))s" echo "--------------------------------------------------------------------------------------" echo if [[ ! -f ${EXTRACTION_INVERSE_WARP} ]]; then if [[ ! -f ${N4_CORRECTED_IMAGES[0]} ]]; then echo "The N4 corrected image doesn't exist:" echo " ${N4_CORRECTED_IMAGES[0]}" exit 1 fi echo echo "--------------------------------------------------------------------------------------" echo " Brain extraction using the following steps:" echo " 1) Register $EXTRACTION_TEMPLATE to ${N4_CORRECTED_IMAGES[0]}" echo " 2) Warp $EXTRACTION_PRIOR to ${ANATOMICAL_IMAGES[0]} using, from 1)," echo " ${OUTPUT_PREFIX}BrainExtractionWarp/Affine" echo " 3) Refine segmentation results using Atropos" echo "--------------------------------------------------------------------------------------" echo ## Step 1 ## logCmd ${ANTSPATH}/ResampleImageBySpacing ${DIMENSION} ${EXTRACTION_TEMPLATE} ${EXTRACTION_INITIAL_AFFINE_FIXED} 4 4 4 1 logCmd ${ANTSPATH}/ResampleImageBySpacing ${DIMENSION} ${N4_CORRECTED_IMAGES[0]} ${EXTRACTION_INITIAL_AFFINE_MOVING} 4 4 4 1 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_LAPLACIAN} Laplacian ${N4_CORRECTED_IMAGES[0]} 1.5 1 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_TEMPLATE_LAPLACIAN} Laplacian ${EXTRACTION_TEMPLATE} 1.5 1 # exe_initial_align="${ANTSPATH}/antsAffineInitializer ${DIMENSION} ${EXTRACTION_INITIAL_AFFINE_FIXED} ${EXTRACTION_INITIAL_AFFINE_MOVING} ${EXTRACTION_INITIAL_AFFINE} 15 0.1 0 10" exe_initial_align="${ANTSPATH}/antsAI -d ${DIMENSION} -v 1" exe_initial_align="${exe_initial_align} -m Mattes[${EXTRACTION_INITIAL_AFFINE_FIXED},${EXTRACTION_INITIAL_AFFINE_MOVING},32,Regular,0.25]" exe_initial_align="${exe_initial_align} -t Affine[0.1]" exe_initial_align="${exe_initial_align} -s [15,0.1]" exe_initial_align="${exe_initial_align} -p 0" exe_initial_align="${exe_initial_align} -c 10" exe_initial_align="${exe_initial_align} -o ${EXTRACTION_INITIAL_AFFINE}" if [[ -f ${EXTRACTION_REGISTRATION_MASK} ]]; then # exe_initial_align="${exe_initial_align} ${EXTRACTION_REGISTRATION_MASK}" exe_initial_align="${exe_initial_align} -x ${EXTRACTION_REGISTRATION_MASK}" fi logCmd $exe_initial_align basecall="${ANTS} -d ${DIMENSION} -u 1 -w [0.025,0.975] -o ${EXTRACTION_WARP_OUTPUT_PREFIX} -r ${EXTRACTION_INITIAL_AFFINE} -z 1 --float ${USE_FLOAT_PRECISION} --verbose 1" if [[ -f ${EXTRACTION_REGISTRATION_MASK} ]]; then basecall="${basecall} -x [${EXTRACTION_REGISTRATION_MASK}]" fi stage1="-m MI[${EXTRACTION_TEMPLATE},${N4_CORRECTED_IMAGES[0]},${ANTS_LINEAR_METRIC_PARAMS}] -c ${ANTS_LINEAR_CONVERGENCE} -t Rigid[0.1] -f 8x4x2x1 -s 4x2x1x0" stage2="-m MI[${EXTRACTION_TEMPLATE},${N4_CORRECTED_IMAGES[0]},${ANTS_LINEAR_METRIC_PARAMS}] -c ${ANTS_LINEAR_CONVERGENCE} -t Affine[0.1] -f 8x4x2x1 -s 4x2x1x0" stage3="-m CC[${EXTRACTION_TEMPLATE},${N4_CORRECTED_IMAGES[0]},0.5,4] -m CC[${EXTRACTION_TEMPLATE_LAPLACIAN},${EXTRACTION_LAPLACIAN},0.5,4] -c [50x10x0,1e-9,15] -t ${ANTS_TRANSFORMATION} -f 4x2x1 -s 2x1x0" exe_brain_extraction_1="${basecall} ${stage1} ${stage2} ${stage3}" logCmd $exe_brain_extraction_1 ## check to see if the output registration transforms exist if [[ ! -f ${EXTRACTION_GENERIC_AFFINE} ]]; then echo "The registration component of the extraction step didn't complete properly." echo "The transform file ${EXTRACTION_GENERIC_AFFINE} does not exist." exit 1 fi if [[ ! -f ${EXTRACTION_INVERSE_WARP} ]]; then echo "The registration component of the extraction step didn't complete properly." echo "The transform file ${EXTRACTION_INVERSE_WARP} does not exist." exit 1 fi fi if [[ ! -f ${EXTRACTION_SEGMENTATION} ]]; then ## Step 2 ## exe_brain_extraction_2="${WARP} -d ${DIMENSION} -i ${EXTRACTION_PRIOR} -o ${EXTRACTION_MASK_PRIOR_WARPED} -r ${ANATOMICAL_IMAGES[0]} -n Gaussian -t [${EXTRACTION_GENERIC_AFFINE},1] -t ${EXTRACTION_INVERSE_WARP} --float ${USE_FLOAT_PRECISION} --verbose 1" logCmd $exe_brain_extraction_2 ## superstep 1b ## logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${EXTRACTION_MASK_PRIOR_WARPED} ${EXTRACTION_MASK_PRIOR_WARPED} 0.5 1 1 0 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK} MD ${EXTRACTION_MASK_PRIOR_WARPED} 2 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK} GetLargestComponent ${EXTRACTION_MASK} ## superstep 6 ## ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE=''; for (( i = 0; i < ${#ANATOMICAL_IMAGES[@]}; i++ )) do ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE="${ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE} -a ${N4_CORRECTED_IMAGES[$i]}"; done exe_brain_extraction_3="${ATROPOS} -d ${DIMENSION} -o ${EXTRACTION_SEGMENTATION} ${ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE} -x ${EXTRACTION_MASK} -i ${ATROPOS_BRAIN_EXTRACTION_INITIALIZATION} -c ${ATROPOS_BRAIN_EXTRACTION_CONVERGENCE} -m ${ATROPOS_BRAIN_EXTRACTION_MRF} -k ${ATROPOS_BRAIN_EXTRACTION_LIKELIHOOD} -r ${USE_RANDOM_SEEDING} --verbose 1" logCmd $exe_brain_extraction_3 fi # Pad image here to avoid errors from dilating into the edge of the image padVoxels=10 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_SEGMENTATION} PadImage ${EXTRACTION_SEGMENTATION} $padVoxels logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK_PRIOR_WARPED} PadImage ${EXTRACTION_MASK_PRIOR_WARPED} $padVoxels logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${EXTRACTION_SEGMENTATION} ${EXTRACTION_WM} ${ATROPOS_WM_CLASS_LABEL} ${ATROPOS_WM_CLASS_LABEL} 1 0 logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${EXTRACTION_SEGMENTATION} ${EXTRACTION_GM} ${ATROPOS_GM_CLASS_LABEL} ${ATROPOS_GM_CLASS_LABEL} 1 0 logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${EXTRACTION_SEGMENTATION} ${EXTRACTION_CSF} ${ATROPOS_CSF_CLASS_LABEL} ${ATROPOS_CSF_CLASS_LABEL} 1 0 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_WM} GetLargestComponent ${EXTRACTION_WM} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_GM} GetLargestComponent ${EXTRACTION_GM} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_TMP} FillHoles ${EXTRACTION_GM} 2 logCmd ${ANTSPATH}/MultiplyImages ${DIMENSION} ${EXTRACTION_GM} ${EXTRACTION_TMP} ${EXTRACTION_GM} logCmd ${ANTSPATH}/MultiplyImages ${DIMENSION} ${EXTRACTION_WM} ${ATROPOS_WM_CLASS_LABEL} ${EXTRACTION_WM} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_TMP} ME ${EXTRACTION_CSF} 10 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_GM} addtozero ${EXTRACTION_GM} ${EXTRACTION_TMP} logCmd ${ANTSPATH}/MultiplyImages ${DIMENSION} ${EXTRACTION_GM} ${ATROPOS_GM_CLASS_LABEL} ${EXTRACTION_GM} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_SEGMENTATION} addtozero ${EXTRACTION_WM} ${EXTRACTION_GM} ## superstep 7 ## logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${EXTRACTION_SEGMENTATION} ${EXTRACTION_MASK} ${ATROPOS_WM_CLASS_LABEL} ${ATROPOS_WM_CLASS_LABEL} 1 0 logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${EXTRACTION_SEGMENTATION} ${EXTRACTION_TMP} ${ATROPOS_GM_CLASS_LABEL} ${ATROPOS_GM_CLASS_LABEL} 1 0 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK} addtozero ${EXTRACTION_MASK} ${EXTRACTION_TMP} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK} ME ${EXTRACTION_MASK} 2 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK} GetLargestComponent ${EXTRACTION_MASK} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK} MD ${EXTRACTION_MASK} 4 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK} FillHoles ${EXTRACTION_MASK} 2 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK} addtozero ${EXTRACTION_MASK} ${EXTRACTION_MASK_PRIOR_WARPED} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK} MD ${EXTRACTION_MASK} 5 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTION_MASK} ME ${EXTRACTION_MASK} 5 # De-pad for img in ${EXTRACTION_SEGMENTATION} ${EXTRACTION_MASK} ${EXTRACTION_WM} ${EXTRACTION_GM} ${EXTRACTION_CSF} ${EXTRACTION_MASK_PRIOR_WARPED} do logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${img} PadImage ${img} -$padVoxels done logCmd ${ANTSPATH}/MultiplyImages ${DIMENSION} ${N4_CORRECTED_IMAGES[0]} ${EXTRACTION_MASK} ${EXTRACTION_BRAIN} if [[ ! -f ${EXTRACTION_MASK} ]]; then echo "Expected output was not produced. The brain mask doesn't exist:" echo " $EXTRACTION_MASK" exit 1 fi if [[ ! -f ${EXTRACTION_BRAIN} ]]; then echo "Expected output was not produced. The brain extracted image doesn't exist:" echo " $EXTRACTION_BRAIN" exit 1 fi time_end_brain_extraction=`date +%s` time_elapsed_brain_extraction=$((time_end_brain_extraction - time_start_brain_extraction)) echo echo "--------------------------------------------------------------------------------------" echo " Done with brain extraction: $(( time_elapsed_brain_extraction / 3600 ))h $(( time_elapsed_brain_extraction %3600 / 60 ))m $(( time_elapsed_brain_extraction % 60 ))s" echo "--------------------------------------------------------------------------------------" echo fi if [[ $KEEP_TMP_IMAGES -eq 0 ]]; then for f in ${TMP_FILES[@]} do if [[ -e $f ]]; then logCmd rm $f else echo "WARNING: expected temp file doesn't exist: $f" fi done fi ################################################################################ # # End of main routine # ################################################################################ exit 0 ants-2.2.0/Scripts/antsCookTemplatePriors.sh000066400000000000000000000541761311104306400211250ustar00rootroot00000000000000#!/bin/bash VERSION="0.0" # Check dependencies PROGRAM_DEPENDENCIES=( 'ImageMath' 'SmoothImage' 'ThresholdImage' 'antsRegistration' 'antsApplyTransforms' 'N4BiasFieldCorrection' 'Atropos' 'KellyKapowski' ) SCRIPTS_DEPENDENCIES=( 'antsCorticalThickness.sh' 'antsBrainExtraction.sh' 'antsAtroposN4.sh' 'antsJointLabelFusion.sh' ) for D in ${PROGRAM_DEPENDENCIES[@]}; do if [[ ! -s ${ANTSPATH}/${D} ]]; then echo "Error: we can't find the $D program." echo "Perhaps you need to \(re\)define \$ANTSPATH in your environment." exit fi done for D in ${SCRIPT_DEPENDENCIES[@]}; do if [[ ! -s ${ANTSPATH}/${D} ]]; then echo "We can't find the $D script." echo "Perhaps you need to \(re\)define \$ANTSPATH in your environment." exit fi done function Usage { cat < -o outputPrefix \${templateImages[@]} Example: bash $0 -d 3 -e brainWithSkullTemplate.nii.gz -m brainPrior.nii.gz -p segmentationPriors%d.nii.gz -o output \${templateImages[@]} Required arguments: -d: Image dimension 2 or 3 (for 2- or 3-dimensional image) -e: Brain reference template Anatomical *intensity* template (possibly created using a population data set with buildtemplateparallel.sh in ANTs). This template is *not* skull-stripped. -m: Brain extraction probability mask Brain *probability* mask created using e.g. LPBA40 labels which have brain masks defined, and warped to anatomical template and averaged resulting in a probability image. -p: Brain segmentation priors Tissue *probability* priors corresponding to the image specified with the -e option. Specified using c-style formatting, e.g. -p labelsPriors%02d.nii.gz. We assume that the first four priors are ordered as follows 1: csf 2: cortical gm 3: wm 4: deep gm -o: Output prefix The following subdirectory and images are created for the single subject template * \${OUTPUT_PREFIX}SingleSubjectTemplate/ * \${OUTPUT_PREFIX}SingleSubjectTemplate/T_template*.nii.gz anatomical images Set of multimodal input data assumed to be specified ordered as follows: \${time1_modality1} \${time1_modality2} ... \${time1_modalityN} \\ \${time2_modality1} \${time2_modality2} ... . . . \${timeN_modality1} ... A single modality is expected by default, in which case the input images are simply ordered by time: \${time1_modality1} \${time2_modality1} ... \${timeN_modality1} If there are multiple modalities, use the -k option to specify how many. Optional arguments: -s: image file suffix Any of the standard ITK IO formats e.g. nrrd, nii.gz (default), mhd -c: control type Control for parallel computation (default 0): 0 = run serially 1 = SGE qsub 2 = use PEXEC (localhost) 3 = Apple XGrid 4 = PBS qsub 5 = SLURM -a: Atlases (assumed to be skull-stripped) used to cook template priors. If atlases aren't used then we simply smooth the single-subject template posteriors after passing through antsCorticalThickness.sh. Example: -a atlas1.nii.gz -a atlas2.nii.gz ... -a atlasN.nii.gz -l: Labels associated with each atlas, in the same order as they are specified with the -a option. The number of labels in each image is assumed to be equal to the number of priors. -f: extraction registration mask Mask (defined in the template space) used during registration for brain extraction. -g: denoise anatomical images Denoise anatomical images (default = 0). -j: number of cpu cores Number of cpu cores to use locally for pexec option (default 2; requires "-c 2") -k: number of modalities Number of modalities used to construct the template (default 1): For example, if one wanted to use multiple modalities consisting of T1, T2, and FA components ("-k 3"). -u: use floating-point precision Use floating point precision in registrations (default = 0) -v: Atropos segmentation weight Atropos spatial prior *probability* weight for the segmentation for the template (default = 0.25) -w: Atropos segmentation weight Atropos spatial prior *probability* weight for the segmentations (default = 0.5) -q: Use quick registration parameters If 'yes' then we use antsRegistrationSyNQuick.sh as the basis for registration. Otherwise use antsRegistrationSyN.sh. The options are as follows: '-q 0' = antsRegistrationSyN for everything (default) '-q 1' = Fast antsCorticalThickness to SST '-q 2' = Fast JLF cooking '-q 3' = Fast everything -z: Test / debug mode If > 0, runs a faster version of the script. Only for testing. Implies -u 0 in the antsCorticalThickness.sh script (i.e., no random seeding). Requires single thread computation for complete reproducibility. USAGE exit 1 } echoParameters() { cat <>>>>>>>>>>>>>>>>>>>" echo $cmd $cmd cmdExit=$? if [[ $cmdExit -gt 0 ]]; then echo "ERROR: command exited with nonzero status $cmdExit" echo "Command: $cmd" echo if [[ ! $DEBUG_MODE -gt 0 ]]; then exit 1 fi fi echo "END <<<<<<<<<<<<<<<<<<<<" echo echo return $cmdExit } HOSTNAME=`hostname` DATE=`date` CURRENT_DIR=`pwd`/ OUTPUT_DIR=${CURRENT_DIR}/tmp$RANDOM/ OUTPUT_PREFIX=${OUTPUT_DIR}/tmp OUTPUT_SUFFIX="nii.gz" DIMENSION=3 NUMBER_OF_MODALITIES=1 TEMPLATE_IMAGES=() RUN_QUICK=1 USE_RANDOM_SEEDING=1 BRAIN_TEMPLATE="" EXTRACTION_PRIOR="" EXTRACTION_REGISTRATION_MASK="" SEGMENTATION_PRIOR="" USE_SST_CORTICAL_THICKNESS_PRIOR=0 REGISTRATION_TEMPLATE="" DO_REGISTRATION_TO_TEMPLATE=0 DENOISE=0 ATROPOS_SEGMENTATION_PRIOR_WEIGHT_SST=0.25 ATROPOS_SEGMENTATION_PRIOR_WEIGHT_TIMEPOINT=0.5 DOQSUB=0 CORES=2 RIGID_ALIGNMENT_TO_SST=0 MALF_ATLASES=() MALF_LABELS=() MALF_LABEL_STRINGS_FOR_PRIORS=() ################################################################################ # # Programs and their parameters # ################################################################################ USE_FLOAT_PRECISION=0 if [[ $# -lt 3 ]] ; then Usage >&2 exit 1 else while getopts "a:b:c:d:e:f:g:h:j:k:l:m:o:p:q:r:s:t:u:v:w:z:" OPT do case $OPT in a) MALF_ATLASES[${#MALF_ATLASES[@]}]=$OPTARG ;; b) # posterior formulation ATROPOS_SEGMENTATION_POSTERIOR_FORMULATION=$OPTARG ;; c) DOQSUB=$OPTARG if [[ $DOQSUB -gt 5 ]]; then echo " DOQSUB must be an integer value (0=serial, 1=SGE qsub, 2=try pexec, 3=XGrid, 4=PBS qsub, 5=SLURM ) you passed -c $DOQSUB " exit 1 fi ;; d) #dimensions DIMENSION=$OPTARG if [[ ${DIMENSION} -gt 3 || ${DIMENSION} -lt 2 ]]; then echo " Error: ImageDimension must be 2 or 3 " exit 1 fi ;; e) #brain extraction anatomical image BRAIN_TEMPLATE=$OPTARG ;; f) #brain extraction registration mask EXTRACTION_REGISTRATION_MASK=$OPTARG ;; g) #denoise DENOISE=$OPTARG ;; h) #help Usage >&2 exit 0 ;; j) #number of cpu cores to use (default = 2) CORES=$OPTARG ;; k) #number of modalities NUMBER_OF_MODALITIES=$OPTARG ;; l) MALF_LABELS[${#MALF_LABELS[@]}]=$OPTARG ;; m) #brain extraction prior probability mask EXTRACTION_PRIOR=$OPTARG ;; n) # use USE_SST_CORTICAL_THICKNESS_PRIOR=$OPTARG ;; o) #output prefix OUTPUT_PREFIX=$OPTARG ;; p) #brain segmentation label prior image SEGMENTATION_PRIOR=$OPTARG ;; q) # run quick RUN_QUICK=$OPTARG ;; u) #use floating point precision USE_FLOAT_PRECISION=$OPTARG ;; v) #atropos prior weight for single subject template ATROPOS_SEGMENTATION_PRIOR_WEIGHT_SST=$OPTARG ;; w) #atropos prior weight for each individual time point ATROPOS_SEGMENTATION_PRIOR_WEIGHT_TIMEPOINT=$OPTARG ;; z) #debug mode DEBUG_MODE=$OPTARG ;; *) # getopts issues an error message echo "ERROR: unrecognized option -$OPT $OPTARG" exit 1 ;; esac done fi FORMAT=${SEGMENTATION_PRIOR} PREFORMAT=${FORMAT%%\%*} POSTFORMAT=${FORMAT##*d} FORMAT=${FORMAT#*\%} FORMAT=${FORMAT%%d*} REPCHARACTER='' TOTAL_LENGTH=0 if [ ${#FORMAT} -eq 2 ] then REPCHARACTER=${FORMAT:0:1} TOTAL_LENGTH=${FORMAT:1:1} fi # MAXNUMBER=$(( 10 ** $TOTAL_LENGTH )) MAXNUMBER=1000 PRIOR_IMAGE_FILENAMES=() WARPED_PRIOR_IMAGE_FILENAMES=() BRAIN_SEGMENTATION_OUTPUT=${OUTPUT_PREFIX}BrainSegmentation SEGMENTATION_WARP_OUTPUT_PREFIX=${BRAIN_SEGMENTATION_OUTPUT}Prior SEGMENTATION_PRIOR_WARPED=${SEGMENTATION_WARP_OUTPUT_PREFIX}Warped for (( i = 1; i < $MAXNUMBER; i++ )) do NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#i} )) ROOT=''; for(( j=0; j < $NUMBER_OF_REPS; j++ )) do ROOT=${ROOT}${REPCHARACTER} done FILENAME=${PREFORMAT}${ROOT}${i}${POSTFORMAT} WARPED_FILENAME=${SEGMENTATION_PRIOR_WARPED}${ROOT}${i}.${OUTPUT_SUFFIX} if [[ -f $FILENAME ]]; then PRIOR_IMAGE_FILENAMES=( ${PRIOR_IMAGE_FILENAMES[@]} $FILENAME ) WARPED_PRIOR_IMAGE_FILENAMES=( ${WARPED_PRIOR_IMAGE_FILENAMES[@]} $WARPED_FILENAME ) else break 1 fi done NUMBER_OF_PRIOR_IMAGES=${#WARPED_PRIOR_IMAGE_FILENAMES[*]} # Shiftsize is calculated because a variable amount of arguments can be used on the command line. # The shiftsize variable will give the correct number of arguments to skip. Issuing shift $shiftsize will # result in skipping that number of arguments on the command line, so that only the input images remain. shiftsize=$(($OPTIND - 1)) shift $shiftsize # The invocation of $* will now read all remaining arguments into the variable IMAGESETVARIABLE IMAGESETVARIABLE=$* NINFILES=$(($nargs - $shiftsize)) IMAGESETARRAY=() for IMG in $IMAGESETVARIABLE do TEMPLATE_IMAGES[${#TEMPLATE_IMAGES[@]}]=$IMG done if [[ ${#TEMPLATE_IMAGES[@]} -eq 0 ]]; then echo "Error: no template images specified." exit 1 fi if [[ $NUMBER_OF_MODALITIES -gt 1 ]]; then echo "--------------------------------------------------------------------------------------" echo " Cortical thickness using the following ${NUMBER_OF_MODALITIES}-tuples: " echo "--------------------------------------------------------------------------------------" for (( i = 0; i < ${#TEMPLATE_IMAGES[@]}; i+=$NUMBER_OF_MODALITIES )) do IMAGEMETRICSET="" for (( j = 0; j < $TEMPLATE_IMAGES; j++ )) do k=0 let k=$i+$j IMAGEMETRICSET="$IMAGEMETRICSET ${TEMPLATE_IMAGES[$k]}" done echo $IMAGEMETRICSET done echo "--------------------------------------------------------------------------------------" fi if [[ ${#MALF_ATLASES[@]} -ne ${#MALF_LABELS[@]} ]] then echo "Error: The number of malf atlases and labels aren't equal." fi # Set up various things related to RUN_QUICK # Can't do everything fast and still get good results if there is large deformation. # Initiate levels of fast: # 0 - Fast SST (old ANTS) but everything else slower for quality # 1 - + Fast antsct to SST # 2 - + Fast MALF cooking # 3 - + Fast everything RUN_OLD_ANTS_SST_CREATION=1 RUN_ANTSCT_TO_SST_QUICK=0 RUN_FAST_MALF_COOKING=0 RUN_FAST_ANTSCT_TO_GROUP_TEMPLATE=0 if [[ $RUN_QUICK -gt 0 ]]; then RUN_ANTSCT_TO_SST_QUICK=1 fi if [[ $RUN_QUICK -gt 1 ]]; then RUN_FAST_MALF_COOKING=1 fi if [[ $RUN_QUICK -gt 2 ]]; then RUN_FAST_ANTSCT_TO_GROUP_TEMPLATE=1 fi ################################################################################ # # Preliminaries: # 1. Check existence of inputs # 2. Figure out output directory and mkdir if necessary # 3. See if $REGISTRATION_TEMPLATE is the same as $BRAIN_TEMPLATE # ################################################################################ for (( i = 0; i < ${#TEMPLATE_IMAGES[@]}; i++ )) do if [[ ! -f ${TEMPLATE_IMAGES[$i]} ]]; then echo "The specified image \"${TEMPLATE_IMAGES[$i]}\" does not exist." exit 1 fi done ################################################################################ # # Run template through antsCorticalThickness.sh # ################################################################################ TEMPLATE_EXTRACTION_MASK=${OUTPUT_PREFIX}BrainExtractionMask.${OUTPUT_SUFFIX} TEMPLATE_EXTRACTION_REGISTRATION_MASK=${OUTPUT_PREFIX}BrainExtractionRegistrationMask.${OUTPUT_SUFFIX} TEMPLATE_PRIOR=${OUTPUT_PREFIX}Priors\%${FORMAT}d.${OUTPUT_SUFFIX} TEMPLATE_EXTRACTION_PRIOR=${OUTPUT_PREFIX}BrainExtractionMaskPrior.${OUTPUT_SUFFIX} TEMPLATE_CORTICAL_THICKNESS=${OUTPUT_PREFIX}CorticalThickness.${OUTPUT_SUFFIX} TEMPLATE_SKULL_STRIPPED=${OUTPUT_PREFIX}BrainExtractionBrain.${OUTPUT_SUFFIX} echo echo "--------------------------------------------------------------------------------------" echo " Creating template priors: running template through antsCorticalThickness " echo "--------------------------------------------------------------------------------------" echo time_start_priors=`date +%s` TEMPLATE_IMAGES_LIST='' for (( j=0; j < $NUMBER_OF_MODALITIES; j++ )) do TEMPLATE_IMAGES_LIST="${TEMPLATE_IMAGES_LIST} -a ${TEMPLATE_IMAGES[$j]}" done REG_MASK="" if [[ -f ${EXTRACTION_REGISTRATION_MASK} ]]; then REG_MASK="-f ${EXTRACTION_REGISTRATION_MASK}" fi if [[ ! -f ${TEMPLATE_CORTICAL_THICKNESS} ]]; then logCmd ${ANTSPATH}/antsCorticalThickness.sh \ -d ${DIMENSION} \ -q ${RUN_FAST_ANTSCT_TO_GROUP_TEMPLATE} \ $TEMPLATE_IMAGES_LIST \ $REG_MASK \ -e ${BRAIN_TEMPLATE} \ -g ${DENOISE} \ -m ${EXTRACTION_PRIOR} \ -k 0 \ -z ${DEBUG_MODE} \ -p ${SEGMENTATION_PRIOR} \ -w ${ATROPOS_SEGMENTATION_PRIOR_WEIGHT_SST} \ -o ${OUTPUT_PREFIX} fi TEMPLATE_POSTERIORS=( ${OUTPUT_PREFIX}BrainSegmentationPosteriors*.${OUTPUT_SUFFIX} ) TEMPLATE_POSTERIORS_EXIST=1 TEMPLATE_PRIORS_EXIST=1 TEMPLATE_PRIORS=() for (( j = 0; j < ${#TEMPLATE_POSTERIORS[@]}; j++ )) do POSTERIOR=${TEMPLATE_POSTERIORS[$j]} if [[ ! -f ${POSTERIOR} ]]; then TEMPLATE_POSTERIORS_EXIST=0 TEMPLATE_PRIORS_EXIST=0 break; fi TEMPLATE_PRIORS[$j]=${POSTERIOR/BrainSegmentationPosteriors/Priors} if [[ ! -f ${TEMPLATE_PRIORS[$j]} ]]; then TEMPLATE_PRIORS_EXIST=0 fi done if [[ ${TEMPLATE_POSTERIORS_EXIST} -eq 0 ]]; then echo "Error: Posteriors for the template do not exist." exit 1 fi logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${TEMPLATE_SKULL_STRIPPED} m ${TEMPLATE_IMAGES[0]} ${TEMPLATE_EXTRACTION_MASK} logCmd ${ANTSPATH}/SmoothImage ${DIMENSION} ${TEMPLATE_EXTRACTION_MASK} 1 ${TEMPLATE_EXTRACTION_PRIOR} 1 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${TEMPLATE_EXTRACTION_REGISTRATION_MASK} MD ${TEMPLATE_EXTRACTION_MASK} 40 if [[ ${TEMPLATE_PRIORS_EXIST} -eq 0 ]]; then if [[ ${#MALF_ATLASES[@]} -eq 0 ]]; then echo echo " ---> Smoothing template posteriors as priors." echo for j in ${TEMPLATE_POSTERIORS[@]} do PRIOR=${j/BrainSegmentationPosteriors/Priors} logCmd ${ANTSPATH}/SmoothImage ${DIMENSION} $j 1 $PRIOR 1 done else echo echo " ---> Cooking template priors using antsJointLabelFusion." echo TEMPLATE_MALF_LABELS_PREFIX=${OUTPUT_PREFIX} TEMPLATE_MALF_LABELS=${OUTPUT_PREFIX}Labels.nii.gz ATLAS_AND_LABELS_STRING='' for (( j=0; j < ${#MALF_ATLASES[@]}; j++ )) do ATLAS_AND_LABELS_STRING="${ATLAS_AND_LABELS_STRING} -g ${MALF_ATLASES[$j]} -l ${MALF_LABELS[$j]}" done if [[ ! -f ${TEMPLATE_MALF_LABELS} ]]; then logCmd ${ANTSPATH}/antsJointLabelFusion.sh \ -d ${DIMENSION} \ -q ${RUN_FAST_MALF_COOKING} \ -x ${TEMPLATE_EXTRACTION_MASK} \ -c ${DOQSUB} \ -j ${CORES} \ -t ${TEMPLATE_SKULL_STRIPPED} \ -o ${TEMPLATE_MALF_LABELS_PREFIX} \ ${ATLAS_AND_LABELS_STRING} fi TEMPLATE_PRIORS=() for (( j = 0; j < ${#TEMPLATE_POSTERIORS[@]}; j++ )) do POSTERIOR=${TEMPLATE_POSTERIORS[$j]} TEMPLATE_PRIORS[$j]=${POSTERIOR/BrainSegmentationPosteriors/Priors} let PRIOR_LABEL=$j+1 logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${TEMPLATE_MALF_LABELS} ${TEMPLATE_PRIORS[$j]} ${PRIOR_LABEL} ${PRIOR_LABEL} 1 0 logCmd ${ANTSPATH}/SmoothImage ${DIMENSION} ${TEMPLATE_PRIORS[$j]} 1 ${TEMPLATE_PRIORS[$j]} 1 done TMP_CSF_POSTERIOR=${OUTPUT_PREFIX}BrainSegmentationCsfPosteriorTmp.${OUTPUT_SUFFIX} logCmd ${ANTSPATH}/SmoothImage ${DIMENSION} ${TEMPLATE_POSTERIORS[0]} 1 ${TMP_CSF_POSTERIOR} 1 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${TEMPLATE_PRIORS[0]} max ${TEMPLATE_PRIORS[0]} ${TMP_CSF_POSTERIOR} # Brian's finishing touches on "cooking"---subtract out CSF from all other priors for (( j = 1; j < ${#TEMPLATE_PRIORS[@]}; j++ )) do let PRIOR_LABEL=$j+1 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${TEMPLATE_PRIORS[$j]} - ${TEMPLATE_PRIORS[$j]} ${TEMPLATE_PRIORS[0]} logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${TEMPLATE_PRIORS[$j]} ${TMP_CSF_POSTERIOR} 0 1 1 0 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${TEMPLATE_PRIORS[$j]} m ${TEMPLATE_PRIORS[$j]} ${TMP_CSF_POSTERIOR} done logCmd rm -f $TMP_CSF_POSTERIOR logCmd rm -f ${EMPLATE_MALF_LABELS_PREFIX}*log.txt fi fi time_end_priors=`date +%s` time_elapsed_priors=$((time_end_priors - time_start_priors)) echo echo "--------------------------------------------------------------------------------------" echo " Done with creating template priors: $(( time_elapsed_priors / 3600 ))h $(( time_elapsed_priors %3600 / 60 ))m $(( time_elapsed_priors % 60 ))s" echo "--------------------------------------------------------------------------------------" echo ants-2.2.0/Scripts/antsCorticalThickness.sh000077500000000000000000001710741311104306400207530ustar00rootroot00000000000000#!/bin/bash VERSION="0.0" # Check dependencies PROGRAM_DEPENDENCIES=( 'antsRegistration' 'antsApplyTransforms' 'N4BiasFieldCorrection' 'Atropos' 'KellyKapowski' ) SCRIPTS_DEPENDENCIES=( 'antsBrainExtraction.sh' 'antsAtroposN4.sh' ) for D in ${PROGRAM_DEPENDENCIES[@]}; do if [[ ! -s ${ANTSPATH}/${D} ]]; then echo "Error: we can't find the $D program." echo "Perhaps you need to \(re\)define \$ANTSPATH in your environment." exit fi done for D in ${SCRIPT_DEPENDENCIES[@]}; do if [[ ! -s ${ANTSPATH}/${D} ]]; then echo "We can't find the $D script." echo "Perhaps you need to \(re\)define \$ANTSPATH in your environment." exit fi done function Usage { cat < -o outputPrefix Example: bash $0 -d 3 -a t1.nii.gz -e brainWithSkullTemplate.nii.gz -m brainPrior.nii.gz -p segmentationPriors%d.nii.gz -o output Required arguments: We use *intensity* to denote the original anatomical image of the brain. We use *probability* to denote a probability image with values in range 0 to 1. We use *label* to denote a label image with values in range 0 to N. -d: Image dimension 2 or 3 (for 2- or 3-dimensional image) -a: Anatomical image Structural *intensity* image, typically T1. If more than one anatomical image is specified, subsequently specified images are used during the segmentation process. However, only the first image is used in the registration of priors. Our suggestion would be to specify the T1 as the first image. -e: Brain template Anatomical *intensity* template (possibly created using a population data set with buildtemplateparallel.sh in ANTs). This template is *not* skull-stripped. -m: Brain extraction probability mask Brain *probability* mask created using e.g. LPBA40 labels which have brain masks defined, and warped to anatomical template and averaged resulting in a probability image. -p: Brain segmentation priors Tissue *probability* priors corresponding to the image specified with the -e option. Specified using c-style formatting, e.g. -p labelsPriors%02d.nii.gz. We assume that the first four priors are ordered as follows 1: csf 2: cortical gm 3: wm 4: deep gm -o: Output prefix The following images are created: * ${OUTPUT_PREFIX}BrainExtractionMask.${OUTPUT_SUFFIX} * ${OUTPUT_PREFIX}BrainSegmentation.${OUTPUT_SUFFIX} * ${OUTPUT_PREFIX}BrainSegmentation*N4.${OUTPUT_SUFFIX} One for each anatomical input * ${OUTPUT_PREFIX}BrainSegmentationPosteriors*1.${OUTPUT_SUFFIX} CSF * ${OUTPUT_PREFIX}BrainSegmentationPosteriors*2.${OUTPUT_SUFFIX} GM * ${OUTPUT_PREFIX}BrainSegmentationPosteriors*3.${OUTPUT_SUFFIX} WM * ${OUTPUT_PREFIX}BrainSegmentationPosteriors*4.${OUTPUT_SUFFIX} DEEP GM * ... * ${OUTPUT_PREFIX}BrainSegmentationPosteriors*N.${OUTPUT_SUFFIX} where there are N priors * Number formatting of posteriors matches that of the priors. * ${OUTPUT_PREFIX}CorticalThickness.${OUTPUT_SUFFIX} Optional arguments: -s: image file suffix Any of the standard ITK IO formats e.g. nrrd, nii.gz (default), mhd -t: template for t1 registration Anatomical *intensity* template (assumed to be skull-stripped). A common use case would be where this would be the same template as specified in the -e option which is not skull stripped. We perform the registration (fixed image = individual subject and moving image = template) to produce the files. The output from this step is * ${OUTPUT_PREFIX}TemplateToSubject0GenericAffine.mat * ${OUTPUT_PREFIX}TemplateToSubject1Warp.${OUTPUT_SUFFIX} * ${OUTPUT_PREFIX}TemplateToSubject1InverseWarp.${OUTPUT_SUFFIX} * ${OUTPUT_PREFIX}TemplateToSubjectLogJacobian.${OUTPUT_SUFFIX} -f: extraction registration mask Mask (defined in the template space) used during registration for brain extraction. -k: keep temporary files Keep brain extraction/segmentation warps, etc (default = 0). -g: denoise anatomical images Denoise anatomical images (default = 0). -i: max iterations for registration ANTS registration max iterations (default = 100x100x70x20) -w: Atropos prior segmentation weight Atropos spatial prior *probability* weight for the segmentation (default = 0.25) -n: number of segmentation iterations N4 -> Atropos -> N4 iterations during segmentation (default = 3) -b: posterior formulation Atropos posterior formulation and whether or not to use mixture model proportions. e.g 'Socrates[1]' (default) or 'Aristotle[1]'. Choose the latter if you want use the distance priors (see also the -l option for label propagation control). -j: use floating-point precision Use floating point precision in registrations (default = 0) -u: use random seeding Use random number generated from system clock in Atropos (default = 1) -v: use b-spline smoothing Use B-spline SyN for registrations and B-spline exponential mapping in DiReCT. -r: cortical label image Cortical ROI labels to use as a prior for ATITH. -l: label propagation Incorporate a distance prior one the posterior formulation. Should be of the form 'label[lambda,boundaryProbability]' where label is a value of 1,2,3,... denoting label ID. The label probability for anything outside the current label = boundaryProbability * exp( -lambda * distanceFromBoundary ) Intuitively, smaller lambda values will increase the spatial capture range of the distance prior. To apply to all label values, simply omit specifying the label, i.e. -l [lambda,boundaryProbability]. -c Add prior combination to combined gray and white matters. For example, when calling KK for normal subjects, we combine the deep gray matter segmentation/posteriors with the white matter segmentation/posteriors. An additional example would be performing cortical thickness in the presence of white matter lesions. We can accommodate this by specifying a lesion mask posterior as an additional posterior (suppose label '7'), and then combine this with white matter by specifying '-c WM[7]' or '-c 3[7]'. -q: Use quick registration parameters If = 1, use antsRegistrationSyNQuick.sh as the basis for registration during brain extraction, brain segmentation, and (optional) normalization to a template. Otherwise use antsRegistrationSyN.sh (default = 0). -x: Number of iterations within Atropos (default 5). -y: Which stage of ACT to run (default = 0, run all). Tries to split in 2 hour chunks. Will produce OutputNameACTStageNComplete.txt for each completed stage. 1: brain extraction 2: template registration 3: tissue segmentation 4: template registration (improved, optional) 5: DiReCT cortical thickness 6: qc, quality control and summary measurements -z: Test / debug mode If > 0, runs a faster version of the script. Only for testing. Implies -u 0. Requires single thread computation for complete reproducibility. USAGE exit 1 } # Check outputs exist, runs at the end of the script # List of outputs is taken from the usage function checkOutputExists() { singleOutputs=( ${OUTPUT_PREFIX}BrainExtractionMask.${OUTPUT_SUFFIX} ${OUTPUT_PREFIX}BrainSegmentation.${OUTPUT_SUFFIX} ${OUTPUT_PREFIX}CorticalThickness.${OUTPUT_SUFFIX} ) singleOutputs=( ${singleOutputs[@]} ${OUTPUT_PREFIX}BrainSegmentationTiledMosaic.png ${OUTPUT_PREFIX}CorticalThicknessTiledMosaic.png ) if [[ -f ${REGISTRATION_TEMPLATE} ]]; then singleOutputs=( ${singleOutputs[@]} ${REGISTRATION_TEMPLATE_OUTPUT_PREFIX}0GenericAffine.mat ${REGISTRATION_TEMPLATE_OUTPUT_PREFIX}1Warp.${OUTPUT_SUFFIX} ${REGISTRATION_TEMPLATE_OUTPUT_PREFIX}1InverseWarp.${OUTPUT_SUFFIX} ${REGISTRATION_TEMPLATE_OUTPUT_PREFIX}LogJacobian.${OUTPUT_SUFFIX} ) fi missingOutput=0 for img in $singleOutputs; do if [[ ! -f $img ]]; then echo "Missing output image $img" missingOutput=1 fi done # Now check numbered output, numbers based on images for (( i = 0; i < ${#ANATOMICAL_IMAGES[@]}; i++ )) do if [[ ! -f ${OUTPUT_PREFIX}BrainSegmentation${i}N4.${OUTPUT_SUFFIX} ]]; then echo "Missing output image ${OUTPUT_PREFIX}BrainSegmentation${i}N4.${OUTPUT_SUFFIX}" missingOutput=1 fi done # Segmentation output depends on the number of priors and the numbering format segNumWidth=${#GRAY_MATTER_LABEL_FORMAT} for (( j = 1; j <= ${NUMBER_OF_PRIOR_IMAGES}; j++ )); do num=$(printf "%0${segNumWidth}d" $j) if [[ ! -f ${OUTPUT_PREFIX}BrainSegmentationPosteriors${num}.${OUTPUT_SUFFIX} ]]; then echo "Missing output image ${OUTPUT_PREFIX}BrainSegmentationPosteriors${num}.${OUTPUT_SUFFIX}" missingOutput=1 fi done if [[ $missingOutput -gt 0 ]]; then echo "Some of the output does not exist" return 1 fi return 0 } echoParameters() { cat <>>>>>>>>>>>>>>>>>>>" echo $cmd $cmd cmdExit=$? if [[ $cmdExit -gt 0 ]]; then echo "ERROR: command exited with nonzero status $cmdExit" echo "Command: $cmd" echo if [[ ! $DEBUG_MODE -gt 0 ]]; then exit 1 fi fi echo "END <<<<<<<<<<<<<<<<<<<<" echo echo return $cmdExit } ################################################################################ # # Main routine # ################################################################################ HOSTNAME=`hostname` DATE=`date` CURRENT_DIR=`pwd`/ OUTPUT_DIR=${CURRENT_DIR}/tmp$RANDOM/ OUTPUT_PREFIX=${OUTPUT_DIR}/tmp OUTPUT_SUFFIX="nii.gz" KEEP_TMP_IMAGES=0 DIMENSION=3 ANATOMICAL_IMAGES=() REGISTRATION_TEMPLATE="" DO_REGISTRATION_TO_TEMPLATE=0 USE_RANDOM_SEEDING=1 RUN_QUICK=0 DENOISE_ANATOMICAL_IMAGES=0 BRAIN_TEMPLATE="" EXTRACTION_PRIOR="" EXTRACTION_REGISTRATION_MASK="" SEGMENTATION_PRIOR="" CORTICAL_LABEL_IMAGE="" CSF_MATTER_LABEL=1 GRAY_MATTER_LABEL=2 WHITE_MATTER_LABEL=3 DEEP_GRAY_MATTER_LABEL=4 ATROPOS_SEGMENTATION_PRIOR_WEIGHT=0.25 ################################################################################ # # Programs and their parameters # ################################################################################ ANTS=${ANTSPATH}/antsRegistration ANTS_MAX_ITERATIONS="100x100x70x20" ANTS_TRANSFORMATION="SyN[0.1,3,0]" ANTS_LINEAR_METRIC_PARAMS="1,32,Regular,0.25" ANTS_LINEAR_CONVERGENCE="[1000x500x250x100,1e-8,10]" ANTS_METRIC="CC" ANTS_METRIC_PARAMS="1,4" WARP=${ANTSPATH}/antsApplyTransforms N4=${ANTSPATH}/N4BiasFieldCorrection N4_CONVERGENCE_1="[50x50x50x50,0.0000001]" N4_CONVERGENCE_2="[50x50x50x50,0.0000001]" N4_SHRINK_FACTOR_1=4 N4_SHRINK_FACTOR_2=2 N4_BSPLINE_PARAMS="[200]" ATROPOS=${ANTSPATH}/Atropos ATROPOS_SEGMENTATION_INITIALIZATION="PriorProbabilityImages" ATROPOS_SEGMENTATION_LIKELIHOOD="Gaussian" ATROPOS_SEGMENTATION_CONVERGENCE="[5,0.0]" ATROPOS_SEGMENTATION_POSTERIOR_FORMULATION="Socrates[1]" ATROPOS_SEGMENTATION_NUMBER_OF_ITERATIONS=3 ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS=5 # to be backward compatible but i like 25 ATROPOS_SEGMENTATION_LABEL_PROPAGATION=() DIRECT=${ANTSPATH}/KellyKapowski DIRECT_CONVERGENCE="[45,0.0,10]" DIRECT_THICKNESS_PRIOR="10" DIRECT_GRAD_STEP_SIZE="0.025" DIRECT_SMOOTHING_PARAMETER="1.5" DIRECT_NUMBER_OF_DIFF_COMPOSITIONS="10" PRIOR_COMBINATIONS=( 'WM[4]' ) USE_FLOAT_PRECISION=0 USE_BSPLINE_SMOOTHING=0 if [[ $# -lt 3 ]] ; then Usage >&2 exit 1 else while getopts "a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:v:w:x:y:z:" OPT do case $OPT in a) #anatomical t1 image ANATOMICAL_IMAGES[${#ANATOMICAL_IMAGES[@]}]=$OPTARG ;; b) # posterior formulation ATROPOS_SEGMENTATION_POSTERIOR_FORMULATION=$OPTARG ;; c) # prior combinations PRIOR_COMBINATIONS[${#PRIOR_COMBINATIONS[@]}]=$OPTARG ;; d) #dimensions DIMENSION=$OPTARG if [[ ${DIMENSION} -gt 3 || ${DIMENSION} -lt 2 ]]; then echo " Error: ImageDimension must be 2 or 3 " exit 1 fi ;; e) #brain extraction anatomical image BRAIN_TEMPLATE=$OPTARG ;; f) #brain extraction registration mask EXTRACTION_REGISTRATION_MASK=$OPTARG ;; g) # denoise anatomical images DENOISE_ANATOMICAL_IMAGES=$OPTARG ;; h) #help Usage >&2 exit 0 ;; i) #max_iterations ANTS_MAX_ITERATIONS=$OPTARG ;; j) #use floating point precision USE_FLOAT_PRECISION=$OPTARG ;; k) #keep tmp images KEEP_TMP_IMAGES=$OPTARG ;; l) ATROPOS_SEGMENTATION_LABEL_PROPAGATION[${#ATROPOS_SEGMENTATION_LABEL_PROPAGATION[@]}]=$OPTARG ;; m) #brain extraction prior probability mask EXTRACTION_PRIOR=$OPTARG ;; n) #atropos segmentation iterations ATROPOS_SEGMENTATION_NUMBER_OF_ITERATIONS=$OPTARG ;; x) #atropos segmentation internal iterations ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS=$OPTARG ;; o) #output prefix OUTPUT_PREFIX=$OPTARG ;; p) #brain segmentation label prior image SEGMENTATION_PRIOR=$OPTARG ;; q) # run quick RUN_QUICK=$OPTARG ;; r) #cortical label image CORTICAL_LABEL_IMAGE=$OPTARG ;; s) #output suffix OUTPUT_SUFFIX=$OPTARG ;; t) #template registration image REGISTRATION_TEMPLATE=$OPTARG DO_REGISTRATION_TO_TEMPLATE=1 ;; u) #use random seeding USE_RANDOM_SEEDING=$OPTARG ;; v) #use b-spline smoothing in registration and direct USE_BSPLINE_SMOOTHING=$OPTARG ;; w) #atropos prior weight ATROPOS_SEGMENTATION_PRIOR_WEIGHT=$OPTARG ;; y) #which stage ACT_STAGE=$OPTARG ;; z) #debug mode DEBUG_MODE=$OPTARG ;; *) # getopts issues an error message echo "ERROR: unrecognized option -$OPT $OPTARG" exit 1 ;; esac done fi if [[ $USE_BSPLINE_SMOOTHING -ne 0 ]]; then ANTS_TRANSFORMATION="BSplineSyN[0.1,26,0,3]" DIRECT_SMOOTHING_PARAMETER="5.75" fi if [[ $DEBUG_MODE -gt 0 ]]; then echo " WARNING - Running in test / debug mode. Results will be suboptimal " OUTPUT_PREFIX="${OUTPUT_PREFIX}testMode_" # Speed up by doing fewer its. Careful about changing this because # certain things are hard coded elsewhere, eg number of levels ANTS_MAX_ITERATIONS="40x40x20x0" ANTS_LINEAR_CONVERGENCE="[100x100x50x0,1e-8,10]" ANTS_METRIC_PARAMS="1,2" # I think this is the number of times we run the whole N4 / Atropos thing, at the cost of about 10 minutes a time ATROPOS_SEGMENTATION_NUMBER_OF_ITERATIONS=1 ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS=5 # internal to atropos DIRECT_CONVERGENCE="[5,0.0,10]" # Fix random seed to replicate exact results on each run USE_RANDOM_SEEDING=0 fi ################################################################################ # # Preliminaries: # 1. Check existence of inputs # 2. Figure out output directory and mkdir if necessary # ################################################################################ for (( i = 0; i < ${#ANATOMICAL_IMAGES[@]}; i++ )) do if [[ ! -f ${ANATOMICAL_IMAGES[$i]} ]]; then echo "The specified image \"${ANATOMICAL_IMAGES[$i]}\" does not exist." exit 1 fi done FORMAT=${SEGMENTATION_PRIOR} PREFORMAT=${FORMAT%%\%*} POSTFORMAT=${FORMAT##*d} FORMAT=${FORMAT#*\%} FORMAT=${FORMAT%%d*} REPCHARACTER='' TOTAL_LENGTH=0 if [ ${#FORMAT} -eq 2 ] then REPCHARACTER=${FORMAT:0:1} TOTAL_LENGTH=${FORMAT:1:1} fi # MAXNUMBER=$(( 10 ** $TOTAL_LENGTH )) MAXNUMBER=1000 PRIOR_IMAGE_FILENAMES=() WARPED_PRIOR_IMAGE_FILENAMES=() BRAIN_SEGMENTATION_OUTPUT=${OUTPUT_PREFIX}BrainSegmentation SEGMENTATION_WARP_OUTPUT_PREFIX=${BRAIN_SEGMENTATION_OUTPUT}Prior SEGMENTATION_PRIOR_WARPED=${SEGMENTATION_WARP_OUTPUT_PREFIX}Warped for (( i = 1; i < $MAXNUMBER; i++ )) do NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#i} )) ROOT=''; for(( j=0; j < $NUMBER_OF_REPS; j++ )) do ROOT=${ROOT}${REPCHARACTER} done FILENAME=${PREFORMAT}${ROOT}${i}${POSTFORMAT} WARPED_FILENAME=${SEGMENTATION_PRIOR_WARPED}${ROOT}${i}.${OUTPUT_SUFFIX} if [[ -f $FILENAME ]]; then PRIOR_IMAGE_FILENAMES=( ${PRIOR_IMAGE_FILENAMES[@]} $FILENAME ) WARPED_PRIOR_IMAGE_FILENAMES=( ${WARPED_PRIOR_IMAGE_FILENAMES[@]} $WARPED_FILENAME ) else break 1 fi done NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#DEEP_GRAY_MATTER_LABEL} )) ROOT=''; for(( j=0; j < $NUMBER_OF_REPS; j++ )) do ROOT=${ROOT}${REPCHARACTER} done DEEP_GRAY_MATTER_LABEL_FORMAT=${ROOT}${DEEP_GRAY_MATTER_LABEL} NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#WHITE_MATTER_LABEL} )) ROOT=''; for(( j=0; j < $NUMBER_OF_REPS; j++ )) do ROOT=${ROOT}${REPCHARACTER} done WHITE_MATTER_LABEL_FORMAT=${ROOT}${WHITE_MATTER_LABEL} NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#GRAY_MATTER_LABEL} )) ROOT=''; for(( j=0; j < $NUMBER_OF_REPS; j++ )) do ROOT=${ROOT}${REPCHARACTER} done GRAY_MATTER_LABEL_FORMAT=${ROOT}${GRAY_MATTER_LABEL} NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#CSF_MATTER_LABEL} )) ROOT=''; for(( j=0; j < $NUMBER_OF_REPS; j++ )) do ROOT=${ROOT}${REPCHARACTER} done CSF_MATTER_LABEL_FORMAT=${ROOT}${CSF_MATTER_LABEL} SEGMENTATION_PRIOR_WARPED=${SEGMENTATION_PRIOR_WARPED}\%${FORMAT}d.${OUTPUT_SUFFIX} NUMBER_OF_PRIOR_IMAGES=${#WARPED_PRIOR_IMAGE_FILENAMES[*]} if [[ ${NUMBER_OF_PRIOR_IMAGES} -lt 4 ]]; then echo "Expected at least 4 prior images (${NUMBER_OF_PRIOR_IMAGES} are specified). Check the command line specification." exit 1 fi for(( j=0; j < $NUMBER_OF_PRIOR_IMAGES; j++ )) do if [[ ! -f ${PRIOR_IMAGE_FILENAMES[$j]} ]]; then echo "Prior image $j ${PRIOR_IMAGE_FILENAMES[$j]} does not exist." exit 1 fi done # These arrays contain the formatted labels that will be used to combine with the white # and gray matter posteriors for the cortical thickness section. CORTICAL_THICKNESS_WHITE_MATTER_OTHER_LABELS=() CORTICAL_THICKNESS_GRAY_MATTER_OTHER_LABELS=() for(( j=0; j < ${#PRIOR_COMBINATIONS[@]}; j++ )) do echo ${PRIOR_COMBINATIONS[$j]} COMBINATION=( $( echo ${PRIOR_COMBINATIONS[$j]} | tr "[]," "\n" ) ) echo ${COMBINATION[@]} if [[ ${COMBINATION[0]} == ${WHITE_MATTER_LABEL} || ${COMBINATION[0]} == 'WM' ]]; then for(( k=1; k < ${#COMBINATION[@]}; k++ )) do OTHER_LABEL=${COMBINATION[$k]} CORTICAL_THICKNESS_WHITE_MATTER_OTHER_LABELS[${#CORTICAL_THICKNESS_WHITE_MATTER_OTHER_LABELS[@]}]=${OTHER_LABEL} done elif [[ ${COMBINATION[0]} == ${GRAY_MATTER_LABEL} || ${COMBINATION[0]} == 'GM' ]]; then for(( k=1; k < ${#COMBINATION[@]}; k++ )) do OTHER_LABEL=${COMBINATION[$k]} CORTICAL_THICKNESS_GRAY_MATTER_OTHER_LABELS[${#CORTICAL_THICKNESS_GRAY_MATTER_OTHER_LABELS[@]}]=${OTHER_LABEL} done else echo "We only combine with the gray matter or the white matter." echo "The label ${COMBINATION[0]} does not correspond to the gray or white matters." exit 1 fi done CORTICAL_THICKNESS_GRAY_MATTER_OTHER_LABELS_FORMAT=( $( echo "${CORTICAL_THICKNESS_GRAY_MATTER_OTHER_LABELS_FORMAT[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ' ) ) CORTICAL_THICKNESS_WHITE_MATTER_OTHER_LABELS_FORMAT=( $( echo "${CORTICAL_THICKNESS_WHITE_MATTER_OTHER_LABELS_FORMAT[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ' ) ) if [[ $DO_REGISTRATION_TO_TEMPLATE -eq 1 ]]; then if [[ ! -f ${REGISTRATION_TEMPLATE} ]] then echo "Template for registration, ${REGISTRATION_TEMPLATE}, does not exist." exit 1 fi fi OUTPUT_DIR=${OUTPUT_PREFIX%\/*} if [[ ! -d $OUTPUT_DIR ]]; then echo "The output directory \"$OUTPUT_DIR\" does not exist. Making it." mkdir -p $OUTPUT_DIR fi echoParameters >&2 echo "--------------------- Running `basename $0` on $HOSTNAME ---------------------" time_start=`date +%s` ################################################################################ # # Output images # ################################################################################ BRAIN_EXTRACTION_MASK=${OUTPUT_PREFIX}BrainExtractionMask.${OUTPUT_SUFFIX} BRAIN_SEGMENTATION=${OUTPUT_PREFIX}BrainSegmentation.${OUTPUT_SUFFIX} CORTICAL_THICKNESS_IMAGE=${OUTPUT_PREFIX}CorticalThickness.${OUTPUT_SUFFIX} ################################################################################ # # Brain extraction # ################################################################################ EXTRACTED_SEGMENTATION_BRAIN=${OUTPUT_PREFIX}BrainExtractionBrain.${OUTPUT_SUFFIX} EXTRACTION_GENERIC_AFFINE=${OUTPUT_PREFIX}BrainExtractionPrior0GenericAffine.mat EXTRACTED_BRAIN_TEMPLATE=${OUTPUT_PREFIX}ExtractedTemplateBrain.${OUTPUT_SUFFIX} if [[ ! -s ${OUTPUT_PREFIX}ACTStage1Complete.txt ]]; then if [[ ${ACT_STAGE} -eq 0 ]] || [[ ${ACT_STAGE} -eq 1 ]] ; then # BAStages bxt if [[ ! -f ${BRAIN_EXTRACTION_MASK} ]]; then if [[ ! -f ${BRAIN_TEMPLATE} ]]; then echo "The extraction template doesn't exist:" echo " $BRAIN_TEMPLATE" exit 1 fi if [[ ! -f ${EXTRACTION_PRIOR} ]]; then echo "The brain extraction prior doesn't exist:" echo " $EXTRACTION_PRIOR" exit 1 fi if [[ -f ${EXTRACTION_REGISTRATION_MASK} ]] then logCmd ${ANTSPATH}/antsBrainExtraction.sh \ -d ${DIMENSION} \ -a ${ANATOMICAL_IMAGES[0]} \ -e ${BRAIN_TEMPLATE} \ -f ${EXTRACTION_REGISTRATION_MASK} \ -m ${EXTRACTION_PRIOR} \ -o ${OUTPUT_PREFIX} \ -k ${KEEP_TMP_IMAGES} \ -s ${OUTPUT_SUFFIX} \ -q ${USE_FLOAT_PRECISION} \ -u ${USE_RANDOM_SEEDING} \ -z ${DEBUG_MODE} else logCmd ${ANTSPATH}/antsBrainExtraction.sh \ -d ${DIMENSION} \ -a ${ANATOMICAL_IMAGES[0]} \ -e ${BRAIN_TEMPLATE} \ -m ${EXTRACTION_PRIOR} \ -o ${OUTPUT_PREFIX} \ -k ${KEEP_TMP_IMAGES} \ -s ${OUTPUT_SUFFIX} \ -q ${USE_FLOAT_PRECISION} \ -u ${USE_RANDOM_SEEDING} \ -z ${DEBUG_MODE} fi fi if [[ ! -f ${EXTRACTED_SEGMENTATION_BRAIN} ]]; then logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTED_SEGMENTATION_BRAIN} m ${ANATOMICAL_IMAGES[0]} ${BRAIN_EXTRACTION_MASK} fi if [[ -f ${BRAIN_TEMPLATE} ]] && [[ ! -f ${EXTRACTED_BRAIN_TEMPLATE} ]]; then logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${EXTRACTION_PRIOR} ${EXTRACTED_BRAIN_TEMPLATE} 0.1 1.01 1 0 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTED_BRAIN_TEMPLATE} m ${BRAIN_TEMPLATE} ${EXTRACTED_BRAIN_TEMPLATE} fi echo ${OUTPUT_PREFIX}ACTStage1Complete.txt > ${OUTPUT_PREFIX}ACTStage1Complete.txt fi # BAStages fi # check completion ################################################################################ # # Brain segmentation # ################################################################################ SEGMENTATION_WARP=${SEGMENTATION_WARP_OUTPUT_PREFIX}1Warp.nii.gz SEGMENTATION_INVERSE_WARP=${SEGMENTATION_WARP_OUTPUT_PREFIX}1InverseWarp.nii.gz SEGMENTATION_GENERIC_AFFINE=${SEGMENTATION_WARP_OUTPUT_PREFIX}0GenericAffine.mat SEGMENTATION_MASK_DILATED=${BRAIN_SEGMENTATION_OUTPUT}MaskDilated.nii.gz SEGMENTATION_CONVERGENCE_FILE=${BRAIN_SEGMENTATION_OUTPUT}Convergence.txt if [[ ! -s ${OUTPUT_PREFIX}ACTStage2Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage1Complete.txt ]]; then if [[ ${ACT_STAGE} -eq 0 ]] || [[ ${ACT_STAGE} -eq 2 ]] ; then # BAStages reg echo echo "--------------------------------------------------------------------------------------" echo " Brain segmentation using the following steps:" echo " 1) Register ${EXTRACTED_BRAIN_TEMPLATE} and ${SEGMENTATION_PRIOR} to ${ANATOMICAL_IMAGES[0]}" echo " 2) Warp priors to ${ANATOMICAL_IMAGES[0]}" echo " 3) N-tissue segmentation using Atropos and N4" echo "--------------------------------------------------------------------------------------" echo time_start_brain_registration=`date +%s` # Check to see if the warped priors exist. If so, we don't need to warp # the template and priors to the individual subject. WARPED_PRIORS_ALREADY_EXIST=1; for (( i = 0; i < ${NUMBER_OF_PRIOR_IMAGES}; i++ )) do if [[ ! -f ${WARPED_PRIOR_IMAGE_FILENAMES[$i]} ]]; then WARPED_PRIORS_ALREADY_EXIST=0 break fi done TMP_FILES=() if [[ $WARPED_PRIORS_ALREADY_EXIST -eq 0 ]] then # Check inputs if [[ ! -f ${EXTRACTED_BRAIN_TEMPLATE} ]]; then echo "The segmentation template doesn't exist:" echo " ${EXTRACTED_BRAIN_TEMPLATE}" rm -f ${OUTPUT_PREFIX}ACTStage1Complete.txt exit 1 fi if [[ ! -f ${EXTRACTED_SEGMENTATION_BRAIN} ]]; then echo "The extracted brain doesn't exist:" echo " ${EXTRACTED_SEGMENTATION_BRAIN}" rm -f ${OUTPUT_PREFIX}ACTStage1Complete.txt exit 1 fi if [[ ! -f ${BRAIN_EXTRACTION_MASK} ]]; then echo "The brain extraction mask does not exist:" echo " ${BRAIN_EXTRACTION_MASK}" rm -f ${OUTPUT_PREFIX}ACTStage1Complete.txt exit 1 fi if [[ ! -f ${SEGMENTATION_WARP} ]]; then logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SEGMENTATION_MASK_DILATED} MD ${BRAIN_EXTRACTION_MASK} 20 basecall='' if [[ ${RUN_QUICK} -ne 0 ]]; then TMP_FILES=( ${TMP_FILES[@]} "${SEGMENTATION_WARP_OUTPUT_PREFIX}Warped.nii.gz" "${SEGMENTATION_WARP_OUTPUT_PREFIX}InverseWarped.nii.gz" ) basecall="${ANTSPATH}/antsRegistrationSyNQuick.sh -d ${DIMENSION} -f ${EXTRACTED_SEGMENTATION_BRAIN}" basecall="${basecall} -m ${EXTRACTED_BRAIN_TEMPLATE} -o ${SEGMENTATION_WARP_OUTPUT_PREFIX} -j 1" if [[ ${USE_FLOAT_PRECISION} -ne 0 ]]; then basecall="${basecall} -p f" fi else basecall="${ANTS} -d ${DIMENSION} -u 1 -w [0.01,0.99] -o ${SEGMENTATION_WARP_OUTPUT_PREFIX} --float ${USE_FLOAT_PRECISION} --verbose 1" IMAGES="${EXTRACTED_SEGMENTATION_BRAIN},${EXTRACTED_BRAIN_TEMPLATE}" if [[ -f ${EXTRACTION_GENERIC_AFFINE} ]]; then basecall="${basecall} -r [${EXTRACTION_GENERIC_AFFINE},1]" else basecall="${basecall} -r [${IMAGES},1]" fi basecall="${basecall} -x [${SEGMENTATION_MASK_DILATED}]" stage1="-m MI[${IMAGES},${ANTS_LINEAR_METRIC_PARAMS}] -c ${ANTS_LINEAR_CONVERGENCE} -t Affine[0.1] -f 8x4x2x1 -s 4x2x1x0" stage2="-m CC[${IMAGES},1,4] -c [${ANTS_MAX_ITERATIONS},1e-9,15] -t ${ANTS_TRANSFORMATION} -f 6x4x2x1 -s 3x2x1x0" basecall="${basecall} ${stage1} ${stage2}" fi exe_brain_segmentation_1=${basecall} # Precision errors in .nii (which stores things as float) headers can cause problems, so attempt to make everything consistent. # Won't be perfectly consistent because we don't change ${ANATOMICAL_IMAGES[0]} and CopyImageHeaderInformation does not make # a perfect copy. But hopefully close enough for img in ${BRAIN_EXTRACTION_MASK} ${EXTRACTED_SEGMENTATION_BRAIN} ${SEGMENTATION_MASK_DILATED}; do logCmd ${ANTSPATH}/CopyImageHeaderInformation ${ANATOMICAL_IMAGES[0]} ${img} ${img} 1 1 1 done logCmd $exe_brain_segmentation_1 fi ## check to see if the output registration transforms exist if [[ ! -f ${SEGMENTATION_GENERIC_AFFINE} ]]; then echo "The registration component of the segmentation step didn't complete properly." echo "The transform file ${SEGMENTATION_GENERIC_AFFINE} does not exist." exit 1 fi if [[ ! -f ${SEGMENTATION_WARP} ]]; then echo "The registration component of the segmentation step didn't complete properly." echo "The transform file ${SEGMENTATION_WARP} does not exist." exit 1 fi ## Step 2 ## for (( i = 0; i < ${NUMBER_OF_PRIOR_IMAGES}; i++ )) do if [[ ! -f ${PRIOR_IMAGE_FILENAMES[$i]} ]]; then echo "The prior image file name does not exist:" echo " ${PRIOR_IMAGE_FILENAMES[$i]}" exit 1 fi exe_brain_segmentation_2="${WARP} -d ${DIMENSION} -i ${PRIOR_IMAGE_FILENAMES[$i]} -o ${WARPED_PRIOR_IMAGE_FILENAMES[$i]} -r ${ANATOMICAL_IMAGES[0]} -n Gaussian -t ${SEGMENTATION_WARP} -t ${SEGMENTATION_GENERIC_AFFINE} --float ${USE_FLOAT_PRECISION} --verbose 1" logCmd $exe_brain_segmentation_2 done fi echo ${OUTPUT_PREFIX}ACTStage2Complete.txt > ${OUTPUT_PREFIX}ACTStage2Complete.txt time_end_brain_registration=`date +%s` time_elapsed_brain_registration=$((time_end_brain_registration - time_start_brain_registration)) echo echo "--------------------------------------------------------------------------------------" echo " Done with brain registration: $(( time_elapsed_brain_segmentation / 3600 ))h $(( time_elapsed_brain_registration %3600 / 60 ))m $(( time_elapsed_brain_registration % 60 ))s" echo "--------------------------------------------------------------------------------------" echo fi # BAStages check reg fi # BAStages check reg # We do two stages of antsAtroposN4. The first stage is to get a good N4 # bias corrected image(s). This bias corrected image(s) is used as input to the # second stage where we only do 2 iterations. if [[ ! -s ${OUTPUT_PREFIX}ACTStage3Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage2Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage1Complete.txt ]] ; then if [[ ${ACT_STAGE} -eq 0 ]] || [[ ${ACT_STAGE} -eq 3 ]] ; then # BAStages seg time_start_brain_segmentation=`date +%s` ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE=''; for (( j = 0; j < ${#ANATOMICAL_IMAGES[@]}; j++ )) do ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE="${ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE} -a ${ANATOMICAL_IMAGES[$j]}" done ATROPOS_LABEL_PROPAGATION_COMMAND_LINE='' for (( j = 0; j < ${#ATROPOS_SEGMENTATION_LABEL_PROPAGATION[@]}; j++ )) do ATROPOS_LABEL_PROPAGATION_COMMAND_LINE="${ATROPOS_LABEL_PROPAGATION_COMMAND_LINE} -l ${ATROPOS_SEGMENTATION_LABEL_PROPAGATION[$j]}"; done # include everything but the csf N4_INCLUDE_PRIORS_COMMAND_LINE='' for (( j = 2; j <= ${NUMBER_OF_PRIOR_IMAGES}; j++ )) do N4_INCLUDE_PRIORS_COMMAND_LINE="${N4_INCLUDE_PRIORS_COMMAND_LINE} -y $j"; done logCmd ${ANTSPATH}/antsAtroposN4.sh \ -d ${DIMENSION} \ -b ${ATROPOS_SEGMENTATION_POSTERIOR_FORMULATION} \ ${ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE} \ ${ATROPOS_LABEL_PROPAGATION_COMMAND_LINE} \ -x ${BRAIN_EXTRACTION_MASK} \ -m ${ATROPOS_SEGMENTATION_NUMBER_OF_ITERATIONS} \ -n ${ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS} \ -c ${NUMBER_OF_PRIOR_IMAGES} \ ${N4_INCLUDE_PRIORS_COMMAND_LINE} \ -p ${SEGMENTATION_PRIOR_WARPED} \ -w ${ATROPOS_SEGMENTATION_PRIOR_WEIGHT} \ -o ${OUTPUT_PREFIX}Brain \ -u ${USE_RANDOM_SEEDING} \ -g ${DENOISE_ANATOMICAL_IMAGES} \ -k ${KEEP_TMP_IMAGES} \ -s ${OUTPUT_SUFFIX} \ -z ${DEBUG_MODE} ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE='' for (( j = 0; j < ${#ANATOMICAL_IMAGES[@]}; j++ )) do ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE="${ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE} -a ${OUTPUT_PREFIX}BrainSegmentation${j}N4.${OUTPUT_SUFFIX}"; done logCmd ${ANTSPATH}/antsAtroposN4.sh \ -d ${DIMENSION} \ -b ${ATROPOS_SEGMENTATION_POSTERIOR_FORMULATION} \ ${ATROPOS_ANATOMICAL_IMAGES_COMMAND_LINE} \ ${ATROPOS_LABEL_PROPAGATION_COMMAND_LINE} \ -x ${BRAIN_EXTRACTION_MASK} \ -m 2 \ -n ${ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS} \ -c ${NUMBER_OF_PRIOR_IMAGES} \ ${N4_INCLUDE_PRIORS_COMMAND_LINE} \ -p ${SEGMENTATION_PRIOR_WARPED} \ -w ${ATROPOS_SEGMENTATION_PRIOR_WEIGHT} \ -o ${OUTPUT_PREFIX}Brain \ -u ${USE_RANDOM_SEEDING} \ -g ${DENOISE_ANATOMICAL_IMAGES} \ -k ${KEEP_TMP_IMAGES} \ -s ${OUTPUT_SUFFIX} \ -z ${DEBUG_MODE} ## Step 3 ### TMP_FILES=( ${TMP_FILES[@]} $EXTRACTION_GENERIC_AFFINE $EXTRACTED_SEGMENTATION_BRAIN $SEGMENTATION_MASK_DILATED $EXTRACTED_BRAIN_TEMPLATE ) TMP_FILES=( ${TMP_FILES[@]} ${WARPED_PRIOR_IMAGE_FILENAMES[@]} ) if [[ $TEMPLATES_ARE_IDENTICAL -eq 0 ]]; then TMP_FILES=( ${TMP_FILES[@]} $SEGMENTATION_WARP $SEGMENTATION_INVERSE_WARP $SEGMENTATION_GENERIC_AFFINE ) fi if [[ $KEEP_TMP_IMAGES -eq 0 ]]; then for f in ${TMP_FILES[@]} do if [[ -e $f ]]; then logCmd rm -f $f else echo "WARNING: expected temp file doesn't exist: $f" fi done fi time_end_brain_segmentation=`date +%s` time_elapsed_brain_segmentation=$((time_end_brain_segmentation - time_start_brain_segmentation)) echo echo "--------------------------------------------------------------------------------------" echo " Done with brain segmentation: $(( time_elapsed_brain_segmentation / 3600 ))h $(( time_elapsed_brain_segmentation %3600 / 60 ))m $(( time_elapsed_brain_segmentation % 60 ))s" echo "--------------------------------------------------------------------------------------" echo echo ${OUTPUT_PREFIX}ACTStage3Complete.txt > ${OUTPUT_PREFIX}ACTStage3Complete.txt fi fi # BAStages seg ################################################################################ # # Registration to a template # ################################################################################ # These affect output; keep them consistent with usage and checkOutputExists function REGISTRATION_TEMPLATE_OUTPUT_PREFIX=${OUTPUT_PREFIX}SubjectToTemplate REGISTRATION_TEMPLATE_GENERIC_AFFINE=${REGISTRATION_TEMPLATE_OUTPUT_PREFIX}0GenericAffine.mat REGISTRATION_TEMPLATE_WARP=${REGISTRATION_TEMPLATE_OUTPUT_PREFIX}1Warp.${OUTPUT_SUFFIX} REGISTRATION_TEMPLATE_INVERSE_WARP=${REGISTRATION_TEMPLATE_OUTPUT_PREFIX}1InverseWarp.${OUTPUT_SUFFIX} REGISTRATION_LOG_JACOBIAN=${REGISTRATION_TEMPLATE_OUTPUT_PREFIX}LogJacobian.${OUTPUT_SUFFIX} # Want to have transforms for both directions REGISTRATION_SUBJECT_OUTPUT_PREFIX=${OUTPUT_PREFIX}TemplateToSubject REGISTRATION_SUBJECT_GENERIC_AFFINE=${REGISTRATION_SUBJECT_OUTPUT_PREFIX}1GenericAffine.mat REGISTRATION_SUBJECT_WARP=${REGISTRATION_SUBJECT_OUTPUT_PREFIX}0Warp.${OUTPUT_SUFFIX} # Use first N4 corrected segmentation image, which we assume to be T1 HEAD_N4_IMAGE=${OUTPUT_PREFIX}BrainSegmentation0N4.${OUTPUT_SUFFIX} EXTRACTED_SEGMENTATION_BRAIN_N4_IMAGE=${OUTPUT_PREFIX}ExtractedBrain0N4.nii.gz if [[ ! -s ${OUTPUT_PREFIX}ACTStage4Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage3Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage2Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage1Complete.txt ]] ; then if [[ ${ACT_STAGE} -eq 0 ]] || [[ ${ACT_STAGE} -eq 4 ]] ; then # BAStages reg if [[ -f ${REGISTRATION_TEMPLATE} ]] && [[ ! -f $REGISTRATION_LOG_JACOBIAN ]]; then TMP_FILES=() echo echo "--------------------------------------------------------------------------------------" echo " Registration brain masked ${HEAD_N4_IMAGE} to ${REGISTRATION_TEMPLATE} " echo "--------------------------------------------------------------------------------------" echo logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTED_SEGMENTATION_BRAIN_N4_IMAGE} m ${HEAD_N4_IMAGE} ${BRAIN_EXTRACTION_MASK} TMP_FILES=( ${TMP_FILES[@]} ${EXTRACTED_SEGMENTATION_BRAIN_N4_IMAGE} ) time_start_template_registration=`date +%s` basecall='' if [[ ${RUN_QUICK} -ne 0 ]]; then TMP_FILES=( ${TMP_FILES[@]} "${REGISTRATION_TEMPLATE_OUTPUT_PREFIX}Warped.nii.gz" "${REGISTRATION_TEMPLATE_OUTPUT_PREFIX}InverseWarped.nii.gz" ) basecall="${ANTSPATH}/antsRegistrationSyNQuick.sh -d ${DIMENSION} -f ${REGISTRATION_TEMPLATE}" basecall="${basecall} -m ${EXTRACTED_SEGMENTATION_BRAIN_N4_IMAGE} -o ${REGISTRATION_TEMPLATE_OUTPUT_PREFIX} -j 1" if [[ ${USE_FLOAT_PRECISION} -ne 0 ]]; then basecall="${basecall} -p f" fi else IMAGES="${REGISTRATION_TEMPLATE},${EXTRACTED_SEGMENTATION_BRAIN_N4_IMAGE}" basecall="${ANTS} -d ${DIMENSION} -v 1 -u 1 -w [0.01,0.99] -o ${REGISTRATION_TEMPLATE_OUTPUT_PREFIX} -r [${IMAGES},1] --float ${USE_FLOAT_PRECISION}" stage1="-m MI[${IMAGES},${ANTS_LINEAR_METRIC_PARAMS}] -c ${ANTS_LINEAR_CONVERGENCE} -t Rigid[0.1] -f 8x4x2x1 -s 3x2x1x0" stage2="-m MI[${IMAGES},${ANTS_LINEAR_METRIC_PARAMS}] -c ${ANTS_LINEAR_CONVERGENCE} -t Affine[0.1] -f 8x4x2x1 -s 3x2x1x0" stage3="-m CC[${IMAGES},1,4] -c [${ANTS_MAX_ITERATIONS},1e-9,15] -t ${ANTS_TRANSFORMATION} -f 6x4x2x1 -s 3x2x1x0" basecall="${basecall} ${stage1} ${stage2} ${stage3}" fi exe_template_registration_1="${basecall}" if [[ ! -f ${REGISTRATION_TEMPLATE_WARP} ]]; then logCmd $exe_template_registration_1 fi ## check to see if the output registration transforms exist if [[ ! -f ${REGISTRATION_TEMPLATE_GENERIC_AFFINE} ]]; then echo "The registration component of the segmentation step didn't complete properly." echo "The transform file ${REGISTRATION_TEMPLATE_GENERIC_AFFINE} does not exist." exit 1 fi if [[ ! -f ${REGISTRATION_TEMPLATE_WARP} ]]; then echo "The registration component of the segmentation step didn't complete properly." echo "The transform file ${REGISTRATION_TEMPLATE_WARP} does not exist." exit 1 fi ## Create symmetric transforms for template to subject warping if [[ -s ${REGISTRATION_TEMPLATE_INVERSE_WARP} ]] && [[ ! -s ${REGISTRATION_SUBJECT_WARP} ]] ; then logCmd mv ${REGISTRATION_TEMPLATE_INVERSE_WARP} ${REGISTRATION_SUBJECT_WARP} fi if [[ ! -s ${REGISTRATION_SUBJECT_WARP} ]] ; then echo "The transform file ${REGISTRATION_SUBJECT_WARP} does not exist." exit 1 fi logCmd ${ANTSPATH}/antsApplyTransforms -d ${DIMENSION} -o Linear[$REGISTRATION_SUBJECT_GENERIC_AFFINE,1] -t $REGISTRATION_TEMPLATE_GENERIC_AFFINE --verbose 1 time_end_template_registration=`date +%s` time_elapsed_template_registration=$((time_end_template_registration - time_start_template_registration)) echo echo "--------------------------------------------------------------------------------------" echo " Done with registration: $(( time_elapsed_template_registration / 3600 ))h $(( time_elapsed_template_registration %3600 / 60 ))m $(( time_elapsed_template_registration % 60 ))s" echo "--------------------------------------------------------------------------------------" echo if [[ $KEEP_TMP_IMAGES -eq 0 ]]; then for f in ${TMP_FILES[@]} do if [[ -e $f ]]; then logCmd rm -f $f else echo "WARNING: expected temp file doesn't exist: $f" fi done fi logCmd ${ANTSPATH}/CreateJacobianDeterminantImage ${DIMENSION} ${REGISTRATION_TEMPLATE_WARP} ${REGISTRATION_LOG_JACOBIAN} 1 1 fi # if registration template & jacobian check if [[ -s ${REGISTRATION_LOG_JACOBIAN} ]] ; then echo ${OUTPUT_PREFIX}ACTStage4Complete.txt > ${OUTPUT_PREFIX}ACTStage4Complete.txt fi fi # BAStages fi # check completion ################################################################################ # # Cortical thickness # ################################################################################ BRAIN_SEGMENTATION_GM=${BRAIN_SEGMENTATION_OUTPUT}Posteriors${GRAY_MATTER_LABEL_FORMAT}.${OUTPUT_SUFFIX} BRAIN_SEGMENTATION_WM=${BRAIN_SEGMENTATION_OUTPUT}Posteriors${WHITE_MATTER_LABEL_FORMAT}.${OUTPUT_SUFFIX} BRAIN_SEGMENTATION_DEEP_GM=${BRAIN_SEGMENTATION_OUTPUT}Posteriors${DEEP_GRAY_MATTER_LABEL_FORMAT}.${OUTPUT_SUFFIX} CORTICAL_THICKNESS_GM=${OUTPUT_PREFIX}CorticalThicknessPosteriors${GRAY_MATTER_LABEL_FORMAT}.${OUTPUT_SUFFIX} CORTICAL_THICKNESS_WM=${OUTPUT_PREFIX}CorticalThicknessPosteriors${WHITE_MATTER_LABEL_FORMAT}.${OUTPUT_SUFFIX} CORTICAL_THICKNESS_SEGMENTATION=${OUTPUT_PREFIX}CorticalThicknessSegmentation.${OUTPUT_SUFFIX} CORTICAL_THICKNESS_GM_SEGMENTATION=${OUTPUT_PREFIX}CorticalThicknessGMSegmentation.${OUTPUT_SUFFIX} CORTICAL_LABEL_THICKNESS_PRIOR=${OUTPUT_PREFIX}CorticalLabelThicknessPrior.${OUTPUT_SUFFIX} if [[ ! -s ${OUTPUT_PREFIX}ACTStage5Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage3Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage2Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage1Complete.txt ]] ; then if [[ ${ACT_STAGE} -eq 0 ]] || [[ ${ACT_STAGE} -eq 5 ]] ; then # BAStages thk if [[ ! -f ${CORTICAL_THICKNESS_IMAGE} ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Cortical thickness using DiReCT (KellyKapowski)" echo "--------------------------------------------------------------------------------------" echo # combine posteriors logCmd cp ${BRAIN_SEGMENTATION_GM} ${CORTICAL_THICKNESS_GM} for(( i=0; i < ${#CORTICAL_THICKNESS_GRAY_MATTER_OTHER_LABELS[@]}; i++ )) do OTHER_LABEL=${CORTICAL_THICKNESS_GRAY_MATTER_OTHER_LABELS[$i]} NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#OTHER_LABEL} )) ROOT=''; for(( l=0; l < $NUMBER_OF_REPS; l++ )) do ROOT=${ROOT}${REPCHARACTER} done OTHER_LABEL_FORMAT=${ROOT}${OTHER_LABEL} BRAIN_SEGMENTATION_OTHER_LABEL=${BRAIN_SEGMENTATION_OUTPUT}Posteriors${OTHER_LABEL_FORMAT}.${OUTPUT_SUFFIX} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${CORTICAL_THICKNESS_GM} + ${CORTICAL_THICKNESS_GM} ${BRAIN_SEGMENTATION_OTHER_LABEL} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${CORTICAL_THICKNESS_SEGMENTATION} ReplaceVoxelValue ${BRAIN_SEGMENTATION} ${OTHER_LABEL} ${OTHER_LABEL} ${GRAY_MATTER_LABEL} done logCmd cp ${BRAIN_SEGMENTATION_WM} ${CORTICAL_THICKNESS_WM} for(( i=0; i < ${#CORTICAL_THICKNESS_WHITE_MATTER_OTHER_LABELS[@]}; i++ )) do OTHER_LABEL=${CORTICAL_THICKNESS_WHITE_MATTER_OTHER_LABELS[$i]} NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#OTHER_LABEL} )) ROOT=''; for(( l=0; l < $NUMBER_OF_REPS; l++ )) do ROOT=${ROOT}${REPCHARACTER} done OTHER_LABEL_FORMAT=${ROOT}${OTHER_LABEL} BRAIN_SEGMENTATION_OTHER_LABEL=${BRAIN_SEGMENTATION_OUTPUT}Posteriors${OTHER_LABEL_FORMAT}.${OUTPUT_SUFFIX} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${CORTICAL_THICKNESS_WM} + ${CORTICAL_THICKNESS_WM} ${BRAIN_SEGMENTATION_OTHER_LABEL} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${CORTICAL_THICKNESS_SEGMENTATION} ReplaceVoxelValue ${BRAIN_SEGMENTATION} ${OTHER_LABEL} ${OTHER_LABEL} ${WHITE_MATTER_LABEL} done # Check inputs if [[ ! -f ${CORTICAL_THICKNESS_SEGMENTATION} ]]; then echo "The brain segmentation image doesn't exist:" echo " $CORTICAL_THICKNESS_SEGMENTATION" exit 1 fi if [[ ! -f ${CORTICAL_THICKNESS_GM} ]]; then echo "The cortical gray matter probability image doesn't exist:" echo " $CORTICAL_THICKNESS_GM" exit 1 fi if [[ ! -f ${CORTICAL_THICKNESS_WM} ]] then echo "The cortical white matter probability image doesn't exist:" echo " $CORTICAL_THICKNESS_WM" exit 1 fi time_start_direct=`date +%s` TMP_FILES=( $CORTICAL_THICKNESS_GM $CORTICAL_THICKNESS_WM $CORTICAL_THICKNESS_SEGMENTATION ) exe_direct="${DIRECT} -d ${DIMENSION} -s [${CORTICAL_THICKNESS_SEGMENTATION},${GRAY_MATTER_LABEL},${WHITE_MATTER_LABEL}] --verbose 1" exe_direct="${exe_direct} -g ${CORTICAL_THICKNESS_GM} -w ${CORTICAL_THICKNESS_WM} -o ${CORTICAL_THICKNESS_IMAGE}" exe_direct="${exe_direct} -c ${DIRECT_CONVERGENCE} -r ${DIRECT_GRAD_STEP_SIZE}" exe_direct="${exe_direct} -m ${DIRECT_SMOOTHING_PARAMETER} -n ${DIRECT_NUMBER_OF_DIFF_COMPOSITIONS} -b ${USE_BSPLINE_SMOOTHING}" if [[ -f ${CORTICAL_LABEL_IMAGE} ]] && [[ -f $REGISTRATION_SUBJECT_WARP ]] && [[ -f $REGISTRATION_SUBJECT_GENERIC_AFFINE ]] ; then # Calculate ATITH and multiply by a heuristically derived scalar factor # logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${CORTICAL_LABEL_THICKNESS_PRIOR} LabelThickness2 ${CORTICAL_LABEL_IMAGE} # logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${CORTICAL_THICKNESS_SEGMENTATION} ${CORTICAL_THICKNESS_GM_SEGMENTATION} 2 2 1 0 # logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${CORTICAL_LABEL_THICKNESS_PRIOR} m ${CORTICAL_LABEL_THICKNESS_PRIOR} ${CORTICAL_THICKNESS_GM_SEGMENTATION} # logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${CORTICAL_LABEL_THICKNESS_PRIOR} m ${CORTICAL_LABEL_THICKNESS_PRIOR} 2.0 logCmd ${ANTSPATH}/antsApplyTransforms -d ${DIMENSION} -i ${CORTICAL_LABEL_IMAGE} -o ${CORTICAL_LABEL_THICKNESS_PRIOR} \ -t $REGISTRATION_SUBJECT_GENERIC_AFFINE -t $REGISTRATION_SUBJECT_WARP -r ${ANATOMICAL_IMAGES[0]} --verbose 1 exe_direct="${exe_direct} -a ${CORTICAL_LABEL_THICKNESS_PRIOR}" TMP_FILES=( ${TMP_FILES[@]} $CORTICAL_LABEL_THICKNESS_PRIOR ${CORTICAL_THICKNESS_GM_SEGMENTATION} ) else exe_direct="${exe_direct} -t ${DIRECT_THICKNESS_PRIOR}" fi logCmd $exe_direct if [[ $KEEP_TMP_IMAGES -eq 0 ]]; then for f in ${TMP_FILES[@]} do if [[ -e $f ]]; then logCmd rm -f $f else echo "WARNING: expected temp file doesn't exist: $f" fi done fi if [[ ! -f ${CORTICAL_THICKNESS_IMAGE} ]]; then echo "Expected output was not produced. The cortical thickness image doesn't exist:" echo " $CORTICAL_THICKNESS_IMAGE" exit 1 fi time_end_direct=`date +%s` time_elapsed_direct=$((time_end_direct - time_start_direct)) echo echo "--------------------------------------------------------------------------------------" echo " Done with cortical thickness estimation: $(( time_elapsed_direct / 3600 ))h $(( time_elapsed_direct %3600 / 60 ))m $(( time_elapsed_direct % 60 ))s" echo "--------------------------------------------------------------------------------------" echo fi echo ${OUTPUT_PREFIX}ACTStage5Complete.txt > ${OUTPUT_PREFIX}ACTStage5Complete.txt fi # BAStages fi # check completion #### BA Edits Begin #### if [[ ! -s ${OUTPUT_PREFIX}ACTStage6Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage5Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage3Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage2Complete.txt ]] && \ [[ -s ${OUTPUT_PREFIX}ACTStage1Complete.txt ]] ; then if [[ ${ACT_STAGE} -eq 0 ]] || [[ ${ACT_STAGE} -eq 6 ]] ; then # BAStages qc echo "--------------------------------------------------------------------------------------" echo "Compute summary measurements" echo "--------------------------------------------------------------------------------------" if [[ ! -s ${OUTPUT_PREFIX}CorticalThickness.nii.gz ]] ; then echo ${OUTPUT_PREFIX}CorticalThickness.nii.gz incomplete! exit 1 fi if [[ -f ${REGISTRATION_TEMPLATE_WARP} ]]; then exe_template_registration_3="${WARP} -d ${DIMENSION} -i ${CORTICAL_THICKNESS_IMAGE} -o ${OUTPUT_PREFIX}CorticalThicknessNormalizedToTemplate.${OUTPUT_SUFFIX} -r ${REGISTRATION_TEMPLATE} -n Gaussian -t ${REGISTRATION_TEMPLATE_WARP} -t ${REGISTRATION_TEMPLATE_GENERIC_AFFINE} --float ${USE_FLOAT_PRECISION} --verbose 1" logCmd $exe_template_registration_3 EXTRACTED_SEGMENTATION_BRAIN_DEFORMED=${OUTPUT_PREFIX}BrainNormalizedToTemplate.${OUTPUT_SUFFIX} REGISTRATION_TEMPLATE_BRAIN_MASK=${OUTPUT_PREFIX}RegistrationTemplateBrainMask.nii.gz logCmd ${ANTSPATH}/ThresholdImage 3 ${REGISTRATION_TEMPLATE} ${REGISTRATION_TEMPLATE_BRAIN_MASK} 1E-6 Inf EXTRACTED_SEGMENTATION_BRAIN_N4_IMAGE=${OUTPUT_PREFIX}ExtractedBrain0N4.nii.gz logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${EXTRACTED_SEGMENTATION_BRAIN_N4_IMAGE} m ${HEAD_N4_IMAGE} ${BRAIN_EXTRACTION_MASK} TMP_FILES=( ${TMP_FILES[@]} ${EXTRACTED_SEGMENTATION_BRAIN_N4_IMAGE} ${EXTRACTED_SEGMENTATION_BRAIN_DEFORMED} ${REGISTRATION_TEMPLATE_BRAIN_MASK} ) logCmd ${WARP} -d ${DIMENSION} -i ${EXTRACTED_SEGMENTATION_BRAIN_N4_IMAGE} -o ${EXTRACTED_SEGMENTATION_BRAIN_DEFORMED} -r ${REGISTRATION_TEMPLATE} -n Linear -t ${REGISTRATION_TEMPLATE_WARP} -t ${REGISTRATION_TEMPLATE_GENERIC_AFFINE} --float ${USE_FLOAT_PRECISION} --verbose 1 fi ccmetric=`${ANTSPATH}/ImageMath ${DIMENSION} a PearsonCorrelation ${REGISTRATION_TEMPLATE} ${EXTRACTED_SEGMENTATION_BRAIN_DEFORMED} ${REGISTRATION_TEMPLATE_BRAIN_MASK}` bvol=`${ANTSPATH}/ImageMath ${DIMENSION} a total ${BRAIN_EXTRACTION_MASK} | cut -d ':' -f 3 | cut -d ' ' -f 2 ` gvol=`${ANTSPATH}/ImageMath ${DIMENSION} a total ${BRAIN_SEGMENTATION_GM} | cut -d ':' -f 3 | cut -d ' ' -f 2 ` wvol=`${ANTSPATH}/ImageMath ${DIMENSION} a total ${BRAIN_SEGMENTATION_WM} | cut -d ':' -f 3 | cut -d ' ' -f 2 ` thks=`${ANTSPATH}/ImageMath ${DIMENSION} a total ${CORTICAL_THICKNESS_IMAGE} | cut -d ':' -f 3 | cut -d ' ' -f 2 ` echo "PearsonCorrelation,BVOL,GVol,WVol,ThicknessSum" > ${OUTPUT_PREFIX}brainvols.csv echo "${ccmetric},${bvol},${gvol},${wvol},${thks}" >> ${OUTPUT_PREFIX}brainvols.csv if [[ -f ${ANTSPATH}/GetMeshAndTopology ]] && [[ ${DIMENSION} -eq 3 ]] ; then ${ANTSPATH}/ThresholdImage ${DIMENSION} ${BRAIN_SEGMENTATION} ${OUTPUT_PREFIX}temp.nii.gz 3 3 ${ANTSPATH}/ImageMath ${DIMENSION} ${OUTPUT_PREFIX}temp.nii.gz ME ${OUTPUT_PREFIX}temp.nii.gz 1 ${ANTSPATH}/ImageMath ${DIMENSION} ${OUTPUT_PREFIX}temp.nii.gz GetLargestComponent ${OUTPUT_PREFIX}temp.nii.gz 1 ${ANTSPATH}/ImageMath ${DIMENSION} ${OUTPUT_PREFIX}temp.nii.gz MD ${OUTPUT_PREFIX}temp.nii.gz 2 ${ANTSPATH}/SmoothImage 3 ${CORTICAL_THICKNESS_IMAGE} 1 ${OUTPUT_PREFIX}temp2.nii.gz # ${ANTSPATH}/GetMeshAndTopology ${OUTPUT_PREFIX}temp.nii.gz ${OUTPUT_PREFIX}temp2.nii.gz ${OUTPUT_PREFIX}.vtk thickness 0.3 0.001 ${OUTPUT_PREFIX}_Thickness.png rm -f ${OUTPUT_PREFIX}temp.nii.gz ${OUTPUT_PREFIX}temp2.nii.gz fi echo "--------------------------------------------------------------------------------------" #### BA Edits End #### ################################################################################ # # Create QA/QC output: # - tiled mosaic of ${OUTPUT_PREFIX}BrainSegmentation0N4.nii.gz with # ${OUTPUT_PREFIX}CorticalThickness.nii.gz overlay ################################################################################ HEAD_N4_IMAGE=${OUTPUT_PREFIX}BrainSegmentation0N4.${OUTPUT_SUFFIX} HEAD_N4_IMAGE_RESAMPLED="${OUTPUT_PREFIX}BrainSegmentation0N4Resampled.nii.gz" CORTICAL_THICKNESS_IMAGE_RESAMPLED="${OUTPUT_PREFIX}CorticalThicknessHotResampled.nii.gz" CORTICAL_THICKNESS_IMAGE_RGB="${OUTPUT_PREFIX}CorticalThicknessHotRGB.nii.gz" CORTICAL_THICKNESS_MOSAIC="${OUTPUT_PREFIX}CorticalThicknessTiledMosaic.png" CORTICAL_THICKNESS_MASK="${OUTPUT_PREFIX}CorticalThicknessMask.${OUTPUT_SUFFIX}" BRAIN_EXTRACTION_MASK_RESAMPLED="${OUTPUT_PREFIX}BrainExtractionMaskResampled.nii.gz" BRAIN_SEGMENTATION_IMAGE_RESAMPLED="${OUTPUT_PREFIX}BrainSegmentationResampled.nii.gz" BRAIN_SEGMENTATION_IMAGE_RGB="${OUTPUT_PREFIX}BrainSegmentationRGB.nii.gz" BRAIN_SEGMENTATION_MOSAIC="${OUTPUT_PREFIX}BrainSegmentationTiledMosaic.png" ITKSNAP_COLORMAP="${OUTPUT_PREFIX}ItkSnapColormap.txt" if [[ ! -f ${CORTICAL_THICKNESS_MOSAIC} || ! -f ${BRAIN_SEGMENTATION_MOSAIC} ]]; then TMP_FILES=( $CORTICAL_THICKNESS_IMAGE_RGB $CORTICAL_THICKNESS_MASK $BRAIN_SEGMENTATION_IMAGE_RGB $ITKSNAP_COLORMAP ) TMP_FILES=( ${TMP_FILES[@]} $HEAD_N4_IMAGE_RESAMPLED $CORTICAL_THICKNESS_IMAGE_RESAMPLED $BRAIN_EXTRACTION_MASK_RESAMPLED $BRAIN_SEGMENTATION_IMAGE_RESAMPLED ) # Resample images resample0="${ANTSPATH}/ResampleImageBySpacing ${DIMENSION} ${BRAIN_EXTRACTION_MASK}" resample0="${resample0} ${BRAIN_EXTRACTION_MASK_RESAMPLED} 1 1" resample1="${ANTSPATH}/ResampleImageBySpacing ${DIMENSION} ${HEAD_N4_IMAGE}" resample1="${resample1} ${HEAD_N4_IMAGE_RESAMPLED} 1 1" resample2="${ANTSPATH}/ResampleImageBySpacing ${DIMENSION} ${CORTICAL_THICKNESS_IMAGE}" resample2="${resample2} ${CORTICAL_THICKNESS_IMAGE_RESAMPLED} 1 1" resample3="${ANTSPATH}/ResampleImageBySpacing ${DIMENSION} ${BRAIN_SEGMENTATION}" resample3="${resample3} ${BRAIN_SEGMENTATION_IMAGE_RESAMPLED} 1 1" if [[ ${DIMENSION} -eq 3 ]]; then resample0="${resample0} 1 0 0 1" resample1="${resample1} 1" resample2="${resample2} 1" resample3="${resample3} 1 0 0 1" else resample0="${resample0} 0 0 1" resample3="${resample3} 0 0 1" fi logCmd $resample0 logCmd $resample1 logCmd $resample2 logCmd $resample3 # Cortical thickness mask="${ANTSPATH}/ThresholdImage ${DIMENSION} ${CORTICAL_THICKNESS_IMAGE_RESAMPLED} ${CORTICAL_THICKNESS_MASK} 0 0 0 1" logCmd $mask conversion="${ANTSPATH}/ConvertScalarImageToRGB ${DIMENSION} ${CORTICAL_THICKNESS_IMAGE_RESAMPLED}" conversion="${conversion} ${CORTICAL_THICKNESS_IMAGE_RGB} none hot none 0 ${DIRECT_THICKNESS_PRIOR}" logCmd $conversion mosaic="${ANTSPATH}/CreateTiledMosaic -i ${HEAD_N4_IMAGE_RESAMPLED} -r ${CORTICAL_THICKNESS_IMAGE_RGB}" mosaic="${mosaic} -o ${CORTICAL_THICKNESS_MOSAIC} -a 1.0 -t -1x-1 -d z -p mask" mosaic="${mosaic} -s [2,mask,mask] -x ${CORTICAL_THICKNESS_MASK}" logCmd $mosaic # Segmentation echo "0 1 0 0 1 0 1" > $ITKSNAP_COLORMAP echo "0 0 1 0 1 1 0" >> $ITKSNAP_COLORMAP echo "0 0 0 1 0 1 1" >> $ITKSNAP_COLORMAP conversion="${ANTSPATH}/ConvertScalarImageToRGB ${DIMENSION} ${BRAIN_SEGMENTATION_IMAGE_RESAMPLED}" conversion="${conversion} ${BRAIN_SEGMENTATION_IMAGE_RGB} none custom $ITKSNAP_COLORMAP 0 6" logCmd $conversion mosaic="${ANTSPATH}/CreateTiledMosaic -i ${HEAD_N4_IMAGE_RESAMPLED} -r ${BRAIN_SEGMENTATION_IMAGE_RGB}" mosaic="${mosaic} -o ${BRAIN_SEGMENTATION_MOSAIC} -a 0.3 -t -1x-1 -d 2 -p mask" mosaic="${mosaic} -s [2,mask,mask] -x ${BRAIN_EXTRACTION_MASK_RESAMPLED}" logCmd $mosaic if [[ $KEEP_TMP_IMAGES -eq 0 ]]; then for f in ${TMP_FILES[@]} do if [[ -e $f ]]; then logCmd rm -f $f else echo "WARNING: expected temp file doesn't exist: $f" fi done fi fi echo ${OUTPUT_PREFIX}ACTStage6Complete.txt > ${OUTPUT_PREFIX}ACTStage6Complete.txt fi # BAStages fi # check completion ################################################################################ # # End of main routine # ################################################################################ if [[ ${ACT_STAGE} -eq 0 ]] || [[ ${ACT_STAGE} -ge 5 ]] ; then logCmd checkOutputExists fi time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo echo "--------------------------------------------------------------------------------------" echo " Done with ANTs processing pipeline: stage $ACT_STAGE" echo " Script executed in $time_elapsed seconds" echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" echo "--------------------------------------------------------------------------------------" exit 0 ants-2.2.0/Scripts/antsIntermodalityIntrasubject.sh000077500000000000000000000271151311104306400225350ustar00rootroot00000000000000#!/bin/bash VERSION="0.0" if [[ ! -s ${ANTSPATH}/antsRegistration ]]; then echo we cant find the antsRegistration program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi if [[ ! -s ${ANTSPATH}/antsApplyTransforms ]]; then echo we cant find the antsApplyTransforms program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi function Usage { cat < -o outputPrefix -l labels in template space -a auxiliary scalar image/s to warp to template -b auxiliary dt image to warp to template Example: bash $0 -d 3 -i pcasl_control.nii.gz -r t1.nii.gz -x t1_mask.nii.gz -a cbf.nii.gz -l template_aal.nii.gz -w t12template_ -t 2 -o output minimal paramters that must be passed include: -d -i -r -x -w -o USAGE exit 1 } echoParameters() { cat <>>>>>>>>>>>>>>>>>>>" echo $cmd $cmd echo "END <<<<<<<<<<<<<<<<<<<<" echo echo } ################################################################################ # # Main routine # ################################################################################ HOSTNAME=`hostname` DATE=`date` CURRENT_DIR=`pwd`/ OUTPUT_DIR=${CURRENT_DIR}/tmp$RANDOM/ OUTPUT_PREFIX=${OUTPUT_DIR}/tmp OUTPUT_SUFFIX="nii.gz" KEEP_TMP_IMAGES=0 DIMENSION=3 BRAIN="" AUX_IMAGES=() TEMPLATE_TRANSFORM="" ANATOMICAL_BRAIN="" ANATOMICAL_SPACE=""; TEMPLATE_MASK="" TEMPLATE_LABELS="" TRANSFORM_TYPE="0" DTI="" TEMPLATE="" ################################################################################ # # Programs and their parameters # ################################################################################ ANTS=${ANTSPATH}/antsRegistration WARP=${ANTSPATH}/antsApplyTransforms if [[ $# -lt 3 ]] ; then Usage >&2 exit 1 else while getopts "a:b:r:x:d:h:i:l:o:t:w:R:T:" OPT do case $OPT in a) # auxiliary scalar images AUX_IMAGES[${#AUX_IMAGES[@]}]=$OPTARG ;; b) #brain extraction registration mask DTI=$OPTARG ;; d) #dimensions DIMENSION=$OPTARG if [[ ${DIMENSION} -gt 3 || ${DIMENSION} -lt 2 ]]; then echo " Error: ImageDimension must be 2 or 3 " exit 1 fi ;; r) #brain extraction anatomical image ANATOMICAL_BRAIN=$OPTARG ;; R) # anatomical warp space ANATOMICAL_SPACE=$OPTARG ;; T) # template TEMPLATE=$OPTARG ;; x) #brain extraction registration mask TEMPLATE_MASK=$OPTARG ;; l) #brain extraction registration mask TEMPLATE_LABELS=$OPTARG ;; h) #help Usage >&2 exit 0 ;; i) #max_iterations BRAIN=$OPTARG ;; o) #output prefix OUTPUT_PREFIX=$OPTARG ;; w) #template registration image TEMPLATE_TRANSFORM=$OPTARG ;; t) #atropos prior weight TRANSFORM_TYPE=$OPTARG ;; *) # getopts issues an error message echo "ERROR: unrecognized option -$OPT $OPTARG" exit 1 ;; esac done fi if [[ ${#TEMPLATE} -lt 3 ]] ; then TEMPLATE=$TEMPLATE_LABELS fi ################################################################################ # # Preliminaries: # 1. Check existence of inputs # 2. Figure out output directory and mkdir if necessary # 3. See if $REGISTRATION_TEMPLATE is the same as $BRAIN_TEMPLATE # ################################################################################ # requires DIMENSION BRAIN ANATOMICAL_BRAIN TEMPLATE_MASK TEMPLATE_TRANSFORM OUTPUT_PREFIX if [[ ${#DIMENSION} -lt 1 ]]; then echo "Please specify -d DIMENSION " exit 1 fi if [[ ! -f ${BRAIN} ]]; then echo "The scalar brain:" echo " $BRAIN" exit 1 fi if [[ ! -f ${ANATOMICAL_BRAIN} ]]; then echo "The extraction template doesn't exist:" echo " $ANATOMICAL_BRAIN" exit 1 fi if [[ ! -f ${TEMPLATE_MASK} ]]; then echo "The brain extraction prior doesn't exist:" echo " $TEMPLATE_MASK" exit 1 fi if [[ ${#ANATOMICAL_SPACE} -lt 1 ]]; then echo setting ANATOMICAL_SPACE = ANATOMICAL_BRAIN ANATOMICAL_SPACE=$ANATOMICAL_BRAIN fi OUTPUT_DIR=${OUTPUT_PREFIX%\/*} if [[ ! -d $OUTPUT_DIR ]]; then echo "The output directory \"$OUTPUT_DIR\" does not exist. Making it." mkdir -p $OUTPUT_DIR fi ANTS_MAX_ITERATIONS="50x50x0" ANTS_TRANSFORMATION="SyN[0.1,3,0]" ANTS_LINEAR_METRIC="MI" ANTS_LINEAR_METRIC_PARAMS="1,32,Regular,0.25" ANTS_LINEAR_CONVERGENCE="[1000x500x250x0,1e-7,5]" ANTS_METRIC="mattes" ANTS_METRIC_PARAMS="1,32" ANTS_TRANS1="" ANTS_TRANS2="" if [ ${TRANSFORM_TYPE} -eq 0 ]; then ANTS_TRANS1="Rigid[0.1]" elif [ ${TRANSFORM_TYPE} -eq 1 ]; then ANTS_TRANS1="Affine[0.1]" elif [ ${TRANSFORM_TYPE} -eq 2 ]; then ANTS_TRANS1="Rigid[0.1]" ANTS_TRANS2="SyN[0.1,3,0]" elif [ ${TRANSFORM_TYPE} -eq 3 ]; then ANTS_TRANS1="Affine[0.1]" ANTS_TRANS2="SyN[0.1,3,0]" fi echoParameters >&2 echo "--------------------- Running `basename $0` on $HOSTNAME ---------------------" time_start=`date +%s` ################################################################################ # # Output images # ################################################################################ #BRAIN_EXTRACTION_MASK=${OUTPUT_PREFIX}BrainExtractionMask.${OUTPUT_SUFFIX} #BRAIN_SEGMENTATION=${OUTPUT_PREFIX}BrainSegmentation.${OUTPUT_SUFFIX} #CORTICAL_THICKNESS_IMAGE=${OUTPUT_PREFIX}CorticalThickness.${OUTPUT_SUFFIX} ################################################################################ # # Intermodality matching # ################################################################################ stage1="-m ${ANTS_LINEAR_METRIC}[${ANATOMICAL_BRAIN},${BRAIN},${ANTS_LINEAR_METRIC_PARAMS}] -c ${ANTS_LINEAR_CONVERGENCE} -t ${ANTS_TRANS1} -f 8x4x2x1 -s 4x2x1x0 -u 1" stage2="" if [[ ${TRANSFORM_TYPE} -gt 1 ]] ; then stage2="-m ${ANTS_METRIC}[${ANATOMICAL_BRAIN},${BRAIN},${ANTS_METRIC_PARAMS}] -c [${ANTS_MAX_ITERATIONS},1e-7,5] -t ${ANTS_TRANS2} -f 4x2x1 -s 2x1x0mm -u 1" fi globalparams=" -z 1 --winsorize-image-intensities [0.005, 0.995] " cmd="${ANTSPATH}/antsRegistration -d $DIMENSION $stage1 $stage2 $globalparams -o ${OUTPUT_PREFIX}" echo $cmd if [[ ! -s ${OUTPUT_PREFIX}0GenericAffine.mat ]] ; then $cmd else echo we already have ${OUTPUT_PREFIX}0GenericAffine.mat fi # warp input image to t1 warp="" iwarp="" if [[ -s ${OUTPUT_PREFIX}1Warp.nii.gz ]]; then warp="-t ${OUTPUT_PREFIX}1Warp.nii.gz" iwarp="-t ${OUTPUT_PREFIX}1InverseWarp.nii.gz" fi ${ANTSPATH}/antsApplyTransforms -d $DIMENSION -i $BRAIN -o ${OUTPUT_PREFIX}anatomical.nii.gz -r $ANATOMICAL_SPACE $warp -t ${OUTPUT_PREFIX}0GenericAffine.mat -n Linear if [[ ! -s ${TEMPLATE_TRANSFORM}1Warp.nii.gz ]] ; then echo ${TEMPLATE_TRANSFORM}1Warp.nii.gz does not exist - please specify in order to proceed to steps that map to the template exit fi cmd="${ANTSPATH}/antsApplyTransforms -d $DIMENSION -i $BRAIN -o ${OUTPUT_PREFIX}template.nii.gz -r ${TEMPLATE} -t ${TEMPLATE_TRANSFORM}1Warp.nii.gz -t ${TEMPLATE_TRANSFORM}0GenericAffine.mat $warp -t ${OUTPUT_PREFIX}0GenericAffine.mat -n Linear" $cmd echo "AUX IMAGES" # warp auxiliary images to t1 for (( i = 0; i < ${#AUX_IMAGES[@]}; i++ )) do # FIXME - how to name these reasonably AUXO=`basename ${AUX_IMAGES[$i]} .nii.gz` #AUXO=${OUTPUT_PREFIX}_aux_${i}_warped.nii.gz ${ANTSPATH}/antsApplyTransforms -d $DIMENSION -i ${AUX_IMAGES[$i]} -r $ANATOMICAL_SPACE $warp -n Linear -o ${OUTPUT_DIR}/${AUXO}_anatomical.nii.gz $warp -t ${OUTPUT_PREFIX}0GenericAffine.mat ${ANTSPATH}/antsApplyTransforms -d $DIMENSION -i ${AUX_IMAGES[$i]} -r ${TEMPLATE} -n Linear -o ${OUTPUT_DIR}/${AUXO}_template.nii.gz -t ${TEMPLATE_TRANSFORM}1Warp.nii.gz -t ${TEMPLATE_TRANSFORM}0GenericAffine.mat $warp -t ${OUTPUT_PREFIX}0GenericAffine.mat done echo "DTI" # warp DT image to t1 if [[ -f $DTI ]]; then ${ANTSPATH}/antsApplyTransforms -d $DIMENSION -i ${DTI} -r $ANATOMICAL_SPACE $warp -t ${OUTPUT_PREFIX}0GenericAffine.mat -n Linear -o ${OUTPUT_PREFIX}dt_anatomical.nii.gz -e 2 ${ANTSPATH}/antsApplyTransforms -d $DIMENSION -e 2 -i ${DTI} -r ${TEMPLATE} -n Linear -o ${OUTPUT_PREFIX}dt_template.nii.gz -t ${TEMPLATE_TRANSFORM}1Warp.nii.gz -t ${TEMPLATE_TRANSFORM}0GenericAffine.mat $warp -t ${OUTPUT_PREFIX}0GenericAffine.mat ${ANTSPATH}/ImageMath 3 ${OUTPUT_PREFIX}fa_template.nii.gz TensorFA ${OUTPUT_PREFIX}dt_template.nii.gz ${ANTSPATH}/ImageMath 3 ${OUTPUT_PREFIX}md_template.nii.gz TensorMeanDiffusion ${OUTPUT_PREFIX}dt_template.nii.gz fi # warp brainmask from anatomy to subject if [[ -f $TEMPLATE_MASK ]]; then ${ANTSPATH}/antsApplyTransforms -d $DIMENSION -i $TEMPLATE_MASK -o ${OUTPUT_PREFIX}brainmask.nii.gz \ -r $BRAIN -n NearestNeighbor \ -t [ ${OUTPUT_PREFIX}0GenericAffine.mat, 1] \ $iwarp fi # warp Labels from template to subject (if passed) if [[ -f $TEMPLATE_LABELS ]]; then ${ANTSPATH}/antsApplyTransforms -d $DIMENSION -i $TEMPLATE_LABELS -o ${OUTPUT_PREFIX}labels.nii.gz \ -r $BRAIN -n NearestNeighbor \ -t [ ${OUTPUT_PREFIX}0GenericAffine.mat, 1] \ $iwarp \ -t ${TEMPLATE_TRANSFORM}1Warp.nii.gz \ -t ${TEMPLATE_TRANSFORM}0GenericAffine.mat fi ################################################################################ # # End of main routine # ################################################################################ time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo echo "--------------------------------------------------------------------------------------" echo " Done with ANTs intermodality intrasubject processing pipeline" echo " Script executed in $time_elapsed seconds" echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" echo "--------------------------------------------------------------------------------------" exit 0 ants-2.2.0/Scripts/antsIntroduction.sh000077500000000000000000000653121311104306400200150ustar00rootroot00000000000000#!/bin/bash VERSION="0.0.8 dev" # trap keyboard interrupt (control-c) trap control_c SIGINT function setPath { cat <&2 fi if [ ! -s ${ANTSPATH}/ANTS ] ; then echo "ANTS program can't be found. Please (re)define \$ANTSPATH in your environment." exit fi function Usage { cat <&2 elif [ $nargs -lt 6 ] then Usage >&2 fi # reading command line arguments while getopts "d:f:r:i:h:o:m:n:l:q:s:t:" OPT do case $OPT in h) #help echo "$Help" exit 0 ;; d) #dimensions DIM=$OPTARG ;; f) #ignore header warnings IGNORE_HDR_WARNING=$OPTARG ;; i) #input or moving image MOVING=$OPTARG OUTPUTNAME=` echo basename $MOVING | cut -d '.' -f 1 ` ;; l) #use label image LABELIMAGE=$OPTARG ;; m) #max iterations other than default MAXITERATIONS=$OPTARG ;; n) #apply bias field correction N4CORRECT=$OPTARG ;; o) #output name prefix OUTPUTNAME=$OPTARG ;; q) #perform quality check after running ANTS DoANTSQC=$OPTARG ;; r) #ref image FIXED=$OPTARG ;; s) #similarity model METRICTYPE=$OPTARG ;; t) #transformation model TRANSFORMATIONTYPE=$OPTARG ;; \?) # getopts issues an error message echo "$USAGE" >&2 exit 1 ;; esac done tmpdir=${currentdir}/tmp_${RANDOM}_${RANDOM}_${RANDOM}_$$ (umask 077 && mkdir ${tmpdir}) || { echo "Could not create temporary directory! Exiting." 1>&2 exit 1 } cp ${FIXED} ${MOVING} ${tmpdir}/ cd ${tmpdir}/ FIXED=`basename ${FIXED}` MOVING=`basename ${MOVING}` # test input parameter before starting if [ ${#DIM} -gt 1 ] then echo "Problem with specified ImageDimension => User Specified Value = $DIM" exit fi if [ ${#FIXED} -lt 1 -o ! -f $FIXED ] then echo "Problem with specified Fixed Image => User Specified Value = $FIXED" exit fi if [ ${#MOVING} -lt 1 -o ! -f $MOVING ] then echo "Problem with specified Moving Image => User Specified Value = $MOVING" exit fi # check the image headers echo "input files: checking" compareheaders=`${ANTSPATH}/ImageMath $DIM ${OUTPUTNAME}repaired.nii.gz CompareHeadersAndImages $FIXED $MOVING | grep FailureState | cut -d ' ' -f 4 ` if [[ $compareheaders -ne 0 ]] ; then dataCheck if [[ $IGNORE_HDR_WARNING -eq 0 ]] ; then exit 1 fi else echo "input files: check passed" fi ITERATLEVEL=(`echo $MAXITERATIONS | tr 'x' ' '`) NUMLEVELS=${#ITERATLEVEL[@]} # Transformation model if [ "${TRANSFORMATIONTYPE}" == "RI" ] then RIGID=1 RIGIDTRANSF=" --do-rigid true " elif [ "${TRANSFORMATIONTYPE}" == "RA" ] then RIGID=1 RIGIDTRANSF=" --rigid-affine true " elif [ "${TRANSFORMATIONTYPE}" == "EL" ] then # Mapping Parameters TRANSFORMATION=Elast[1] REGULARIZATION=Gauss[3,0.5] # Gauss[3,x] is usually the best option. x is usually 0 for SyN --- if you want to reduce flexibility/increase mapping smoothness, the set x > 0. # We did a large scale evaluation of SyN gradient parameters in normal brains and found 0.25 => 0.5 to perform best when # combined with default Gauss[3,0] regularization. You would increase the gradient step in some cases, though, to make # the registration converge faster --- though oscillations occur if the step is too high and other instability might happen too. elif [ "${TRANSFORMATIONTYPE}" == "S2" ] then # Mapping Parameters for the LDDMM style SyN --- the params are SyN[GradientStepLength,NTimeDiscretizationPoints,IntegrationTimeStep] # increasing IntegrationTimeStep increases accuracy in the diffeomorphism integration and takes more computation time. # NTimeDiscretizationPoints is set to 2 here TRANSFORMATION=SyN[1,2,0.05] REGULARIZATION=Gauss[3,0.] elif [ "${TRANSFORMATIONTYPE}" == "SY" ] then # Mapping Parameters for the LDDMM style SyN --- the params are SyN[GradientStepLength,NTimeDiscretizationPoints,IntegrationTimeStep] # increasing IntegrationTimeStep increases accuracy in the diffeomorphism integration and takes more computation time. # NTimeDiscretizationPoints is the number of spatial indices in the time dimension (the 4th dim when doing 3D registration) # increasing NTimeDiscretizationPoints increases flexibility and takes more computation time. # the --geodesic option enables either 1 asymmetric gradient estimation or 2 symmetric gradient estimation (the default here ) TRANSFORMATION=" SyN[1,2,0.05] --geodesic 2 " REGULARIZATION=Gauss[3,0.] elif [ "${TRANSFORMATIONTYPE}" == "LDDMM" ] then # Mapping Parameters for the LDDMM style SyN --- the params are SyN[GradientStepLength,NTimeDiscretizationPoints,IntegrationTimeStep] # increasing IntegrationTimeStep increases accuracy in the diffeomorphism integration and takes more computation time. # NTimeDiscretizationPoints is the number of spatial indices in the time dimension (the 4th dim when doing 3D registration) # increasing NTimeDiscretizationPoints increases flexibility and takes more computation time. # the --geodesic option enables either 1 asymmetric gradient estimation or 2 symmetric gradient estimation (the default here ) TRANSFORMATION=" SyN[1,2,0.05] --geodesic 1 " REGULARIZATION=Gauss[3,0.] elif [ "${TRANSFORMATIONTYPE}" == "GR" ] then # Mapping Parameters for the greedy gradient descent (fast) version of SyN -- only needs GradientStepLength TRANSFORMATION=SyN[0.25] REGULARIZATION=Gauss[3,0] elif [ "${TRANSFORMATIONTYPE}" == "GR_Constrained" ] then # Mapping Parameters for the greedy gradient descent (fast) version of SyN -- only needs GradientStepLength TRANSFORMATION=SyN[0.25] REGULARIZATION=Gauss[3,0.5] elif [ "${TRANSFORMATIONTYPE}" == "EX" ] then # Mapping Parameters TRANSFORMATION=Exp[0.5,10] REGULARIZATION=Gauss[3,0.5] elif [ "${TRANSFORMATIONTYPE}" == "DD" ] then # Mapping Parameters for diffemorphic demons style optimization Exp[GradientStepLength,NTimePointsInIntegration] # NTimePointsInIntegration controls the number of compositions in the transformation update , see the DD paper TRANSFORMATION=GreedyExp[0.5,10] REGULARIZATION=Gauss[3,0.5] else echo "Invalid transformation metric. Use RI, RA, EL, SY, S2, GR , DD or EX or type bash `basename $0` -h." exit 1 fi # Similarity Measures if [ "${METRICTYPE}" == "PR" ] then # Mapping Parameters METRIC=PR[ METRICPARAMS=1,4] elif [ "${METRICTYPE}" == "CC" ] then # Mapping Parameters METRIC=CC[ METRICPARAMS=1,5] elif [ "${METRICTYPE}" == "MI" ] then # Mapping Parameters METRIC=MI[ METRICPARAMS=1,32] elif [ "${METRICTYPE}" == "MSQ" ] then # Mapping Parameters METRIC=MSQ[ METRICPARAMS=1,0] else echo "Invalid similarity metric. Use CC, MI, MSQ or PR or type bash`basename $0` -h." exit 1 fi # main script reportMappingParameters # write config file MOVINGBASE=` echo ${MOVING} | cut -d '.' -f 1 ` if [ -f ${MOVINGBASE}.cfg ] ; then rm -f ${MOVINGBASE}.cfg fi echo "REGDIR=${currentdir}" >> ${MOVINGBASE}.cfg echo "DIM=$DIM" >> ${MOVINGBASE}.cfg echo "FIXED=$FIXED" >> ${MOVINGBASE}.cfg echo "MOVING=$MOVING" >> ${MOVINGBASE}.cfg echo "LABELIMAGE=$LABELIMAGE" >> ${MOVINGBASE}.cfg echo "N4CORRECT=$N4CORRECT" >> ${MOVINGBASE}.cfg echo "DoANTSQC=$DoANTSQC" >> ${MOVINGBASE}.cfg echo "METRICTYPE=$METRICTYPE" >> ${MOVINGBASE}.cfg echo "TRANSFORMATIONTYPE=$TRANSFORMATIONTYPE" >> ${MOVINGBASE}.cfg echo "REGULARIZATION=$REGULARIZATION" >> ${MOVINGBASE}.cfg echo "MAXITERATIONS=$MAXITERATIONS" >> ${MOVINGBASE}.cfg echo "NUMLEVELS=$NUMLEVELS" >> ${MOVINGBASE}.cfg echo "OUTPUTNAME=$OUTPUTNAME" >> ${MOVINGBASE}.cfg # Default of 10000x10000x10000x10000x10000 retained for backwards compatibility, can cause # memory problems (allocates too much RAM for five levels) and bad affine initialization (images downsampled too far) # # A value of 1000x1000x1000 should suffice for most purposes AFFINEITERATIONS=10000x10000x10000x10000x10000 if [ ${N4CORRECT} -eq 0 ] && [ ${RIGID} -eq 0 ] then # Apply ANTS mapping command without N4BiasFieldCorrection exe="${ANTSPATH}/ANTS $DIM -m ${METRIC}${FIXED},${MOVING},${METRICPARAMS} -t $TRANSFORMATION -r $REGULARIZATION -o ${OUTPUTNAME} -i $MAXITERATIONS --use-Histogram-Matching --number-of-affine-iterations $AFFINEITERATIONS --MI-option 32x16000 " echo echo "--------------------------------------------------------------------------------------" echo "ANTS command:" echo "$exe " echo "--------------------------------------------------------------------------------------" $exe echo "execants=$exe" >> ${MOVINGBASE}.cfg # Apply forward transformation to MOVING echo echo "--------------------------------------------------------------------------------------" echo "Applying forward transformation to ${MOVING}" echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/WarpImageMultiTransform $DIM ${MOVING} ${OUTPUTNAME}deformed.nii.gz ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt -R ${FIXED} echo "warpfw=${ANTSPATH}/WarpImageMultiTransform $DIM ${MOVING} ${OUTPUTNAME}deformed.nii.gz ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt -R ${FIXED}" >> ${MOVINGBASE}.cfg # Apply inverse transformation to FIXED if [ "${TRANSFORMATIONTYPE}" == "EL" ] then echo echo "--------------------------------------------------------------------------------------" echo "Applying inverse transformation to ${FIXED} is not possible for elastic registration." echo "--------------------------------------------------------------------------------------" else echo echo "--------------------------------------------------------------------------------------" echo "Applying inverse transformation to ${FIXED}" echo "--------------------------------------------------------------------------------------" FIXEDBASE=` echo ${FIXED} | cut -d '.' -f 1 ` ${ANTSPATH}/WarpImageMultiTransform $DIM ${FIXED} ${FIXEDBASE}_InverseWarp.nii.gz -R ${MOVING} -i ${OUTPUTNAME}Affine.txt ${OUTPUTNAME}InverseWarp.nii.gz echo "warpinv=${ANTSPATH}/WarpImageMultiTransform $DIM ${FIXED} ${FIXEDBASE}_InverseWarp.nii.gz -R ${MOVING} -i ${OUTPUTNAME}Affine.txt ${OUTPUTNAME}InverseWarp.nii.gz" >> ${MOVINGBASE}.cfg fi elif [ ${N4CORRECT} -eq 1 ] && [ ${RIGID} -eq 0 ] then # Apply N4BiasFieldCorrection #Uncomment/comment below to switch between N3 and N4 bias field correction binaries #exe="${ANTSPATH}/N3BiasFieldCorrection $DIM $MOVING ${OUTPUTNAME}.nii.gz 4" exe="${ANTSPATH}/N4BiasFieldCorrection -d $DIM -i $MOVING -o ${OUTPUTNAME}.nii.gz -b [200] -s 3 -c [50x50x30x20,1e-6]" echo echo "--------------------------------------------------------------------------------------" echo "N4BiasFieldCorrection command:" echo "$exe " echo "--------------------------------------------------------------------------------------" $exe echo "execN4=$exe" >> ${MOVINGBASE}.cfg # Apply ANTS mapping command on N3 corrected image exe="${ANTSPATH}/ANTS $DIM -m ${METRIC}${FIXED},${OUTPUTNAME}.nii.gz,${METRICPARAMS} -t $TRANSFORMATION -r $REGULARIZATION -o ${OUTPUTNAME} -i $MAXITERATIONS --use-Histogram-Matching --number-of-affine-iterations $AFFINEITERATIONS --MI-option 32x16000 " echo echo "--------------------------------------------------------------------------------------" echo "ANTS command:" echo "$exe " echo "--------------------------------------------------------------------------------------" $exe echo "execants=$exe" >> ${MOVINGBASE}.cfg # Apply forward transformation to MOVING echo echo "--------------------------------------------------------------------------------------" echo "Applying forward transformation to ${MOVING}" echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/WarpImageMultiTransform $DIM ${OUTPUTNAME}.nii.gz ${OUTPUTNAME}deformed.nii.gz ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt -R ${FIXED} echo "warpfw=${ANTSPATH}/WarpImageMultiTransform $DIM ${OUTPUTNAME}.nii.gz ${OUTPUTNAME}deformed.nii.gz ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt -R ${FIXED}" >> ${MOVINGBASE}.cfg # Apply inverse transformation to FIXED if [ "${TRANSFORMATIONTYPE}" == "EL" ] then echo echo "--------------------------------------------------------------------------------------" echo "Applying inverse transformation to ${FIXED} is not possible for elastic registration." echo "--------------------------------------------------------------------------------------" else echo echo "--------------------------------------------------------------------------------------" echo "Applying inverse transformation to ${FIXED}" echo "--------------------------------------------------------------------------------------" FIXEDBASE=` echo ${FIXED} | cut -d '.' -f 1 ` ${ANTSPATH}/WarpImageMultiTransform $DIM ${FIXED} ${FIXEDBASE}_InverseWarp.nii.gz -i ${OUTPUTNAME}Affine.txt ${OUTPUTNAME}InverseWarp.nii.gz -R ${OUTPUTNAME}.nii.gz echo "warpinv=${ANTSPATH}/WarpImageMultiTransform $DIM ${FIXED} ${FIXEDBASE}_InverseWarp.nii.gz -R ${OUTPUTNAME}.nii.gz -i ${OUTPUTNAME}Affine.txt ${OUTPUTNAME}InverseWarp.nii.gz" >> ${MOVINGBASE}.cfg fi elif [ ${N4CORRECT} -eq 0 ] && [ ${RIGID} -eq 1 ] then exe=" ${ANTSPATH}/ANTS $DIM -m ${METRIC}${FIXED},${MOVING},${METRICPARAMS} -o ${OUTPUTNAME}.nii.gz -i 0 --use-Histogram-Matching --number-of-affine-iterations $AFFINEITERATIONS --MI-option 32x16000 ${RIGIDTRANSF} " echo echo "--------------------------------------------------------------------------------------" echo "ANTS command:" echo "$exe " echo "--------------------------------------------------------------------------------------" $exe echo "execants=$exe" >> ${MOVINGBASE}.cfg # Apply forward transformation to MOVING echo echo "--------------------------------------------------------------------------------------" echo "Applying rigid transformation to ${MOVING}" echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/WarpImageMultiTransform $DIM ${MOVING} ${OUTPUTNAME}deformed.nii.gz ${OUTPUTNAME}Affine.txt -R ${FIXED} elif [ ${N4CORRECT} -eq 1 ] && [ ${RIGID} -eq 1 ] then # Apply N4BiasFieldCorrection #Uncomment/comment below to switch between N3 and N4 bias field correction binaries #exe="${ANTSPATH}/N3BiasFieldCorrection $DIM $MOVING ${OUTPUTNAME}.nii.gz 4" exe="${ANTSPATH}/N4BiasFieldCorrection -d $DIM -i $MOVING -o ${OUTPUTNAME}.nii.gz -b [200] -s 3 -c [50x50x30x20,1e-6]" echo echo "--------------------------------------------------------------------------------------" echo "N4BiasFieldCorrection command:" echo "$exe " echo "--------------------------------------------------------------------------------------" $exe echo "execN4=$exe" >> ${MOVINGBASE}.cfg exe=" ${ANTSPATH}/ANTS $DIM -m MI[${FIXED},${MOVING},1,32] -o ${OUTPUTNAME}.nii.gz -i 0 --use-Histogram-Matching --number-of-affine-iterations $AFFINEITERATIONS --MI-option 32x16000 ${RIGIDTRANSF} " echo echo "--------------------------------------------------------------------------------------" echo "ANTS command:" echo "$exe " echo "--------------------------------------------------------------------------------------" $exe echo "execants=$exe" >> ${MOVINGBASE}.cfg # Apply forward transformation to MOVING echo echo "--------------------------------------------------------------------------------------" echo "Applying rigid transformation to ${MOVING}" echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/WarpImageMultiTransform $DIM ${MOVING} ${OUTPUTNAME}deformed.nii.gz ${OUTPUTNAME}Affine.txt -R ${FIXED} fi # Apply transformation on labeled image? if [ ${#LABELIMAGE} -gt 3 ] then ${ANTSPATH}/WarpImageMultiTransform $DIM $LABELIMAGE ${OUTPUTNAME}labeled.nii.gz -i ${OUTPUTNAME}Affine.txt ${OUTPUTNAME}InverseWarp.nii.gz -R ${MOVING} --use-NN fi if [ $DoANTSQC -eq 1 ] then # measure image similarity - 0 = intensity difference, 1 = correlation , 2 = mutual information for SIM in 0 1 2 ; do ${ANTSPATH}/MeasureImageSimilarity $DIM $SIM $FIXED ${OUTPUTNAME}deformed.nii.gz done # the lines below measure dice overlap and min-dist-sum from the approximately segmented brain (3 tissues and background) ${ANTSPATH}/ThresholdImage $DIM $FIXED ${OUTPUTNAME}fixthresh.nii.gz Otsu 4 ${ANTSPATH}/ThresholdImage $DIM $MOVING ${OUTPUTNAME}movthresh.nii.gz Otsu 4 ${ANTSPATH}/WarpImageMultiTransform $DIM ${OUTPUTNAME}movthresh.nii.gz ${OUTPUTNAME}defthresh.nii.gz ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt -R ${FIXED} --use-NN ${ANTSPATH}/ImageMath $DIM ${OUTPUTNAME}dicestats.txt DiceAndMinDistSum ${OUTPUTNAME}fixthresh.nii.gz ${OUTPUTNAME}movthresh.nii.gz ${OUTPUTNAME}mindistsum.nii.gz # below not used unless you want to compare segmentation volumes to those estimated by the jacobian # labelstats for jacobian wrt segmenation below, to compose # ${ANTSPATH}/ComposeMultiTransform $DIM ${OUTPUTNAME}CompWarp.nii.gz -R $FIXED ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt # ${ANTSPATH}/CreateJacobianDeterminantImage $DIM ${OUTPUTNAME}CompWarp.nii.gz ${OUTPUTNAME}jacobian.nii.gz 0 # ${ANTSPATH}/ImageMath $DIM ${OUTPUTNAME}movlabstat.txt LabelStats ${OUTPUTNAME}movthresh.nii.gz ${OUTPUTNAME}movthresh.nii.gz # ${ANTSPATH}/ImageMath $DIM ${OUTPUTNAME}jaclabstat.txt LabelStats ${OUTPUTNAME}defthresh.nii.gz ${OUTPUTNAME}jacobian.nii.gz # we compare the output of these last two lines: # the Volume of the movlabstat computation vs. the mass of the jaclabstat fi # save output in starting dir - remove inputs, then remove tempdir rm `basename ${FIXED}` rm `basename ${MOVING}` cp * ../ cd ${currentdir} rm -rf ${tmpdir}/ time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo " ">> ${MOVINGBASE}.cfg echo " Script executed in $time_elapsed seconds" >> ${MOVINGBASE}.cfg echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" >> ${MOVINGBASE}.cfg exit ants-2.2.0/Scripts/antsJointLabelFusion.sh000077500000000000000000001212511311104306400205360ustar00rootroot00000000000000#!/bin/bash VERSION="0.0.0" # trap keyboard interrupt (control-c) trap control_c SIGINT function setPath { cat <&2 fi # Test availability of helper scripts. # No need to test this more than once. Can reside outside of the main loop. ANTS=${ANTSPATH}/antsRegistration WARP=${ANTSPATH}/antsApplyTransforms JLF=${ANTSPATH}/antsJointFusion PEXEC=${ANTSPATH}ANTSpexec.sh SGE=${ANTSPATH}waitForSGEQJobs.pl PBS=${ANTSPATH}waitForPBSQJobs.pl XGRID=${ANTSPATH}waitForXGridJobs.pl SLURM=${ANTSPATH}/waitForSlurmJobs.pl fle_error=0 for FLE in $JLF $ANTS $WARP $PEXEC $SGE $XGRID $PBS $SLURM do if [[ ! -x $FLE ]]; then echo echo "--------------------------------------------------------------------------------------" echo " FILE $FLE DOES NOT EXIST -- OR -- IS NOT EXECUTABLE !!! $0 will terminate." echo "--------------------------------------------------------------------------------------" echo " if the file is not executable, please change its permissions. " fle_error=1 fi done if [[ $fle_error = 1 ]]; then echo "missing helper script" exit 1 fi #assuming .nii.gz as default file type. This is the case for ANTS 1.7 and up function Usage { cat < Compulsory arguments (minimal command line requires SGE cluster, otherwise use -c & -j options): -d: ImageDimension: 2 or 3. -o: OutputPrefix: A prefix that is prepended to all output files. -t: TargetImage: Target image to be labeled. -g: Atlas: Atlas to be warped to target image. -l: Labels: Labels corresponding to atlas (cf -g). Optional arguments: -m: Majority vote: Use majority vote instead of joint label fusion (default = 0). -k: Keep files: Keep warped atlas and label files (default = 0). -c: Control for parallel computation (default 0) -- 0 == run serially, 1 == SGE qsub, 2 == use PEXEC (localhost), 3 == Apple XGrid, 4 == PBS qsub, 5 == SLURM. -j: Number of cpu cores to use (default 2; -- requires "-c 2"). -r: qsub options -q: Use quick registration parameters: Either 0 or 1 (default = 1). -p: Save posteriors: Save posteriors in specified c-style format e.g. posterior%04d.nii.gz Need to specify output directory. -f: Float precision: Use float precision (default = 1) -- 0 == double, 1 == float. -u: Registration walltime (default = 20:00:00): Option for PBS/SLURM qsub specifying requested time per pairwise registration. -v: Registration memory limit (default = 8gb): Option for PBS/SLURM qsub specifying requested memory per pairwise registration. -w: JLF walltime (default = 20:00:00): Option for PBS/SLURM qsub specifying requested time for the joint label fusion call. -z: JLF Memory limit (default = 8gb): Option for PBS/SLURM qsub specifying requested memory for the joint label fusion call. -y: transform type (default = 's') t: translation r: rigid a: rigid + affine s: rigid + affine + deformable syn sr: rigid + deformable syn so: deformable syn only b: rigid + affine + deformable b-spline syn br: rigid + deformable b-spline syn bo: deformable b-spline syn only -x: Target mask image (default = 'otsu') otsu: use otsu thresholding to define foreground/background or: 'or' all the warped atlas images to defined foreground/background : a user-specified mask none: don't use a mask Example: `basename $0` -d 3 -t target.nii.gz -o malf \ -p malfPosteriors%04d.nii.gz \ -g atlas1.nii.gz -l labels1.nii.gz \ -g atlas2.nii.gz -l labels2.nii.gz \ -g atlas3.nii.gz -l labels3.nii.gz -------------------------------------------------------------------------------------- JLF was created by: -------------------------------------------------------------------------------------- Hongzhi Wang and Paul Yushkevich Penn Image Computing And Science Laboratory University of Pennsylvania Please reference http://www.ncbi.nlm.nih.gov/pubmed/22732662 when employing this script in your studies. Wang H, Suh JW, Das SR, Pluta J, Craige C, Yushkevich PA. Multi-Atlas Segmentation with Joint Label Fusion. IEEE Trans Pattern Anal Mach Intell. -------------------------------------------------------------------------------------- script by Nick Tustison -------------------------------------------------------------------------------------- USAGE exit 1 } function Help { cat < Example Case: `basename $0` -d 3 -t target.nii.gz -o malf \ -p malfPosteriors%04d.nii.gz \ -g atlas1.nii.gz -l labels1.nii.gz \ -g atlas2.nii.gz -l labels2.nii.gz \ -g atlas3.nii.gz -l labels3.nii.gz Compulsory arguments (minimal command line requires SGE cluster, otherwise use -c & -j options): -d: ImageDimension: 2 or 3. -o: OutputPrefix: A prefix that is prepended to all output files. -t: TargetImage: Target image to be labeled. -g: Atlas: Atlas to be warped to target image. -l: Labels: Labels corresponding to atlas (cf -g). Optional arguments: -m: Majority vote: Use majority vote instead of joint label fusion (default = 0). -k: Keep files: Keep warped atlas and label files (default = 0). -c: Control for parallel computation (default 0) -- 0 == run serially, 1 == SGE qsub, 2 == use PEXEC (localhost), 3 == Apple XGrid, 4 == PBS qsub, 5 == SLURM. -j: Number of cpu cores to use (default 2; -- requires "-c 2"). -q: Use quick registration parameters: Either 0 or 1 (default = 1). -p: Save posteriors: Save posteriors in specified c-style format e.g. posterior%04d.nii.gz Need to specify output directory. -f: Float precision: Use float precision (default = 1) -- 0 == double, 1 == float. -u: Registration walltime (default = 20:00:00): Option for PBS/SLURM qsub specifying requested time per pairwise registration. -v: Registration memory limit (default = 8gb): Option for PBS/SLURM qsub specifying requested memory per pairwise registration. -w: JLF walltime (default = 20:00:00): Option for PBS/SLURM qsub specifying requested time for the joint label fusion call. -z: JLF Memory limit (default = 8gb): Option for PBS/SLURM qsub specifying requested memory for the joint label fusion call. -y: Transform type (default = 's') t: translation r: rigid a: rigid + affine s: rigid + affine + deformable syn sr: rigid + deformable syn so: deformable syn only b: rigid + affine + deformable b-spline syn br: rigid + deformable b-spline syn bo: deformable b-spline syn only -x: Target mask image (default = 'otsu') otsu: use otsu thresholding to define foreground/background or: 'or' all the warped atlas images to defined foreground/background : a user-specified mask none: don't use a mask Requirements: This scripts relies on the following scripts in your $ANTSPATH directory. The script will terminate prematurely if these files are not present or are not executable. - pexec.sh - waitForSGEQJobs.pl (only for use with Sun Grid Engine) - waitForPBSQJobs.pl (only for use with Portable Batch System) - ANTSpexec.sh (only for use with localhost parallel execution) - waitForXGridJobs.pl (only for use with Apple XGrid) - waitForSlurmJobs.pl (only for use with SLURM) - antsRegistrationSyN.sh - antsRegistrationSyNQuick.sh ( quick parameters ) -------------------------------------------------------------------------------------- Get the latest ANTS version at: -------------------------------------------------------------------------------------- https://github.com/stnava/ANTs/ -------------------------------------------------------------------------------------- Read the ANTS documentation at: -------------------------------------------------------------------------------------- http://stnava.github.io/ANTs/ -------------------------------------------------------------------------------------- JLF was created by: -------------------------------------------------------------------------------------- Hongzhi Wang and Paul Yushkevich Penn Image Computing And Science Laboratory University of Pennsylvania Please reference http://www.ncbi.nlm.nih.gov/pubmed/22732662 when employing this script in your studies. Wang H, Suh JW, Das SR, Pluta J, Craige C, Yushkevich PA. Multi-Atlas Segmentation with Joint Label Fusion. IEEE Trans Pattern Anal Mach Intell. -------------------------------------------------------------------------------------- script by Nick Tustison -------------------------------------------------------------------------------------- HELP exit 1 } function reportParameters { cat <&2 fi MAJORITYVOTE=0 RUNQUICK=1 TRANSFORM_TYPE="s" # reading command line arguments while getopts "c:d:f:g:h:j:k:l:m:o:p:q:r:t:u:v:w:x:y:z:" OPT do case $OPT in h) #help echo "$USAGE" exit 0 ;; c) #use SGE cluster DOQSUB=$OPTARG if [[ $DOQSUB -gt 5 ]]; then echo " DOQSUB must be an integer value (0=serial, 1=SGE qsub, 2=try pexec, 3=XGrid, 4=PBS qsub, 5=SLURM ) you passed -c $DOQSUB " exit 1 fi ;; d) #dimensions DIM=$OPTARG if [[ ${DIM} -ne 2 && $DIM -ne 3 ]]; then echo " Dimensionality is only valid for 2 or 3. You passed -d $DIM." exit 1 fi ;; f) PRECISION=$OPTARG ;; g) ATLAS_IMAGES[${#ATLAS_IMAGES[@]}]=$OPTARG ;; j) #number of cpu cores to use CORES=$OPTARG ;; k) KEEP_ALL_IMAGES=$OPTARG ;; m) #majority voting option MAJORITYVOTE=$OPTARG ;; p) OUTPUT_POSTERIORS_FORMAT=$OPTARG ;; q) RUNQUICK=$OPTARG ;; r) QSUB_OPTS=$OPTARG ;; l) ATLAS_LABELS[${#ATLAS_LABELS[@]}]=$OPTARG ;; o) OUTPUT_PREFIX=$OPTARG OUTPUT_DIR=`dirname ${OUTPUT_PREFIX}` ;; t) TARGET_IMAGE=$OPTARG ;; u) REGISTRATION_WALLTIME=$OPTARG ;; v) REGISTRATION_MEMORY=$OPTARG ;; w) JLF_WALLTIME=$OPTARG ;; z) JLF_MEMORY=$OPTARG ;; x) TARGET_MASK_IMAGE=$OPTARG ;; y) TRANSFORM_TYPE=$OPTARG ;; \?) # getopts issues an error message echo "$USAGE" >&2 exit 1 ;; esac done if [[ $DOQSUB -eq 1 || $DOQSUB -eq 4 ]]; then qq=`which qsub` if [[ ${#qq} -lt 1 ]]; then echo "do you have qsub? if not, then choose another c option ... if so, then check where the qsub alias points ..." exit 1 fi fi if [[ $DOQSUB -eq 5 ]]; then qq=`which sbatch` if [[ ${#qq} -lt 1 ]]; then echo "do you have sbatch? if not, then choose another c option ... if so, then check where the sbatch alias points ..." exit fi fi if [[ ! -f "$TARGET_IMAGE" ]]; then echo "Target image '$TARGET_IMAGE' does not exist. See usage: '$0 -h 1'" exit fi if [[ ${#ATLAS_IMAGES[@]} -ne ${#ATLAS_LABELS[@]} ]]; then echo "The number of atlas images does not equal the number of labels. Ensure that a corresponding set of labels exist for each image." exit 1 fi PRECISIONFLAG='f' if [[ ${PRECISION} -eq 0 ]]; then PRECISIONFLAG='d' fi mkdir ${OUTPUT_DIR} ########################################################################## # # Perform JLF labeling by # 1) registering all atlases to target image # 2) call 'jointfusion' # ########################################################################## echo echo "--------------------------------------------------------------------------------------" echo " Start JLFization" echo "--------------------------------------------------------------------------------------" reportParameters jobIDs="" WARPED_ATLAS_IMAGES=() WARPED_ATLAS_LABELS=() AFFINE_FILES=() WARP_FIELDS=() INVERSE_WARP_FIELDS=() for (( i = 0; i < ${#ATLAS_IMAGES[@]}; i++ )) do IMG_BASE=`basename ${ATLAS_IMAGES[$i]}` BASENAME=` echo ${IMG_BASE} | cut -d '.' -f 1 ` qscript="${OUTPUT_DIR}/job_${BASENAME}_${i}.sh" WARPED_ATLAS_IMAGES[${#WARPED_ATLAS_IMAGES[@]}]="${OUTPUT_PREFIX}${BASENAME}_${i}_Warped.nii.gz" INVERSE_WARPED_ATLAS_IMAGES[${#INVERSE_WARPED_ATLAS_IMAGES[@]}]="${OUTPUT_PREFIX}${BASENAME}_${i}_InverseWarped.nii.gz" WARPED_ATLAS_LABELS[${#WARPED_ATLAS_LABELS[@]}]="${OUTPUT_PREFIX}${BASENAME}_${i}_WarpedLabels.nii.gz" WARP_FIELDS[${#WARP_FIELDS[@]}]="${OUTPUT_PREFIX}${BASENAME}_${i}_1Warp.nii.gz" INVERSE_WARP_FIELDS[${#INVERSE_WARP_FIELDS[@]}]="${OUTPUT_PREFIX}${BASENAME}_${i}_1InverseWarp.nii.gz" AFFINE_FILES[${#AFFINE_FILES[@]}]="${OUTPUT_PREFIX}${BASENAME}_${i}_0GenericAffine.mat" if [[ -f "${OUTPUT_PREFIX}${BASENAME}_${i}_WarpedLabels.nii.gz" ]]; then echo ${OUTPUT_PREFIX}${BASENAME}_${i}_WarpedLabels.nii.gz already exists. rm -f $qscript continue fi regcall=${ANTSPATH}/antsRegistrationSyN.sh if [[ $RUNQUICK -eq 1 ]]; then regcall=${ANTSPATH}/antsRegistrationSyNQuick.sh fi registrationCall="$regcall \ -d ${DIM} \ -p ${PRECISIONFLAG} \ -j 1 \ -t ${TRANSFORM_TYPE} \ -f ${TARGET_IMAGE} \ -m ${ATLAS_IMAGES[$i]} \ -o ${OUTPUT_PREFIX}${BASENAME}_${i}_ > ${OUTPUT_PREFIX}${BASENAME}_${i}_log.txt" labelXfrmCall="${ANTSPATH}/antsApplyTransforms \ -d ${DIM} \ --float 1 \ -i ${ATLAS_LABELS[$i]} \ -r ${TARGET_IMAGE} \ -o ${OUTPUT_PREFIX}${BASENAME}_${i}_WarpedLabels.nii.gz \ -n NearestNeighbor \ -t ${OUTPUT_PREFIX}${BASENAME}_${i}_1Warp.nii.gz \ -t ${OUTPUT_PREFIX}${BASENAME}_${i}_0GenericAffine.mat >> ${OUTPUT_PREFIX}${BASENAME}_${i}_log.txt" copyImageHeaderCall="${ANTSPATH}/CopyImageHeaderInformation \ ${TARGET_IMAGE} \ ${OUTPUT_PREFIX}${BASENAME}_${i}_Warped.nii.gz \ ${OUTPUT_PREFIX}${BASENAME}_${i}_Warped.nii.gz 1 1 1" copyLabelsHeaderCall="${ANTSPATH}/CopyImageHeaderInformation \ ${TARGET_IMAGE} \ ${OUTPUT_PREFIX}${BASENAME}_${i}_WarpedLabels.nii.gz \ ${OUTPUT_PREFIX}${BASENAME}_${i}_WarpedLabels.nii.gz 1 1 1" rm -f $qscript if [[ $DOQSUB -eq 5 ]]; then # SLURM job scripts must start with a shebang echo '#!/bin/sh' > $qscript fi echo "$registrationCall" >> $qscript echo "$labelXfrmCall" >> $qscript echo "$copyImageHeaderCall" >> $qscript echo "$copyLabelsHeaderCall" >> $qscript if [[ $DOQSUB -eq 1 ]]; then id=`qsub -cwd -S /bin/bash -N antsJlfReg -v ANTSPATH=$ANTSPATH $QSUB_OPTS $qscript | awk '{print $3}'` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 4 ]]; then id=`qsub -N antsJlfReg -v ANTSPATH=$ANTSPATH $QSUB_OPTS -q nopreempt -l nodes=1:ppn=1 -l mem=${REGISTRATION_MEMORY} -l walltime=${REGISTRATION_WALLTIME} $qscript | awk '{print $1}'` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 3 ]]; then id=`xgrid $XGRID_OPTS -job submit /bin/bash $qscript | awk '{sub(/;/,"");print $3}' | tr '\n' ' ' | sed 's: *: :g'` jobIDs="$jobIDs $id" elif [[ $DOQSUB -eq 5 ]]; then id=`sbatch --job-name=antsJlfReg${i} --export=ANTSPATH=$ANTSPATH $QSUB_OPTS --nodes=1 --cpus-per-task=1 --time=${REGISTRATION_WALLTIME} --mem=${REGISTRATION_MEMORY} $qscript | rev | cut -f1 -d\ | rev` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 0 ]]; then echo $qscript bash $qscript fi done if [[ $DOQSUB -eq 2 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting JLF on max ${CORES} cpucores. " echo "--------------------------------------------------------------------------------------" chmod +x ${OUTPUT_DIR}/job_*.sh $PEXEC -j ${CORES} "sh" ${OUTPUT_DIR}/job_*.sh fi jlfCall="${ANTSPATH}/antsJointFusion -d ${DIM} -t $TARGET_IMAGE --verbose 1 " if [[ $DOQSUB -eq 0 ]]; then # Run job locally echo echo "--------------------------------------------------------------------------------------" echo " Starting JLF" echo "--------------------------------------------------------------------------------------" EXISTING_WARPED_ATLAS_IMAGES=() EXISTING_WARPED_ATLAS_LABELS=() for (( i = 0; i < ${#WARPED_ATLAS_IMAGES[@]}; i++ )) do echo ${WARPED_ATLAS_IMAGES[$i]} if [[ -f ${WARPED_ATLAS_IMAGES[$i]} ]] && [[ -f ${WARPED_ATLAS_LABELS[$i]} ]]; then EXISTING_WARPED_ATLAS_IMAGES[${#EXISTING_WARPED_ATLAS_IMAGES[@]}]=${WARPED_ATLAS_IMAGES[$i]} EXISTING_WARPED_ATLAS_LABELS[${#EXISTING_WARPED_ATLAS_LABELS[@]}]=${WARPED_ATLAS_LABELS[$i]} fi done if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -lt 2 ]]; then echo "Error: At least 2 warped image/label pairs needs to exist for jointFusion." exit 1 fi if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -ne ${#WARPED_ATLAS_LABELS[@]} ]]; then echo "Warning: One or more registrations failed." fi maskCall='' if [[ $MAJORITYVOTE -eq 1 ]]; then jlfCall="${ANTSPATH}/ImageMath ${DIM} ${OUTPUT_PREFIX}MajorityVotingLabels.nii.gz MajorityVoting ${EXISTING_WARPED_ATLAS_LABELS[@]} " else for (( i = 0; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do jlfCall="${jlfCall} -g ${EXISTING_WARPED_ATLAS_IMAGES[$i]} -l ${EXISTING_WARPED_ATLAS_LABELS[$i]}" done if [[ -z "${OUTPUT_POSTERIORS_FORMAT}" ]]; then jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz]" else jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz,${OUTPUT_POSTERIORS_FORMAT}]" fi if [[ ${TARGET_MASK_IMAGE} == 'otsu' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOtsu.nii.gz" maskCall="${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_IMAGE} ${TARGET_MASK_IMAGE} Otsu 1;" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ ${TARGET_MASK_IMAGE} == 'or' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOr.nii.gz" maskCall="${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${EXISTING_WARPED_ATLAS_IMAGES[0]} ${EXISTING_WARPED_ATLAS_IMAGES[1]};" for (( i = 2; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do maskCall="${maskCall} ${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${TARGET_MASK_IMAGE} ${EXISTING_WARPED_ATLAS_IMAGES[$i]};" done maskCall="${maskCall} ${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_MASK_IMAGE} ${TARGET_MASK_IMAGE} 0 0 0 1" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ -f ${TARGET_MASK_IMAGE} ]]; then jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" fi fi qscript2="${OUTPUT_PREFIX}JLF.sh" echo "$maskCall" > $qscript2 echo "$jlfCall" >> $qscript2 echo $qscript2 bash $qscript2 fi if [[ $DOQSUB -eq 1 ]]; then # Run jobs on SGE and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting JLF on SGE cluster. " echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/waitForSGEQJobs.pl 1 600 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "qsub submission failed - jobs went into error state" exit 1; fi EXISTING_WARPED_ATLAS_IMAGES=() EXISTING_WARPED_ATLAS_LABELS=() for (( i = 0; i < ${#WARPED_ATLAS_IMAGES[@]}; i++ )) do echo ${WARPED_ATLAS_IMAGES[$i]} if [[ -f ${WARPED_ATLAS_IMAGES[$i]} ]] && [[ -f ${WARPED_ATLAS_LABELS[$i]} ]]; then EXISTING_WARPED_ATLAS_IMAGES[${#EXISTING_WARPED_ATLAS_IMAGES[@]}]=${WARPED_ATLAS_IMAGES[$i]} EXISTING_WARPED_ATLAS_LABELS[${#EXISTING_WARPED_ATLAS_LABELS[@]}]=${WARPED_ATLAS_LABELS[$i]} fi done if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -lt 2 ]]; then echo "Error: At least 2 warped image/label pairs needs to exist for jointFusion." exit 1 fi if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -ne ${#WARPED_ATLAS_LABELS[@]} ]]; then echo "Warning: One or more registrations failed." fi maskCall='' if [[ $MAJORITYVOTE -eq 1 ]]; then jlfCall="${ANTSPATH}/ImageMath ${DIM} ${OUTPUT_PREFIX}MajorityVotingLabels.nii.gz MajorityVoting ${EXISTING_WARPED_ATLAS_LABELS[@]} " else for (( i = 0; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do jlfCall="${jlfCall} -g ${EXISTING_WARPED_ATLAS_IMAGES[$i]} -l ${EXISTING_WARPED_ATLAS_LABELS[$i]}" done if [[ -z "${OUTPUT_POSTERIORS_FORMAT}" ]]; then jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz]" else jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz,${OUTPUT_POSTERIORS_FORMAT}]" fi if [[ ${TARGET_MASK_IMAGE} == 'otsu' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOtsu.nii.gz" maskCall="${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_IMAGE} ${TARGET_MASK_IMAGE} Otsu 1;" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ ${TARGET_MASK_IMAGE} == 'or' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOr.nii.gz" maskCall="${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${EXISTING_WARPED_ATLAS_IMAGES[0]} ${EXISTING_WARPED_ATLAS_IMAGES[1]};" for (( i = 2; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do maskCall="${maskCall} ${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${TARGET_MASK_IMAGE} ${EXISTING_WARPED_ATLAS_IMAGES[$i]};" done maskCall="${maskCall} ${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_MASK_IMAGE} ${TARGET_MASK_IMAGE} 0 0 0 1" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ -f ${TARGET_MASK_IMAGE} ]]; then jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" fi fi qscript2="${OUTPUT_PREFIX}JLF.sh" echo "$maskCall" > $qscript2 echo "$jlfCall" >> $qscript2 jobIDs=`qsub -cwd -S /bin/bash -N antsJlf -v ANTSPATH=$ANTSPATH $QSUB_OPTS $qscript2 | awk '{print $3}'` ${ANTSPATH}/waitForSGEQJobs.pl 1 600 $jobIDs fi if [[ $DOQSUB -eq 4 ]]; then # Run jobs on PBS and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting JLF on PBS cluster. " echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/waitForPBSQJobs.pl 1 600 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "qsub submission failed - jobs went into error state" exit 1; fi EXISTING_WARPED_ATLAS_IMAGES=() EXISTING_WARPED_ATLAS_LABELS=() for (( i = 0; i < ${#WARPED_ATLAS_IMAGES[@]}; i++ )) do echo ${WARPED_ATLAS_IMAGES[$i]} if [[ -f ${WARPED_ATLAS_IMAGES[$i]} ]] && [[ -f ${WARPED_ATLAS_LABELS[$i]} ]]; then EXISTING_WARPED_ATLAS_IMAGES[${#EXISTING_WARPED_ATLAS_IMAGES[@]}]=${WARPED_ATLAS_IMAGES[$i]} EXISTING_WARPED_ATLAS_LABELS[${#EXISTING_WARPED_ATLAS_LABELS[@]}]=${WARPED_ATLAS_LABELS[$i]} fi done if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -lt 2 ]]; then echo "Error: At least 2 warped image/label pairs needs to exist for jointFusion." exit 1 fi if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -ne ${#WARPED_ATLAS_LABELS[@]} ]]; then echo "Warning: One or more registrations failed." fi maskCall='' if [[ $MAJORITYVOTE -eq 1 ]]; then jlfCall="${ANTSPATH}/ImageMath ${DIM} ${OUTPUT_PREFIX}MajorityVotingLabels.nii.gz MajorityVoting ${EXISTING_WARPED_ATLAS_LABELS[@]} " else for (( i = 0; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do jlfCall="${jlfCall} -g ${EXISTING_WARPED_ATLAS_IMAGES[$i]} -l ${EXISTING_WARPED_ATLAS_LABELS[$i]}" done if [[ -z "${OUTPUT_POSTERIORS_FORMAT}" ]]; then jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz]" else jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz,${OUTPUT_POSTERIORS_FORMAT}]" fi if [[ ${TARGET_MASK_IMAGE} == 'otsu' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOtsu.nii.gz" maskCall="${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_IMAGE} ${TARGET_MASK_IMAGE} Otsu 1;" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ ${TARGET_MASK_IMAGE} == 'or' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOr.nii.gz" maskCall="${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${EXISTING_WARPED_ATLAS_IMAGES[0]} ${EXISTING_WARPED_ATLAS_IMAGES[1]};" for (( i = 2; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do maskCall="${maskCall} ${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${TARGET_MASK_IMAGE} ${EXISTING_WARPED_ATLAS_IMAGES[$i]};" done maskCall="${maskCall} ${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_MASK_IMAGE} ${TARGET_MASK_IMAGE} 0 0 0 1" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ -f ${TARGET_MASK_IMAGE} ]]; then jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" fi fi qscript2="${OUTPUT_PREFIX}JLF.sh" echo "$maskCall" > $qscript2 echo "$jlfCall" >> $qscript2 jobIDs=`qsub -N antsJlf -v ANTSPATH=$ANTSPATH $QSUB_OPTS -q nopreempt -l nodes=1:ppn=1 -l mem=${JLF_MEMORY} -l walltime=${JLF_WALLTIME} $qscript2 | awk '{print $1}'` ${ANTSPATH}/waitForPBSQJobs.pl 1 600 $jobIDs fi if [[ $DOQSUB -eq 2 ]]; then EXISTING_WARPED_ATLAS_IMAGES=() EXISTING_WARPED_ATLAS_LABELS=() for (( i = 0; i < ${#WARPED_ATLAS_IMAGES[@]}; i++ )) do echo ${WARPED_ATLAS_IMAGES[$i]} if [[ -f ${WARPED_ATLAS_IMAGES[$i]} ]] && [[ -f ${WARPED_ATLAS_LABELS[$i]} ]]; then EXISTING_WARPED_ATLAS_IMAGES[${#EXISTING_WARPED_ATLAS_IMAGES[@]}]=${WARPED_ATLAS_IMAGES[$i]} EXISTING_WARPED_ATLAS_LABELS[${#EXISTING_WARPED_ATLAS_LABELS[@]}]=${WARPED_ATLAS_LABELS[$i]} fi done if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -lt 2 ]]; then echo "Error: At least 2 warped image/label pairs needs to exist for jointFusion." exit 1 fi if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -ne ${#WARPED_ATLAS_LABELS[@]} ]]; then echo "Warning: One or more registrations failed." fi maskCall='' if [[ $MAJORITYVOTE -eq 1 ]]; then jlfCall="${ANTSPATH}/ImageMath ${DIM} ${OUTPUT_PREFIX}MajorityVotingLabels.nii.gz MajorityVoting ${EXISTING_WARPED_ATLAS_LABELS[@]} " else for (( i = 0; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do jlfCall="${jlfCall} -g ${EXISTING_WARPED_ATLAS_IMAGES[$i]} -l ${EXISTING_WARPED_ATLAS_LABELS[$i]}" done if [[ -z "${OUTPUT_POSTERIORS_FORMAT}" ]]; then jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz]" else jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz,${OUTPUT_POSTERIORS_FORMAT}]" fi if [[ ${TARGET_MASK_IMAGE} == 'otsu' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOtsu.nii.gz" maskCall="${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_IMAGE} ${TARGET_MASK_IMAGE} Otsu 1;" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ ${TARGET_MASK_IMAGE} == 'or' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOr.nii.gz" maskCall="${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${EXISTING_WARPED_ATLAS_IMAGES[0]} ${EXISTING_WARPED_ATLAS_IMAGES[1]};" for (( i = 2; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do maskCall="${maskCall} ${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${TARGET_MASK_IMAGE} ${EXISTING_WARPED_ATLAS_IMAGES[$i]};" done maskCall="${maskCall} ${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_MASK_IMAGE} ${TARGET_MASK_IMAGE} 0 0 0 1" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ -f ${TARGET_MASK_IMAGE} ]]; then jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" fi fi qscript2="${OUTPUT_PREFIX}JLF.sh" echo "$maskCall" > $qscript2 echo "$jlfCall" >> $qscript2 sh $qscript2 fi if [[ $DOQSUB -eq 3 ]]; then # Run jobs on XGrid and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting JLF on XGrid cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/waitForXGridJobs.pl -xgridflags "$XGRID_OPTS" -verbose -delay 30 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "XGrid submission failed - jobs went into error state" exit 1; fi EXISTING_WARPED_ATLAS_IMAGES=() EXISTING_WARPED_ATLAS_LABELS=() for (( i = 0; i < ${#WARPED_ATLAS_IMAGES[@]}; i++ )) do echo ${WARPED_ATLAS_IMAGES[$i]} if [[ -f ${WARPED_ATLAS_IMAGES[$i]} ]] && [[ -f ${WARPED_ATLAS_LABELS[$i]} ]]; then EXISTING_WARPED_ATLAS_IMAGES[${#EXISTING_WARPED_ATLAS_IMAGES[@]}]=${WARPED_ATLAS_IMAGES[$i]} EXISTING_WARPED_ATLAS_LABELS[${#EXISTING_WARPED_ATLAS_LABELS[@]}]=${WARPED_ATLAS_LABELS[$i]} fi done if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -lt 2 ]]; then echo "Error: At least 2 warped image/label pairs needs to exist for jointFusion." exit 1 fi if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -ne ${#WARPED_ATLAS_LABELS[@]} ]]; then echo "Warning: One or more registrations failed." fi maskCall='' if [[ $MAJORITYVOTE -eq 1 ]]; then jlfCall="${ANTSPATH}/ImageMath ${DIM} ${OUTPUT_PREFIX}MajorityVotingLabels.nii.gz MajorityVoting ${EXISTING_WARPED_ATLAS_LABELS[@]} " else for (( i = 0; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do jlfCall="${jlfCall} -g ${EXISTING_WARPED_ATLAS_IMAGES[$i]} -l ${EXISTING_WARPED_ATLAS_LABELS[$i]}" done if [[ -z "${OUTPUT_POSTERIORS_FORMAT}" ]]; then jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz]" else jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz,${OUTPUT_POSTERIORS_FORMAT}]" fi if [[ ${TARGET_MASK_IMAGE} == 'otsu' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOtsu.nii.gz" maskCall="${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_IMAGE} ${TARGET_MASK_IMAGE} Otsu 1;" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ ${TARGET_MASK_IMAGE} == 'or' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOr.nii.gz" maskCall="${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${EXISTING_WARPED_ATLAS_IMAGES[0]} ${EXISTING_WARPED_ATLAS_IMAGES[1]};" for (( i = 2; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do maskCall="${maskCall} ${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${TARGET_MASK_IMAGE} ${EXISTING_WARPED_ATLAS_IMAGES[$i]};" done maskCall="${maskCall} ${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_MASK_IMAGE} ${TARGET_MASK_IMAGE} 0 0 0 1" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ -f ${TARGET_MASK_IMAGE} ]]; then jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" fi fi qscript2="${OUTPUT_PREFIX}JLF.sh" echo "$maskCall" > $qscript2 echo "$jlfCall" >> $qscript2 sh $qscript2 fi if [[ $DOQSUB -eq 5 ]]; then # Run jobs on SLURM and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting JLF on SLURM cluster. " echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/waitForSlurmJobs.pl 1 600 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "SLURM submission failed - jobs went into error state" exit 1; fi # Remove the SLURM output files (which are likely to be empty) rm -f ${OUTPUT_DIR}/slurm-*.out EXISTING_WARPED_ATLAS_IMAGES=() EXISTING_WARPED_ATLAS_LABELS=() for (( i = 0; i < ${#WARPED_ATLAS_IMAGES[@]}; i++ )) do echo ${WARPED_ATLAS_IMAGES[$i]} if [[ -f ${WARPED_ATLAS_IMAGES[$i]} ]] && [[ -f ${WARPED_ATLAS_LABELS[$i]} ]]; then EXISTING_WARPED_ATLAS_IMAGES[${#EXISTING_WARPED_ATLAS_IMAGES[@]}]=${WARPED_ATLAS_IMAGES[$i]} EXISTING_WARPED_ATLAS_LABELS[${#EXISTING_WARPED_ATLAS_LABELS[@]}]=${WARPED_ATLAS_LABELS[$i]} fi done if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -lt 2 ]]; then echo "Error: At least 2 warped image/label pairs needs to exist for jointFusion." exit 1 fi if [[ ${#EXISTING_WARPED_ATLAS_LABELS[@]} -ne ${#WARPED_ATLAS_LABELS[@]} ]]; then echo "Warning: One or more registrations failed." fi maskCall='' if [[ $MAJORITYVOTE -eq 1 ]]; then jlfCall="${ANTSPATH}/ImageMath ${DIM} ${OUTPUT_PREFIX}MajorityVotingLabels.nii.gz MajorityVoting ${EXISTING_WARPED_ATLAS_LABELS[@]} " else for (( i = 0; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do jlfCall="${jlfCall} -g ${EXISTING_WARPED_ATLAS_IMAGES[$i]} -l ${EXISTING_WARPED_ATLAS_LABELS[$i]}" done if [[ -z "${OUTPUT_POSTERIORS_FORMAT}" ]]; then jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz]" else jlfCall="${jlfCall} -o [${OUTPUT_PREFIX}Labels.nii.gz,${OUTPUT_PREFIX}Intensity.nii.gz,${OUTPUT_POSTERIORS_FORMAT}]" fi if [[ ${TARGET_MASK_IMAGE} == 'otsu' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOtsu.nii.gz" maskCall="${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_IMAGE} ${TARGET_MASK_IMAGE} Otsu 1;" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ ${TARGET_MASK_IMAGE} == 'or' ]]; then TARGET_MASK_IMAGE="${OUTPUT_PREFIX}TargetMaskImageOr.nii.gz" maskCall="${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${EXISTING_WARPED_ATLAS_IMAGES[0]} ${EXISTING_WARPED_ATLAS_IMAGES[1]};" for (( i = 2; i < ${#EXISTING_WARPED_ATLAS_IMAGES[@]}; i++ )) do maskCall="${maskCall} ${ANTSPATH}/ImageMath ${DIM} ${TARGET_MASK_IMAGE} max ${TARGET_MASK_IMAGE} ${EXISTING_WARPED_ATLAS_IMAGES[$i]};" done maskCall="${maskCall} ${ANTSPATH}/ThresholdImage ${DIM} ${TARGET_MASK_IMAGE} ${TARGET_MASK_IMAGE} 0 0 0 1" jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" elif [[ -f ${TARGET_MASK_IMAGE} ]]; then jlfCall="${jlfCall} -x ${TARGET_MASK_IMAGE}" fi fi qscript2="${OUTPUT_PREFIX}JLF.sh" echo "#!/bin/sh" > $qscript2 echo "$maskCall" >> $qscript2 echo "$jlfCall" >> $qscript2 jobIDs=`sbatch --job-name=antsJlf --export=ANTSPATH=$ANTSPATH $QSUB_OPTS --nodes=1 --cpus-per-task=1 --time=${JLF_WALLTIME} --mem=${JLF_MEMORY} $qscript2 | rev | cut -f1 -d\ | rev` ${ANTSPATH}/waitForSlurmJobs.pl 1 600 $jobIDs fi # clean up rm -f ${OUTPUT_DIR}/job_*.sh if [[ $KEEP_ALL_IMAGES -eq 0 ]]; then rm -f ${WARPED_ATLAS_IMAGES[@]} rm -f ${INVERSE_WARPED_ATLAS_IMAGES[@]} rm -f ${WARPED_ATLAS_LABELS[@]} rm -f ${AFFINE_FILES[@]} rm -f ${WARP_FIELDS[@]} rm -f ${INVERSE_WARP_FIELDS[@]} rm -f $qscript rm -f $qscript2 rm -f ${OUTPUT_DIR}/slurm-*.out fi time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo echo "--------------------------------------------------------------------------------------" if [[ $MAJORITYVOTE -eq 1 ]]; then echo " Done creating: ${OUTPUT_PREFIX}MajorityVotingLabels.nii.gz" else echo " Done creating: ${OUTPUT_PREFIX}Labels.nii.gz" fi echo " Script executed in $time_elapsed seconds" echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" echo "--------------------------------------------------------------------------------------" exit 0 ants-2.2.0/Scripts/antsLaplacianBoundaryCondition.R000077500000000000000000000034231311104306400223550ustar00rootroot00000000000000#!/usr/bin/env Rscript library(getopt) options(digits=3) Args <- commandArgs() self<-Args[4] self<-substring(self,8,nchar(as.character(self))) spec = c( 'mask' , 'm', "0", "character" ," name of brain mask image ", 'input' , 'i', "1", "character"," the input prefix ", 'dim' , 'd', "3", "character"," the input prefix ", 'output' , 'o', "output.nii.gz", "character"," the output prefix ") # ............................................. # spec=matrix(spec,ncol=5,byrow=TRUE) # get the options opt = getopt(spec) # ............................................. # #help was asked for. if ( !is.null(opt$help) || length(opt) == 1 ) { #print a friendly message and exit with a non-zero error code cat("\n") cat(paste(self,"\n")) for ( x in 1:nrow(spec) ) { cat("\n") longopt<-paste("--",spec[x,1],sep='') shortopt<-paste("-",spec[x,2],sep='') hlist<-paste(shortopt,"|",longopt,spec[x,5],"\n \n") # print(hlist,quote=F) cat(format(hlist, width=40, justify = c("left"))) } cat(format("Example: \n", width=40, justify = c("left"))) ex<-paste(self," --output myoutput.nii.gz --input image.nii.gz --mask mask.nii.gz \n \n ") ex<-format(ex, width=length(ex), justify = c("left")) cat("\n") cat(ex) q(status=1); } if ( is.null( opt$dim ) ) dim<-3 else dim<-as.numeric( opt$dim ) # # take care of optional parameters for ( myfn in c( opt$mask, opt$input ) ) { if ( !file.exists(myfn) ) { print(paste("input file",myfn,"does not exist. Exiting.")) q(status=1) } # else print(paste(" have input file",myfn)) } library(ANTsR) img<-antsImageRead( opt$input, dim ) mask<-antsImageRead( opt$mask, dim ) mymean<-mean( img[ mask == 1] ) outimg<-antsImageClone(img) outimg[ mask == 0 ]<-mymean outimg = iMath( outimg, "Laplacian", 1, 1 ) antsImageWrite( outimg , opt$output ) ants-2.2.0/Scripts/antsLongitudinalCorticalThickness.sh000077500000000000000000001232741311104306400233240ustar00rootroot00000000000000#!/bin/bash VERSION="0.0" # Check dependencies PROGRAM_DEPENDENCIES=( 'antsRegistration' 'antsApplyTransforms' 'N4BiasFieldCorrection' 'Atropos' 'KellyKapowski' ) SCRIPTS_DEPENDENCIES=( 'antsBrainExtraction.sh' 'antsAtroposN4.sh' 'antsMultivariateTemplateConstruction2.sh' 'antsJointLabelFusion.sh' ) for D in ${PROGRAM_DEPENDENCIES[@]}; do if [[ ! -s ${ANTSPATH}/${D} ]]; then echo "Error: we can't find the $D program." echo "Perhaps you need to \(re\)define \$ANTSPATH in your environment." exit fi done for D in ${SCRIPT_DEPENDENCIES[@]}; do if [[ ! -s ${ANTSPATH}/${D} ]]; then echo "We can't find the $D script." echo "Perhaps you need to \(re\)define \$ANTSPATH in your environment." exit fi done function Usage { cat < -o outputPrefix \${anatomicalImages[@]} Example: bash $0 -d 3 -e brainWithSkullTemplate.nii.gz -m brainPrior.nii.gz -p segmentationPriors%d.nii.gz -o output \${anatomicalImages[@]} Required arguments: -d: Image dimension 2 or 3 (for 2- or 3-dimensional image) -e: Brain template Anatomical *intensity* template (possibly created using a population data set with buildtemplateparallel.sh in ANTs). This template is *not* skull-stripped. -m: Brain extraction probability mask Brain *probability* mask created using e.g. LPBA40 labels which have brain masks defined, and warped to anatomical template and averaged resulting in a probability image. -p: Brain segmentation priors Tissue *probability* priors corresponding to the image specified with the -e option. Specified using c-style formatting, e.g. -p labelsPriors%02d.nii.gz. We assume that the first four priors are ordered as follows 1: csf 2: cortical gm 3: wm 4: deep gm -o: Output prefix The following subdirectory and images are created for the single subject template * \${OUTPUT_PREFIX}SingleSubjectTemplate/ * \${OUTPUT_PREFIX}SingleSubjectTemplate/T_template*.nii.gz anatomical images Set of multimodal input data assumed to be specified ordered as follows: \${time1_modality1} \${time1_modality2} ... \${time1_modalityN} \\ \${time2_modality1} \${time2_modality2} ... . . . \${timeN_modality1} ... A single modality is expected by default, in which case the input images are simply ordered by time: \${time1_modality1} \${time2_modality1} ... \${timeN_modality1} If there are multiple modalities, use the -k option to specify how many. Optional arguments: -s: image file suffix Any of the standard ITK IO formats e.g. nrrd, nii.gz (default), mhd -c: control type Control for parallel computation (default 0): 0 = run serially 1 = SGE qsub 2 = use PEXEC (localhost) 3 = Apple XGrid 4 = PBS qsub 5 = SLURM -t: template for t1 registration Anatomical *intensity* template (assumed to be skull-stripped). A common use case would be where this would be the same template as specified in the -e option which is not skull stripped. -a: Atlases (assumed to be skull-stripped) used to cook template priors. If atlases aren't used then we simply smooth the single-subject template posteriors after passing through antsCorticalThickness.sh. Example: -a atlas1.nii.gz -a atlas2.nii.gz ... -a atlasN.nii.gz -l: Labels associated with each atlas, in the same order as they are specified with the -a option. The number of labels in each image is assumed to be equal to the number of priors. -f: extraction registration mask Mask (defined in the template space) used during registration for brain extraction. -g: denoise anatomical images Denoise anatomical images (default = 0). -j: number of cpu cores Number of cpu cores to use locally for pexec option (default 2; requires "-c 2") -k: number of modalities Number of modalities used to construct the template (default 1): For example, if one wanted to use multiple modalities consisting of T1, T2, and FA components ("-k 3"). -n: use SST cortical thickness prior If set to '1', the cortical thickness map from the single-subject template is used as a prior constraint for each of the individual calls to antsCorticalThickness.sh (default = 0). -u: use floating-point precision Use floating point precision in registrations (default = 0) -v: Atropos segmentation weight (SST) Atropos spatial prior *probability* weight for the segmentation for the single subject template (default = 0.25) -w: Atropos segmentation weight (Indiv.) Atropos spatial prior *probability* weight for the segmentation for the individual time points (default = 0.5) -x: Number of iterations within Atropos (default 5). -q: Use quick registration parameters If 'yes' then we use antsRegistrationSyNQuick.sh as the basis for registration. Otherwise use antsRegistrationSyN.sh. The options are as follows: '-q 0' = antsRegistrationSyN for everything (default) '-q 1' = Fast antsCorticalThickness to SST '-q 2' = Fast JLF cooking '-q 3' = Fast everything -r: rigid alignment to SST This option dictates if the individual subjects are registered to the single subject template before running through antsCorticalThickness. This potentially reduces bias caused by subject orientation and voxel spacing (default = 0). -b: keep temporary files Keep brain extraction/segmentation warps, etc (default = 0). -y: averge rigid transform component Update the template with the full affine transform (default 0). If 1, the rigid component of the affine transform will be used to update the template. -z: Test / debug mode If > 0, runs a faster version of the script. Only for testing. Implies -u 0 in the antsCorticalThickness.sh script (i.e., no random seeding). Requires single thread computation for complete reproducibility. USAGE exit 1 } echoParameters() { cat <>>>>>>>>>>>>>>>>>>>" echo $cmd $cmd cmdExit=$? if [[ $cmdExit -gt 0 ]]; then echo "ERROR: command exited with nonzero status $cmdExit" echo "Command: $cmd" echo if [[ ! $DEBUG_MODE -gt 0 ]]; then exit 1 fi fi echo "END <<<<<<<<<<<<<<<<<<<<" echo echo return $cmdExit } ################################################################################ # # Main routine # ################################################################################ HOSTNAME=`hostname` DATE=`date` CURRENT_DIR=`pwd`/ OUTPUT_DIR=${CURRENT_DIR}/tmp$RANDOM/ OUTPUT_PREFIX=${OUTPUT_DIR}/tmp OUTPUT_SUFFIX="nii.gz" DIMENSION=3 NUMBER_OF_MODALITIES=1 ANATOMICAL_IMAGES=() RUN_QUICK=1 USE_RANDOM_SEEDING=1 BRAIN_TEMPLATE="" EXTRACTION_PRIOR="" EXTRACTION_REGISTRATION_MASK="" SEGMENTATION_PRIOR="" USE_SST_CORTICAL_THICKNESS_PRIOR=0 REGISTRATION_TEMPLATE="" DO_REGISTRATION_TO_TEMPLATE=0 DENOISE=0 ATROPOS_SEGMENTATION_PRIOR_WEIGHT_SST=0.25 ATROPOS_SEGMENTATION_PRIOR_WEIGHT_TIMEPOINT=0.5 ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS=5 AFFINE_UPDATE_FULL=0 DOQSUB=0 CORES=2 RIGID_ALIGNMENT_TO_SST=0 MALF_ATLASES=() MALF_LABELS=() MALF_LABEL_STRINGS_FOR_PRIORS=() ################################################################################ # # Programs and their parameters # ################################################################################ USE_FLOAT_PRECISION=0 KEEP_TMP_IMAGES=0 if [[ $# -lt 3 ]] ; then Usage >&2 exit 1 else while getopts "a:b:c:d:e:f:g:h:j:k:l:m:n:o:p:q:r:s:t:u:v:x:w:y:z:" OPT do case $OPT in a) MALF_ATLASES[${#MALF_ATLASES[@]}]=$OPTARG ;; b) KEEP_TMP_IMAGES=$OPTARG ;; c) DOQSUB=$OPTARG if [[ $DOQSUB -gt 5 ]]; then echo " DOQSUB must be an integer value (0=serial, 1=SGE qsub, 2=try pexec, 3=XGrid, 4=PBS qsub, 5=SLURM ) you passed -c $DOQSUB " exit 1 fi ;; d) #dimensions DIMENSION=$OPTARG if [[ ${DIMENSION} -gt 3 || ${DIMENSION} -lt 2 ]]; then echo " Error: ImageDimension must be 2 or 3 " exit 1 fi ;; e) #brain extraction anatomical image BRAIN_TEMPLATE=$OPTARG ;; f) #brain extraction registration mask EXTRACTION_REGISTRATION_MASK=$OPTARG ;; g) #denoise DENOISE=$OPTARG ;; h) #help Usage >&2 exit 0 ;; j) #number of cpu cores to use (default = 2) CORES=$OPTARG ;; k) #number of modalities NUMBER_OF_MODALITIES=$OPTARG ;; l) MALF_LABELS[${#MALF_LABELS[@]}]=$OPTARG ;; m) #brain extraction prior probability mask EXTRACTION_PRIOR=$OPTARG ;; n) # use USE_SST_CORTICAL_THICKNESS_PRIOR=$OPTARG ;; x) #atropos segmentation internal iterations ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS=$OPTARG ;; o) #output prefix OUTPUT_PREFIX=$OPTARG ;; p) #brain segmentation label prior image SEGMENTATION_PRIOR=$OPTARG ;; r) #rigid alignment to SST RIGID_ALIGNMENT_TO_SST=$OPTARG ;; t) #template registration image REGISTRATION_TEMPLATE=$OPTARG DO_REGISTRATION_TO_TEMPLATE=1 ;; q) # run quick RUN_QUICK=$OPTARG ;; u) #use floating point precision USE_FLOAT_PRECISION=$OPTARG ;; v) #atropos prior weight for single subject template ATROPOS_SEGMENTATION_PRIOR_WEIGHT_SST=$OPTARG ;; w) #atropos prior weight for each individual time point ATROPOS_SEGMENTATION_PRIOR_WEIGHT_TIMEPOINT=$OPTARG ;; y) # 1 update with full affine, 0 for no rigid (default = 0) AFFINE_UPDATE_FULL=$OPTARG ;; z) #debug mode DEBUG_MODE=$OPTARG ;; *) # getopts issues an error message echo "ERROR: unrecognized option -$OPT $OPTARG" exit 1 ;; esac done fi FORMAT=${SEGMENTATION_PRIOR} PREFORMAT=${FORMAT%%\%*} POSTFORMAT=${FORMAT##*d} FORMAT=${FORMAT#*\%} FORMAT=${FORMAT%%d*} REPCHARACTER='' TOTAL_LENGTH=0 if [ ${#FORMAT} -eq 2 ] then REPCHARACTER=${FORMAT:0:1} TOTAL_LENGTH=${FORMAT:1:1} fi # MAXNUMBER=$(( 10 ** $TOTAL_LENGTH )) MAXNUMBER=1000 PRIOR_IMAGE_FILENAMES=() WARPED_PRIOR_IMAGE_FILENAMES=() BRAIN_SEGMENTATION_OUTPUT=${OUTPUT_PREFIX}BrainSegmentation SEGMENTATION_WARP_OUTPUT_PREFIX=${BRAIN_SEGMENTATION_OUTPUT}Prior SEGMENTATION_PRIOR_WARPED=${SEGMENTATION_WARP_OUTPUT_PREFIX}Warped for (( i = 1; i < $MAXNUMBER; i++ )) do NUMBER_OF_REPS=$(( $TOTAL_LENGTH - ${#i} )) ROOT=''; for(( j=0; j < $NUMBER_OF_REPS; j++ )) do ROOT=${ROOT}${REPCHARACTER} done FILENAME=${PREFORMAT}${ROOT}${i}${POSTFORMAT} WARPED_FILENAME=${SEGMENTATION_PRIOR_WARPED}${ROOT}${i}.${OUTPUT_SUFFIX} if [[ -f $FILENAME ]]; then PRIOR_IMAGE_FILENAMES=( ${PRIOR_IMAGE_FILENAMES[@]} $FILENAME ) WARPED_PRIOR_IMAGE_FILENAMES=( ${WARPED_PRIOR_IMAGE_FILENAMES[@]} $WARPED_FILENAME ) else break 1 fi done NUMBER_OF_PRIOR_IMAGES=${#WARPED_PRIOR_IMAGE_FILENAMES[*]} # Shiftsize is calculated because a variable amount of arguments can be used on the command line. # The shiftsize variable will give the correct number of arguments to skip. Issuing shift $shiftsize will # result in skipping that number of arguments on the command line, so that only the input images remain. shiftsize=$(($OPTIND - 1)) shift $shiftsize # The invocation of $* will now read all remaining arguments into the variable IMAGESETVARIABLE IMAGESETVARIABLE=$* NINFILES=$(($nargs - $shiftsize)) IMAGESETARRAY=() for IMG in $IMAGESETVARIABLE do ANATOMICAL_IMAGES[${#ANATOMICAL_IMAGES[@]}]=$IMG done if [[ ${#ANATOMICAL_IMAGES[@]} -eq 0 ]]; then echo "Error: no anatomical images specified." exit 1 fi if [[ $NUMBER_OF_MODALITIES -gt 1 ]]; then echo "--------------------------------------------------------------------------------------" echo " Cortical thickness using the following ${NUMBER_OF_MODALITIES}-tuples: " echo "--------------------------------------------------------------------------------------" for (( i = 0; i < ${#ANATOMICAL_IMAGES[@]}; i+=$NUMBER_OF_MODALITIES )) do IMAGEMETRICSET="" for (( j = 0; j < $ANATOMICAL_IMAGES; j++ )) do k=0 let k=$i+$j IMAGEMETRICSET="$IMAGEMETRICSET ${ANATOMICAL_IMAGES[$k]}" done echo $IMAGEMETRICSET done echo "--------------------------------------------------------------------------------------" fi if [[ ${#MALF_ATLASES[@]} -ne ${#MALF_LABELS[@]} ]] then echo "Error: The number of malf atlases and labels aren't equal." fi # Set up various things related to RUN_QUICK # Can't do everything fast and still get good results if there is large deformation. # Initiate levels of fast: # 0 - Fast SST (old ANTS) but everything else slower for quality # 1 - + Fast antsct to SST # 2 - + Fast MALF cooking # 3 - + Fast everything RUN_OLD_ANTS_SST_CREATION=1 RUN_ANTSCT_TO_SST_QUICK=0 RUN_FAST_MALF_COOKING=0 RUN_FAST_ANTSCT_TO_GROUP_TEMPLATE=0 if [[ $RUN_QUICK -gt 0 ]]; then RUN_ANTSCT_TO_SST_QUICK=1 fi if [[ $RUN_QUICK -gt 1 ]]; then RUN_FAST_MALF_COOKING=1 fi if [[ $RUN_QUICK -gt 2 ]]; then RUN_FAST_ANTSCT_TO_GROUP_TEMPLATE=1 fi ################################################################################ # # Preliminaries: # 1. Check existence of inputs # 2. Figure out output directory and mkdir if necessary # 3. See if $REGISTRATION_TEMPLATE is the same as $BRAIN_TEMPLATE # ################################################################################ for (( i = 0; i < ${#ANATOMICAL_IMAGES[@]}; i++ )) do if [[ ! -f ${ANATOMICAL_IMAGES[$i]} ]]; then echo "The specified image \"${ANATOMICAL_IMAGES[$i]}\" does not exist." exit 1 fi done if [[ ${#ANATOMICAL_IMAGES[@]} -eq ${NUMBER_OF_MODALITIES} ]]; then echo "Only one set of anatomical images. Running antsCorticalThickness.sh on the single subject." SUBJECT_ANATOMICAL_IMAGES='' for (( j=0; j < $NUMBER_OF_MODALITIES; j++ )) do SUBJECT_ANATOMICAL_IMAGES="${SUBJECT_ANATOMICAL_IMAGES} -a ${ANATOMICAL_IMAGES[$j]}" done # Won't be quick unless -q 3 was specified # But if you are running a longitudinal script without longitudinal data, that may not be the only problem if [[ $DO_REGISTRATION_TO_TEMPLATE -eq 1 ]]; then logCmd ${ANTSPATH}/antsCorticalThickness.sh \ -d ${DIMENSION} -x ${ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS} \ -t ${REGISTRATION_TEMPLATE} \ -q ${RUN_FAST_ANTSCT_TO_GROUP_TEMPLATE} \ ${SUBJECT_ANATOMICAL_IMAGES} \ -e ${BRAIN_TEMPLATE} \ -f ${EXTRACTION_REGISTRATION_MASK} \ -m ${EXTRACTION_PRIOR} \ -k ${KEEP_TMP_IMAGES} \ -g ${DENOISE} \ -w ${ATROPOS_SEGMENTATION_PRIOR_WEIGHT_TIMEPOINT} \ -z ${DEBUG_MODE} \ -p ${SEGMENTATION_PRIOR} \ -o ${OUTPUT_PREFIX} fi if [[ $DO_REGISTRATION_TO_TEMPLATE -eq 0 ]]; then logCmd ${ANTSPATH}/antsCorticalThickness.sh \ -d ${DIMENSION} -x ${ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS} \ -q ${RUN_FAST_ANTSCT_TO_GROUP_TEMPLATE} \ ${SUBJECT_ANATOMICAL_IMAGES} \ -e ${BRAIN_TEMPLATE} \ -f ${EXTRACTION_REGISTRATION_MASK} \ -m ${EXTRACTION_PRIOR} \ -k ${KEEP_TMP_IMAGES} \ -g ${DENOISE} \ -w ${ATROPOS_SEGMENTATION_PRIOR_WEIGHT_TIMEPOINT} \ -z ${DEBUG_MODE} \ -p ${SEGMENTATION_PRIOR} \ -o ${OUTPUT_PREFIX} fi exit 0 fi if [[ ! -f ${BRAIN_TEMPLATE} ]]; then echo "The extraction template doesn't exist:" echo " $BRAIN_TEMPLATE" exit 1 fi if [[ ! -f ${EXTRACTION_PRIOR} ]]; then echo "The brain extraction prior doesn't exist:" echo " $EXTRACTION_PRIOR" exit 1 fi if [[ $DO_REGISTRATION_TO_TEMPLATE -eq 1 ]]; then if [[ ! -f ${REGISTRATION_TEMPLATE} ]] then echo "Template for registration, ${REGISTRATION_TEMPLATE}, does not exist." exit 1 fi fi OUTPUT_DIR=${OUTPUT_PREFIX%\/*} if [[ ! -d $OUTPUT_DIR ]]; then echo "The output directory \"$OUTPUT_DIR\" does not exist. Making it." mkdir -p $OUTPUT_DIR fi echoParameters >&2 echo "--------------------- Running `basename $0` on $HOSTNAME ---------------------" time_start=`date +%s` ################################################################################ # # Single-subject template creation # ################################################################################ echo echo "--------------------------------------------------------------------------------------" echo " Creating single-subject template " echo "--------------------------------------------------------------------------------------" echo TEMPLATE_MODALITY_WEIGHT_VECTOR='1' for(( i=1; i < ${NUMBER_OF_MODALITIES}; i++ )) do TEMPLATE_MODALITY_WEIGHT_VECTOR="${TEMPLATE_MODALITY_WEIGHT_VECTOR}x1" done TEMPLATE_Z_IMAGES='' OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE="${OUTPUT_PREFIX}SingleSubjectTemplate/" logCmd mkdir -p ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE} # Pad initial template image to avoid problems with SST drifting out of FOV for(( i=0; i < ${NUMBER_OF_MODALITIES}; i++ )) do TEMPLATE_INPUT_IMAGE="${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}initTemplateModality${i}.nii.gz" logCmd ${ANTSPATH}/ImageMath 3 ${TEMPLATE_INPUT_IMAGE} PadImage ${ANATOMICAL_IMAGES[$i]} 5 TEMPLATE_Z_IMAGES="${TEMPLATE_Z_IMAGES} -z ${TEMPLATE_INPUT_IMAGE}" done SINGLE_SUBJECT_TEMPLATE=${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_template0.nii.gz time_start_sst_creation=`date +%s` if [[ ! -f $SINGLE_SUBJECT_TEMPLATE ]]; then if [[ $RUN_OLD_ANTS_SST_CREATION -gt 0 ]]; then logCmd ${ANTSPATH}/antsMultivariateTemplateConstruction.sh \ -d ${DIMENSION} \ -o ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_ \ -b 0 \ -g 0.25 \ -i 4 \ -c ${DOQSUB} \ -j ${CORES} \ -k ${NUMBER_OF_MODALITIES} \ -w ${TEMPLATE_MODALITY_WEIGHT_VECTOR} \ -m 100x70x30x3 \ -n 1 \ -r 1 \ -s CC \ -t GR \ -y ${AFFINE_UPDATE_FULL} \ ${TEMPLATE_Z_IMAGES} \ ${ANATOMICAL_IMAGES[@]} else logCmd ${ANTSPATH}/antsMultivariateTemplateConstruction2.sh \ -d ${DIMENSION} \ -o ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_ \ -a 0 \ -b 0 \ -g 0.25 \ -i 4 \ -c ${DOQSUB} \ -j ${CORES} \ -e ${USE_FLOAT_PRECISION} \ -k ${NUMBER_OF_MODALITIES} \ -w ${TEMPLATE_MODALITY_WEIGHT_VECTOR} \ -q 100x70x30x3 \ -f 8x4x2x1 \ -s 3x2x1x0 \ -n 1 \ -r 1 \ -l 1 \ -m CC[4] \ -t SyN \ -y ${AFFINE_UPDATE_FULL} \ ${TEMPLATE_Z_IMAGES} \ ${ANATOMICAL_IMAGES[@]} fi fi if [[ ! -f ${SINGLE_SUBJECT_TEMPLATE} ]]; then echo "Error: The single subject template was not created. Exiting." exit 1 fi # clean up SINGLE_SUBJECT_ANTSCT_PREFIX=${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}/T_template logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}job*.sh logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}job*.txt logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}rigid* logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}*Repaired* logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}*WarpedToTemplate* logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_template0warp.nii.gz logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_template0Affine.txt logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_templatewarplog.txt logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}initTemplateModality*.nii.gz # Also remove the warp files but we have to be careful to not remove the affine and # warp files generated in subsequent steps (specifically from running the SST through # the cortical thickness pipeline if somebody has to re-run the longitudinal pipeline if [[ -f ${SINGLE_SUBJECT_ANTSCT_PREFIX}SubjectToTemplate1Warp.nii.gz ]]; then logCmd mkdir -p ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}/TmpFiles/ logCmd mv -f ${SINGLE_SUBJECT_ANTSCT_PREFIX}SubjectToTemplate1*Warp.nii.gz \ ${SINGLE_SUBJECT_ANTSCT_PREFIX}SubjectToTemplate0GenericAffine.mat \ ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}/TmpFiles/ logCmd mv -f ${SINGLE_SUBJECT_ANTSCT_PREFIX}TemplateToSubject0*Warp.nii.gz \ ${SINGLE_SUBJECT_ANTSCT_PREFIX}TemplateToSubject1GenericAffine.mat \ ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}/TmpFiles/ logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_*Warp.nii.gz logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_*Affine.txt logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_*GenericAffine* logCmd mv -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}/TmpFiles/* ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE} logCmd rm -rf ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}/TmpFiles/ else logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_*Warp.nii.gz logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_*Affine.txt logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}T_*GenericAffine* fi # Need to change the number of iterations to -q \ time_end_sst_creation=`date +%s` time_elapsed_sst_creation=$((time_end_sst_creation - time_start_sst_creation)) echo echo "--------------------------------------------------------------------------------------" echo " Done with single subject template: $(( time_elapsed_sst_creation / 3600 ))h $(( time_elapsed_sst_creation %3600 / 60 ))m $(( time_elapsed_sst_creation % 60 ))s" echo "--------------------------------------------------------------------------------------" echo ################################################################################ # # Create template priors # ################################################################################ SINGLE_SUBJECT_TEMPLATE_EXTRACTION_MASK=${SINGLE_SUBJECT_ANTSCT_PREFIX}BrainExtractionMask.${OUTPUT_SUFFIX} SINGLE_SUBJECT_TEMPLATE_EXTRACTION_REGISTRATION_MASK=${SINGLE_SUBJECT_ANTSCT_PREFIX}BrainExtractionRegistrationMask.${OUTPUT_SUFFIX} SINGLE_SUBJECT_TEMPLATE_PRIOR=${SINGLE_SUBJECT_ANTSCT_PREFIX}Priors\%${FORMAT}d.${OUTPUT_SUFFIX} SINGLE_SUBJECT_TEMPLATE_EXTRACTION_PRIOR=${SINGLE_SUBJECT_ANTSCT_PREFIX}BrainExtractionMaskPrior.${OUTPUT_SUFFIX} SINGLE_SUBJECT_TEMPLATE_CORTICAL_THICKNESS=${SINGLE_SUBJECT_ANTSCT_PREFIX}CorticalThickness.${OUTPUT_SUFFIX} SINGLE_SUBJECT_TEMPLATE_SKULL_STRIPPED=${SINGLE_SUBJECT_ANTSCT_PREFIX}BrainExtractionBrain.${OUTPUT_SUFFIX} echo echo "--------------------------------------------------------------------------------------" echo " Creating template priors: running SST through antsCorticalThickness " echo "--------------------------------------------------------------------------------------" echo time_start_priors=`date +%s` if [[ ! -f ${SINGLE_SUBJECT_TEMPLATE_CORTICAL_THICKNESS} ]]; then if [[ $DO_REGISTRATION_TO_TEMPLATE -eq 0 ]]; then logCmd ${ANTSPATH}/antsCorticalThickness.sh \ -d ${DIMENSION} -x ${ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS} \ -q ${RUN_FAST_ANTSCT_TO_GROUP_TEMPLATE} \ -a ${SINGLE_SUBJECT_TEMPLATE} \ -e ${BRAIN_TEMPLATE} \ -f ${EXTRACTION_REGISTRATION_MASK} \ -g ${DENOISE} \ -m ${EXTRACTION_PRIOR} \ -k ${KEEP_TMP_IMAGES} \ -z ${DEBUG_MODE} \ -p ${SEGMENTATION_PRIOR} \ -w ${ATROPOS_SEGMENTATION_PRIOR_WEIGHT_SST} \ -o ${SINGLE_SUBJECT_ANTSCT_PREFIX} else logCmd ${ANTSPATH}/antsCorticalThickness.sh \ -d ${DIMENSION} -x ${ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS} \ -t ${REGISTRATION_TEMPLATE} \ -q ${RUN_FAST_ANTSCT_TO_GROUP_TEMPLATE} \ -a ${SINGLE_SUBJECT_TEMPLATE} \ -e ${BRAIN_TEMPLATE} \ -f ${EXTRACTION_REGISTRATION_MASK} \ -g ${DENOISE} \ -m ${EXTRACTION_PRIOR} \ -k ${KEEP_TMP_IMAGES} \ -z ${DEBUG_MODE} \ -p ${SEGMENTATION_PRIOR} \ -w ${ATROPOS_SEGMENTATION_PRIOR_WEIGHT_SST} \ -o ${SINGLE_SUBJECT_ANTSCT_PREFIX} fi fi SINGLE_SUBJECT_TEMPLATE_POSTERIORS=( ${SINGLE_SUBJECT_ANTSCT_PREFIX}BrainSegmentationPosteriors*.${OUTPUT_SUFFIX} ) SINGLE_SUBJECT_TEMPLATE_POSTERIORS_EXIST=1 SINGLE_SUBJECT_TEMPLATE_PRIORS_EXIST=1 SINGLE_SUBJECT_TEMPLATE_PRIORS=() for (( j = 0; j < ${#SINGLE_SUBJECT_TEMPLATE_POSTERIORS[@]}; j++ )) do POSTERIOR=${SINGLE_SUBJECT_TEMPLATE_POSTERIORS[$j]} if [[ ! -f ${POSTERIOR} ]]; then SINGLE_SUBJECT_TEMPLATE_POSTERIORS_EXIST=0 SINGLE_SUBJECT_TEMPLATE_PRIORS_EXIST=0 break; fi SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]=${POSTERIOR/BrainSegmentationPosteriors/Priors} if [[ ! -f ${SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]} ]]; then SINGLE_SUBJECT_TEMPLATE_PRIORS_EXIST=0 fi done if [[ ${SINGLE_SUBJECT_TEMPLATE_POSTERIORS_EXIST} -eq 0 ]]; then echo "Error: Posteriors for the single-subject template do not exist." exit 1 fi logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SINGLE_SUBJECT_TEMPLATE_SKULL_STRIPPED} m ${SINGLE_SUBJECT_TEMPLATE} ${SINGLE_SUBJECT_TEMPLATE_EXTRACTION_MASK} logCmd ${ANTSPATH}/SmoothImage ${DIMENSION} ${SINGLE_SUBJECT_TEMPLATE_EXTRACTION_MASK} 1 ${SINGLE_SUBJECT_TEMPLATE_EXTRACTION_PRIOR} 1 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SINGLE_SUBJECT_TEMPLATE_EXTRACTION_REGISTRATION_MASK} MD ${SINGLE_SUBJECT_TEMPLATE_EXTRACTION_MASK} 40 if [[ ${SINGLE_SUBJECT_TEMPLATE_PRIORS_EXIST} -eq 0 ]]; then if [[ ${#MALF_ATLASES[@]} -eq 0 ]]; then echo echo " ---> Smoothing single-subject template posteriors as priors." echo for j in ${SINGLE_SUBJECT_TEMPLATE_POSTERIORS[@]} do PRIOR=${j/BrainSegmentationPosteriors/Priors} logCmd ${ANTSPATH}/SmoothImage ${DIMENSION} $j 1 $PRIOR 1 done else echo echo " ---> Cooking single-subject priors using antsJointLabelFusion." echo SINGLE_SUBJECT_TEMPLATE_MALF_LABELS_PREFIX=${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_TEMPLATE}/T_template SINGLE_SUBJECT_TEMPLATE_MALF_LABELS=${SINGLE_SUBJECT_TEMPLATE_MALF_LABELS_PREFIX}Labels.nii.gz ATLAS_AND_LABELS_STRING='' for (( j=0; j < ${#MALF_ATLASES[@]}; j++ )) do ATLAS_AND_LABELS_STRING="${ATLAS_AND_LABELS_STRING} -g ${MALF_ATLASES[$j]} -l ${MALF_LABELS[$j]}" done if [[ ! -f ${SINGLE_SUBJECT_TEMPLATE_MALF_LABELS} ]]; then logCmd ${ANTSPATH}/antsJointLabelFusion.sh \ -d ${DIMENSION} \ -q ${RUN_FAST_MALF_COOKING} \ -x ${SINGLE_SUBJECT_TEMPLATE_EXTRACTION_MASK} \ -c ${DOQSUB} \ -j ${CORES} \ -t ${SINGLE_SUBJECT_TEMPLATE_SKULL_STRIPPED} \ -o ${SINGLE_SUBJECT_TEMPLATE_MALF_LABELS_PREFIX} \ ${ATLAS_AND_LABELS_STRING} fi SINGLE_SUBJECT_TEMPLATE_PRIORS=() for (( j = 0; j < ${#SINGLE_SUBJECT_TEMPLATE_POSTERIORS[@]}; j++ )) do POSTERIOR=${SINGLE_SUBJECT_TEMPLATE_POSTERIORS[$j]} SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]=${POSTERIOR/BrainSegmentationPosteriors/Priors} let PRIOR_LABEL=$j+1 logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${SINGLE_SUBJECT_TEMPLATE_MALF_LABELS} ${SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]} ${PRIOR_LABEL} ${PRIOR_LABEL} 1 0 logCmd ${ANTSPATH}/SmoothImage ${DIMENSION} ${SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]} 1 ${SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]} 1 done TMP_CSF_POSTERIOR=${SINGLE_SUBJECT_ANTSCT_PREFIX}BrainSegmentationCsfPosteriorTmp.${OUTPUT_SUFFIX} logCmd ${ANTSPATH}/SmoothImage ${DIMENSION} ${SINGLE_SUBJECT_TEMPLATE_POSTERIORS[0]} 1 ${TMP_CSF_POSTERIOR} 1 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SINGLE_SUBJECT_TEMPLATE_PRIORS[0]} max ${SINGLE_SUBJECT_TEMPLATE_PRIORS[0]} ${TMP_CSF_POSTERIOR} # Brian's finishing touches on "cooking"---subtract out CSF from all other priors for (( j = 1; j < ${#SINGLE_SUBJECT_TEMPLATE_PRIORS[@]}; j++ )) do let PRIOR_LABEL=$j+1 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]} - ${SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]} ${SINGLE_SUBJECT_TEMPLATE_PRIORS[0]} logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]} ${TMP_CSF_POSTERIOR} 0 1 1 0 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]} m ${SINGLE_SUBJECT_TEMPLATE_PRIORS[$j]} ${TMP_CSF_POSTERIOR} done logCmd rm -f $TMP_CSF_POSTERIOR logCmd rm -f ${SINGLE_SUBJECT_TEMPLATE_MALF_LABELS_PREFIX}*log.txt fi fi time_end_priors=`date +%s` time_elapsed_priors=$((time_end_priors - time_start_priors)) echo echo "--------------------------------------------------------------------------------------" echo " Done with creating template priors: $(( time_elapsed_priors / 3600 ))h $(( time_elapsed_priors %3600 / 60 ))m $(( time_elapsed_priors % 60 ))s" echo "--------------------------------------------------------------------------------------" echo ################################################################################ # # Run each individual subject through antsCorticalThickness # ################################################################################ echo echo "--------------------------------------------------------------------------------------" echo " Run each individual through antsCorticalThickness " echo "--------------------------------------------------------------------------------------" echo time_start_antsct=`date +%s` SUBJECT_COUNT=0 for (( i=0; i < ${#ANATOMICAL_IMAGES[@]}; i+=$NUMBER_OF_MODALITIES )) do BASENAME_ID=`basename ${ANATOMICAL_IMAGES[$i]}` BASENAME_ID=${BASENAME_ID/\.nii\.gz/} BASENAME_ID=${BASENAME_ID/\.nii/} OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS=${OUTPUT_DIR}/${BASENAME_ID} OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS=${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS}_${SUBJECT_COUNT} echo $OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS if [[ ! -d $OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS ]]; then echo "The output directory \"$OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS\" does not exist. Making it." mkdir -p $OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS fi let SUBJECT_COUNT=${SUBJECT_COUNT}+1 ANATOMICAL_REFERENCE_IMAGE=${ANATOMICAL_IMAGES[$i]} SUBJECT_ANATOMICAL_IMAGES='' if [[ ${RIGID_ALIGNMENT_TO_SST} -ne 0 ]]; then logCmd ${ANTSPATH}/antsRegistrationSyN.sh \ -d ${DIMENSION} \ -o ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS}/${BASENAME_ID}RigidToSST \ -m ${ANATOMICAL_IMAGES[$i]} \ -f ${SINGLE_SUBJECT_TEMPLATE} \ -t r logCmd rm -f ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS}/${BASENAME_ID}RigidToSSTInverseWarped.nii.gz ANATOMICAL_REFERENCE_IMAGE=${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS}/${BASENAME_ID}RigidToSSTWarped.nii.gz let k=$i+$NUMBER_OF_MODALITIES for (( j=$i; j < $k; j++ )) do BASENAME_LOCAL_ID=`basename ${ANATOMICAL_IMAGES[$j]}` BASENAME_LOCAL_ID=${BASENAME_LOCAL_ID/\.nii\.gz/} BASENAME_LOCAL_ID=${BASENAME_LOCAL_ID/\.nii/} logCmd ${ANTSPATH}/antsApplyTransforms \ -d ${DIMENSION} \ -i ${ANATOMICAL_IMAGES[$j]} \ -r ${SINGLE_SUBJECT_TEMPLATE} \ -o ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS}/${BASENAME_LOCAL_ID}RigidToSSTWarped.nii.gz \ -n Linear \ -t ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS}/${BASENAME_ID}RigidToSST0GenericAffine.mat SUBJECT_ANATOMICAL_IMAGES="${SUBJECT_ANATOMICAL_IMAGES} -a ${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS}/${BASENAME_LOCAL_ID}RigidToSSTWarped.nii.gz" done else let k=$i+$NUMBER_OF_MODALITIES for (( j=$i; j < $k; j++ )) do SUBJECT_ANATOMICAL_IMAGES="${SUBJECT_ANATOMICAL_IMAGES} -a ${ANATOMICAL_IMAGES[$j]}" done fi if [[ ${USE_SST_CORTICAL_THICKNESS_PRIOR} -ne 0 ]]; then SUBJECT_ANATOMICAL_IMAGES="${SUBJECT_ANATOMICAL_IMAGES} -r ${SINGLE_SUBJECT_TEMPLATE_CORTICAL_THICKNESS}" fi OUTPUT_LOCAL_PREFIX=${OUTPUT_DIRECTORY_FOR_SINGLE_SUBJECT_CORTICAL_THICKNESS}/${BASENAME_ID} logCmd ${ANTSPATH}/antsCorticalThickness.sh \ -d ${DIMENSION} -x ${ATROPOS_SEGMENTATION_INTERNAL_ITERATIONS} \ -q ${RUN_ANTSCT_TO_SST_QUICK} \ ${SUBJECT_ANATOMICAL_IMAGES} \ -e ${SINGLE_SUBJECT_TEMPLATE} \ -m ${SINGLE_SUBJECT_TEMPLATE_EXTRACTION_PRIOR} \ -f ${SINGLE_SUBJECT_TEMPLATE_EXTRACTION_REGISTRATION_MASK} \ -g ${DENOISE} \ -k ${KEEP_TMP_IMAGES} \ -z ${DEBUG_MODE} \ -w ${ATROPOS_SEGMENTATION_PRIOR_WEIGHT_TIMEPOINT} \ -p ${SINGLE_SUBJECT_TEMPLATE_PRIOR} \ -t ${SINGLE_SUBJECT_TEMPLATE_SKULL_STRIPPED} \ -o ${OUTPUT_LOCAL_PREFIX} if [[ $DO_REGISTRATION_TO_TEMPLATE -eq 1 ]]; then if [[ ! -f ${OUTPUT_LOCAL_PREFIX}SubjectToGroupTemplateWarp.nii.gz ]]; then logCmd ${ANTSPATH}/antsApplyTransforms \ -d ${DIMENSION} \ -r ${REGISTRATION_TEMPLATE} \ -o [${OUTPUT_LOCAL_PREFIX}SubjectToGroupTemplateWarp.nii.gz,1] \ -t ${SINGLE_SUBJECT_ANTSCT_PREFIX}SubjectToTemplate1Warp.nii.gz \ -t ${SINGLE_SUBJECT_ANTSCT_PREFIX}SubjectToTemplate0GenericAffine.mat \ -t ${OUTPUT_LOCAL_PREFIX}SubjectToTemplate1Warp.nii.gz \ -t ${OUTPUT_LOCAL_PREFIX}SubjectToTemplate0GenericAffine.mat fi if [[ ! -f ${OUTPUT_LOCAL_PREFIX}GroupTemplateToSubjectWarp.nii.gz ]]; then logCmd ${ANTSPATH}/antsApplyTransforms \ -d ${DIMENSION} \ -r ${ANATOMICAL_REFERENCE_IMAGE} \ -o [${OUTPUT_LOCAL_PREFIX}GroupTemplateToSubjectWarp.nii.gz,1] \ -t ${OUTPUT_LOCAL_PREFIX}TemplateToSubject1GenericAffine.mat \ -t ${OUTPUT_LOCAL_PREFIX}TemplateToSubject0Warp.nii.gz \ -t ${SINGLE_SUBJECT_ANTSCT_PREFIX}TemplateToSubject1GenericAffine.mat \ -t ${SINGLE_SUBJECT_ANTSCT_PREFIX}TemplateToSubject0Warp.nii.gz fi if [[ -f ${CORTICAL_LABEL_IMAGE} ]]; then SUBJECT_CORTICAL_LABELS=${OUTPUT_LOCAL_PREFIX}CorticalLabels.${OUTPUT_SUFFIX} SUBJECT_CORTICAL_THICKNESS=${OUTPUT_LOCAL_PREFIX}CorticalThickness.${OUTPUT_SUFFIX} SUBJECT_TMP=${OUTPUT_LOCAL_PREFIX}Tmp.${OUTPUT_SUFFIX} SUBJECT_STATS=${OUTPUT_LOCAL_PREFIX}LabelThickness.csv if [[ ! -f ${SUBJECT_CORTICAL_LABELS} ]]; then logCmd ${ANTSPATH}/antsApplyTransforms \ -d ${DIMENSION} \ -i ${CORTICAL_LABEL_IMAGE} \ -r ${ANATOMICAL_REFERENCE_IMAGE} \ -o ${SUBJECT_CORTICAL_LABELS} \ -n MultiLabel \ -t ${OUTPUT_LOCAL_PREFIX}GroupTemplateToSubjectWarp.nii.gz logCmd ${ANTSPATH}/ThresholdImage ${DIMENSION} ${OUTPUT_LOCAL_PREFIX}BrainSegmentation.${OUTPUT_SUFFIX} ${SUBJECT_TMP} 2 2 1 0 logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SUBJECT_CORTICAL_LABELS} m ${SUBJECT_TMP} ${SUBJECT_CORTICAL_LABELS} logCmd ${ANTSPATH}/ImageMath ${DIMENSION} ${SUBJECT_STATS} LabelStats ${SUBJECT_CORTICAL_LABELS} ${SUBJECT_CORTICAL_THICKNESS} fi logCmd rm -f $SUBJECT_TMP fi fi done time_end_antsct=`date +%s` time_elapsed_antsct=$((time_end_antsct - time_start_antsct)) echo echo "--------------------------------------------------------------------------------------" echo " Done with individual cortical thickness: $(( time_elapsed_antsct / 3600 ))h $(( time_elapsed_antsct %3600 / 60 ))m $(( time_elapsed_antsct % 60 ))s" echo "--------------------------------------------------------------------------------------" echo time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo echo "--------------------------------------------------------------------------------------" echo " Done with ANTs longitudinal processing pipeline" echo " Script executed in $time_elapsed seconds" echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" echo "--------------------------------------------------------------------------------------" exit 0 ants-2.2.0/Scripts/antsMotionCorrExample000066400000000000000000000015331311104306400203220ustar00rootroot00000000000000#! /bin/bash in=$1 if [[ ! -s $in ]] || [[ ${#in} -lt 3 ]] ; then echo open this example file and take a look to see what is done exit fi out=motcorr # average the time series antsMotionCorr -d 3 -a $in -o ${out}_avg.nii.gz # do affine motion correction antsMotionCorr -d 3 -o [${out},${out}.nii.gz,${out}_avg.nii.gz] -m gc[ ${out}_avg.nii.gz , $in , 1 , 1 , Random, 0.05 ] -t Affine[ 0.005 ] -i 20 -u 1 -e 1 -s 0 -f 1 -n 10 # do affine and deformable correction antsMotionCorr -d 3 -o [${out},${out}.nii.gz,${out}_avg.nii.gz] -m gc[ ${out}_avg.nii.gz , $in , 1 , 1 , Random, 0.05 ] -t Affine[ 0.005 ] -i 20 -u 1 -e 1 -s 0 -f 1 -m CC[ ${out}_avg.nii.gz , $in , 1 , 2 ] -t GaussianDisplacementField[0.15,3,0.5] -i 20 -u 1 -e 1 -s 0 -f 1 -n 10 # check a result by taking a 3d slice from 4d ExtractSliceFromImage 4 ${out}.nii.gz temp.nii.gz 0 32 ants-2.2.0/Scripts/antsMultivariateTemplateConstruction.sh000077500000000000000000001530451311104306400241120ustar00rootroot00000000000000#!/bin/bash VERSION="0.0.0" # trap keyboard interrupt (control-c) trap control_c SIGINT function setPath { cat <&2 fi # Test availability of helper scripts. # No need to test this more than once. Can reside outside of the main loop. ANTS=${ANTSPATH}/ANTS WARP=${ANTSPATH}/WarpImageMultiTransform N4=${ANTSPATH}/N4BiasFieldCorrection PEXEC=${ANTSPATH}/ANTSpexec.sh SGE=${ANTSPATH}/waitForSGEQJobs.pl PBS=${ANTSPATH}/waitForPBSQJobs.pl XGRID=${ANTSPATH}/waitForXGridJobs.pl SLURM=${ANTSPATH}/waitForSlurmJobs.pl fle_error=0 for FLE in $ANTS $WARP $N4 $PEXEC $SGE $XGRID $PBS $SLURM do if [[ ! -x $FLE ]]; then echo echo "--------------------------------------------------------------------------------------" echo " FILE $FLE DOES NOT EXIST -- OR -- IS NOT EXECUTABLE !!! $0 will terminate." echo "--------------------------------------------------------------------------------------" echo " if the file is not executable, please change its permissions. " fle_error=1 fi done if [[ $fle_error = 1 ]]; then echo "missing helper script" exit 1 fi function Usage { cat < Compulsory arguments (minimal command line requires SGE cluster, otherwise use -c & -j options): -d: ImageDimension: 2 or 3 (for 2 or 3 dimensional registration of single volume) ImageDimension: 4 (for template generation of time-series data) -o: OUTPREFIX; A prefix that is prepended to all output files. List of images in the current directory, eg *_t1.nii.gz. Should be at the end of the command. Optionally, one can specify a .csv or .txt file where each line is the location of the input image. One can also specify more than one file for each image for multi-modal template construction (e.g. t1 and t2). For the multi-modal case, the templates will be consecutively numbered (e.g. ${OUTPUTPREFIX}template0.nii.gz, ${OUTPUTPREFIX}template1.nii.gz, ...). NB: All images to be added to the template should be in the same directory, and this script should be invoked from that directory. Optional arguments: -c: Control for parallel computation (default 1) -- 0 == run serially, 1 == SGE qsub, 2 == use PEXEC (localhost), 3 == Apple XGrid, 4 == PBS qsub, 5 == SLURM -g: Gradient step size (default 0.25) -- smaller in magnitude results in more cautious steps. Use smaller steps to refine template details. 0.25 is an upper (aggressive) limit for this parameter. -i: Iteration limit (default 4) -- iterations of the template construction (Iteration limit)*NumImages registrations. -j: Number of cpu cores to use (default 2; -- requires "-c 2") -k: Number of modalities used to construct the template (default 1) -w: Modality weights used in the similarity metric (default = 1) --- specified as e.g. 1x0.5x0.75 -m: Max-iterations in each registration -n: N4BiasFieldCorrection of moving image (default 1) -- 0 == off, 1 == on -p: Commands to prepend to job scripts (e.g., change into appropriate directory, set paths, etc) -r: Do rigid-body registration of inputs before creating template (default 0) -- 0 == off 1 == on. Only useful when you do not have an initial template -s: Type of similarity metric used for registration. -t: Type of transformation model used for registration. -x: XGrid arguments (e.g., -x "-p password -h controlhost") -y: Update the template with the full affine transform (default 1). If 0, the rigid component of the affine transform will not be used to update the template. If your template drifts in translation or orientation try -y 0. -z: Use this this volume as the target of all inputs. When not used, the script will create an unbiased starting point by averaging all inputs. Use the full path! -b: Boolean for saving iteration output to directories (default = 0). Example: `basename $0` -d 3 -m 30x50x20 -t GR -s CC -c 1 -o MY -z InitialTemplate.nii.gz *RF*T1x.nii.gz - In this example 30x50x20 iterations per registration are used for template creation (that is the default) - Greedy-SyN and CC are the metrics to guide the mapping. - Output is prepended with MY and the initial template is InitialTemplate.nii.gz (optional). - The -c option is set to 1, which will result in using the Sun Grid Engine (SGE) to distribute the computation. - if you do not have SGE, read the help for multi-core computation on the local machine, or Apple X-grid options. -------------------------------------------------------------------------------------- ANTS was created by: -------------------------------------------------------------------------------------- Brian B. Avants, Nick Tustison and Gang Song Penn Image Computing And Science Laboratory University of Pennsylvania Please reference http://www.ncbi.nlm.nih.gov/pubmed/20851191 when employing this script in your studies. A reproducible evaluation of ANTs similarity metric performance in brain image registration: * Avants BB, Tustison NJ, Song G, Cook PA, Klein A, Gee JC. Neuroimage, 2011. Also see http://www.ncbi.nlm.nih.gov/pubmed/19818860 for more details. The script has been updated and improved since this publication. -------------------------------------------------------------------------------------- script adapted by N.M. van Strien, http://www.mri-tutorial.com | NTNU MR-Center multivariate template adaption by Nick Tustison -------------------------------------------------------------------------------------- Apple XGrid support by Craig Stark -------------------------------------------------------------------------------------- USAGE exit 1 } function reportMappingParameters { cat < subject)" echo " ---transform the inverse field by the resulting average affine transform" echo " ${ANTSPATH}/${AVERAGE_AFFINE_PROGRAM} ${dim} ${templatename}0Affine.txt ${outputname}*Affine.txt" echo " ${ANTSPATH}/WarpImageMultiTransform ${dim} ${templatename}0warp.nii.gz ${templatename}0warp.nii.gz -i ${templatename}0Affine.txt -R ${template}" echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/${AVERAGE_AFFINE_PROGRAM} ${dim} ${templatename}0Affine.txt ${outputname}*Affine.txt ${ANTSPATH}/WarpImageMultiTransform ${dim} ${templatename}0warp.nii.gz ${templatename}0warp.nii.gz -i ${templatename}0Affine.txt -R ${template} ${ANTSPATH}/MeasureMinMaxMean ${dim} ${templatename}0warp.nii.gz ${templatename}warplog.txt 1 fi echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---warp each template by the resulting transforms" echo " ${ANTSPATH}/WarpImageMultiTransform ${dim} ${template} ${template} -i ${templatename}0Affine.txt ${templatename}0warp.nii.gz ${templatename}0warp.nii.gz ${templatename}0warp.nii.gz ${templatename}0warp.nii.gz -R ${template}" echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/WarpImageMultiTransform ${dim} ${template} ${template} -i ${templatename}0Affine.txt ${templatename}0warp.nii.gz ${templatename}0warp.nii.gz ${templatename}0warp.nii.gz ${templatename}0warp.nii.gz -R ${template} } function jobfnamepadding { outdir=`dirname ${TEMPLATES[0]}` if [[ ${#outdir} -eq 0 ]] then outdir=`pwd` fi files=`ls ${outdir}/job*.sh` BASENAME1=`echo $files[1] | cut -d 'b' -f 1` for file in ${files} do if [[ "${#file}" -eq "9" ]]; then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_000${BASENAME2}" elif [[ "${#file}" -eq "10" ]]; then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_00${BASENAME2}" elif [[ "${#file}" -eq "11" ]]; then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_0${BASENAME2}" fi done } function setCurrentImageSet() { WHICHMODALITY=$1 CURRENTIMAGESET=() COUNT=0 for (( g = $WHICHMODALITY; g < ${#IMAGESETARRAY[@]}; g+=$NUMBEROFMODALITIES )) do CURRENTIMAGESET[$COUNT]=${IMAGESETARRAY[$g]} (( COUNT++ )) done } cleanup() { echo "\n*** Performing cleanup, please wait ***\n" runningANTSpids=$( ps --ppid $$ -o pid= ) for thePID in $runningANTSpids do echo "killing: ${thePID}" kill ${thePID} done return $? } control_c() # run if user hits control-c { echo -en "\n*** User pressed CTRL + C ***\n" cleanup exit $? echo -en "\n*** Script cancelled by user ***\n" } #initializing variables with global scope time_start=`date +%s` currentdir=`pwd` nargs=$# MAXITERATIONS=30x90x20 LABELIMAGE=0 # initialize optional parameter METRICTYPE=() TRANSFORMATIONTYPE="GR" # initialize optional parameter if [[ $dim == 4 ]]; then # we use a more constrained regularization for 4D mapping b/c we expect deformations to be relatively small and local TRANSFORMATIONTYPE="GR_Constrained" fi NUMBEROFMODALITIES=1 MODALITYWEIGHTSTRING="" N4CORRECT=1 # initialize optional parameter DOQSUB=1 # By default, antsMultivariateTemplateConstruction tries to do things in parallel GRADIENTSTEP=0.25 # Gradient step size, smaller in magnitude means more smaller (more cautious) steps ITERATIONLIMIT=4 CORES=2 TDIM=0 RIGID=0 RIGIDTYPE="" # set to an empty string to use affine initialization range=0 REGTEMPLATES=() TEMPLATES=() CURRENTIMAGESET=() XGRIDOPTS="" SCRIPTPREPEND="" # System specific queue options, eg "-q name" to submit to a specific queue # It can be set to an empty string if you do not need any special cluster options QSUBOPTS="" # EDIT THIS OUTPUTNAME=antsBTP BACKUP_EACH_ITERATION=0 AFFINE_UPDATE_FULL=1 ##Getting system info from linux can be done with these variables. # RAM=`cat /proc/meminfo | sed -n -e '/MemTotal/p' | awk '{ printf "%s %s\n", $2, $3 ; }' | cut -d " " -f 1` # RAMfree=`cat /proc/meminfo | sed -n -e '/MemFree/p' | awk '{ printf "%s %s\n", $2, $3 ; }' | cut -d " " -f 1` # cpu_free_ram=$((${RAMfree}/${cpu_count})) if [[ ${OSTYPE:0:6} == 'darwin' ]]; then cpu_count=`sysctl -n hw.physicalcpu` else cpu_count=`cat /proc/cpuinfo | grep processor | wc -l` fi # Provide output for Help if [[ "$1" == "-h" ]]; then Usage >&2 fi # reading command line arguments while getopts "b:c:d:g:h:i:j:k:m:n:o:p:s:r:t:w:x:y:z:" OPT do case $OPT in h) #help echo "$USAGE" exit 0 ;; b) #backup each iteration (default = 0) BACKUP_EACH_ITERATION=$OPTARG ;; c) #use SGE cluster DOQSUB=$OPTARG if [[ ${#DOQSUB} -gt 2 ]]; then echo " DOQSUB must be an integer value (0=serial, 1=SGE qsub, 2=try pexec, 3=XGrid, 4=PBS qsub, 5=SLURM) you passed -c $DOQSUB " exit 1 fi ;; d) #dimensions DIM=$OPTARG if [[ ${DIM} -eq 4 ]]; then DIM=3 TDIM=4 fi ;; g) #gradient stepsize (default = 0.25) GRADIENTSTEP=$OPTARG ;; i) #iteration limit (default = 3) ITERATIONLIMIT=$OPTARG ;; j) #number of cpu cores to use (default = 2) CORES=$OPTARG ;; k) #number of modalities used to construct the template (default = 1) NUMBEROFMODALITIES=$OPTARG ;; w) #modality weights (default = 1) MODALITYWEIGHTSTRING=$OPTARG ;; m) #max iterations other than default MAXITERATIONS=$OPTARG ;; n) #apply bias field correction N4CORRECT=$OPTARG ;; o) #output name prefix OUTPUTNAME=$OPTARG TEMPLATENAME=${OUTPUTNAME}template ;; p) #Script prepend SCRIPTPREPEND=$OPTARG ;; s) #similarity model METRICTYPE[${#METRICTYPE[@]}]=$OPTARG ;; r) #start with rigid-body registration RIGID=$OPTARG ;; t) #transformation model TRANSFORMATIONTYPE=$OPTARG ;; x) #initialization template XGRIDOPTS=$XGRIDOPTS ;; y) # update with full affine, 0 for no rigid (default = 1) AFFINE_UPDATE_FULL=$OPTARG ;; z) #initialization template REGTEMPLATES[${#REGTEMPLATES[@]}]=$OPTARG ;; \?) # getopts issues an error message echo "$USAGE" >&2 exit 1 ;; esac done # Provide different output for Usage and Help if [[ ${TDIM} -eq 4 && $nargs -lt 5 ]]; then Usage >&2 elif [[ ${TDIM} -eq 4 && $nargs -eq 5 ]]; then echo "" # This option is required to run 4D template creation on SGE with a minimal command line elif [[ $nargs -lt 6 ]] then Usage >&2 fi OUTPUT_DIR=${OUTPUTNAME%\/*} if [[ ! -d $OUTPUT_DIR ]]; then echo "The output directory \"$OUTPUT_DIR\" does not exist. Making it." mkdir -p $OUTPUT_DIR fi if [[ $DOQSUB -eq 1 || $DOQSUB -eq 4 ]]; then qq=`which qsub` if [[ ${#qq} -lt 1 ]]; then echo "do you have qsub? if not, then choose another c option ... if so, then check where the qsub alias points ..." exit fi fi if [[ $DOQSUB -eq 5 ]]; then qq=`which sbatch` if [[ ${#qq} -lt 1 ]]; then echo "do you have sbatch? if not, then choose another c option ... if so, then check where the sbatch alias points ..." exit fi fi for (( i = 0; i < $NUMBEROFMODALITIES; i++ )) do TEMPLATES[$i]=${TEMPLATENAME}${i}.nii.gz done if [[ ${#METRICTYPE[@]} -eq 0 ]]; then METRICTYPE[0]=CC fi if [[ ${#METRICTYPE[@]} -eq 1 ]]; then for (( i = 1; i < $NUMBEROFMODALITIES; i++ )) do METRICTYPE[${#METRICTYPE[@]}]=${METRICTYPE[0]} done fi if [[ ${#METRICTYPE[@]} -ne $NUMBEROFMODALITIES ]]; then echo "The number of similarity metrics does not match the number of specified modalities (see -s option)" exit fi if [[ ! -n "$MODALITYWEIGHTSTRING" ]]; then for (( i = 0; i < $NUMBEROFMODALITIES; i++ )) do MODALITYWEIGHTS[$i]=1 done else MODALITYWEIGHTS=(`echo $MODALITYWEIGHTSTRING | tr 'x' "\n"`) if [[ ${#MODALITYWEIGHTS[@]} -ne $NUMBEROFMODALITIES ]]; then echo "The number of weights (specified e.g. -w 1x1x1) does not match the number of specified modalities (see -k option)"; exit fi fi # Creating the file list of images to make a template from. # Shiftsize is calculated because a variable amount of arguments can be used on the command line. # The shiftsize variable will give the correct number of arguments to skip. Issuing shift $shiftsize will # result in skipping that number of arguments on the command line, so that only the input images remain. shiftsize=$(($OPTIND - 1)) shift $shiftsize # The invocation of $* will now read all remaining arguments into the variable IMAGESETVARIABLE IMAGESETVARIABLE=$* NINFILES=$(($nargs - $shiftsize)) IMAGESETARRAY=() AVERAGE_AFFINE_PROGRAM="AverageAffineTransform" if [[ $AFFINE_UPDATE_FULL -eq 0 ]]; then AVERAGE_AFFINE_PROGRAM="AverageAffineTransformNoRigid" fi # FSL not needed anymore, all dependent on ImageMath # #test if FSL is available in case of 4D, exit if not # if [[ ${TDIM} -eq 4 && ${#FSLDIR} -le 0 ]]; # then # setFSLPath >&2 # fi if [[ ${NINFILES} -eq 0 ]]; then echo "Please provide at least 2 filenames for the template." echo "Use `basename $0` -h for help" exit 1 elif [[ ${NINFILES} -eq 1 ]]; then extension=`echo ${IMAGESETVARIABLE#*.}` if [[ $extension = 'csv' || $extension = 'txt' ]]; then IMAGESFILE=$IMAGESETVARIABLE IMAGECOUNT=0 while read line do files=(`echo $line | tr ',' ' '`) if [[ ${#files[@]} -ne $NUMBEROFMODALITIES ]]; then echo "The number of files in the csv file does not match the specified number of modalities." echo "See the -k option." exit 1 fi for (( i = 0; i < ${#files[@]}; i++ )); do IMAGESETARRAY[$IMAGECOUNT]=${files[$i]} ((IMAGECOUNT++)) done done < $IMAGESFILE else range=`${ANTSPATH}/ImageMath $TDIM abs nvols ${IMAGESETVARIABLE} | tail -1 | cut -d "," -f 4 | cut -d " " -f 2 | cut -d "]" -f 1 ` if [[ ${range} -eq 1 && ${TDIM} -ne 4 ]]; then echo "Please provide at least 2 filenames for the template." echo "Use `basename $0` -h for help" exit 1 elif [[ ${range} -gt 1 && ${TDIM} -ne 4 ]] then echo "This is a multivolume file. Use -d 4" echo "Use `basename $0` -h for help" exit 1 elif [[ ${range} -gt 1 && ${TDIM} -eq 4 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Creating template of 4D input. " echo "--------------------------------------------------------------------------------------" #splitting volume #setting up working dirs tmpdir=${currentdir}/tmp_${RANDOM}_${RANDOM}_${RANDOM}_$$ (umask 077 && mkdir ${tmpdir}) || { echo "Could not create temporary directory! Exiting." 1>&2 exit 1 } mkdir ${tmpdir}/selection #split the 4D file into 3D elements cp ${IMAGESETVARIABLE} ${tmpdir}/ cd ${tmpdir}/ # ${ANTSPATH}/ImageMath $TDIM vol0.nii.gz TimeSeriesSubset ${IMAGESETVARIABLE} ${range} # rm -f ${IMAGESETVARIABLE} # selecting 16 volumes randomly from the timeseries for averaging, placing them in tmp/selection folder. # the script will automatically divide timeseries into $total_volumes/16 bins from wich to take the random volumes; # if there are more than 32 volumes in the time-series (in case they are smaller nfmribins=16 if [[ ${range} -gt 31 ]]; then BINSIZE=$((${range} / ${nfmribins})) j=1 # initialize counter j for ((i = 0; i < ${nfmribins}; i++)) do FLOOR=$((${i} * ${BINSIZE})) BINrange=$((${j} * ${BINSIZE})) # Retrieve random number between two limits. number=0 #initialize while [[ "$number" -le $FLOOR ]]; do number=$RANDOM if [[ $i -lt 15 ]]; then let "number %= $BINrange" # Scales $number down within $range. elif [[ $i -eq 15 ]]; then let "number %= $range" # Scales $number down within $range. fi done #debug only echo echo "Random number between $FLOOR and $BINrange --- $number" # echo "Random number between $FLOOR and $range --- $number" if [[ ${number} -lt 10 ]]; then ${ANTSPATH}/ImageMath $TDIM selection/vol000${number}.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol000${number}.nii.gz selection/ elif [[ ${number} -ge 10 && ${number} -lt 100 ]]; then ${ANTSPATH}/ImageMath $TDIM selection/vol00${number}.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol00${number}.nii.gz selection/ elif [[ ${number} -ge 100 && ${number} -lt 1000 ]]; then ${ANTSPATH}/ImageMath $TDIM selection/vol0${number}.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol0${number}.nii.gz selection/ fi let j++ done fi elif [[ ${range} -gt ${nfmribins} && ${range} -lt 32 ]]; then for ((i = 0; i < ${nfmribins} ; i++)) do number=$RANDOM let "number %= $range" if [[ ${number} -lt 10 ]]; then ${ANTSPATH}/ImageMath $TDIM selection/vol0.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol000${number}.nii.gz selection/ elif [[ ${number} -ge 10 && ${number} -lt 100 ]]; then ${ANTSPATH}/ImageMath $TDIM selection/vol0.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol00${number}.nii.gz selection/ fi done elif [[ ${range} -le ${nfmribins} ]]; then ${ANTSPATH}/ImageMath selection/$TDIM vol0.nii.gz TimeSeriesSubset ${IMAGESETVARIABLE} ${range} # cp *.nii.gz selection/ fi # set filelist variable rm -f ${IMAGESETVARIABLE} cd selection/ IMAGESETVARIABLE=`ls *.nii.gz` IMAGESETARRAY=() for IMG in $IMAGESETVARIABLE do IMAGESETARRAY[${#IMAGESETARRAY[@]}]=$IMG done fi else IMAGESETARRAY=() for IMG in $IMAGESETVARIABLE do IMAGESETARRAY[${#IMAGESETARRAY[@]}]=$IMG done fi if [[ $NUMBEROFMODALITIES -gt 1 ]]; then echo "--------------------------------------------------------------------------------------" echo " Multivariate template construction using the following ${NUMBEROFMODALITIES}-tuples: " echo "--------------------------------------------------------------------------------------" for (( i = 0; i < ${#IMAGESETARRAY[@]}; i+=$NUMBEROFMODALITIES )) do IMAGEMETRICSET="" for (( j = 0; j < $NUMBEROFMODALITIES; j++ )) do k=0 let k=$i+$j IMAGEMETRICSET="$IMAGEMETRICSET ${IMAGESETARRAY[$k]}" done echo $IMAGEMETRICSET done echo "--------------------------------------------------------------------------------------" fi # check for initial template images for (( i = 0; i < $NUMBEROFMODALITIES; i++ )) do setCurrentImageSet $i if [[ -s ${REGTEMPLATES[$i]} ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Initial template $i found. This will be used for guiding the registration. use : ${REGTEMPLATES[$i]} and ${TEMPLATES[$i]} " echo "--------------------------------------------------------------------------------------" # now move the initial registration template to OUTPUTNAME, otherwise this input gets overwritten. cp ${REGTEMPLATES[$i]} ${TEMPLATES[$i]} else echo echo "--------------------------------------------------------------------------------------" echo " Creating template ${TEMPLATES[$i]} from a population average image from the inputs." echo " ${CURRENTIMAGESET[@]}" echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/AverageImages $DIM ${TEMPLATES[$i]} 1 ${CURRENTIMAGESET[@]} fi if [[ ! -s ${TEMPLATES[$i]} ]]; then echo "Your template : $TEMPLATES[$i] was not created. This indicates trouble! You may want to check correctness of your input parameters. exiting." exit fi done # remove old job bash scripts outdir=`dirname ${TEMPLATES[0]}` if [[ ${#outdir} -eq 0 ]]; then outdir=`pwd` fi rm -f ${outdir}/job*.sh ########################################################################## # # perform rigid body registration if requested # ########################################################################## if [[ "$RIGID" -eq 1 ]]; then count=0 jobIDs="" for (( i = 0; i < ${#IMAGESETARRAY[@]}; i+=$NUMBEROFMODALITIES )) do IMAGEMETRICSET="" for (( j = 0; j < $NUMBEROFMODALITIES; j++ )) do k=0 let k=$i+$j IMAGEMETRICSET="$IMAGEMETRICSET -m MI[${TEMPLATES[$j]},${IMAGESETARRAY[$k]},${MODALITYWEIGHTS[$j]},32]" done qscript="${outdir}/job_${count}_qsub.sh" rm -f $qscript if [[ $DOQSUB -eq 5 ]]; then # SLURM job scripts must start with a shebang echo '#!/bin/sh' > $qscript fi echo "$SCRIPTPREPEND" >> $qscript IMGbase=`basename ${IMAGESETARRAY[$i]}` BASENAME=` echo ${IMGbase} | cut -d '.' -f 1 ` RIGID="${outdir}/rigid${i}_0_${IMGbase}" exe="$ANTS $DIM $IMAGEMETRICSET -o $RIGID -i 0 --use-Histogram-Matching $LINEARTRANSFORMPARAMS $RIGIDTYPE" echo "$exe" >> $qscript exe2=''; pexe2=''; pexe=" $exe > ${outdir}/job_${count}_metriclog.txt " for (( j = 0; j < $NUMBEROFMODALITIES; j++ )) do k=0 let k=$i+$j IMGbase=`basename ${IMAGESETARRAY[$k]}` BASENAME=` echo ${IMGbase} | cut -d '.' -f 1 ` RIGID="${outdir}/rigid${i}_${j}_${IMGbase}" IMGbaseBASE=`basename ${IMAGESETARRAY[$i]}` BASENAMEBASE=` echo ${IMGbaseBASE} | cut -d '.' -f 1 ` exe2="$exe2 ${WARP} $DIM ${IMAGESETARRAY[$k]} $RIGID ${outdir}/rigid${i}_0_${BASENAMEBASE}Affine.txt -R ${TEMPLATES[$j]}\n" pexe2="$exe2 ${WARP} $DIM ${IMAGESETARRAY[$k]} $RIGID ${outdir}/rigid${i}_0_${BASENAMEBASE}Affine.txt -R ${TEMPLATES[$j]} >> ${outdir}/job_${count}_metriclog.txt\n" done echo -e "$exe2" >> $qscript; if [[ $DOQSUB -eq 1 ]]; then id=`qsub -cwd -S /bin/bash -N antsBuildTemplate_rigid -v ANTSPATH=$ANTSPATH $QSUBOPTS $qscript | awk '{print $3}'` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 4 ]]; then id=`qsub -N antsrigid -v ANTSPATH=$ANTSPATH $QSUBOPTS -q nopreempt -l nodes=1:ppn=1 -l walltime=20:00:00 -l mem=8gb $qscript | awk '{print $1}'` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 2 ]]; then # Send pexe and exe2 to same job file so that they execute in series echo $pexe >> ${outdir}/job${count}_r.sh echo -e $pexe2 >> ${outdir}/job${count}_r.sh elif [[ $DOQSUB -eq 3 ]]; then id=`xgrid $XGRIDOPTS -job submit /bin/bash $qscript | awk '{sub(/;/,"");print $3}' | tr '\n' ' ' | sed 's: *: :g'` #echo "xgrid $XGRIDOPTS -job submit /bin/bash $qscript" jobIDs="$jobIDs $id" elif [[ $DOQSUB -eq 5 ]]; then id=`sbatch --job-name=antsrigid --export=ANTSPATH=$ANTSPATH $QSUBOPTS --nodes=1 --cpus-per-task=1 --time=20:00:00 --mem=8192M $qscript | rev | cut -f1 -d\ | rev` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 0 ]]; then # execute jobs in series bash $qscript fi ((count++)) done if [[ $DOQSUB -eq 1 ]]; then # Run jobs on SGE and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on SGE cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForSGEQJobs.pl 1 60 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "qsub submission failed - jobs went into error state" exit 1; fi fi if [[ $DOQSUB -eq 4 ]]; then # Run jobs on PBS and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on PBS cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForPBSQJobs.pl 1 60 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "qsub submission failed - jobs went into error state" exit 1; fi fi # Run jobs on localhost and wait to finish if [[ $DOQSUB -eq 2 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on max ${CORES} cpucores. " echo " Progress can be viewed in ${outdir}/job*_metriclog.txt" echo "--------------------------------------------------------------------------------------" jobfnamepadding #adds leading zeros to the jobnames, so they are carried out chronologically chmod +x ${outdir}/job*_r.sh $PEXEC -j ${CORES} "sh" ${outdir}/job*_r.sh fi if [[ $DOQSUB -eq 3 ]]; then # Run jobs on XGrid and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on XGrid cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForXGridJobs.pl -xgridflags "$XGRIDOPTS" -verbose -delay 30 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "XGrid submission failed - jobs went into error state" exit 1; fi fi if [[ $DOQSUB -eq 5 ]]; then # Run jobs on SLURM and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on SLURM cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForSlurmJobs.pl 1 60 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "SLURM submission failed - jobs went into error state" exit 1; fi fi for (( j = 0; j < $NUMBEROFMODALITIES; j++ )) do IMAGERIGIDSET=() for (( i = $j; i < ${#IMAGESETARRAY[@]}; i+=$NUMBEROFMODALITIES )) do k=0 let k=$i-$j IMGbase=`basename ${IMAGESETARRAY[$i]}` BASENAME=` echo ${IMGbase} | cut -d '.' -f 1 ` RIGID="${outdir}/rigid${k}_${j}_${IMGbase}" IMAGERIGIDSET[${#IMAGERIGIDSET[@]}]=$RIGID done echo echo "${ANTSPATH}/AverageImages $DIM ${TEMPLATES[$j]} 1 ${IMAGERIGIDSET[@]}" ${ANTSPATH}/AverageImages $DIM ${TEMPLATES[$j]} 1 ${IMAGERIGIDSET[@]} done # cleanup and save output in seperate folder if [[ BACKUP_EACH_ITERATION -eq 1 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Backing up results from rigid iteration" echo "--------------------------------------------------------------------------------------" mkdir ${outdir}/rigid mv ${outdir}/rigid*.nii.gz ${outdir}/*GenericAffine.mat ${outdir}/rigid/ # backup logs if [[ $DOQSUB -eq 1 ]]; then mv ${outdir}/antsBuildTemplate_rigid* ${outdir}/rigid/ # Remove qsub scripts rm -f ${outdir}/job_${count}_qsub.sh elif [[ $DOQSUB -eq 4 ]]; then mv ${outdir}/antsrigid* ${outdir}/rigid/ # Remove qsub scripts rm -f ${outdir}/job_${count}_qsub.sh elif [[ $DOQSUB -eq 2 ]]; then mv ${outdir}/job*.txt ${outdir}/rigid/ elif [[ $DOQSUB -eq 3 ]]; then rm -f ${outdir}/job_*_qsub.sh elif [[ $DOQSUB -eq 5 ]]; then mv ${outdir}/slurm-*.out ${outdir}/rigid/ mv ${outdir}/job*.txt ${outdir}/rigid/ # Remove qsub scripts rm -f ${outdir}/job_${count}_qsub.sh fi else rm -f ${outdir}/rigid*.* ${outdir}/job*.txt ${outdir}/slurm-*.out fi fi # endif RIGID ########################################################################## # # begin main level # ########################################################################## ITERATLEVEL=(` echo $MAXITERATIONS | tr 'x' ' ' `) NUMLEVELS=${#ITERATLEVEL[@]} # # debugging only #echo $ITERATLEVEL #echo $NUMLEVELS #echo ${ITERATIONLIMIT} # echo echo "--------------------------------------------------------------------------------------" echo " Start to build templates: ${TEMPLATES[@]}" echo "--------------------------------------------------------------------------------------" reportMappingParameters # TRANSFORMATION='' REGULARIZATION='' if [[ "${TRANSFORMATIONTYPE}" == "EL" ]]; then # Mapping Parameters TRANSFORMATION=Elast[1] REGULARIZATION=Gauss[3,0.5] # Gauss[3,x] is usually the best option. x is usually 0 for SyN --- if you want to reduce flexibility/increase mapping smoothness, the set x > 0. # We did a large scale evaluation of SyN gradient parameters in normal brains and found 0.25 => 0.5 to perform best when # combined with default Gauss[3,0] regularization. You would increase the gradient step in some cases, though, to make # the registration converge faster --- though oscillations occur if the step is too high and other instability might happen too. elif [[ "${TRANSFORMATIONTYPE}" == "S2" ]]; then # Mapping Parameters for the LDDMM style SyN --- the params are SyN[GradientStepLength,NTimeDiscretizationPoints,IntegrationTimeStep] # increasing IntegrationTimeStep increases accuracy in the diffeomorphism integration and takes more computation time. # NTimeDiscretizationPoints is set to 2 here TRANSFORMATION=SyN[1,2,0.05] REGULARIZATION=Gauss[3,0.] elif [[ "${TRANSFORMATIONTYPE}" == "SY" ]]; then # Mapping Parameters for the LDDMM style SyN --- the params are SyN[GradientStepLength,NTimeDiscretizationPoints,IntegrationTimeStep] # increasing IntegrationTimeStep increases accuracy in the diffeomorphism integration and takes more computation time. # NTimeDiscretizationPoints is the number of spatial indices in the time dimension (the 4th dim when doing 3D registration) # increasing NTimeDiscretizationPoints increases flexibility and takes more computation time. # the --geodesic option enables either 1 asymmetric gradient estimation or 2 symmetric gradient estimation (the default here ) TRANSFORMATION=" SyN[1,2,0.05] --geodesic 2 " REGULARIZATION=Gauss[3,0.] elif [[ "${TRANSFORMATIONTYPE}" == "LDDMM" ]]; then # Mapping Parameters for the LDDMM style SyN --- the params are SyN[GradientStepLength,NTimeDiscretizationPoints,IntegrationTimeStep] # increasing IntegrationTimeStep increases accuracy in the diffeomorphism integration and takes more computation time. # NTimeDiscretizationPoints is the number of spatial indices in the time dimension (the 4th dim when doing 3D registration) # increasing NTimeDiscretizationPoints increases flexibility and takes more computation time. # the --geodesic option enables either 1 asymmetric gradient estimation or 2 symmetric gradient estimation (the default here ) TRANSFORMATION=" SyN[1,2,0.05] --geodesic 1 " REGULARIZATION=Gauss[3,0.] elif [[ "${TRANSFORMATIONTYPE}" == "GR" ]]; then # Mapping Parameters for the greedy gradient descent (fast) version of SyN -- only needs GradientStepLength TRANSFORMATION=SyN[0.25] REGULARIZATION=Gauss[3,0] elif [[ "${TRANSFORMATIONTYPE}" == "GR_Constrained" ]]; then # Mapping Parameters for the greedy gradient descent (fast) version of SyN -- only needs GradientStepLength TRANSFORMATION=SyN[0.25] REGULARIZATION=Gauss[3,0.5] elif [[ "${TRANSFORMATIONTYPE}" == "EX" ]]; then # Mapping Parameters TRANSFORMATION=Exp[0.5,10] REGULARIZATION=Gauss[3,0.5] elif [[ "${TRANSFORMATIONTYPE}" == "DD" ]]; then # Mapping Parameters for diffemorphic demons style optimization Exp[GradientStepLength,NTimePointsInIntegration] # NTimePointsInIntegration controls the number of compositions in the transformation update , see the DD paper TRANSFORMATION=GreedyExp[0.5,10] REGULARIZATION=Gauss[3,0.5] else echo "Invalid transformation metric. Use EL, SY, S2, GR , DD or EX or type bash `basename $0` -h." exit 1 fi i=0 while [[ $i -lt ${ITERATIONLIMIT} ]]; do itdisplay=$((i+1)) rm -f ${OUTPUTNAME}*Warp*.nii* rm -f ${outdir}/job*.sh # Used to save time by only running coarse registration for the first couple of iterations # But with decent initialization, this is probably not worthwhile. # If you uncomment this, replace MAXITERATIONS with ITERATIONS in the call to ants below # # # For the first couple of iterations, use high-level registration only # # eg if MAXITERATIONS=30x90x20, then for iteration 0, do 30x0x0 # # for iteration 1 do 30x90x0, then do 30x90x20 on subsequent iterations # if [[ $i -gt $((NUMLEVELS - 1)) ]]; # then # ITERATIONS=$MAXITERATIONS # else # # ITERATIONS=${ITERATLEVEL[0]} # # for (( n = 1 ; n < ${NUMLEVELS}; n++ )) # do # ITERATIONS=${ITERATIONS}x$((${ITERATLEVEL[n]} * $((n <= i)) )) # done # fi # Job IDs of jobs submitted to queue in loop below jobIDs="" # Reinitialize count to 0 count=0 # Submit registration of each input to volume template to SGE or run locally. for (( j = 0; j < ${#IMAGESETARRAY[@]}; j+=$NUMBEROFMODALITIES )) do IMAGEMETRICSET='' exe='' warpexe='' pexe='' warppexe='' for (( k = 0; k < $NUMBEROFMODALITIES; k++ )) do l=0 let l=$j+$k if [[ "${METRICTYPE[$k]}" == "PR" ]]; then # Mapping Parameters METRIC=PR[ METRICPARAMS="${MODALITYWEIGHTS[$k]},4]" elif [[ "${METRICTYPE[$k]}" == "CC" ]]; then # Mapping Parameters METRIC=CC[ METRICPARAMS="${MODALITYWEIGHTS[$k]},5]" elif [[ "${METRICTYPE[$k]}" == "MI" ]]; then # Mapping Parameters METRIC=MI[ METRICPARAMS="${MODALITYWEIGHTS[$k]},32]" elif [[ "${METRICTYPE[$k]}" == "MSQ" ]]; then # Mapping Parameters METRIC=MSQ[ METRICPARAMS="${MODALITYWEIGHTS[$k]},0]" else echo "Invalid similarity metric. Use CC, MI, MSQ or PR or type bash `basename $0` -h." exit 1 fi TEMPLATEbase=`basename ${TEMPLATES[$k]}` indir=`dirname ${IMAGESETARRAY[$j]}` if [[ ${#indir} -eq 0 ]]; then indir=`pwd` fi IMGbase=`basename ${IMAGESETARRAY[$l]}` POO=${OUTPUTNAME}template${k}${IMGbase} OUTFN=${POO%.*.*} OUTFN=`basename ${OUTFN}` DEFORMED="${outdir}/${OUTFN}${l}WarpedToTemplate.nii.gz" IMGbase=`basename ${IMAGESETARRAY[$j]}` POO=${OUTPUTNAME}${IMGbase} OUTWARPFN=${POO%.*.*} OUTWARPFN=`basename ${OUTWARPFN}` OUTWARPFN="${OUTWARPFN}${j}" if [[ $N4CORRECT -eq 1 ]]; then REPAIRED="${outdir}/${OUTFN}Repaired.nii.gz" exe=" $exe $N4 -d ${DIM} -b [200] -c [50x50x40x30,0.00000001] -i ${IMAGESETARRAY[$l]} -o ${REPAIRED} -r 0 -s 2\n" pexe=" $pexe $N4 -d ${DIM} -b [200] -c [50x50x40x30,0.00000001] -i ${IMAGESETARRAY[$l]} -o ${REPAIRED} -r 0 -s 2 >> ${outdir}/job_${count}_metriclog.txt\n" IMAGEMETRICSET="$IMAGEMETRICSET -m ${METRIC}${TEMPLATES[$k]},${REPAIRED},${METRICPARAMS}" warpexe=" $warpexe ${WARP} ${DIM} ${REPAIRED} ${DEFORMED} -R ${TEMPLATES[$k]} ${outdir}/${OUTWARPFN}Warp.nii.gz ${outdir}/${OUTWARPFN}Affine.txt\n" warppexe=" $warppexe ${WARP} ${DIM} ${REPAIRED} ${DEFORMED} -R ${TEMPLATES[$k]} ${outdir}/${OUTWARPFN}Warp.nii.gz ${outdir}/${OUTWARPFN}Affine.txt >> ${outdir}/job_${count}_metriclog.txt\n" else IMAGEMETRICSET="$IMAGEMETRICSET -m ${METRIC}${TEMPLATES[$k]},${IMAGESETARRAY[$l]},${METRICPARAMS}"; warpexe=" $warpexe ${WARP} ${DIM} ${IMAGESETARRAY[$l]} ${DEFORMED} -R ${TEMPLATES[$k]} ${outdir}/${OUTWARPFN}Warp.nii.gz ${outdir}/${OUTWARPFN}Affine.txt\n" warppexe=" $warppexe ${WARP} ${DIM} ${IMAGESETARRAY[$l]} ${DEFORMED} -R ${TEMPLATES[$k]} ${outdir}/${OUTWARPFN}Warp.nii.gz ${outdir}/${OUTWARPFN}Affine.txt >> ${outdir}/job_${count}_metriclog.txt\n" fi done IMGbase=`basename ${IMAGESETARRAY[$j]}` POO=${OUTPUTNAME}${IMGbase} OUTWARPFN=${POO%.*.*} OUTWARPFN=`basename ${OUTWARPFN}${j}` LINEARTRANSFORMPARAMS="--number-of-affine-iterations 10000x10000x1000 --MI-option 32x16000" exe="$exe $ANTS ${DIM} $IMAGEMETRICSET -i ${MAXITERATIONS} -t ${TRANSFORMATION} -r $REGULARIZATION -o ${outdir}/${OUTWARPFN} --use-Histogram-Matching $LINEARTRANSFORMPARAMS\n" exe="$exe $warpexe" pexe="$pexe $ANTS ${DIM} $IMAGEMETRICSET -i ${MAXITERATIONS} -t ${TRANSFORMATION} -r $REGULARIZATION -o ${outdir}/${OUTWARPFN} --use-Histogram-Matching $LINEARTRANSFORMPARAMS >> ${outdir}/job_${count}_metriclog.txt\n" pexe="$pexe $warppexe" qscript="${outdir}/job_${count}_${i}.sh" echo -e $exe >> ${outdir}/job_${count}_${i}_metriclog.txt # 6 submit to SGE (DOQSUB=1), PBS (DOQSUB=4), PEXEC (DOQSUB=2), XGrid (DOQSUB=3) or else run locally (DOQSUB=0) if [[ $DOQSUB -eq 1 ]]; then echo -e "$SCRIPTPREPEND" > $qscript echo -e "$exe" >> $qscript id=`qsub -cwd -N antsBuildTemplate_deformable_${i} -S /bin/bash -v ANTSPATH=$ANTSPATH $QSUBOPTS $qscript | awk '{print $3}'` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 4 ]]; then echo -e "$SCRIPTPREPEND" > $qscript echo -e "$exe" >> $qscript id=`qsub -N antsdef${i} -v ANTSPATH=$ANTSPATH -q nopreempt -l nodes=1:ppn=1 -l walltime=20:00:00 -l mem=8gb $QSUBOPTS $qscript | awk '{print $1}'` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 2 ]]; then echo -e $pexe >> ${outdir}/job${count}_r.sh elif [[ $DOQSUB -eq 3 ]]; then echo -e "$SCRIPTPREPEND" > $qscript echo -e "$exe" >> $qscript id=`xgrid $XGRIDOPTS -job submit /bin/bash $qscript | awk '{sub(/;/,"");print $3}' | tr '\n' ' ' | sed 's: *: :g'` jobIDs="$jobIDs $id" elif [[ $DOQSUB -eq 5 ]]; then echo '#!/bin/sh' > $qscript echo -e "$SCRIPTPREPEND" >> $qscript echo -e "$exe" >> $qscript id=`sbatch --job-name=antsdef${i} --export=ANTSPATH=$ANTSPATH --nodes=1 --cpus-per-task=1 --time=20:00:00 --mem=8192M $QSUBOPTS $qscript | rev | cut -f1 -d\ | rev` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 0 ]]; then echo -e $exe > $qscript bash $qscript fi # counter updated, but not directly used in this loop count=`expr $count + 1`; # echo " submitting job number $count " # for debugging only done # SGE wait for script to finish if [[ $DOQSUB -eq 1 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on SGE cluster. Iteration: $itdisplay of $ITERATIONLIMIT" echo "--------------------------------------------------------------------------------------" # now wait for the stuff to finish - this will take a while so poll queue every 10 mins ${ANTSPATH}/waitForSGEQJobs.pl 1 600 $jobIDs if [[ ! $? -eq 0 ]]; then echo "qsub submission failed - jobs went into error state" exit 1; fi elif [[ $DOQSUB -eq 4 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on PBS cluster. Iteration: $itdisplay of $ITERATIONLIMIT" echo "--------------------------------------------------------------------------------------" # now wait for the stuff to finish - this will take a while so poll queue every 10 mins ${ANTSPATH}/waitForPBSQJobs.pl 1 600 $jobIDs if [[ ! $? -eq 0 ]]; then echo "qsub submission failed - jobs went into error state" exit 1; fi fi # Run jobs on localhost and wait to finish if [[ $DOQSUB -eq 2 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on max ${CORES} cpucores. Iteration: $itdisplay of $ITERATIONLIMIT" echo " Progress can be viewed in job*_${i}_metriclog.txt" echo "--------------------------------------------------------------------------------------" jobfnamepadding #adds leading zeros to the jobnames, so they are carried out chronologically chmod +x ${outdir}/job*.sh $PEXEC -j ${CORES} sh ${outdir}/job*.sh fi if [[ $DOQSUB -eq 3 ]]; then # Run jobs on XGrid and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on XGrid cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. This is slow, so poll less often ${ANTSPATH}/waitForXGridJobs.pl -xgridflags "$XGRIDOPTS" -verbose -delay 300 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "XGrid submission failed - jobs went into error state" exit 1; fi fi if [[ $DOQSUB -eq 5 ]]; then # Run jobs on SLURM and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on SLURM cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the stuff to finish - this will take a while so poll queue every 10 mins ${ANTSPATH}/waitForSlurmJobs.pl 1 600 $jobIDs if [[ ! $? -eq 0 ]]; then echo "SLURM submission failed - jobs went into error state" exit 1; fi fi for (( j = 0; j < $NUMBEROFMODALITIES; j++ )) do shapeupdatetotemplate ${DIM} ${TEMPLATES[$j]} ${TEMPLATENAME} ${OUTPUTNAME} ${GRADIENTSTEP} ${j} done if [[ BACKUP_EACH_ITERATION -eq 1 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Backing up results from iteration $itdisplay" echo "--------------------------------------------------------------------------------------" mkdir ${outdir}/${TRANSFORMATIONTYPE}_iteration_${i} cp ${TEMPLATENAME}${j}warplog.txt ${outdir}/*.cfg ${OUTPUTNAME}*.nii.gz ${outdir}/${TRANSFORMATIONTYPE}_iteration_${i}/ # backup logs if [[ $DOQSUB -eq 1 ]]; then mv ${outdir}/antsBuildTemplate_deformable_* ${outdir}/${TRANSFORMATIONTYPE}_iteration_${i} elif [[ $DOQSUB -eq 4 ]]; then mv ${outdir}/antsdef* ${outdir}/${TRANSFORMATIONTYPE}_iteration_${i} elif [[ $DOQSUB -eq 2 ]]; then mv ${outdir}/job*.txt ${outdir}/${TRANSFORMATIONTYPE}_iteration_${i} elif [[ $DOQSUB -eq 3 ]]; then rm -f ${outdir}/job_*.sh elif [[ $DOQSUB -eq 5 ]]; then mv ${outdir}/slurm-*.out ${outdir}/${TRANSFORMATIONTYPE}_iteration_${i} mv ${outdir}/job*.txt ${outdir}/${TRANSFORMATIONTYPE}_iteration_${i} fi else rm -f ${outdir}/job*.txt ${outdir}/slurm-*.out fi ((i++)) done # end main loop rm -f job*.sh #cleanup of 4D files if [[ "${range}" -gt 1 && "${TDIM}" -eq 4 ]]; then mv ${tmpdir}/selection/${TEMPLATES[@]} ${currentdir}/ cd ${currentdir} rm -rf ${tmpdir}/ fi time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo echo "--------------------------------------------------------------------------------------" echo " Done creating: ${TEMPLATES[@]}" echo " Script executed in $time_elapsed seconds" echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" echo "--------------------------------------------------------------------------------------" exit 0 ants-2.2.0/Scripts/antsMultivariateTemplateConstruction2.sh000077500000000000000000001626221311104306400241750ustar00rootroot00000000000000#!/bin/bash VERSION="0.0.0" # trap keyboard interrupt (control-c) trap control_c SIGINT function setPath { cat <&2 fi # Test availability of helper scripts. # No need to test this more than once. Can reside outside of the main loop. ANTS=${ANTSPATH}/antsRegistration WARP=${ANTSPATH}/antsApplyTransforms N4=${ANTSPATH}/N4BiasFieldCorrection PEXEC=${ANTSPATH}/ANTSpexec.sh SGE=${ANTSPATH}/waitForSGEQJobs.pl PBS=${ANTSPATH}/waitForPBSQJobs.pl XGRID=${ANTSPATH}/waitForXGridJobs.pl SLURM=${ANTSPATH}/waitForSlurmJobs.pl fle_error=0 for FLE in $ANTS $WARP $N4 $PEXEC $SGE $XGRID $PBS $SLURM do if [[ ! -x $FLE ]]; then echo echo "-----------------------------------------------------------------------------" echo " FILE $FLE DOES NOT EXIST -- OR -- IS NOT EXECUTABLE !!! $0 will terminate." echo "-----------------------------------------------------------------------------" echo " if the file is not executable, please change its permissions. " fle_error=1 fi done if [[ $fle_error = 1 ]]; then echo "missing helper script" exit 1 fi function Usage { cat < Compulsory arguments (minimal command line requires SGE/PBS cluster, otherwise use -c and -j options): -d: ImageDimension: 2 or 3 (for 2 or 3 dimensional registration of single volume) ImageDimension: 4 (for template generation of time-series data) -o: OutputPrefix; A prefix that is prepended to all output files. List of images in the current directory, eg *_t1.nii.gz. Should be at the end of the command. Optionally, one can specify a .csv or .txt file where each line is the location of the input image. One can also specify more than one file for each image for multi-modal template construction (e.g. t1 and t2). For the multi-modal case, the templates will be consecutively numbered (e.g. ${OutputPrefix}template0.nii.gz, ${OutputPrefix}template1.nii.gz, ...). NB: All images to be added to the template should be in the same directory, and this script should be invoked from that directory. Optional arguments: -a image statistic used to summarize images (default 1) 0 = mean 1 = mean of normalized intensities 2 = median -b: Backup images and results from all iterations (default = 0): Boolean to save the transform files, bias corrected, and warped images for each iteration. -c: Control for parallel computation (default 0): 0 = run serially 1 = SGE qsub 2 = use PEXEC (localhost) 3 = Apple XGrid 4 = PBS qsub 5 = SLURM -e use single precision ( default 1 ) -g: Gradient step size (default 0.25): smaller in magnitude results in more cautious steps. Use smaller steps to refine template details. 0.25 is an upper (aggressive) limit for this parameter. -i: Iteration limit (default 4): iterations of the template construction (Iteration limit)*NumImages registrations. -j: Number of cpu cores to use locally for pexec option (default 2; requires "-c 2") -k: Number of modalities used to construct the template (default 1): For example, if one wanted to create a multimodal template consisting of T1,T2,and FA components ("-k 3"). -w: Modality weights used in the similarity metric (default = 1): specified as e.g. 1x0.5x0.75. -q: Max iterations for each pairwise registration (default = 100x100x70x20): specified in the form ...xJxKxL where J = max iterations at coarsest resolution (here, reduced by power of 2^2) K = middle resolution iterations (here, reduced by power of 2) L = fine resolution iteratioxns (here, full resolution). Finer resolutions take much more time per iteration than coarser resolutions. -f: Shrink factors (default = 6x4x2x1): Also in the same form as -q max iterations.   Needs to have the same number of components. -s: Smoothing factors (default = 3x2x1x0): Also in the same form as -q max iterations. Needs to have the same number of components. -n: N4BiasFieldCorrection of moving image: 0 == off, 1 == on (default 1). -p: Commands to prepend to job scripts (e.g., change into appropriate directory, set paths, etc) -r: Do rigid-body registration of inputs before creating template (default 0): 0 == off 1 == on. Only useful when you do not have an initial template -l: Use linear image registration stages during the pairwise (template/subject) deformable registration. Otherwise, registration is limited to SyN or B-spline SyN (see '-t' option). This is '1' by default. -m: Type of similarity metric used for registration (default = CC): Options are CC = cross-correlation MI = mutual information MSQ = mean square difference DEMONS = demon's metric A similarity metric per modality can be specified. If the CC metric is chosen, one can also specify the radius in brackets, e.g. '-m CC[4]'. -t: Type of transformation model used for registration (default = SyN): Options are SyN = Greedy SyN BSplineSyN = Greedy B-spline SyN TimeVaryingVelocityField = Time-varying velocity field TimeVaryingBSplineVelocityField = Time-varying B-spline velocity field -u: Walltime (default = 20:00:00): Option for PBS/SLURM qsub specifying requested time per pairwise registration. -v: Memory limit (default = 8gb): Option for PBS/SLURM qsub specifying requested memory per pairwise registration. -x: XGrid arguments (e.g., -x "-p password -h controlhost") -y: Update the template with the full affine transform (default 1). If 0, the rigid component of the affine transform will not be used to update the template. If your template drifts in translation or orientation try -y 0. -z: Use this this volume as the target of all inputs. When not used, the script will create an unbiased starting point by averaging all inputs. Use the full path. For multiple modalities, specify -z modality1.nii.gz -z modality2.nii.gz ... in the same modality order as the input images. Example: `basename $0` -d 3 -i 3 -k 1 -f 4x2x1 -s 2x1x0vox -q 30x20x4 -t SyN -m CC -c 0 -o MY sub*avg.nii.gz Multimodal example: `basename $0` -d 3 -i 3 -k 2 -f 4x2x1 -s 2x1x0vox -q 30x20x4 -t SyN -z t1.nii.gz -z t2.nii.gz \ -m CC -c 0 -o MY templateInput.csv where templateInput.csv contains subjectA_t1.nii.gz,subjectA_t2.nii.gz subjectB_t1.nii.gz,subjectB_t2.nii.gz ... -------------------------------------------------------------------------------------- ANTS was created by: -------------------------------------------------------------------------------------- Brian B. Avants, Nick Tustison and Gang Song Penn Image Computing And Science Laboratory University of Pennsylvania Please reference http://www.ncbi.nlm.nih.gov/pubmed/20851191 when employing this script in your studies. A reproducible evaluation of ANTs similarity metric performance in brain image registration: * Avants BB, Tustison NJ, Song G, Cook PA, Klein A, Gee JC. Neuroimage, 2011. Also see http://www.ncbi.nlm.nih.gov/pubmed/19818860 for more details. The script has been updated and improved since this publication. -------------------------------------------------------------------------------------- Script by Nick Tustison -------------------------------------------------------------------------------------- Apple XGrid support by Craig Stark -------------------------------------------------------------------------------------- USAGE exit 1 } function reportMappingParameters { cat <> ${output}_list.txt done ${ANTSPATH}/ImageSetStatistics $dim ${output}_list.txt ${output} 0 rm ${output}_list.txt ;; esac } function shapeupdatetotemplate() { echo "shapeupdatetotemplate()" # local declaration of values dim=$1 template=$2 templatename=$3 outputname=$4 gradientstep=-$5 whichtemplate=$6 statsmethod=$7 # debug only # echo $dim # echo ${template} # echo ${templatename} # echo ${outputname} # echo ${outputname}*WarpedToTemplate.nii* # echo ${gradientstep} # We find the average warp to the template and apply its inverse to the template image # This keeps the template shape stable over multiple iterations of template building echo echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---voxel-wise averaging of the warped images to the current template" date #echo " ${ANTSPATH}/AverageImages $dim ${template} 1 ${templatename}${whichtemplate}*WarpedToTemplate.nii.gz " #echo " ${ANTSPATH}/ImageSetStatistics $dim ${whichtemplate}WarpedToTemplateList.txt ${template} 0" echo "--------------------------------------------------------------------------------------" imagelist=(`ls ${outputname}template${whichtemplate}*WarpedToTemplate.nii.gz`) if [[ ${#imagelist[@]} -eq 0 ]] ; then echo ERROR shapeupdatedtotemplate - imagelist length is 0 exit 1 fi summarizeimageset $dim $template $statsmethod ${imagelist[@]} WARPLIST=( `ls ${outputname}*[0-9]Warp.nii.gz 2> /dev/null` ) NWARPS=${#WARPLIST[*]} echo "number of warps = $NWARPS" echo "$WARPLIST" if [[ $whichtemplate -eq 0 ]]; then if [[ $NWARPS -ne 0 ]]; then echo "$NWARPS does not equal 0" echo echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---voxel-wise averaging of the inverse warp fields (from subject to template)" echo " ${ANTSPATH}/AverageImages $dim ${templatename}${whichtemplate}warp.nii.gz 0 `ls ${outputname}*Warp.nii.gz | grep -v "InverseWarp"`" date echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/AverageImages $dim ${templatename}${whichtemplate}warp.nii.gz 0 `ls ${outputname}*Warp.nii.gz | grep -v "InverseWarp"` echo echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---scale the averaged inverse warp field by the gradient step" echo " ${ANTSPATH}/MultiplyImages $dim ${templatename}${whichtemplate}warp.nii.gz ${gradientstep} ${templatename}${whichtemplate}warp.nii.gz" date echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/MultiplyImages $dim ${templatename}${whichtemplate}warp.nii.gz ${gradientstep} ${templatename}${whichtemplate}warp.nii.gz fi echo echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---average the affine transforms (template <-> subject)" echo " ---transform the inverse field by the resulting average affine transform" echo " ${ANTSPATH}/${AVERAGE_AFFINE_PROGRAM} ${dim} ${templatename}0GenericAffine.mat ${outputname}*GenericAffine.mat" echo " ${WARP} -d ${dim} -e vector -i ${templatename}0warp.nii.gz -o ${templatename}0warp.nii.gz -t [${templatename}0GenericAffine.mat,1] -r ${template} --verbose 1" echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/${AVERAGE_AFFINE_PROGRAM} ${dim} ${templatename}0GenericAffine.mat ${outputname}*GenericAffine.mat if [[ $NWARPS -ne 0 ]]; then ${WARP} -d ${dim} -e vector -i ${templatename}0warp.nii.gz -o ${templatename}0warp.nii.gz -t [${templatename}0GenericAffine.mat,1] -r ${template} --verbose 1 ${ANTSPATH}/MeasureMinMaxMean ${dim} ${templatename}0warp.nii.gz ${templatename}warplog.txt 1 fi fi echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---warp each template by the resulting transforms" echo " ${WARP} -d ${dim} --float $USEFLOAT --verbose 1 -i ${template} -o ${template} -t [${templatename}0GenericAffine.mat,1] -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -r ${template}" echo "--------------------------------------------------------------------------------------" if [ -f "${templatename}0warp.nii.gz" ]; then ${WARP} -d ${dim} --float $USEFLOAT --verbose 1 -i ${template} -o ${template} -t [${templatename}0GenericAffine.mat,1] -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -r ${template} else ${WARP} -d ${dim} --float $USEFLOAT --verbose 1 -i ${template} -o ${template} -t [${templatename}0GenericAffine.mat,1] -r ${template} fi } function jobfnamepadding { outdir=`dirname ${TEMPLATES[0]}` if [[ ${#outdir} -eq 0 ]] then outdir=`pwd` fi files=`ls ${outdir}/job*.sh` BASENAME1=`echo $files[1] | cut -d 'b' -f 1` for file in ${files} do if [[ "${#file}" -eq "9" ]]; then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_000${BASENAME2}" elif [[ "${#file}" -eq "10" ]]; then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_00${BASENAME2}" elif [[ "${#file}" -eq "11" ]]; then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_0${BASENAME2}" fi done } function setCurrentImageSet() { WHICHMODALITY=$1 CURRENTIMAGESET=() COUNT=0 for (( g = $WHICHMODALITY; g < ${#IMAGESETARRAY[@]}; g+=$NUMBEROFMODALITIES )) do CURRENTIMAGESET[$COUNT]=${IMAGESETARRAY[$g]} (( COUNT++ )) done } cleanup() { echo "\n*** Performing cleanup, please wait ***\n" runningANTSpids=$( ps --ppid $$ -o pid= ) for thePID in $runningANTSpids do echo "killing: ${thePID}" kill ${thePID} done return $? } control_c() # run if user hits control-c { echo -en "\n*** User pressed CTRL + C ***\n" cleanup exit $? echo -en "\n*** Script cancelled by user ***\n" } #initializing variables with global scope time_start=`date +%s` currentdir=`pwd` nargs=$# STATSMETHOD=1 USEFLOAT=1 BACKUPEACHITERATION=0 MAXITERATIONS=100x100x70x20 SMOOTHINGFACTORS=3x2x1x0 SHRINKFACTORS=6x4x2x1 METRICTYPE=() TRANSFORMATIONTYPE="SyN" NUMBEROFMODALITIES=1 MODALITYWEIGHTSTRING="" N4CORRECT=1 DOLINEAR=1 NOWARP=0 DOQSUB=0 GRADIENTSTEP=0.25 ITERATIONLIMIT=4 CORES=2 TDIM=0 RIGID=0 range=0 REGTEMPLATES=() TEMPLATES=() CURRENTIMAGESET=() XGRIDOPTS="" SCRIPTPREPEND="" WALLTIME="20:00:00" MEMORY="8gb" # System specific queue options, eg "-q name" to submit to a specific queue # It can be set to an empty string if you do not need any special cluster options QSUBOPTS="" # EDIT THIS OUTPUTNAME=antsBTP AFFINE_UPDATE_FULL=1 ##Getting system info from linux can be done with these variables. # RAM=`cat /proc/meminfo | sed -n -e '/MemTotal/p' | awk '{ printf "%s %s\n", $2, $3 ; }' | cut -d " " -f 1` # RAMfree=`cat /proc/meminfo | sed -n -e '/MemFree/p' | awk '{ printf "%s %s\n", $2, $3 ; }' | cut -d " " -f 1` # cpu_free_ram=$((${RAMfree}/${cpu_count})) if [[ ${OSTYPE:0:6} == 'darwin' ]]; then cpu_count=`sysctl -n hw.physicalcpu` else cpu_count=`cat /proc/cpuinfo | grep processor | wc -l` fi # Provide output for Help if [[ "$1" == "-h" ]]; then Usage >&2 fi # reading command line arguments while getopts "a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:s:r:t:u:v:w:x:y:z:" OPT do case $OPT in h) #help Usage >&2 exit 0 ;; a) # summarizing statisitic STATSMETHOD=$OPTARG ;; b) #backup each iteration BACKUPEACHITERATION=$OPTARG ;; e) #float boolean USEFLOAT=$OPTARG ;; c) #use SGE cluster DOQSUB=$OPTARG if [[ $DOQSUB -gt 5 ]]; then echo " DOQSUB must be an integer value (0=serial, 1=SGE qsub, 2=try pexec, 3=XGrid, 4=PBS qsub, 5=SLURM) you passed -c $DOQSUB " exit 1 fi ;; d) #dimensions DIM=$OPTARG if [[ ${DIM} -eq 4 ]]; then DIM=3 TDIM=4 fi ;; g) #gradient stepsize (default = 0.25) GRADIENTSTEP=$OPTARG ;; i) #iteration limit (default = 3) ITERATIONLIMIT=$OPTARG ;; j) #number of cpu cores to use (default = 2) CORES=$OPTARG ;; k) #number of modalities used to construct the template (default = 1) NUMBEROFMODALITIES=$OPTARG ;; l) #do linear (rigid + affine) for deformable registration DOLINEAR=$OPTARG ;; w) #modality weights (default = 1) MODALITYWEIGHTSTRING=$OPTARG ;; q) #max iterations other than default MAXITERATIONS=$OPTARG ;; f) #shrink factors SHRINKFACTORS=$OPTARG ;; s) #smoothing factors SMOOTHINGFACTORS=$OPTARG ;; n) #apply bias field correction N4CORRECT=$OPTARG ;; o) #output name prefix OUTPUTNAME=$OPTARG TEMPLATENAME=${OUTPUTNAME}template ;; p) #Script prepend SCRIPTPREPEND=$OPTARG ;; m) #similarity model METRICTYPE[${#METRICTYPE[@]}]=$OPTARG ;; r) #start with rigid-body registration RIGID=$OPTARG ;; t) #transformation model TRANSFORMATIONTYPE=$OPTARG ;; u) WALLTIME=$OPTARG ;; v) MEMORY=$OPTARG ;; x) #initialization template XGRIDOPTS=$OPTARG ;; y) # update with full affine, 0 for no rigid (default = 1) AFFINE_UPDATE_FULL=$OPTARG ;; z) #initialization template REGTEMPLATES[${#REGTEMPLATES[@]}]=$OPTARG ;; \?) # getopts issues an error message echo "$USAGE" >&2 exit 1 ;; esac done # Provide different output for Usage and Help if [[ ${TDIM} -eq 4 && $nargs -lt 5 ]]; then Usage >&2 elif [[ ${TDIM} -eq 4 && $nargs -eq 5 ]]; then echo "" # This option is required to run 4D template creation on SGE with a minimal command line elif [[ $nargs -lt 6 ]] then Usage >&2 fi OUTPUT_DIR=`dirname ${OUTPUTNAME}` if [[ ! -d $OUTPUT_DIR ]]; then echo "The output directory \"$OUTPUT_DIR\" does not exist. Making it." mkdir -p $OUTPUT_DIR fi if [[ $DOQSUB -eq 1 || $DOQSUB -eq 4 ]]; then qq=`which qsub` if [[ ${#qq} -lt 1 ]]; then echo "do you have qsub? if not, then choose another c option ... if so, then check where the qsub alias points ..." exit fi fi if [[ $DOQSUB -eq 5 ]]; then qq=`which sbatch` if [[ ${#qq} -lt 1 ]]; then echo "do you have sbatch? if not, then choose another c option ... if so, then check where the sbatch alias points ..." exit fi fi for (( i = 0; i < $NUMBEROFMODALITIES; i++ )) do TEMPLATES[$i]=${TEMPLATENAME}${i}.nii.gz done if [[ ${#METRICTYPE[@]} -eq 0 ]]; then METRICTYPE[0]=CC fi if [[ ${#METRICTYPE[@]} -eq 1 ]]; then for (( i = 1; i < $NUMBEROFMODALITIES; i++ )) do METRICTYPE[${#METRICTYPE[@]}]=${METRICTYPE[0]} done fi if [[ ${#METRICTYPE[@]} -ne $NUMBEROFMODALITIES ]]; then echo "The number of similarity metrics does not match the number of specified modalities (see -s option)" exit fi if [[ ! -n "$MODALITYWEIGHTSTRING" ]]; then for (( i = 0; i < $NUMBEROFMODALITIES; i++ )) do MODALITYWEIGHTS[$i]=1 done else MODALITYWEIGHTS=(`echo $MODALITYWEIGHTSTRING | tr 'x' "\n"`) if [[ ${#MODALITYWEIGHTS[@]} -ne $NUMBEROFMODALITIES ]]; then echo "The number of weights (specified e.g. -w 1x1x1) does not match the number of specified modalities (see -k option)"; exit 1 fi fi TRANSFORMATION='' if [[ $TRANSFORMATIONTYPE == "BSplineSyN"* ]]; then if [[ $TRANSFORMATIONTYPE == "BSplineSyN["*"]" ]] then TRANSFORMATION=${TRANSFORMATIONTYPE} else TRANSFORMATION=BSplineSyN[0.1,26,0,3] fi elif [[ $TRANSFORMATIONTYPE == "SyN"* ]]; then if [[ $TRANSFORMATIONTYPE == "SyN["*"]" ]] then TRANSFORMATION=${TRANSFORMATIONTYPE} else TRANSFORMATION=SyN[0.1,3,0] fi elif [[ $TRANSFORMATIONTYPE == "TimeVaryingVelocityField"* ]]; then if [[ $TRANSFORMATIONTYPE == "TimeVaryingVelocityField["*"]" ]] then TRANSFORMATION=${TRANSFORMATIONTYPE} else TRANSFORMATION=TimeVaryingVelocityField[0.5,4,3,0,0,0] fi elif [[ $TRANSFORMATIONTYPE == "TimeVaryingBSplineVelocityField"* ]]; then if [[ $TRANSFORMATIONTYPE == "TimeVaryingBSplineVelocityField["*"]" ]] then TRANSFORMATION=${TRANSFORMATIONTYPE} else TRANSFORMATION=TimeVaryingVelocityField[0.5,12x12x12x2,4,3] fi elif [[ $TRANSFORMATIONTYPE == "Affine"* ]]; then echo "Linear transforms only!!!!" NOWARP=1 if [[ $TRANSFORMATIONTYPE == "Affine["*"]" ]] then TRANSFORMATION=${TRANSFORMATIONTYPE} else TRANSFORMATION=Affine[0.1] fi else echo "Invalid transformation. See `basename $0` -h for help menu." exit 1 fi if [[ $STATSMETHOD -gt 2 ]]; then echo "Invalid stats type: using normalized mean (1)" STATSMETHOD=1 fi AVERAGE_AFFINE_PROGRAM="AverageAffineTransform" if [[ $AFFINE_UPDATE_FULL -eq 0 ]]; then AVERAGE_AFFINE_PROGRAM="AverageAffineTransformNoRigid" fi # Creating the file list of images to make a template from. # Shiftsize is calculated because a variable amount of arguments can be used on the command line. # The shiftsize variable will give the correct number of arguments to skip. Issuing shift $shiftsize will # result in skipping that number of arguments on the command line, so that only the input images remain. shiftsize=$(($OPTIND - 1)) shift $shiftsize # The invocation of $* will now read all remaining arguments into the variable IMAGESETVARIABLE IMAGESETVARIABLE=$* NINFILES=$(($nargs - $shiftsize)) IMAGESETARRAY=() if [[ ${NINFILES} -eq 0 ]]; then echo "Please provide at least 2 filenames for the template inputs." echo "Use `basename $0` -h for help" exit 1 elif [[ ${NINFILES} -eq 1 ]]; then extension=`echo ${IMAGESETVARIABLE#*.}` if [[ $extension = 'csv' || $extension = 'txt' ]]; then IMAGESFILE=$IMAGESETVARIABLE IMAGECOUNT=0 while read line do files=(`echo $line | tr ',' ' '`) if [[ ${#files[@]} -ne $NUMBEROFMODALITIES ]]; then echo "The number of files in the csv file does not match the specified number of modalities." echo "See the -k option." exit 1 fi for (( i = 0; i < ${#files[@]}; i++ )); do IMAGESETARRAY[$IMAGECOUNT]=${files[$i]} ((IMAGECOUNT++)) done done < $IMAGESFILE else range=`${ANTSPATH}/ImageMath $TDIM abs nvols ${IMAGESETVARIABLE} | tail -1 | cut -d "," -f 4 | cut -d " " -f 2 | cut -d "]" -f 1 ` if [[ ${range} -eq 1 && ${TDIM} -ne 4 ]]; then echo "Please provide at least 2 filenames for the template." echo "Use `basename $0` -h for help" exit 1 elif [[ ${range} -gt 1 && ${TDIM} -ne 4 ]] then echo "This is a multivolume file. Use -d 4" echo "Use `basename $0` -h for help" exit 1 elif [[ ${range} -gt 1 && ${TDIM} -eq 4 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Creating template of 4D input. " echo "--------------------------------------------------------------------------------------" #splitting volume #setting up working dirs tmpdir=${currentdir}/tmp_${RANDOM}_${RANDOM}_${RANDOM}_$$ (umask 077 && mkdir ${tmpdir}) || { echo "Could not create temporary directory! Exiting." 1>&2 exit 1 } mkdir ${tmpdir}/selection #split the 4D file into 3D elements cp ${IMAGESETVARIABLE} ${tmpdir}/ cd ${tmpdir}/ # ${ANTSPATH}/ImageMath $TDIM vol0.nii.gz TimeSeriesSubset ${IMAGESETVARIABLE} ${range} # rm -f ${IMAGESETVARIABLE} # selecting 16 volumes randomly from the timeseries for averaging, placing them in tmp/selection folder. # the script will automatically divide timeseries into $total_volumes/16 bins from wich to take the random volumes; # if there are more than 32 volumes in the time-series (in case they are smaller nfmribins=16 if [[ ${range} -gt 31 ]]; then BINSIZE=$((${range} / ${nfmribins})) j=1 # initialize counter j for ((i = 0; i < ${nfmribins}; i++)) do FLOOR=$((${i} * ${BINSIZE})) BINrange=$((${j} * ${BINSIZE})) # Retrieve random number between two limits. number=0 #initialize while [[ "$number" -le $FLOOR ]]; do number=$RANDOM if [[ $i -lt 15 ]]; then let "number %= $BINrange" # Scales $number down within $range. elif [[ $i -eq 15 ]]; then let "number %= $range" # Scales $number down within $range. fi done #debug only echo echo "Random number between $FLOOR and $BINrange --- $number" # echo "Random number between $FLOOR and $range --- $number" if [[ ${number} -lt 10 ]]; then ${ANTSPATH}/ImageMath $TDIM selection/vol000${number}.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol000${number}.nii.gz selection/ elif [[ ${number} -ge 10 && ${number} -lt 100 ]]; then ${ANTSPATH}/ImageMath $TDIM selection/vol00${number}.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol00${number}.nii.gz selection/ elif [[ ${number} -ge 100 && ${number} -lt 1000 ]]; then ${ANTSPATH}/ImageMath $TDIM selection/vol0${number}.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol0${number}.nii.gz selection/ fi let j++ done fi elif [[ ${range} -gt ${nfmribins} && ${range} -lt 32 ]]; then for ((i = 0; i < ${nfmribins} ; i++)) do number=$RANDOM let "number %= $range" if [[ ${number} -lt 10 ]]; then ${ANTSPATH}/ImageMath $TDIM selection/vol0.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol000${number}.nii.gz selection/ elif [[ ${number} -ge 10 && ${number} -lt 100 ]]; then ${ANTSPATH}/ImageMath $TDIM selection/vol0.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol00${number}.nii.gz selection/ fi done elif [[ ${range} -le ${nfmribins} ]]; then ${ANTSPATH}/ImageMath selection/$TDIM vol0.nii.gz TimeSeriesSubset ${IMAGESETVARIABLE} ${range} # cp *.nii.gz selection/ fi # set filelist variable rm -f ${IMAGESETVARIABLE} cd selection/ IMAGESETVARIABLE=`ls *.nii.gz` IMAGESETARRAY=() for IMG in $IMAGESETVARIABLE do IMAGESETARRAY[${#IMAGESETARRAY[@]}]=$IMG done fi else IMAGESETARRAY=() for IMG in $IMAGESETVARIABLE do IMAGESETARRAY[${#IMAGESETARRAY[@]}]=$IMG done fi if [[ $NUMBEROFMODALITIES -gt 1 ]]; then echo "--------------------------------------------------------------------------------------" echo " Multivariate template construction using the following ${NUMBEROFMODALITIES}-tuples: " echo "--------------------------------------------------------------------------------------" for (( i = 0; i < ${#IMAGESETARRAY[@]}; i+=$NUMBEROFMODALITIES )) do IMAGEMETRICSET="" for (( j = 0; j < $NUMBEROFMODALITIES; j++ )) do k=0 let k=$i+$j IMAGEMETRICSET="$IMAGEMETRICSET ${IMAGESETARRAY[$k]}" done echo $IMAGEMETRICSET done echo "--------------------------------------------------------------------------------------" fi # check for initial template images for (( i = 0; i < $NUMBEROFMODALITIES; i++ )) do setCurrentImageSet $i if [[ -s ${REGTEMPLATES[$i]} ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Initial template $i found. This will be used for guiding the registration. use : ${REGTEMPLATES[$i]} and ${TEMPLATES[$i]} " echo "--------------------------------------------------------------------------------------" # now move the initial registration template to OUTPUTNAME, otherwise this input gets overwritten. cp ${REGTEMPLATES[$i]} ${TEMPLATES[$i]} else echo echo "--------------------------------------------------------------------------------------" echo " Creating template ${TEMPLATES[$i]} from a population average image from the inputs." echo " ${CURRENTIMAGESET[@]}" echo "--------------------------------------------------------------------------------------" summarizeimageset $DIM ${TEMPLATES[$i]} $STATSMETHOD ${CURRENTIMAGESET[@]} #${ANTSPATH}/AverageImages $DIM ${TEMPLATES[$i]} 1 ${CURRENTIMAGESET[@]} fi if [[ ! -s ${TEMPLATES[$i]} ]]; then echo "Your initial template : $TEMPLATES[$i] was not created. This indicates trouble! You may want to check correctness of your input parameters. exiting." exit 1 fi done # remove old job bash scripts outdir=`dirname ${TEMPLATES[0]}` if [[ ${#outdir} -eq 0 ]]; then outdir=`pwd` fi rm -f ${outdir}/job*.sh ########################################################################## # # perform rigid body registration if requested # ########################################################################## if [[ "$RIGID" -eq 1 ]]; then count=0 jobIDs="" for (( i = 0; i < ${#IMAGESETARRAY[@]}; i+=$NUMBEROFMODALITIES )) do basecall="${ANTS} -d ${DIM} --float $USEFLOAT --verbose 1 -u 1 -w [0.01,0.99] -z 1 -r [${TEMPLATES[0]},${IMAGESETARRAY[$i]},1]" IMAGEMETRICSET="" for (( j = 0; j < $NUMBEROFMODALITIES; j++ )) do k=0 let k=$i+$j IMAGEMETRICSET="$IMAGEMETRICSET -m MI[${TEMPLATES[$j]},${IMAGESETARRAY[$k]},${MODALITYWEIGHTS[$j]},32,Regular,0.25]" done stage1="-t Rigid[0.1] ${IMAGEMETRICSET} -c [1000x500x250x0,1e-6,10] -f 6x4x2x1 -s 3x2x1x0 -o ${outdir}/rigid${i}_" #stage1="-t Rigid[0.1] ${IMAGEMETRICSET} -c [10x10x10x10,1e-8,10] -f 8x4x2x1 -s 4x2x1x0 -o ${outdir}/rigid${i}_" exe="${basecall} ${stage1}" qscript="${outdir}/job_${count}_qsub.sh" rm -f $qscript if [[ $DOQSUB -eq 5 ]]; then # SLURM job scripts must start with a shebang echo '#!/bin/sh' > $qscript fi echo "$SCRIPTPREPEND" >> $qscript IMGbase=`basename ${IMAGESETARRAY[$i]}` BASENAME=` echo ${IMGbase} | cut -d '.' -f 1 ` RIGID="${outdir}/rigid${i}_0_${IMGbase}" echo "$exe" >> $qscript exe2=''; pexe2=''; pexe=" $exe > ${outdir}/job_${count}_metriclog.txt " for (( j = 0; j < $NUMBEROFMODALITIES; j++ )) do k=0 let k=$i+$j IMGbase=`basename ${IMAGESETARRAY[$k]}` BASENAME=` echo ${IMGbase} | cut -d '.' -f 1 ` RIGID="${outdir}/rigid${i}_${j}_${IMGbase}" IMGbaseBASE=`basename ${IMAGESETARRAY[$i]}` BASENAMEBASE=` echo ${IMGbaseBASE} | cut -d '.' -f 1 ` exe2="$exe2 ${WARP} -d $DIM --float $USEFLOAT --verbose 1 -i ${IMAGESETARRAY[$k]} -o $RIGID -t ${outdir}/rigid${i}_0GenericAffine.mat -r ${TEMPLATES[$j]}\n" pexe2="$exe2 ${WARP} -d $DIM --float $USEFLOAT --verbose 1 -i ${IMAGESETARRAY[$k]} -o $RIGID -t ${outdir}/rigid${i}_0GenericAffine.mat -r ${TEMPLATES[$j]} >> ${outdir}/job_${count}_metriclog.txt\n" done echo -e "$exe2" >> $qscript; if [[ $DOQSUB -eq 1 ]]; then id=`qsub -cwd -S /bin/bash -N antsBuildTemplate_rigid -v ANTSPATH=$ANTSPATH $QSUBOPTS $qscript | awk '{print $3}'` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 4 ]]; then id=`qsub -N antsrigid -v ANTSPATH=$ANTSPATH $QSUBOPTS -q nopreempt -l nodes=1:ppn=1 -l mem=${MEMORY} -l walltime=${WALLTIME} $qscript | awk '{print $1}'` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 2 ]]; then # Send pexe and exe2 to same job file so that they execute in series echo $pexe >> ${outdir}/job${count}_r.sh echo -e $pexe2 >> ${outdir}/job${count}_r.sh elif [[ $DOQSUB -eq 3 ]]; then id=`xgrid $XGRIDOPTS -job submit /bin/bash $qscript | awk '{sub(/;/,"");print $3}' | tr '\n' ' ' | sed 's: *: :g'` #echo "xgrid $XGRIDOPTS -job submit /bin/bash $qscript" jobIDs="$jobIDs $id" elif [[ $DOQSUB -eq 5 ]]; then id=`sbatch --job-name=antsrigid --export=ANTSPATH=$ANTSPATH $QSUBOPTS --nodes=1 --cpus-per-task=1 --time=${WALLTIME} --mem=${MEMORY} $qscript | rev | cut -f1 -d\ | rev` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 0 ]]; then echo $qscript # execute jobs in series bash $qscript fi ((count++)) done if [[ $DOQSUB -eq 1 ]]; then # Run jobs on SGE and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on SGE cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForSGEQJobs.pl 1 60 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "qsub submission failed - jobs went into error state" exit 1; fi fi if [[ $DOQSUB -eq 4 ]]; then # Run jobs on PBS and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on PBS cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForPBSQJobs.pl 1 60 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "qsub submission failed - jobs went into error state" exit 1; fi fi # Run jobs on localhost and wait to finish if [[ $DOQSUB -eq 2 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on max ${CORES} cpucores. " echo " Progress can be viewed in ${outdir}/job*_metriclog.txt" echo "--------------------------------------------------------------------------------------" jobfnamepadding #adds leading zeros to the jobnames, so they are carried out chronologically chmod +x ${outdir}/job*_r.sh $PEXEC -j ${CORES} "sh" ${outdir}/job*_r.sh fi if [[ $DOQSUB -eq 3 ]]; then # Run jobs on XGrid and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on XGrid cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForXGridJobs.pl -xgridflags "$XGRIDOPTS" -verbose -delay 30 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "XGrid submission failed - jobs went into error state" exit 1; fi fi if [[ $DOQSUB -eq 5 ]]; then # Run jobs on SLURM and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on SLURM cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForSlurmJobs.pl 1 60 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "SLURM submission failed - jobs went into error state" exit 1; fi fi for (( j = 0; j < $NUMBEROFMODALITIES; j++ )) do IMAGERIGIDSET=() for (( i = $j; i < ${#IMAGESETARRAY[@]}; i+=$NUMBEROFMODALITIES )) do k=0 let k=$i-$j IMGbase=`basename ${IMAGESETARRAY[$i]}` BASENAME=` echo ${IMGbase} | cut -d '.' -f 1 ` RIGID="${outdir}/rigid${k}_${j}_${IMGbase}" IMAGERIGIDSET[${#IMAGERIGIDSET[@]}]=$RIGID done echo echo "${ANTSPATH}/AverageImages $DIM ${TEMPLATES[$j]} 1 ${IMAGERIGIDSET[@]}" summarizeimageset $DIM ${TEMPLATES[$j]} $STATSMETHOD ${IMAGERIGIDSET[@]} #${ANTSPATH}/AverageImages $DIM ${TEMPLATES[$j]} 1 ${IMAGERIGIDSET[@]} done # cleanup and save output in seperate folder if [[ BACKUPEACHITERATION -eq 1 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Backing up results from rigid iteration" echo "--------------------------------------------------------------------------------------" mkdir ${outdir}/rigid mv ${outdir}/rigid*.nii.gz ${outdir}/*GenericAffine.mat ${outdir}/rigid/ # backup logs if [[ $DOQSUB -eq 1 ]]; then mv ${outdir}/antsBuildTemplate_rigid* ${outdir}/rigid/ # Remove qsub scripts rm -f ${outdir}/job_${count}_qsub.sh elif [[ $DOQSUB -eq 4 ]]; then mv ${outdir}/antsrigid* ${outdir}/job* ${outdir}/rigid/ elif [[ $DOQSUB -eq 2 ]]; then mv ${outdir}/job*.txt ${outdir}/rigid/ elif [[ $DOQSUB -eq 3 ]]; then rm -f ${outdir}/job_*_qsub.sh elif [[ $DOQSUB -eq 5 ]]; then mv ${outdir}/slurm-*.out ${outdir}/rigid/ mv ${outdir}/job*.txt ${outdir}/rigid/ # Remove submission scripts rm -f ${outdir}/job_${count}_qsub.sh fi else rm -f ${outdir}/rigid*.* ${outdir}/job*.txt ${outdir}/slurm-*.out fi fi # endif RIGID ########################################################################## # # begin main level # ########################################################################## ITERATLEVEL=(` echo $MAXITERATIONS | tr 'x' ' ' `) NUMLEVELS=${#ITERATLEVEL[@]} # # debugging only #echo $ITERATLEVEL #echo $NUMLEVELS #echo ${ITERATIONLIMIT} # echo echo "--------------------------------------------------------------------------------------" echo " Start to build templates: ${TEMPLATES[@]}" echo "--------------------------------------------------------------------------------------" # reportMappingParameters i=0 while [[ $i -lt ${ITERATIONLIMIT} ]]; do itdisplay=$((i+1)) rm -f ${OUTPUTNAME}*Warp.nii* rm -f ${OUTPUTNAME}*GenericAffine.mat rm -f ${outdir}/job*.sh # Used to save time by only running coarse registration for the first couple of iterations # But with decent initialization, this is probably not worthwhile. # If you uncomment this, replace MAXITERATIONS with ITERATIONS in the call to ants below # # if [[ $i -gt $((NUMLEVELS - 1)) ]]; # then # ITERATIONS=$MAXITERATIONS # else # # ITERATIONS=${ITERATLEVEL[0]} # # for (( n = 1 ; n < ${NUMLEVELS}; n++ )) # do # ITERATIONS=${ITERATIONS}x$((${ITERATLEVEL[n]} * $((n <= i)) )) # done # fi # Job IDs of jobs submitted to queue in loop below jobIDs="" # Reinitialize count to 0 count=0 # Submit registration of each input to volume template to SGE or run locally. for (( j = 0; j < ${#IMAGESETARRAY[@]}; j+=$NUMBEROFMODALITIES )) do basecall="${ANTS} -d ${DIM} --float $USEFLOAT --verbose 1 -u 1 -w [0.01,0.99] -z 1" IMAGEMETRICLINEARSET='' IMAGEMETRICSET='' exe='' warpexe='' pexe='' warppexe='' for (( k = 0; k < $NUMBEROFMODALITIES; k++ )) do l=0 let l=$j+$k if [[ "${METRICTYPE[$k]}" == "DEMONS" ]]; then # Mapping Parameters METRIC=Demons[ METRICPARAMS="${MODALITYWEIGHTS[$k]},4]" elif [[ "${METRICTYPE[$k]}" == CC* ]]; then METRIC=CC[ RADIUS=4 if [[ "${METRICTYPE[$k]}" == CC[* ]] then RADIUS=${METRICTYPE[$k]%]*} RADIUS=${RADIUS##*[} fi METRICPARAMS="${MODALITYWEIGHTS[$k]},${RADIUS}]" elif [[ "${METRICTYPE[$k]}" == "MI" ]]; then # Mapping Parameters METRIC=MI[ METRICPARAMS="${MODALITYWEIGHTS[$k]},32]" elif [[ "${METRICTYPE[$k]}" == "MSQ" ]]; then # Mapping Parameters METRIC=MeanSquares[ METRICPARAMS="${MODALITYWEIGHTS[$k]},0]" else echo "Invalid similarity metric. Use CC, MI, MSQ, DEMONS or type bash `basename $0` -h." exit 1 fi TEMPLATEbase=`basename ${TEMPLATES[$k]}` indir=`dirname ${IMAGESETARRAY[$j]}` if [[ ${#indir} -eq 0 ]]; then indir=`pwd` fi IMGbase=`basename ${IMAGESETARRAY[$l]}` OUTFN=${OUTPUTNAME}template${k}${IMGbase%%.*} OUTFN=`basename ${OUTFN}` DEFORMED="${outdir}/${OUTFN}${l}WarpedToTemplate.nii.gz" IMGbase=`basename ${IMAGESETARRAY[$j]}` OUTWARPFN=${OUTPUTNAME}${IMGbase%%.*} OUTWARPFN=`basename ${OUTWARPFN}` OUTWARPFN="${OUTWARPFN}${j}" if [ $NOWARP -eq 0 ]; then OUTPUTTRANSFORMS="-t ${outdir}/${OUTWARPFN}1Warp.nii.gz -t ${outdir}/${OUTWARPFN}0GenericAffine.mat" else OUTPUTTRANSFORMS="-t ${outdir}/${OUTWARPFN}0GenericAffine.mat" fi if [[ $N4CORRECT -eq 1 ]]; then REPAIRED="${outdir}/${OUTFN}Repaired.nii.gz" exe=" $exe $N4 -d ${DIM} -b [200] -c [50x50x40x30,0.00000001] -i ${IMAGESETARRAY[$l]} -o ${REPAIRED} -r 0 -s 2 --verbose 1\n" pexe=" $pexe $N4 -d ${DIM} -b [200] -c [50x50x40x30,0.00000001] -i ${IMAGESETARRAY[$l]} -o ${REPAIRED} -r 0 -s 2 --verbose 1 >> ${outdir}/job_${count}_metriclog.txt >> ${outdir}/job_${count}_metriclog.txt\n" IMAGEMETRICSET="$IMAGEMETRICSET -m ${METRIC}${TEMPLATES[$k]},${REPAIRED},${METRICPARAMS}" IMAGEMETRICLINEARSET="$IMAGEMETRICLINEARSET -m MI[${TEMPLATES[$k]},${REPAIRED},${MODALITYWEIGHTS[$k]},32,Regular,0.25]" warpexe=" $warpexe ${WARP} -d ${DIM} --float $USEFLOAT --verbose 1 -i ${REPAIRED} -o ${DEFORMED} -r ${TEMPLATES[$k]} ${OUTPUTTRANSFORMS}\n" warppexe=" $warppexe ${WARP} -d ${DIM} --float $USEFLOAT --verbose 1 -i ${REPAIRED} -o ${DEFORMED} -r ${TEMPLATES[$k]} ${OUTPUTTRANSFORMS} >> ${outdir}/job_${count}_metriclog.txt\n" else IMAGEMETRICSET="$IMAGEMETRICSET -m ${METRIC}${TEMPLATES[$k]},${IMAGESETARRAY[$l]},${METRICPARAMS}" IMAGEMETRICLINEARSET="$IMAGEMETRICLINEARSET -m MI[${TEMPLATES[$k]},${IMAGESETARRAY[$l]},${MODALITYWEIGHTS[$k]},32,Regular,0.25]" warpexe=" $warpexe ${WARP} -d ${DIM} --float $USEFLOAT --verbose 1 -i ${IMAGESETARRAY[$l]} -o ${DEFORMED} -r ${TEMPLATES[$k]} ${OUTPUTTRANSFORMS}\n" warppexe=" $warppexe ${WARP} -d ${DIM} --float $USEFLOAT --verbose 1 -i ${IMAGESETARRAY[$l]} -o ${DEFORMED} -r ${TEMPLATES[$k]} ${OUTPUTTRANSFORMS} >> ${outdir}/job_${count}_metriclog.txt\n" fi done IMGbase=`basename ${IMAGESETARRAY[$j]}` OUTWARPFN=${OUTPUTNAME}${IMGbase%%.*} OUTWARPFN=`basename ${OUTWARPFN}${j}` stage0="-r [${TEMPLATES[0]},${IMAGESETARRAY[$j]},1]" stage1="-t Rigid[0.1] ${IMAGEMETRICLINEARSET} -c [1000x500x250x0,1e-6,10] -f 6x4x2x1 -s 4x2x1x0" stage2="-t Affine[0.1] ${IMAGEMETRICLINEARSET} -c [1000x500x250x0,1e-6,10] -f 6x4x2x1 -s 4x2x1x0" #stage1="-t Rigid[0.1] ${IMAGEMETRICLINEARSET} -c [10x10x10x10,1e-8,10] -f 8x4x2x1 -s 4x2x1x0" #stage2="-t Affine[0.1] ${IMAGEMETRICLINEARSET} -c [10x10x10x10,1e-8,10] -f 8x4x2x1 -s 4x2x1x0" stage3="-t ${TRANSFORMATION} ${IMAGEMETRICSET} -c [${MAXITERATIONS},1e-9,10] -f ${SHRINKFACTORS} -s ${SMOOTHINGFACTORS} -o ${outdir}/${OUTWARPFN}" stageId="-t Rigid[0.1] ${IMAGEMETRICLINEARSET} -c [0,1e-8,10] -f 1 -s 0" exebase=$exe pexebase=$pexe if [[ $DOLINEAR -eq 0 ]]; then exe="$exe ${basecall} ${stageId} ${stage3}\n" pexe="$pexe ${basecall} ${stageId} ${stage3} >> ${outdir}/job_${count}_metriclog.txt\n" elif [[ $NOWARP -eq 1 ]]; then exe="$exebase ${basecall} ${stage0} ${stage1} ${stage2}\n"; pexe="$pexebase ${basecall} ${stage0} ${stage1} ${stage2} >> ${outdir}/job_${count}_metriclog.txt\n" else exe="$exe ${basecall} ${stage0} ${stage1} ${stage2} ${stage3}\n" pexe="$pexe ${basecall} ${stage0} ${stage1} ${stage2} ${stage3} >> ${outdir}/job_${count}_metriclog.txt\n" fi exe="$exe $warpexe" pexe="$pexe $warppexe" qscript="${outdir}/job_${count}_${i}.sh" echo -e $exe >> ${outdir}/job_${count}_${i}_metriclog.txt # 6 submit to SGE (DOQSUB=1), PBS (DOQSUB=4), PEXEC (DOQSUB=2), XGrid (DOQSUB=3), SLURM (DOQSUB=5) or else run locally (DOQSUB=0) if [[ $DOQSUB -eq 1 ]]; then echo "$SCRIPTPREPEND" > $qscript echo -e "$exe" >> $qscript id=`qsub -cwd -N antsBuildTemplate_deformable_${i} -S /bin/bash -v ANTSPATH=$ANTSPATH $QSUBOPTS $qscript | awk '{print $3}'` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 4 ]]; then echo -e "$SCRIPTPREPEND" > $qscript echo -e "$exe" >> $qscript id=`qsub -N antsdef${i} -v ANTSPATH=$ANTSPATH -q nopreempt -l nodes=1:ppn=1 -l mem=${MEMORY} -l walltime=${WALLTIME} $QSUBOPTS $qscript | awk '{print $1}'` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 2 ]]; then echo -e $pexe >> ${outdir}/job${count}_r.sh elif [[ $DOQSUB -eq 3 ]]; then echo -e "$SCRIPTPREPEND" > $qscript echo -e "$exe" >> $qscript id=`xgrid $XGRIDOPTS -job submit /bin/bash $qscript | awk '{sub(/;/,"");print $3}' | tr '\n' ' ' | sed 's: *: :g'` jobIDs="$jobIDs $id" elif [[ $DOQSUB -eq 5 ]]; then echo '#!/bin/sh' > $qscript echo -e "$SCRIPTPREPEND" >> $qscript echo -e "$exe" >> $qscript id=`sbatch --job-name=antsdef${i} --export=ANTSPATH=$ANTSPATH --nodes=1 --cpus-per-task=1 --time=${WALLTIME} --mem=${MEMORY} $QSUBOPTS $qscript | rev | cut -f1 -d\ | rev` jobIDs="$jobIDs $id" sleep 0.5 elif [[ $DOQSUB -eq 0 ]]; then echo -e $exe > $qscript bash $qscript fi # counter updated, but not directly used in this loop count=`expr $count + 1`; # echo " submitting job number $count " # for debugging only done # SGE wait for script to finish if [[ $DOQSUB -eq 1 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on SGE cluster. Iteration: $itdisplay of $ITERATIONLIMIT" echo "--------------------------------------------------------------------------------------" # now wait for the stuff to finish - this will take a while so poll queue every 10 mins ${ANTSPATH}/waitForSGEQJobs.pl 1 600 $jobIDs if [[ ! $? -eq 0 ]]; then echo "qsub submission failed - jobs went into error state" exit 1; fi elif [[ $DOQSUB -eq 4 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on PBS cluster. Iteration: $itdisplay of $ITERATIONLIMIT" echo "--------------------------------------------------------------------------------------" # now wait for the stuff to finish - this will take a while so poll queue every 10 mins ${ANTSPATH}/waitForPBSQJobs.pl 1 600 $jobIDs if [[ ! $? -eq 0 ]]; then echo "qsub submission failed - jobs went into error state" exit 1; fi fi # Run jobs on localhost and wait to finish if [[ $DOQSUB -eq 2 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on max ${CORES} cpucores. Iteration: $itdisplay of $ITERATIONLIMIT" echo " Progress can be viewed in job*_${i}_metriclog.txt" echo "--------------------------------------------------------------------------------------" jobfnamepadding #adds leading zeros to the jobnames, so they are carried out chronologically chmod +x ${outdir}/job*.sh $PEXEC -j ${CORES} sh ${outdir}/job*.sh fi if [[ $DOQSUB -eq 3 ]]; then # Run jobs on XGrid and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on XGrid cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. This is slow, so poll less often ${ANTSPATH}/waitForXGridJobs.pl -xgridflags "$XGRIDOPTS" -verbose -delay 300 $jobIDs # Returns 1 if there are errors if [[ ! $? -eq 0 ]]; then echo "XGrid submission failed - jobs went into error state" exit 1; fi fi if [[ $DOQSUB -eq 5 ]]; then # Run jobs on SLURM and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on SLURM cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the stuff to finish - this will take a while so poll queue every 10 mins ${ANTSPATH}/waitForSlurmJobs.pl 1 600 $jobIDs if [[ ! $? -eq 0 ]]; then echo "SLURM submission failed - jobs went into error state" exit 1; fi fi WARPFILES=`ls ${OUTPUTNAME}*Warp.nii.gz | grep -v "InverseWarp"` AFFINEFILES=`ls ${OUTPUTNAME}*GenericAffine.mat` if [[ ${#WARPFILES[@]} -eq 0 || ${#AFFINEFILES[@]} -eq 0 ]]; then echo "The registrations did not terminate properly. There are no warp files" echo "or affine files." exit 1 fi if [[ ${#WARPFILES[@]} -ne ${#AFFINEFILES[@]} ]]; then echo "The registrations did not terminate properly. The number of warp files" echo "does not match the number of affine files." exit 1 fi for (( j = 0; j < $NUMBEROFMODALITIES; j++ )) do shapeupdatetotemplate ${DIM} ${TEMPLATES[$j]} ${TEMPLATENAME} ${OUTPUTNAME} ${GRADIENTSTEP} ${j} ${STATSMETHOD} done if [[ $BACKUPEACHITERATION -eq 1 ]]; then echo echo "--------------------------------------------------------------------------------------" echo " Backing up results from iteration $itdisplay" echo "--------------------------------------------------------------------------------------" mkdir ${outdir}/ANTs_iteration_${i} cp ${TEMPLATENAME}${j}warplog.txt ${outdir}/*.cfg ${OUTPUTNAME}*.nii.gz ${OUTPUTNAME}*.mat ${outdir}/ANTs_iteration_${i}/ # backup logs if [[ $DOQSUB -eq 1 ]]; then mv ${outdir}/antsBuildTemplate_deformable_* ${outdir}/ANTs_iteration_${i} elif [[ $DOQSUB -eq 4 ]]; then mv ${outdir}/antsdef* ${outdir}/ANTs_iteration_${i} elif [[ $DOQSUB -eq 2 ]]; then mv ${outdir}/job*.txt ${outdir}/ANTs_iteration_${i} elif [[ $DOQSUB -eq 3 ]]; then rm -f ${outdir}/job_*.sh elif [[ $DOQSUB -eq 5 ]]; then mv ${outdir}/slurm-*.out ${outdir}/ANTs_iteration_${i} mv ${outdir}/job*.txt ${outdir}/ANTs_iteration_${i} fi else rm -f ${outdir}/job*.txt ${outdir}/slurm-*.out fi ((i++)) done # end main loop rm -f job*.sh #cleanup of 4D files if [[ "${range}" -gt 1 && "${TDIM}" -eq 4 ]]; then mv ${tmpdir}/selection/${TEMPLATES[@]} ${currentdir}/ cd ${currentdir} rm -rf ${tmpdir}/ fi time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo echo "--------------------------------------------------------------------------------------" echo " Done creating: ${TEMPLATES[@]}" echo " Script executed in $time_elapsed seconds" echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" echo "--------------------------------------------------------------------------------------" exit 0 ants-2.2.0/Scripts/antsNetworkAnalysis.R000077500000000000000000000241011311104306400202470ustar00rootroot00000000000000#!/usr/bin/env Rscript options(digits=3) Args <- commandArgs() self<-Args[4] self<-substring(self,8,nchar(as.character(self))) getPckg <- function(pckg) install.packages(pckg, repos = "http://cran.r-project.org") pckg = try(require(getopt)) if(!pckg) { cat("Installing 'getopt' from CRAN\n") getPckg("getopt") require("getopt") } pckg = try(require(igraph)) if(!pckg) { getPckg("igraph") } library(igraph) # # helper functions # avgimg <- function( mylist , mask ) { avg<-antsImageClone( mylist[[1]] ) avg[ mask == 1 ]<-0 for ( i in 1:length(mylist) ) { avg[ mask == 1 ] <- avg[ mask == 1 ] + mylist[[i]][ mask == 1 ] * 1/length(mylist) } return( avg ) } # sdimg <- function( mylist , mask ) { avg<-avgimg( mylist , mask ) sdi<-antsImageClone( avg ) sdi[ mask == 1 ]<-0 for ( i in 1:length(mylist) ) { sdi[ mask == 1 ] <- sdi[ mask == 1 ] + abs( mylist[[i]][ mask == 1 ] - avg[ mask == 1 ] ) * 1/length(mylist) } return( sdi ) } # interleave <- function(v1,v2) { ord1 <- 2*(1:length(v1))-1 ord2 <- 2*(1:length(v2)) c(v1,v2)[order(c(ord1,ord2))] } # # spec = c( 'labels' , 'l', "0", "character" ," name of (aal) label image ", 'mask' , 'x', "0", "character" ," name of brain mask image ", 'seg' , 's', "0", "character" ," name of 3-tissue segmentation image 2 == gm ", 'fmri' , 'f', "0", "character" ," name of fmri (ASL or BOLD)", 'modality' , 'w', "BOLD", "character" ," which modality ASLCBF, ASLBOLD or BOLD ", 'freq' , 'q', "0.01x0.1", "character" ," low x high frequency for filtering", 'gdens' , 'g', 0.25, "numeric","graph density", 'tr' , 't', "2x4", "character","TR for BOLD and ASL e.g. 2.2x4", 'help' , 'h', 0, "logical" ," print the help ", 'output' , 'o', "1", "character"," the output prefix ", 'bloodt1' , 'b', 2, "numeric", "blood relaxation (inv of t1, defaults to 0.67 s^-1", 'robust' , 'r', 2, "numeric", "robustness parameter", 'nboot' , 'n', 2, "numeric", "number of bootstrap runs", 'pctboot' , 'p', 2, "numeric", "percent to sample per bootstrap run", 'replace ' , 'e', 2, "logical", "resample with replacement during bootstrap?") # ............................................. # spec=matrix(spec,ncol=5,byrow=TRUE) # get the options opt = getopt(spec) # ............................................. # #help was asked for. if ( !is.null(opt$help) || length(opt) == 1 ) { #print a friendly message and exit with a non-zero error code cat("\n") cat(paste(self,"\n")) for ( x in 1:nrow(spec) ) { cat("\n") longopt<-paste("--",spec[x,1],sep='') shortopt<-paste("-",spec[x,2],sep='') hlist<-paste(shortopt,"|",longopt,spec[x,5],"\n \n") # print(hlist,quote=F) cat(format(hlist, width=40, justify = c("left"))) } cat(format("Example: \n", width=40, justify = c("left"))) ex<-paste(self," -o myoutput -x mask.nii.gz --labels labels.nii.gz --fmri bold.nii.gz --modality ASLCBF --freq 0.03x0.08 \n \n ") ex<-format(ex, width=length(ex), justify = c("left")) cat("\n") cat(ex) q(status=1); } # # take care of optional parameters if(is.null(opt$bloodt1)) opt$bloodt1 <- 0.67 if(is.null(opt$robust)) opt$robust <- 0.95 if(is.null(opt$nboot)) opt$nboot <- 20 if(is.null(opt$pctboot)) opt$pctboot <- 0.70 if(opt$pctboot > 1.0) { cat('pctboot was greater than 1; setting to 70%.\n') opt$pctboot <- 0.70 } if(is.null(opt$replace)) opt$replace <- FALSE for ( myfn in c( opt$mask, opt$fmri, opt$labels ) ) { if ( !file.exists(myfn) ) { print(paste("input file",myfn,"does not exist. Exiting.")) q(status=1) } # else print(paste(" have input file",myfn)) } freqLo<-0.01 freqHi<-0.1 trASL<-4 trBOLD<-2.2 if ( is.null( opt$gdens ) ) opt$gdens<-0.25 if ( ! is.null( opt$freq ) ) { freqLo<-as.numeric( strsplit(opt$freq,"x")[[1]][1] ) freqHi<-as.numeric( strsplit(opt$freq,"x")[[1]][2] ) } if ( ! is.null( opt$tr ) ) { trASL<-as.numeric( strsplit(opt$tr,"x")[[1]][2] ) trBOLD<-as.numeric( strsplit(opt$tr,"x")[[1]][1] ) } print( paste( "fLo",freqLo,"fHi", freqHi ,"modality",opt$modality,"graphdensity", opt$gdens ) ) suppressMessages( library(ANTsR) ) seg<-NA if ( ! is.null( opt$seg ) ) seg<-antsImageRead( opt$seg , 3 ) fmri<-antsImageRead( opt$fmri, 4 ) aal2fmri<-antsImageRead( opt$labels, 3 ) mask<-antsImageRead( opt$mask, 3 ) if ( as.character(opt$modality) == "ASLCBF" | as.character(opt$modality) == "ASLBOLD" ) { mat<-timeseries2matrix( fmri, mask ) cbflist<-list( ) moco_results <- motion_correction(fmri) regweights <- aslPerfusion(fmri, mask=mask, moreaccurate=T, dorobust=opt$robust, moco_results=moco_results)$regweights for ( i in 1:opt$nboot ) { timeinds<-sample( 2:nrow(mat) , round( nrow(mat) )*(opt$pctboot/2) , replace=opt$replace ) timeinds<-( timeinds %% 2 )+timeinds timeinds<-interleave( timeinds-1, timeinds ) aslarr<-as.array( fmri ) aslarr2<-aslarr[,,,timeinds] aslsub<-as.antsImage( aslarr2 ) antsSetSpacing( aslsub , antsGetSpacing( fmri ) ) mocoarr <- as.array(moco_results$moco_img) mocoarr2<-mocoarr[,,,timeinds] mocosub<-as.antsImage( mocoarr2 ) antsSetSpacing( mocosub , antsGetSpacing( fmri ) ) mocoparams <- as.data.frame(moco_results$moco_params) mocoparams.sub <- mocoparams[timeinds, ] moco_results.sub <- list(moco_img=mocosub, moco_params=mocoparams.sub, moco_avg_img=moco_results$moco_avg_img) regweights.sub <- regweights[timeinds] proc <- aslPerfusion( aslsub, mask=mask, moreaccurate=TRUE, dorobust=opt$robust, moco_results=moco_results.sub, regweights=regweights.sub) param <- list( sequence="pcasl", m0=proc$m0 ) cbf <- quantifyCBF( proc$perfusion, mask, param ) cbflist<-lappend( cbflist, cbf$kmeancbf ) } write.csv(data.frame(Regweights=regweights), paste(opt$output, 'ExcludedTimePoints.csv', sep='')) motion <- as.data.frame(moco_results$moco_params) templateFD<-rep(0,nrow(motion)) DVARS<-rep(0,nrow(motion)) omat <- mat for ( i in 2:nrow(motion) ) { mparams1<-c( motion[i,3:14] ) tmat1<-matrix( as.numeric(mparams1[1:9]), ncol = 3, nrow = 3) mparams2<-c( motion[i-1,3:14] ) tmat2<-matrix( as.numeric(mparams2[1:9]), ncol = 3, nrow = 3) pt<-t( matrix( rep(10,3), nrow=1) ) newpt1<-data.matrix(tmat1) %*% data.matrix( pt )+as.numeric(mparams1[10:12]) newpt2<-data.matrix(tmat2) %*% data.matrix( pt )+as.numeric(mparams1[10:12]) templateFD[i]<-sum(abs(newpt2-newpt1)) DVARS[i]<-sqrt( mean( ( omat[i,] - omat[i-1,] )^2 ) ) } omotionnuis<-as.matrix(motion[, 3:ncol(motion)] ) motnuisshift<-ashift(omotionnuis,c(1,0)) motmag<-apply( omotionnuis, FUN=mean,MARGIN=2) matmag<-sqrt( sum(motmag[1:9]*motmag[1:9]) ) tranmag<-sqrt( sum(motmag[10:12]*motmag[10:12]) ) motsd<-apply( omotionnuis-motnuisshift, FUN=mean,MARGIN=2) matsd<-sqrt( sum(motsd[1:9]*motsd[1:9]) ) transd<-sqrt( sum(motsd[10:12]*motsd[10:12]) ) dmatrix<-(omotionnuis-motnuisshift)[,1:9] dtran<-(omotionnuis-motnuisshift)[,10:12] dmatrixm<-apply( dmatrix * dmatrix , FUN=sum, MARGIN=1 ) dtranm<-apply( dtran * dtran , FUN=sum, MARGIN=1 ) names(matmag)<-"MatrixMotion" names(tranmag)<-"TransMotion" names(matsd)<-"DMatrixMotion" names(transd)<-"DTransMotion" write.csv(cbind(motion, data.frame(templateFD=templateFD, DVARS=DVARS)), paste(opt$output, 'MotionParams.csv', sep='')) write.csv(data.frame(MatrixMotion=matmag, Transmotion=tranmag, DMatrixMotion=matsd, DTransMotion=transd), paste(opt$output, 'MotionSummary.csv', sep=''), row.names=F) cbfout<-antsImageClone( mask ) avgcbf<-avgimg( cbflist , mask ) sdi<-sdimg( cbflist , mask ) thresh <- 75 cbfout[ sdi > thresh ] <- 0 cbfout[ sdi <= thresh ] <- avgcbf[ sdi <= thresh ] fn<-paste( opt$output,"_kcbf.nii.gz",sep='') antsImageWrite( cbfout , fn ) pcasl.processing <- aslPerfusion( fmri, mask=mask, moreaccurate=TRUE , dorobust = opt$robust, moco_results=moco_results) pcasl.parameters <- list( sequence="pcasl", m0=pcasl.processing$m0, T1b=opt$bloodt1) cbf <- quantifyCBF( pcasl.processing$perfusion, mask, pcasl.parameters ) filterpcasl<-getfMRInuisanceVariables( fmri, mask = mask , moreaccurate=TRUE ) xideal<-pcasl.processing$xideal tsResid<-residuals( lm( filterpcasl$matrixTimeSeries ~ filterpcasl$nuisancevariables + xideal )) mynetwork<-filterfMRIforNetworkAnalysis( tsResid , tr=trASL, mask=mask ,cbfnetwork = opt$modality, labels = aal2fmri , graphdensity = opt$gdens, freqLo = freqLo, freqHi = freqHi , seg = seg ) } if ( as.character(opt$modality) == "BOLD" ) { dd<-getfMRInuisanceVariables( fmri, mask = mask , moreaccurate=TRUE ) ###### do a simple thing instead mask<-dd$mask negmask<-antsImageClone( mask ) backgroundvoxels <- negmask == 0 neginds<-which( backgroundvoxels ) neginds<-sample(neginds,length(neginds)/25) negmask[ negmask >= 0 ] <- 0 backgroundvoxels[ ]<-FALSE backgroundvoxels[ neginds ]<-TRUE negmask[ backgroundvoxels ]<-1 mynuis<-svd( timeseries2matrix( fmri, negmask ) )$u[, 1:8] colnames(mynuis)<-paste("bgdNuis",1:8,sep='') dd$nuisancevariables<-cbind(dd$nuisancevariables[,1:4],mynuis) tsResid<-residuals( lm( dd$matrixTimeSeries ~ dd$nuisancevariables )) # see http://www.ncbi.nlm.nih.gov/pubmed/21889994 mynetwork<-filterfMRIforNetworkAnalysis( tsResid , tr=trBOLD, mask=dd$mask ,cbfnetwork = "BOLD", labels = aal2fmri , graphdensity = opt$gdens, freqLo = freqLo, freqHi = freqHi , seg = seg ) } print( names( mynetwork ) ) fn<-paste( opt$output,opt$modality,"_corrmat.mha",sep='') print( paste( "write correlation matrix",fn ) ) antsImageWrite( as.antsImage( mynetwork$corrmat ) , fn ) g<-mynetwork$graph print( names( g ) ) fn<-paste( opt$output,opt$modality,"_graph_metrics.csv",sep='') print( paste( "write graph metrics",fn ) ) graphmetrics<-data.frame( degree = g$degree , closeness = g$closeness , pagerank = g$pagerank , betweenness = g$betweeness , localtransitivity = g$localtransitivity , modularity = g$walktrapcomm$modularity ) write.csv( graphmetrics , fn, row.names = F , quote = F ) ants-2.2.0/Scripts/antsNeuroimagingBattery000077500000000000000000000374671311104306400207140ustar00rootroot00000000000000#!/usr/bin/perl -w use strict; use File::Path; use File::Spec; use File::Basename; use Getopt::Long; my $usage = qq{ antsNeuroimagngBattery align MR modalities to a common within-subject (and optional template) space Usage: antsNeuroimagingBattery.pl --input-directory where to look for modality images --output-directory where output goes (where antsCorticalThickness output lives) --output-name file prefix for outputs --anatomical reference subject image (usually T1) --anatomical-mask mask of anatomical image, should contain cerebrum, cerebellum, and brainstem ## OPTIONAL INPUTS ### --template template image --template-transform-name basename of tranforms from anatomical to template space (must be in output base dir) --dti-flag DIRNAME/fileflag/outid [for example DTI/_30dir_dt.nii.gz/30dir for a file in DTI/*_30dir_dt.nii.gz ] --pcasl-flag DIRNAME/fileflag/outid --pasl-flag DIRNAME/fileflag/outid --pasl-m0-flag DIRNAME/fileflag/outid --bold-flag DIRNAME/fileflag/outid --rsbold-flag DIRNAME/fileflag/outid --mt-flag DIRNAME/fileflag/outid --no-mt-flag DIRNAME/fileflag/outid --temp-directory DIRNAME --help --info-only look for inputs, output what is there, but don't process any data. ## NOTES ## Modality flags should only return one image, but you may provide multiple flags of a single type If you have the files DTI/name_12dir_dt.nii.gz & DTI/name_30dir_dt.nii.gz, you should call --dti-flag DTI/12dir_dt --DTI/30dir_dt If you call --dti-flag DTI/dt it will find both files and fail due to insufficient specificiation }; my $outdir = ""; my $outname = ""; my $subdir = ""; my $anat = ""; my $mask = ""; my $template = ""; my $templateWarps = ""; my @dtiflag = (); my @pcaslflag = (); my @paslflag = (); my @paslm0flag = (); my @boldflag = (); my @rsboldflag = (); my @mtflag = (); my @nomtflag = (); my $temp = "/state/partition1/"; my $help=0; my $info=0; my $ANTSPATH = $ENV{ANTSPATH}; GetOptions ("input-directory=s" => \$subdir, # string "output-directory=s" => \$outdir, # string "output-name=s" => \$outname, # string "anatomical=s" => \$anat, "anatomical-mask=s" => \$mask, "template=s" => \$template, "template-transform-name=s" => \$templateWarps, "dti-flag=s" => \@dtiflag, "pcasl-flag=s" => \@pcaslflag, "pasl-flag=s" => \@paslflag, "pasl-m0-flag=s" => \@paslm0flag, "bold-flag=s" => \@boldflag, "rsbold-flag=s" => \@rsboldflag, "mt-flag=s" => \@mtflag, "no-mt-flag=s" => \@nomtflag, "temp-directory=s" => \$temp, # string "info=i" => \$info, # flag "help" => \$help) # flag or die("Error in command line arguments\n"); if ( $help > 0 ) { print( "$usage \n" ); exit(0); } # Check for required inputs if ( ! -s "$anat" ) { die( "Missing anatomical image: \"$anat\"\n" ); } if ( ! -s "$mask" ) { die( "Missing anatomical mask image: \"$mask\"\n" ); } if ( ! -d "$outdir" ) { die( "Missing output directory: \"$outdir\"\n" ); } if ( "$outname" eq "" ) { die( "Missing output name: \"$outname\"\n" ); } if ( ! -d "$subdir" ) { die( "Missing input directory: \"$subdir\"\n" ); } $outdir = dirname($outdir).'/'.basename($outdir).'/'; $subdir = dirname($subdir).'/'.basename($subdir).'/'; my $warpflag = ""; if ( "$templateWarps" ne "" ) { print( "Looking for $outdir$templateWarps* \n"); my @mat = glob( "${outdir}*$templateWarps*.mat"); my @def = glob( "${outdir}*$templateWarps*1Warp.nii.gz"); if ( (scalar(@mat) > 1 ) || (scalar(@def) > 1) ) { print( "Too many template transforms found\n"); print( "Linear: @mat\n" ); print( "Warp: @def\n"); } else { $warpflag = "-w ${outdir}$templateWarps"; } } print( "Info = $info\n"); #FIXME - add input echos if ( $info > 0 ) { print( "FIXME = add summary of input parameters\n"); } my ($dtifiles, $dtinames, $dtiids) = ParseFlag( $subdir, @dtiflag ); my ($pcaslfiles, $pcaslnames, $pcaslids) = ParseFlag( $subdir, @pcaslflag ); my ($paslfiles, $paslnames, $paslids) = ParseFlag( $subdir, @paslflag ); my ($paslm0files, $paslm0names, $paslm0ids) = ParseFlag( $subdir, @paslm0flag ); my ($boldfiles, $boldnames, $boldids) = ParseFlag( $subdir, @boldflag ); my ($rsboldfiles, $rsboldnames, $rsboldids) = ParseFlag( $subdir, @rsboldflag ); my ($mtfiles, $mtnames, $mtids) = ParseFlag( $subdir, @mtflag ); my ($nomtfiles, $nomtnames, $nomtids) = ParseFlag( $subdir, @nomtflag ); print( " Images to process\n\n" ); print( " DTI files: @$dtifiles \n" ); print( " DTI names: @$dtinames \n\n" ); print( " PCASL files: @$pcaslfiles \n" ); print( " PCASL names: @$pcaslnames \n\n" ); print( " PASL files: @$paslfiles \n" ); print( " PASL names: @$paslnames \n\n" ); print( " PASL-M0 files: @$paslm0files \n" ); print( " PASL-M0 names: @$paslm0names \n\n" ); print( " BOLD files: @$boldfiles \n" ); print( " BOLD names: @$boldnames \n\n" ); print( " RsBOLD files: @$rsboldfiles \n" ); print( " RsBOLD names: @$rsboldnames \n\n" ); print( " MT files: @$mtfiles \n" ); print( " MT names: @$mtnames \n\n" ); print( " NoMT files: @$nomtfiles \n" ); print( " NoMT names: @$nomtnames \n\n" ); if ( $info > 0 ) { exit(0); } ###### DTI ######## for my $dtfile ( @$dtifiles ) { print( "DTI Image - $dtfile \n"); my $flag = shift(@dtiflag); my $dirname = (split("/",$flag))[0].'/'; my $outid = shift(@$dtiids); # Find expected averagedwi image my @dwiparts = split( "_dt.nii.gz", $dtfile ); my $dwi = "$dwiparts[0]_averageB0.nii.gz"; if ( ! -s "$dwi" ) { print( "Could not find $dwi\n"); print( "Unable to align $dtfile\n"); } else { if ( ! -d "${outdir}${dirname}") { `mkdir -p ${outdir}${dirname}`; } my $obase = "${outdir}${dirname}${outname}${outid}"; my $ref = "${obase}ref.nii.gz"; system("${ANTSPATH}/ImageMath 3 $ref m $anat $mask" ); my $res = "${ANTSPATH}/ResampleImageBySpacing 3 $ref $ref 2.0 2.0 2.0"; print("$res\n"); system($res); my $dtiexe = "sh ${ANTSPATH}/antsIntermodalityIntrasubject.sh -d 3 -t 2 -i $dwi -b $dtfile -r $ref -R $anat -x $mask $warpflag -o ${outdir}${dirname}${outname}${outid} -T $template"; print( "$dtiexe\n"); system( $dtiexe ); system("${ANTSPATH}/ImageMath 3 ${outdir}${dirname}${outname}${outid}fa_anatomical.nii.gz TensorFA ${outdir}${dirname}${outname}${outid}dt_anatomical.nii.gz"); system("${ANTSPATH}/ImageMath 3 ${outdir}${dirname}${outname}${outid}fa_anatomical.nii.gz m ${outdir}${dirname}${outname}${outid}fa_anatomical.nii.gz $mask"); system("${ANTSPATH}/ImageMath 3 ${outdir}${dirname}${outname}${outid}md_anatomical.nii.gz TensorMeanDiffusion ${outdir}${dirname}${outname}${outid}dt_anatomical.nii.gz"); system("${ANTSPATH}/ImageMath 3 ${outdir}${dirname}${outname}${outid}md_anatomical.nii.gz m ${outdir}${dirname}${outname}${outid}md_anatomical.nii.gz $mask"); system("${ANTSPATH}/ImageMath 3 ${outdir}${dirname}${outname}${outid}rd_anatomical.nii.gz TensorRadialDiffusion ${outdir}${dirname}${outname}${outid}dt_anatomical.nii.gz"); system("${ANTSPATH}/ImageMath 3 ${outdir}${dirname}${outname}${outid}rd_anatomical.nii.gz m ${outdir}${dirname}${outname}${outid}rd_anatomical.nii.gz $mask"); # Remove for space reasons system("rm ${outdir}${dirname}${outname}${outid}dt_anatomical.nii.gz"); if ( -s "${outdir}${dirname}${outname}${outid}dt_template.nii.gz") { system("${ANTSPATH}/ImageMath 3 ${outdir}${dirname}${outname}${outid}fa_template.nii.gz TensorFA ${outdir}${dirname}${outname}${outid}dt_template.nii.gz"); system("${ANTSPATH}/ImageMath 3 ${outdir}${dirname}${outname}${outid}md_template.nii.gz TensorMeanDiffusion ${outdir}${dirname}${outname}${outid}dt_template.nii.gz"); system("${ANTSPATH}/ImageMath 3 ${outdir}${dirname}${outname}${outid}rd_template.nii.gz TensorRadialDiffusion ${outdir}${dirname}${outname}${outid}dt_template.nii.gz"); system("rm ${outdir}${dirname}${outname}${outid}dt_template.nii.gz"); } } } ###### PCASL ######## for my $pcaslfile ( @$pcaslfiles ) { print( "PCASL Image - $pcaslfile \n"); my $flag = shift(@pcaslflag); my $dirname = (split("/",$flag))[0].'/'; my $outid = shift(@$pcaslids); if ( ! -e "$pcaslfile" ) { print("Could not find $pcaslfile\n"); } else { if ( ! -d "${outdir}${dirname}") { `mkdir -p ${outdir}${dirname}`; } my $cbf = "${outdir}${dirname}${outname}${outid}CBF.nii.gz"; my $obase = "${outdir}${dirname}${outname}${outid}"; if ( ! -e "$cbf" ) { system("${ANTSPATH}/antsASLProcessing.R -s $pcaslfile -o ${outdir}${dirname}${outname}${outid} -a ${outdir}${outname} -t $template"); } #system("${ANTSPATH}/antsMotionCorrStats -x ${outdir}${dirname}${outname}${outid}brainmask.nii.gz -m ${outdir}${dirname}${outname}${outid}MOCOparams.csv -o ${outdir}${dirname}${outname}${outid}MOCOStatsFramewise.csv -f 1"); #system("${ANTSPATH}/antsMotionCorrStats -x ${outdir}${dirname}${outname}${outid}brainmask.nii.gz -m ${outdir}${dirname}${outname}${outid}MOCOparams.csv -o ${outdir}${dirname}${outname}${outid}MOCOStatsReference.csv -f 0"); } } ###### PASL + M0 ######## if ( scalar(@$paslfiles) == scalar(@$paslm0files) ) { for my $paslfile ( @$paslfiles ) { print( "PASL Image - $paslfile\n"); my $m0file = shift( @$paslm0files ); print( "M0 Image - $m0file\n"); my $flag = shift(@paslflag); my $dirname = (split("/",$flag))[0].'/'; my $outid = shift(@$paslids); # Find expected images if ( ! -e "$paslfile" ) { print( "Could not find $paslfile\n"); } else { if ( ! -d "${outdir}${dirname}") { `mkdir -p ${outdir}${dirname}`; } my $cbf = "${outdir}${dirname}${outname}${outid}meancbf.nii.gz"; my $M0 = "${outdir}${dirname}${outname}${outid}moco_m0.nii.gz"; my $meanM0 = "${outdir}${dirname}${outname}${outid}mean_m0.nii.gz"; my $obase = "${outdir}${dirname}${outname}${outid}"; my $ref = "${obase}ref.nii.gz"; if ( ! -e "$cbf" ) { # Create anatomical reference to align to system("${ANTSPATH}/ImageMath 3 $ref m $anat $mask" ); my $res = "${ANTSPATH}/ResampleImageBySpacing 3 $ref $ref 2.0 2.0 2.0"; system($res); # Motion correct M0 my $m0Moco1 = "${ANTSPATH}/antsMotionCorr -d 3 -a $m0file -o $meanM0"; system($m0Moco1); my $m0Moco2 = "${ANTSPATH}/antsMotionCorr -d 3 -o [ ${outdir}${dirname}${outname}${outid}m0, $M0, $meanM0 ] -u 1 -m mi[ $meanM0, $m0file, 1, 32, Regular, 0.1 ] -t Affine[0.2] -i 25 -e 1 -f 1 -s 0 -l 0"; print( "$m0Moco2\n"); system( $m0Moco2 ); #my $pasl = "${outdir}${dirname}${outname}${outid}pasl.nii.gz"; #my $meanpasl = "${outdir}${dirname}${outname}${outid}meanpasl.nii.gz"; #my $paslMoco = "antsMotionCorr -d 3 -o [ ${outdir}${dirname}${outname}${outid}pasl, $pasl, $meanpasl ] -u 1 -m mi[ $meanM0, $paslfile, 1, 32, Regular, 0.1 ] -t Affine[0.2] -i 25 -e 1 -f 1 -s 0 -l 0"; #print("$paslMoco\n"); #system($paslMoco); my $makecbf = "${ANTSPATH}/cbf_pasl_robust_batch.R $paslfile $meanM0 $cbf"; print( "\n\n\n$makecbf\n\n\n"); system( $makecbf ); } my $alignexe = "sh ${ANTSPATH}/antsIntermodalityIntrasubject.sh -d 3 -t 2 -a $cbf -i $meanM0 -r $ref -R $anat -x $mask $warpflag -o ${outdir}${dirname}${outname}${outid} -T $template"; print( "$alignexe\n"); system( $alignexe ); } } } else { print( "Inconsistent number of pasl/M0 images\n"); } ###### BOLD ######## for my $boldfile ( @$boldfiles ) { my $flag = shift(@boldflag); my $dirname = (split("/",$flag))[0].'/'; my $outid = shift(@$boldids); print( "${outdir}${dirname} \n"); if ( ! -d "${outdir}${dirname}") { print( "Create diretory\n"); `mkdir -p ${outdir}${dirname}`; } if ( ! -s "$boldfile" ) { print( "No $boldfile found\n"); } else { print( "BOLD Image - $boldfile \n"); my $obase = "${outdir}${dirname}${outname}${outid}"; my $bold = "${obase}bold.nii.gz"; my $meanbold = "${obase}meanbold.nii.gz"; my $ref = "${obase}ref.nii.gz"; if ( ! -e "$meanbold" ) { # Slice timing correction my $stc = "${ANTSPATH}/ImageMath 4 $bold SliceTimingCorrection $boldfile 0 bspline"; system($stc); # Motion correction system("${ANTSPATH}/antsMotionCorr -d 3 -a $bold -o $meanbold"); system("${ANTSPATH}/antsMotionCorr -d 3 -o [ ${obase}, $bold, $meanbold ] -u 1 -m mi[ $meanbold, $bold, 1, 32, Regular, 0.1 ] -t Affine[0.2] -i 25 -e 1 -f 1 -s 0 -l 0 -u 1"); system("${ANTSPATH}/ImageMath 3 $ref m $anat $mask" ); my $res = "${ANTSPATH}/ResampleImageBySpacing 3 $ref $ref 2.0 2.0 2.0"; system($res); } my $alignexe = "sh ${ANTSPATH}/antsIntermodalityIntrasubject.sh -d 3 -t 2 -i $meanbold -r $ref -R $anat -x $mask $warpflag -o ${outdir}${dirname}${outname}${outid} -T $template"; system( $alignexe ); system("${ANTSPATH}/antsMotionCorrStats -x ${outdir}${dirname}${outname}${outid}_brainmask.nii.gz -m ${outdir}${dirname}${outname}${outid}_MOCOparams.csv -0 ${outdir}${dirname}${outname}${outid}_MOCOstats.csv -f 1"); } } ###### RSBOLD ######## for my $rsboldfile ( @$rsboldfiles ) { my $flag = shift(@rsboldflag); my $dirname = (split("/",$flag))[0].'/'; my $outid = shift(@$rsboldids); print( "${outdir}${dirname} \n"); if ( ! -d "${outdir}${dirname}") { print( "Create diretory\n"); `mkdir -p ${outdir}${dirname}`; } if ( ! -s "$rsboldfile" ) { print( "No $rsboldfile found\n"); } else { print( "BOLD Image - $rsboldfile \n"); my $obase = "${outdir}${dirname}${outname}${outid}"; my $bold = "${obase}rsbold.nii.gz"; my $meanbold = "${obase}meanbold.nii.gz"; my $ref = "${obase}ref.nii.gz"; if ( ! -e "$meanbold" ) { # Motion correction system("${ANTSPATH}/antsMotionCorr -d 3 -a $rsboldfile -o $meanbold"); print("${ANTSPATH}/antsMotionCorr -d 3 -o [ ${obase}, $bold, $meanbold ] -u 1 -m mi[ $meanbold, $rsboldfile, 1, 32, Regular, 0.1 ] -t Affine[0.2] -i 25 -e 1 -f 1 -s 0 -l 0 -u 1 \n"); system("${ANTSPATH}/antsMotionCorr -d 3 -o [ ${obase}, $bold, $meanbold ] -m mi[ $meanbold, $rsboldfile, 1, 32, Regular, 0.1 ] -t Affine[0.2] -i 25 -e 1 -f 1 -s 0 -l 0 -u 1"); system("${ANTSPATH}/ImageMath 3 $ref m $anat $mask" ); my $res = "${ANTSPATH}/ResampleImageBySpacing 3 $ref $ref 2.0 2.0 2.0"; system($res); } my $alignexe = "sh ${ANTSPATH}/antsIntermodalityIntrasubject.sh -d 3 -t 2 -i $meanbold -r $ref -R $anat -x $mask $warpflag -o ${outdir}${dirname}${outname}${outid} -T $template"; system( $alignexe ); system("${ANTSPATH}/antsMotionCorrStats -x ${outdir}${dirname}${outname}${outid}brainmask.nii.gz -m ${outdir}${dirname}${outname}${outid}MOCOparams.csv -o ${outdir}${dirname}${outname}${outid}MOCOStatsFramewise.csv -f 1"); system("${ANTSPATH}/antsMotionCorrStats -x ${outdir}${dirname}${outname}${outid}brainmask.nii.gz -m ${outdir}${dirname}${outname}${outid}MOCOparams.csv -o ${outdir}${dirname}${outname}${outid}MOCOStatsReference.csv -f 0"); } } ###### MT + NoMT ######## for my $mtfile ( @$mtfiles ) { print( "MT Image - $mtfile \n"); } sub ParseFlag { my @files = (); my @names = (); my @ids = (); my $subdir = shift( @_ ); for my $flag ( @_ ) { chomp($flag); print( "$flag \n" ); my @flagsplit = split("/", $flag); my @flagfiles = glob( "${subdir}$flagsplit[0]/*$flagsplit[1]*" ); print( "@flagfiles\n"); if ( scalar(@flagfiles) > 1 ) { print( "Found too many files for flag \"$flag\", please provide a unique identifier\n" ); print( "@flagfiles" ); exit(1); } if ( scalar(@flagfiles) > 0 ) { push(@files, $flagfiles[0]); push(@names, $flagsplit[1]); push(@ids, $flagsplit[2]); } } return (\@files, \@names, \@ids); } exit(1); ants-2.2.0/Scripts/antsRegistrationSpaceTime.sh000077500000000000000000000246461311104306400216060ustar00rootroot00000000000000#!/bin/bash VERSION="0.0.0 test" # trap keyboard interrupt (control-c) trap control_c SIGINT function setPath { cat <&2 fi ANTS=${ANTSPATH}/antsRegistration if [[ ! -s ${ANTS} ]]; then echo "antsRegistration program can't be found. Please (re)define \$ANTSPATH in your environment." exit fi function Usage { cat <&2 fi ################# # # default values # ################# DIM=3 FIXEDIMAGES=() MOVINGIMAGES=() OUTPUTNAME=output NUMBEROFTHREADS=1 SPLINEDISTANCE=26 TRANSFORMTYPE='s' PRECISIONTYPE='d' nrepeats=2 MASK=0 USEHISTOGRAMMATCHING=0 timespacing=1 # reading command line arguments while getopts "d:f:h:m:j:n:o:p:r:s:t:x:" OPT do case $OPT in h) #help Help exit 0 ;; d) # dimensions DIM=$OPTARG ;; j) # histogram matching USEHISTOGRAMMATCHING=$OPTARG ;; x) # inclusive mask MASK=$OPTARG ;; f) # fixed image FIXEDIMAGES[${#FIXEDIMAGES[@]}]=$OPTARG ;; m) # moving image MOVINGIMAGES[${#MOVINGIMAGES[@]}]=$OPTARG ;; n) # number of threads NUMBEROFTHREADS=$OPTARG ;; o) #output name prefix OUTPUTNAME=$OPTARG ;; p) # precision type PRECISIONTYPE=$OPTARG ;; r) # n repeats nrepeats=$OPTARG ;; s) # spline distance SPLINEDISTANCE=$OPTARG ;; t) # spacing in time timespacing=$OPTARG ;; \?) # getopts issues an error message echo "$USAGE" >&2 exit 1 ;; esac done ############################### # # Check inputs # ############################### for(( i=0; i<${#MOVINGIMAGES[@]}; i++ )) do if [[ ! -f "${MOVINGIMAGES[$i]}" ]]; then echo "Moving image '${MOVINGIMAGES[$i]}' does not exist. See usage: '$0 -h 1'" exit 1 fi done ############################### # # Set number of threads # ############################### ORIGINALNUMBEROFTHREADS=${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=$NUMBEROFTHREADS export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS ############################## # # Print out options # ############################## reportMappingParameters # what we do below # # stack the moving images together to produce ND+1 input # antsMotionCorr to get affine to the fixed template # stack fixed template to ND+1 # register with -r warp to the stacked template # jacobian ... let DIMP1=$DIM+1 echo $DIMP1 zero=${OUTPUTNAME}zero.nii.gz zerob=${OUTPUTNAME}zerob.nii.gz stack=${OUTPUTNAME}stack.nii.gz nmov=${#MOVINGIMAGES[@]} let nmov=$nmov-1 MultiplyImages $DIM ${MOVINGIMAGES[0]} 1 $zero stackmovparam=" $zero $zero " for mov in ${MOVINGIMAGES[@]} ; do for k in `seq 1 $nrepeats ` ; do stackmovparam=" $stackmovparam $mov " done done let nm1=${#MOVINGIMAGES[@]}-1 MultiplyImages $DIM ${MOVINGIMAGES[nm1]} 1 $zerob stackmovparam=" $stackmovparam $zerob $zerob " if [[ $DIM == 2 ]] ; then StackSlices $stack -1 -1 0 $stackmovparam | grep -v Slice fi if [[ $DIM == 3 ]] ; then ImageMath $DIMP1 $stack TimeSeriesAssemble $timespacing 0 ${stackmovparam} fi ImageMath $DIMP1 $stack SetTimeSpacing $stack $timespacing nm=${OUTPUTNAME} MultiplyImages $DIM ${FIXEDIMAGES} 1 $zero stackparam=" $zero $zero " stacktemplate=${OUTPUTNAME}template.nii.gz for mov in ${MOVINGIMAGES[@]} ; do for k in `seq 1 $nrepeats ` ; do stackparam=" $stackparam ${FIXEDIMAGES} " done done stackparam=" $stackparam $zero $zero " if [[ $DIM == 2 ]] ; then StackSlices $stacktemplate -1 -1 0 ${stackparam} fi if [[ $DIM == 3 ]] ; then ImageMath $DIMP1 $stacktemplate TimeSeriesAssemble $timespacing 0 ${stackparam} fi ImageMath $DIMP1 $stacktemplate SetTimeSpacing $stacktemplate $timespacing # echo $stackparam # echo $stackmovparam # echo $nrepeats $timespacing if [[ $DIM == 2 ]] ; then rxt="1x1x0"; fi if [[ $DIM == 3 ]] ; then rxt="1x1x1x0"; fi antsMotionCorr -d $DIM \ -o [ ${nm}aff, ${nm}aff.nii.gz,${nm}_affavg.nii.gz] \ -m MI[${FIXEDIMAGES}, ${stack}, 1 , 20, Regular, 0.1 ] \ -t rigid[ 0.1 ] -u 1 -e 1 -s 4x2x1x0 -f 6x4x2x1 \ -i 100x100x0x0 \ -m MI[${FIXEDIMAGES}, ${stack}, 1 , 20, Regular, 0.2 ] \ -t Affine[ 0.1 ] -u 1 -e 1 -s 4x2x1x0 -f 6x4x2x1 \ -i 100x100x100x15 \ -n ${#MOVINGIMAGES[@]} -w 1 --verbose 1 ImageMath $DIMP1 ${nm}affWarp.nii.gz SetTimeSpacingWarp ${nm}affWarp.nii.gz $timespacing ImageMath $DIMP1 ${nm}affInverseWarp.nii.gz SetTimeSpacingWarp ${nm}affInverseWarp.nii.gz $timespacing # if below does not work - have to drop the -r # and put the aff version into the metric antsRegistration -d $DIMP1 -r ${nm}affWarp.nii.gz \ -c [100x70x50x10,1e-6,10] \ -f 6x4x2x1 \ -s 3x2x1x0vox \ -m CC[ $stacktemplate, ${nm}stack.nii.gz, 1, 2 ] \ -t SyN[0.15,3,0] --restrict-deformation $rxt \ -o [${nm},${nm}diffeoWarped.nii.gz,${nm}diffeoInvWarped.nii.gz] -z 0 CreateJacobianDeterminantImage $DIMP1 ${nm}1Warp.nii.gz ${nm}0logjacobian.nii.gz 1 1 ############################### # # Restore original number of threads # ############################### ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=$ORIGINALNUMBEROFTHREADS export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS ants-2.2.0/Scripts/antsRegistrationSyN.sh000077500000000000000000000344111311104306400204340ustar00rootroot00000000000000#!/bin/bash VERSION="0.0.0 test" # trap keyboard interrupt (control-c) trap control_c SIGINT function setPath { cat <&2 fi ANTS=${ANTSPATH}/antsRegistration if [[ ! -s ${ANTS} ]]; then echo "antsRegistration program can't be found. Please (re)define \$ANTSPATH in your environment." exit fi function Usage { cat <&2 fi ################# # # default values # ################# DIM=3 FIXEDIMAGES=() MOVINGIMAGES=() OUTPUTNAME=output NUMBEROFTHREADS=1 SPLINEDISTANCE=26 TRANSFORMTYPE='s' PRECISIONTYPE='d' CCRADIUS=4 MASK=0 USEHISTOGRAMMATCHING=0 # reading command line arguments while getopts "d:f:h:m:j:n:o:p:r:s:t:x:" OPT do case $OPT in h) #help Help exit 0 ;; d) # dimensions DIM=$OPTARG ;; x) # inclusive mask MASK=$OPTARG ;; f) # fixed image FIXEDIMAGES[${#FIXEDIMAGES[@]}]=$OPTARG ;; j) # histogram matching USEHISTOGRAMMATCHING=$OPTARG ;; m) # moving image MOVINGIMAGES[${#MOVINGIMAGES[@]}]=$OPTARG ;; n) # number of threads NUMBEROFTHREADS=$OPTARG ;; o) #output name prefix OUTPUTNAME=$OPTARG ;; p) # precision type PRECISIONTYPE=$OPTARG ;; r) # cc radius CCRADIUS=$OPTARG ;; s) # spline distance SPLINEDISTANCE=$OPTARG ;; t) # transform type TRANSFORMTYPE=$OPTARG ;; \?) # getopts issues an error message echo "$USAGE" >&2 exit 1 ;; esac done ############################### # # Check inputs # ############################### if [[ ${#FIXEDIMAGES[@]} -ne ${#MOVINGIMAGES[@]} ]]; then echo "Number of fixed images is not equal to the number of moving images." exit 1 fi for(( i=0; i<${#FIXEDIMAGES[@]}; i++ )) do if [[ ! -f "${FIXEDIMAGES[$i]}" ]]; then echo "Fixed image '${FIXEDIMAGES[$i]}' does not exist. See usage: '$0 -h 1'" exit 1 fi if [[ ! -f "${MOVINGIMAGES[$i]}" ]]; then echo "Moving image '${MOVINGIMAGES[$i]}' does not exist. See usage: '$0 -h 1'" exit 1 fi done ############################### # # Set number of threads # ############################### ORIGINALNUMBEROFTHREADS=${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=$NUMBEROFTHREADS export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS ############################## # # Print out options # ############################## reportMappingParameters ############################## # # Mask stuff # ############################## if [[ ${#MASK} -lt 3 ]]; then NULLMASK="" MASK="" HAVEMASK=0 else NULLMASK=" -x [NULL,NULL] " MASK=" -x [$MASK, NULL] " HAVEMASK=1 fi ############################## # # Infer the number of levels based on # the size of the input fixed image. # ############################## ISLARGEIMAGE=0 SIZESTRING=$( ${ANTSPATH}/PrintHeader ${FIXEDIMAGES[0]} 2 ) SIZESTRING="${SIZESTRING%\\n}" SIZE=( `echo $SIZESTRING | tr 'x' ' '` ) for (( i=0; i<${#SIZE[@]}; i++ )) do if [[ ${SIZE[$i]} -gt 256 ]]; then ISLARGEIMAGE=1 break fi done ############################## # # Construct mapping stages # ############################## RIGIDCONVERGENCE="[1000x500x250x100,1e-6,10]" RIGIDSHRINKFACTORS="8x4x2x1" RIGIDSMOOTHINGSIGMAS="3x2x1x0vox" AFFINECONVERGENCE="[1000x500x250x100,1e-6,10]" AFFINESHRINKFACTORS="8x4x2x1" AFFINESMOOTHINGSIGMAS="3x2x1x0vox" SYNCONVERGENCE="[100x70x50x20,1e-6,10]" SYNSHRINKFACTORS="8x4x2x1" SYNSMOOTHINGSIGMAS="3x2x1x0vox" if [[ $ISLARGEIMAGE -eq 1 ]]; then RIGIDCONVERGENCE="[1000x500x250x100,1e-6,10]" RIGIDSHRINKFACTORS="12x8x4x2" RIGIDSMOOTHINGSIGMAS="4x3x2x1vox" AFFINECONVERGENCE="[1000x500x250x100,1e-6,10]" AFFINESHRINKFACTORS="12x8x4x2" AFFINESMOOTHINGSIGMAS="4x3x2x1vox" SYNCONVERGENCE="[100x100x70x50x20,1e-6,10]" SYNSHRINKFACTORS="10x6x4x2x1" SYNSMOOTHINGSIGMAS="5x3x2x1x0vox" fi tx=Rigid if [[ $TRANSFORMTYPE == 't' ]] ; then tx=Translation fi INITIALSTAGE="--initial-moving-transform [${FIXEDIMAGES[0]},${MOVINGIMAGES[0]},1]" RIGIDSTAGE="--transform ${tx}[0.1] \ --metric MI[${FIXEDIMAGES[0]},${MOVINGIMAGES[0]},1,32,Regular,0.25] \ --convergence $RIGIDCONVERGENCE \ --shrink-factors $RIGIDSHRINKFACTORS \ --smoothing-sigmas $RIGIDSMOOTHINGSIGMAS" AFFINESTAGE="--transform Affine[0.1] \ --metric MI[${FIXEDIMAGES[0]},${MOVINGIMAGES[0]},1,32,Regular,0.25] \ --convergence $AFFINECONVERGENCE \ --shrink-factors $AFFINESHRINKFACTORS \ --smoothing-sigmas $AFFINESMOOTHINGSIGMAS" SYNMETRICS='' for(( i=0; i<${#FIXEDIMAGES[@]}; i++ )) do SYNMETRICS="$SYNMETRICS --metric CC[${FIXEDIMAGES[$i]},${MOVINGIMAGES[$i]},1,${CCRADIUS}]" done SYNSTAGE="${SYNMETRICS} \ --convergence $SYNCONVERGENCE \ --shrink-factors $SYNSHRINKFACTORS \ --smoothing-sigmas $SYNSMOOTHINGSIGMAS" if [[ $TRANSFORMTYPE == 'sr' ]] || [[ $TRANSFORMTYPE == 'br' ]]; then SYNCONVERGENCE="[50x0,1e-6,10]" SYNSHRINKFACTORS="2x1" SYNSMOOTHINGSIGMAS="1x0vox" SYNSTAGE="${SYNMETRICS} \ --convergence $SYNCONVERGENCE \ --shrink-factors $SYNSHRINKFACTORS \ --smoothing-sigmas $SYNSMOOTHINGSIGMAS" fi if [[ $TRANSFORMTYPE == 'b' ]] || [[ $TRANSFORMTYPE == 'br' ]] || [[ $TRANSFORMTYPE == 'bo' ]]; then SYNSTAGE="--transform BSplineSyN[0.1,${SPLINEDISTANCE},0,3] \ $SYNSTAGE" fi if [[ $TRANSFORMTYPE == 's' ]] || [[ $TRANSFORMTYPE == 'sr' ]] || [[ $TRANSFORMTYPE == 'so' ]]; then SYNSTAGE="--transform SyN[0.1,3,0] \ $SYNSTAGE" fi STAGES='' case "$TRANSFORMTYPE" in "r" | "t") STAGES="$INITIALSTAGE $RIGIDSTAGE" if [[ $HAVEMASK -eq 1 ]] ; then $STAGES=" $INITIALSTAGE $RIGIDSTAGE $MASK " fi ;; "a") STAGES="$INITIALSTAGE $RIGIDSTAGE $AFFINESTAGE" if [[ $HAVEMASK -eq 1 ]] ; then STAGES="$INITIALSTAGE $RIGIDSTAGE $NULLMASK $AFFINESTAGE $MASK " fi ;; "b" | "s") STAGES="$INITIALSTAGE $RIGIDSTAGE $AFFINESTAGE $SYNSTAGE" if [[ $HAVEMASK -eq 1 ]] ; then STAGES="$INITIALSTAGE $RIGIDSTAGE $NULLMASK $AFFINESTAGE $NULLMASK $SYNSTAGE $MASK " fi ;; "br" | "sr") STAGES="$INITIALSTAGE $RIGIDSTAGE $SYNSTAGE" if [[ $HAVEMASK -eq 1 ]] ; then STAGES="$INITIALSTAGE $RIGIDSTAGE $NULLMASK $SYNSTAGE $MASK " fi ;; "bo" | "so") STAGES="$INITIALSTAGE $SYNSTAGE" if [[ $HAVEMASK -eq 1 ]] ; then $STAGES=" $INITIALSTAGE $SYNSTAGE $MASK " fi ;; *) echo "Transform type '$TRANSFORMTYPE' is not an option. See usage: '$0 -h 1'" exit ;; esac PRECISION='' case "$PRECISIONTYPE" in "f") PRECISION="--float 1" ;; "d") PRECISION="--float 0" ;; *) echo "Precision type '$PRECISIONTYPE' is not an option. See usage: '$0 -h 1'" exit ;; esac COMMAND="${ANTS} --verbose 1 \ --dimensionality $DIM $PRECISION \ --output [$OUTPUTNAME,${OUTPUTNAME}Warped.nii.gz,${OUTPUTNAME}InverseWarped.nii.gz] \ --interpolation Linear \ --use-histogram-matching ${USEHISTOGRAMMATCHING} \ --winsorize-image-intensities [0.005,0.995] \ $STAGES" echo " antsRegistration call:" echo "--------------------------------------------------------------------------------------" echo ${COMMAND} echo "--------------------------------------------------------------------------------------" $COMMAND ############################### # # Restore original number of threads # ############################### ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=$ORIGINALNUMBEROFTHREADS export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS ants-2.2.0/Scripts/antsRegistrationSyNQuick.sh000077500000000000000000000344041311104306400214330ustar00rootroot00000000000000#!/bin/bash VERSION="0.0.0 test" # trap keyboard interrupt (control-c) trap control_c SIGINT function setPath { cat <&2 fi ANTS=${ANTSPATH}/antsRegistration if [[ ! -s ${ANTS} ]]; then echo "antsRegistration program can't be found. Please (re)define \$ANTSPATH in your environment." exit fi function Usage { cat <&2 fi ################# # # default values # ################# DIM=3 FIXEDIMAGES=() MOVINGIMAGES=() OUTPUTNAME=output NUMBEROFTHREADS=1 SPLINEDISTANCE=26 TRANSFORMTYPE='s' PRECISIONTYPE='d' NUMBEROFBINS=32 MASK=0 USEHISTOGRAMMATCHING=0 # reading command line arguments while getopts "d:f:h:m:j:n:o:p:r:s:t:x:" OPT do case $OPT in h) #help Help exit 0 ;; d) # dimensions DIM=$OPTARG ;; x) # inclusive mask MASK=$OPTARG ;; f) # fixed image FIXEDIMAGES[${#FIXEDIMAGES[@]}]=$OPTARG ;; j) # histogram matching USEHISTOGRAMMATCHING=$OPTARG ;; m) # moving image MOVINGIMAGES[${#MOVINGIMAGES[@]}]=$OPTARG ;; n) # number of threads NUMBEROFTHREADS=$OPTARG ;; o) #output name prefix OUTPUTNAME=$OPTARG ;; p) # precision type PRECISIONTYPE=$OPTARG ;; r) # cc radius NUMBEROFBINS=$OPTARG ;; s) # spline distance SPLINEDISTANCE=$OPTARG ;; t) # transform type TRANSFORMTYPE=$OPTARG ;; \?) # getopts issues an error message echo "$USAGE" >&2 exit 1 ;; esac done ############################### # # Check inputs # ############################### if [[ ${#FIXEDIMAGES[@]} -ne ${#MOVINGIMAGES[@]} ]]; then echo "Number of fixed images is not equal to the number of moving images." exit 1 fi for(( i=0; i<${#FIXEDIMAGES[@]}; i++ )) do if [[ ! -f "${FIXEDIMAGES[$i]}" ]]; then echo "Fixed image '${FIXEDIMAGES[$i]}' does not exist. See usage: '$0 -h 1'" exit 1 fi if [[ ! -f "${MOVINGIMAGES[$i]}" ]]; then echo "Moving image '${MOVINGIMAGES[$i]}' does not exist. See usage: '$0 -h 1'" exit 1 fi done ############################### # # Set number of threads # ############################### ORIGINALNUMBEROFTHREADS=${ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS} ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=$NUMBEROFTHREADS export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS ############################## # # Print out options # ############################## reportMappingParameters ############################## # # Mask stuff # ############################## if [[ ${#MASK} -lt 3 ]]; then NULLMASK="" MASK="" HAVEMASK=0 else NULLMASK=" -x [NULL,NULL] " MASK=" -x [$MASK, NULL] " HAVEMASK=1 fi ############################## # # Infer the number of levels based on # the size of the input fixed image. # ############################## ISLARGEIMAGE=0 SIZESTRING=$( ${ANTSPATH}/PrintHeader ${FIXEDIMAGES[0]} 2 ) SIZESTRING="${SIZESTRING%\\n}" SIZE=( `echo $SIZESTRING | tr 'x' ' '` ) for (( i=0; i<${#SIZE[@]}; i++ )) do if [[ ${SIZE[$i]} -gt 256 ]]; then ISLARGEIMAGE=1 break fi done ############################## # # Construct mapping stages # ############################## RIGIDCONVERGENCE="[1000x500x250x0,1e-6,10]" RIGIDSHRINKFACTORS="8x4x2x1" RIGIDSMOOTHINGSIGMAS="3x2x1x0vox" AFFINECONVERGENCE="[1000x500x250x0,1e-6,10]" AFFINESHRINKFACTORS="8x4x2x1" AFFINESMOOTHINGSIGMAS="3x2x1x0vox" SYNCONVERGENCE="[100x70x50x0,1e-6,10]" SYNSHRINKFACTORS="8x4x2x1" SYNSMOOTHINGSIGMAS="3x2x1x0vox" if [[ $ISLARGEIMAGE -eq 1 ]]; then RIGIDCONVERGENCE="[1000x500x250x0,1e-6,10]" RIGIDSHRINKFACTORS="12x8x4x2" RIGIDSMOOTHINGSIGMAS="4x3x2x1vox" AFFINECONVERGENCE="[1000x500x250x0,1e-6,10]" AFFINESHRINKFACTORS="12x8x4x2" AFFINESMOOTHINGSIGMAS="4x3x2x1vox" SYNCONVERGENCE="[100x100x70x50x0,1e-6,10]" SYNSHRINKFACTORS="10x6x4x2x1" SYNSMOOTHINGSIGMAS="5x3x2x1x0vox" fi tx=Rigid if [[ $TRANSFORMTYPE == 't' ]] ; then tx=Translation fi INITIALSTAGE="--initial-moving-transform [${FIXEDIMAGES[0]},${MOVINGIMAGES[0]},1]" RIGIDSTAGE="--transform ${tx}[0.1] \ --metric MI[${FIXEDIMAGES[0]},${MOVINGIMAGES[0]},1,32,Regular,0.25] \ --convergence $RIGIDCONVERGENCE \ --shrink-factors $RIGIDSHRINKFACTORS \ --smoothing-sigmas $RIGIDSMOOTHINGSIGMAS" AFFINESTAGE="--transform Affine[0.1] \ --metric MI[${FIXEDIMAGES[0]},${MOVINGIMAGES[0]},1,32,Regular,0.25] \ --convergence $AFFINECONVERGENCE \ --shrink-factors $AFFINESHRINKFACTORS \ --smoothing-sigmas $AFFINESMOOTHINGSIGMAS" SYNMETRICS='' for(( i=0; i<${#FIXEDIMAGES[@]}; i++ )) do SYNMETRICS="$SYNMETRICS --metric MI[${FIXEDIMAGES[$i]},${MOVINGIMAGES[$i]},1,${NUMBEROFBINS}]" done SYNSTAGE="${SYNMETRICS} \ --convergence $SYNCONVERGENCE \ --shrink-factors $SYNSHRINKFACTORS \ --smoothing-sigmas $SYNSMOOTHINGSIGMAS" if [[ $TRANSFORMTYPE == 'sr' ]] || [[ $TRANSFORMTYPE == 'br' ]]; then SYNCONVERGENCE="[50x0,1e-6,10]" SYNSHRINKFACTORS="2x1" SYNSMOOTHINGSIGMAS="1x0vox" SYNSTAGE="${SYNMETRICS} \ --convergence $SYNCONVERGENCE \ --shrink-factors $SYNSHRINKFACTORS \ --smoothing-sigmas $SYNSMOOTHINGSIGMAS" fi if [[ $TRANSFORMTYPE == 'b' ]] || [[ $TRANSFORMTYPE == 'br' ]] || [[ $TRANSFORMTYPE == 'bo' ]]; then SYNSTAGE="--transform BSplineSyN[0.1,${SPLINEDISTANCE},0,3] \ $SYNSTAGE" fi if [[ $TRANSFORMTYPE == 's' ]] || [[ $TRANSFORMTYPE == 'sr' ]] || [[ $TRANSFORMTYPE == 'so' ]]; then SYNSTAGE="--transform SyN[0.1,3,0] \ $SYNSTAGE" fi STAGES='' case "$TRANSFORMTYPE" in "r" | "t") STAGES="$INITIALSTAGE $RIGIDSTAGE" if [[ $HAVEMASK -eq 1 ]] ; then $STAGES=" $INITIALSTAGE $RIGIDSTAGE $MASK " fi ;; "a") STAGES="$INITIALSTAGE $RIGIDSTAGE $AFFINESTAGE" if [[ $HAVEMASK -eq 1 ]] ; then STAGES="$INITIALSTAGE $RIGIDSTAGE $NULLMASK $AFFINESTAGE $MASK " fi ;; "b" | "s") STAGES="$INITIALSTAGE $RIGIDSTAGE $AFFINESTAGE $SYNSTAGE" if [[ $HAVEMASK -eq 1 ]] ; then STAGES="$INITIALSTAGE $RIGIDSTAGE $NULLMASK $AFFINESTAGE $NULLMASK $SYNSTAGE $MASK " fi ;; "br" | "sr") STAGES="$INITIALSTAGE $RIGIDSTAGE $SYNSTAGE" if [[ $HAVEMASK -eq 1 ]] ; then STAGES="$INITIALSTAGE $RIGIDSTAGE $NULLMASK $SYNSTAGE $MASK " fi ;; "bo" | "so") STAGES="$INITIALSTAGE $SYNSTAGE" if [[ $HAVEMASK -eq 1 ]] ; then $STAGES=" $INITIALSTAGE $SYNSTAGE $MASK " fi ;; *) echo "Transform type '$TRANSFORMTYPE' is not an option. See usage: '$0 -h 1'" exit ;; esac PRECISION='' case "$PRECISIONTYPE" in "f") PRECISION="--float 1" ;; "d") PRECISION="--float 0" ;; *) echo "Precision type '$PRECISIONTYPE' is not an option. See usage: '$0 -h 1'" exit ;; esac COMMAND="${ANTS} --verbose 1 \ --dimensionality $DIM $PRECISION \ --output [$OUTPUTNAME,${OUTPUTNAME}Warped.nii.gz,${OUTPUTNAME}InverseWarped.nii.gz] \ --interpolation Linear \ --use-histogram-matching ${USEHISTOGRAMMATCHING} \ --winsorize-image-intensities [0.005,0.995] \ $STAGES" echo " antsRegistration call:" echo "--------------------------------------------------------------------------------------" echo ${COMMAND} echo "--------------------------------------------------------------------------------------" $COMMAND ############################### # # Restore original number of threads # ############################### ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=$ORIGINALNUMBEROFTHREADS export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS ants-2.2.0/Scripts/antsaffine.sh000077500000000000000000000040511311104306400165550ustar00rootroot00000000000000#!/bin/bash if [ ${#ANTSPATH} -le 3 ] ; then echo we guess at your ants path export ANTSPATH=${ANTSPATH:="$HOME/bin/ants/"} # EDIT THIS fi if [ ! -s ${ANTSPATH}/ANTS ] ; then echo we cant find the ANTS program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi NUMPARAMS=$# if [ $NUMPARAMS -lt 3 ] then echo " USAGE :: " echo " sh antsaffine.sh ImageDimension fixed.ext moving.ext OPTIONAL-OUTPREFIX PURELY-RIGID " echo " be sure to set ANTSPATH environment variable " echo " affine only registration " exit fi #ANTSPATH=YOURANTSPATH if [ ${#ANTSPATH} -le 0 ] then echo " Please set ANTSPATH=LocationOfYourAntsBinaries " echo " Either set this in your profile or directly, here in the script. " echo " For example : " echo $ANTSPATH echo " ANTSPATH=/home/yourname/bin/ants/ " exit else echo " ANTSPATH is $ANTSPATH " fi #initialization, here, is unbiased DIM=$1 if [ ${#DIM} -gt 1 ] then echo " Problem with specified ImageDimension => User Specified Value = $DIM " exit fi FIXED=$2 if [ ${#FIXED} -lt 1 -o ! -f $FIXED ] then echo " Problem with specified Fixed Image => User Specified Value = $FIXED " exit fi MOVING=$3 if [ ${#MOVING} -lt 1 -o ! -f $MOVING ] then echo " Problem with specified Moving Image => User Specified Value = $MOVING " exit fi OUTPUTNAME=` echo $MOVING | cut -d '.' -f 1 ` if [ $NUMPARAMS -gt 3 ] then OUTPUTNAME=${4} fi RIGID=" --rigid-affine false " if [ $NUMPARAMS -gt 4 ] then RIGID=" --rigid-affine true --affine-gradient-descent-option 0.5x0.95x1.e-4x1.e-4 " fi echo " Will this mapping be purely rigid? $RIGID " echo " ANTSPATH is $ANTSPATH " #below, some affine options #--MI-option 16x8000 #-a InitAffine.txt --continue-affine 0 exe=" ${ANTSPATH}/ANTS $DIM -m MI[${FIXED},${MOVING},1,32] -o ${OUTPUTNAME} -i 0 --use-Histogram-Matching --number-of-affine-iterations 10000x10000x10000x10000x10000 $RIGID " echo " $exe " $exe ${ANTSPATH}/WarpImageMultiTransform $DIM $MOVING ${OUTPUTNAME}deformed.nii.gz ${OUTPUTNAME}Affine.txt -R ${FIXED} ants-2.2.0/Scripts/antsbashstats.sh000077500000000000000000000076751311104306400173400ustar00rootroot00000000000000#!/bin/bash # sttdev.sh: Standard Deviation # Original version obtained from: http://tldp.org/LDP/abs/html/contributed-scripts.html#STDDEV # 2009-03-09: Panagiotis Kritikakos # Function stat_median() added for calculating the median value # of the data set # ------------------------------------------------------------ # The Standard Deviation indicates how consistent a set of data is. # It shows to what extent the individual data points deviate from the #+ arithmetic mean, i.e., how much they "bounce around" (or cluster). # It is essentially the average deviation-distance of the #+ data points from the mean. # =========================================================== # # To calculate the Standard Deviation: # # 1 Find the arithmetic mean (average) of all the data points. # 2 Subtract each data point from the arithmetic mean, # and square that difference. # 3 Add all of the individual difference-squares in # 2. # 4 Divide the sum in # 3 by the number of data points. # This is known as the "variance." # 5 The square root of # 4 gives the Standard Deviation. # =========================================================== # count=0 # Number of data points; global. SC=9 # Scale to be used by bc. Nine decimal places. E_DATAFILE=90 # Data file error. # ----------------- Set data file --------------------- if [ ! -z "$1" ] # Specify filename as cmd-line arg? then datafile="$1" # ASCII text file, else #+ one (numerical) data point per line! datafile=sample.dat fi # See example data file, below. if [ ! -e "$datafile" ] then echo "\""$datafile"\" does not exist!" echo " Usage : " echo " sh $0 textdatafile.txt " exit $E_DATAFILE fi # ----------------------------------------------------- arith_mean () { local rt=0 # Running total. local am=0 # Arithmetic mean. local ct=0 # Number of data points. while read value # Read one data point at a time. do echo $value rt=$(echo "scale=$SC; $rt + $value" | bc) (( ct++ )) done am=$(echo "scale=$SC; $rt / $ct" | bc) echo $am; return $ct # This function "returns" TWO values! # Caution: This little trick will not work if $ct > 255! # To handle a larger number of data points, #+ simply comment out the "return $ct" above. } <"$datafile" # Feed in data file. sd () { mean1=$1 # Arithmetic mean (passed to function). n=$2 # How many data points. sum2=0 # Sum of squared differences ("variance"). avg2=0 # Average of $sum2. sdev=0 # Standard Deviation. while read value # Read one line at a time. do diff=$(echo "scale=$SC; $mean1 - $value" | bc) # Difference between arith. mean and data point. dif2=$(echo "scale=$SC; $diff * $diff" | bc) # Squared. sum2=$(echo "scale=$SC; $sum2 + $dif2" | bc) # Sum of squares. done avg2=$(echo "scale=$SC; $sum2 / $n" | bc) # Avg. of sum of squares. sdev=$(echo "scale=$SC; sqrt($avg2)" | bc) # Square root = echo $sdev # Standard Deviation. } <"$datafile" # Rewinds data file. stat_median() { NUMS=(`sort -n $1`) TOTALNUMS=${#NUMS[*]} MOD=$(($TOTALNUMS % 2)) if [ $MOD -eq 0 ]; then ARRAYMIDDLE=$(echo "($TOTALNUMS / 2)-1" | bc) ARRAYNEXTMIDDLE=$(($ARRAYMIDDLE + 1)) MEDIAN=$(echo "scale=$SC; ((${NUMS[$ARRAYMIDDLE]})+(${NUMS[$ARRAYNEXTMIDDLE]})) / 2" | bc) elif [ $MOD -eq 1 ]; then ARRAYMIDDLE=$(echo "($TOTALNUMS / 2)" | bc) MEDIAN=${NUMS[$ARRAYMIDDLE]} fi echo $MEDIAN } # ======================================================= # mean=$(arith_mean); count=$? # Two returns from function! std_dev=$(sd $mean $count) median=$(stat_median $1) echo " Mean : $mean SD: $std_dev " #echo #echo "Number of data points in \""$datafile"\" = $count" #echo "Arithmetic mean (average) = $mean" #echo "Standard Deviation = $std_dev" #echo "Median number (middle) = $median" #echo # ======================================================= # exit ants-2.2.0/Scripts/antsdeformationmag.sh000077500000000000000000000037221311104306400203250ustar00rootroot00000000000000#!/bin/bash NUMPARAMS=$# if [ $NUMPARAMS -lt 4 ] then echo " USAGE :: " echo " $0 inWarp WhichTypeOfMagnitude outputmagnitudeimage.nii ANTSPATH " echo " WhichTypeOfMagnitude == Laplacian Grad Euclidean " echo " the numbers that come out of this depend on some itk filters being dimensionally correct and consistent. " echo " this may not be the case -- however, relative values should be ok , e.g comparing Grad values to Grad values etc " exit fi inwarp=$1 whichmag=$2 magimage=$3 ANTSPATH=$4 dimarr=(`${ANTSPATH}/PrintHeader ${inwarp}xvec.nii.gz | grep Spacing | grep , | wc `) DIM=${dimarr[0]} let DIM=${DIM}+1 # multiply some image of the right size by 0 to make the initial magnitude image ${ANTSPATH}/ImageMath $DIM $magimage m ${inwarp}xvec.nii.gz 0 ${ANTSPATH}/ImageMath $DIM ${magimage}temp.nii.gz m ${inwarp}xvec.nii.gz 0 ok=0 for x in x y z do if [ -s ${inwarp}${x}vec.nii.gz ] then # compute the magnitude of the x-component deformation if [ $whichmag == Laplacian ] then echo " Laplacian $x " ok=1 ${ANTSPATH}/ImageMath $DIM ${magimage}temp.nii.gz Laplacian ${inwarp}${x}vec.nii.gz 1.0 0 elif [ $whichmag == Grad ] then echo " Grad $x " ok=1 ${ANTSPATH}/ImageMath $DIM ${magimage}temp.nii.gz Grad ${inwarp}${x}vec.nii.gz 1.0 0 #ImageMath $DIM temp.nii.gz m temp.nii.gz temp.nii.gz elif [ $whichmag == Euclidean ] then echo " Euclidean $x " ok=1 ${ANTSPATH}/ImageMath $DIM ${magimage}temp.nii.gz m ${inwarp}${x}vec.nii.gz ${inwarp}${x}vec.nii.gz fi # add the xvec magnitude to the magimage ${ANTSPATH}/ImageMath $DIM $magimage + $magimage ${magimage}temp.nii.gz fi # ifz done #loop if [ $ok -eq 1 ] then # take the square root ${ANTSPATH}/ImageMath $DIM $magimage ^ $magimage 0.5 ${ANTSPATH}/MeasureMinMaxMean $DIM $magimage #thus, #magimage=sqrt( x*x + y*y + z*z ) else echo " User Requested deformation measure :: $whichmag " echo " that is not available -- user needs to choose another type of deformation measure " fi rm -f ${magimage}temp.nii.gz ants-2.2.0/Scripts/antsqsub.sh000077500000000000000000000017721311104306400163060ustar00rootroot00000000000000#!/bin/bash #$ -S /bin/bash dir=$1 cmd=$2 para=$3 naming=$4 fixname=$5 movingname=$6 echo $6 opta=$7 echo $7 optb=$8 echo $8 optc=$9 echo $9 optd=${10} echo ${10} opte=${11} echo ${11} optf=${12} echo ${12} optg=${13} echo ${13} opth=${14} echo ${14} opti=${15} echo ${15} optj=${16} echo ${16} optk=${17} echo ${17} optl=${18} echo ${18} optm=${19} echo ${19} optn=${20} echo ${20} opto=${21} echo ${21} optp=${22} echo ${22} optq=${23} echo ${23} optr=${24} echo ${24} opts=${25} echo ${25} optt=${26} echo ${26} echo " dir " echo $1 cd $1 echo $cmd $para $naming $fixname $movingname $opta $optb $optc $optd $opte $optf $optg $opth $opti $optj $optk $optl $optm $optn $opto $optp $optq $optr $opts $optt $cmd $para $naming $fixname $movingname $opta $optb $optc $optd $opte $optf $optg $opth $opti $optj $optk $optl $optm $optn $opto $optp $optq $optr $opts $optt ${27} ${28} ${29} ${30} ${31} ${32} ${33} ${34} ${35} ${36} ${37} ${38} ${39} ${40} ${41} ${42} ${43} ${44} ${45} ${46} ${47} ${48} ${49} cd - ants-2.2.0/Scripts/antswithdt.sh000077500000000000000000000112761311104306400166370ustar00rootroot00000000000000#!/bin/bash NUMPARAMS=$# MAXITERATIONS=30x90x20 export ANTSPATH=${ANTSPATH:="$HOME/bin/ants/"} if [ $NUMPARAMS -lt 3 ] then echo " USAGE :: " echo " sh ants.sh ImageDimension fixed.ext moving.ext Subject/Moving-DT-To-Deform-To-Fixed-Image OPTIONAL-Subject/Moving-BZero-To-DistortionCorrect-To-Moving-T1-Image " echo " be sure to set ANTSPATH environment variable " echo " Max-Iterations in form : JxKxL where " echo " J = max iterations at coarsest resolution (here, reduce by power of 2^2) " echo " K = middle resolution iterations ( here, reduce by power of 2 ) " echo " L = fine resolution iterations ( here, full resolution ) -- this level takes much more time per iteration " echo " an extra Ix before JxKxL would add another level " echo " Default ierations is $MAXITERATIONS -- you can often get away with fewer for many apps " echo " Other parameters are defaults used in the A. Klein evaluation paper in Neuroimage, 2009 " echo " " echo " The DT component is distortion corrected to the T1 image either by the B-Zero Image / Average-DWI Image " echo " or whatever is passed as the last parameter --- Alternatively, we compute the FA from the DTI and then " echo " distortion correct to it. One should test the validity of this approach on your data before widely applying ." echo " We use the cross-correlation to perform the distortion correction. " echo " Some parameter tuning may be required, depending on the distortions present in your acquisition. " exit fi if [ ${#ANTSPATH} -le 3 ] ; then echo we guess at your ants path export ANTSPATH=${ANTSPATH:="$HOME/bin/ants/"} # EDIT THIS fi if [ ! -s ${ANTSPATH}/ANTS ] ; then echo we cant find the ANTS program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi #initialization, here, is unbiased DIM=$1 if [ ${#DIM} -gt 1 ] then echo " Problem with specified ImageDimension => User Specified Value = $DIM " exit fi FIXED=$2 if [ ${#FIXED} -lt 1 -o ! -f $FIXED ] then echo " Problem with specified Fixed Image => User Specified Value = $FIXED " exit fi MOVING=$3 if [ ${#MOVING} -lt 1 -o ! -f $MOVING ] then echo " Problem with specified Moving Image => User Specified Value = $MOVING " exit fi OUTPUTNAME=${MOVING%.*.*} if [ ${#OUTPUTNAME} -eq ${#MOVING} ] then OUTPUTNAME=${MOVING%.*} fi MOVINGDT=0 if [ $NUMPARAMS -gt 3 ] then MOVINGDT=$4 if [ ! -f $MOVINGDT ] then echo " THIS DTI DOES NOT EXIST : $MOVINGDT " MOVINGDT="" fi fi MOVINGBZ=0 if [ $NUMPARAMS -gt 4 ] then MOVINGBZ=$5 if [ ! -f $MOVINGBZ ] then echo " THIS BZero DOES NOT EXIST : $MOVINGBZ " MOVINGBZ=0 fi fi if [ ${#MOVINGDT} -gt 3 ] then echo " The BZero DOES NOT EXIST : Using FA Instead!!" ${ANTSPATH}/ImageMath 3 ${OUTPUTNAME}_fa.nii TensorFA $MOVINGDT MOVINGBZ=${OUTPUTNAME}_fa.nii fi # Mapping Parameters TRANSFORMATION=SyN[0.25] ITERATLEVEL=(`echo $MAXITERATIONS | tr 'x' ' '`) NUMLEVELS=${#ITERATLEVEL[@]} echo $NUMLEVELS REGULARIZATION=Gauss[3,0] METRIC=CC[ METRICPARAMS=1,2] #echo " $METRICPARAMS & $METRIC " #exit echo " ANTSPATH $ANTSPATH " echo " Mapping Parameters :: " echo " Transformation is: $TRANSFORMATION " echo " MaxIterations : $MAXITERATIONS " echo " Number Of MultiResolution Levels $NUMLEVELS " echo " Regularization : $REGULARIZATION " echo " Metric : ${METRIC} " echo " OutputName : $OUTPUTNAME " echo " " echo " if the files and parameters are all ok then uncomment the exit call below this line " echo " " #exit # first, do distortion correction of MOVINGDT to MOVING # use the B0 image ${ANTSPATH}/ANTS 3 -m CC[${MOVINGBZ},${MOVING},1,2] -o ${OUTPUTNAME}distcorr -r Gauss[3,0] -t SyN[0.25] -i 25x20x0 --number-of-affine-iterations 10000x10000x10000 ${ANTSPATH}/WarpImageMultiTransform 3 $MOVING ${OUTPUTNAME}distcorr.nii.gz ${OUTPUTNAME}distcorrWarp.nii.gz ${OUTPUTNAME}distcorrAffine.txt -R $MOVINGBZ #exit if [[ ! -s ${OUTPUTNAME}Affine.txt ]] ; then sh ${ANTSPATH}/ants.sh $DIM $FIXED $MOVING ${OUTPUTNAME} fi if [[ -s ${MOVINGDT} ]] && [[ -s ${OUTPUTNAME}Affine.txt ]] ; then DTDEF=${OUTPUTNAME}DTdeformed.nii.gz FIXEDSUB=${OUTPUTNAME}fixedsub.nii.gz # ${ANTSPATH}/ResampleImageBySpacing 3 $FIXED $FIXEDSUB 2 2 2 FIXEDSUB=$FIXED echo " Warp DT " ${ANTSPATH}/WarpTensorImageMultiTransform $DIM $MOVINGDT $DTDEF ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt -i ${OUTPUTNAME}distcorrAffine.txt ${OUTPUTNAME}distcorrInverseWarp.nii.gz -R $FIXEDSUB COMPWARP=${OUTPUTNAME}DTwarp.nii.gz ${ANTSPATH}/ComposeMultiTransform $DIM $COMPWARP -R ${FIXEDSUB} ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt -i ${OUTPUTNAME}distcorrAffine.txt ${OUTPUTNAME}distcorrInverseWarp.nii.gz ${ANTSPATH}/ReorientTensorImage 3 $DTDEF $DTDEF $COMPWARP fi exit ants-2.2.0/Scripts/asymmetry.sh000077500000000000000000000060711311104306400164750ustar00rootroot00000000000000#!/bin/bash usage=" $0 -d 3 -f symmetric_template.nii.gz -m moving.nii.gz -o output_prefix " :<&2 dim=$OPTARG ;; f) echo "-f $OPTARG" >&2 A=$OPTARG ;; h) echo $usage exit 0; ;; m) echo "-m $OPTARG" >&2 B=$OPTARG ;; o) echo "-o $OPTARG " >&2 prefix=$OPTARG ;; a) echo "-a $OPTARG " >&2 a=$OPTARG ;; \?) echo "Usage: $usage " >&2 exit 0 ;; esac done echo inputs: $A $B $prefix $dim if [[ ${#dim} -lt 1 ]] ; then echo must provide input dimension $dim ; echo $usage ; exit 0 ; fi if [[ ${#prefix} -lt 3 ]] ; then echo must provide output prefix $prefix ; echo $usage ; exit 0 ; fi if [[ ! -s $A ]] || [[ ! -s $B ]] ; then echo inputs: $A $B $prefix ; echo $usage ; exit 1 ; fi ##################################################### reg=antsRegistration uval=0 affits=999x999x1550x200 rig=" -t rigid[ 0.2 ] -c [ $affits ,1.e-7,20] -s 3x2x1x0 -f 8x4x2x1 -u $uval -l 0 " aff=" -t affine[ 0.2 ] -c [ $affits ,1.e-7,20] -s 3x2x1x0 -f 8x4x2x1 -u $uval -l 0 " metparams=" 1 , 32, regular , 0.5 " synits=220x220x100x50 #BA # synits=0x0x0x0 #BA dtx="syn[ 0.25, 3, 0. ]" ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=2 export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS ##################################################### ImageMath $dim ${prefix}_reflection.mat ReflectionMatrix $A $a antsApplyTransforms -d $dim -t ${prefix}_reflection.mat -i $B -o ${prefix}_reflect.nii.gz -r $B imgs=" $A, $B " antsAffineInitializer ${dim} $A $B ${prefix}_init.mat 5 0.25 0 3 $reg -d $dim -r ${prefix}_init.mat\ -m mattes[ $imgs, $metparams ] $rig \ -m mattes[ $imgs, $metparams ] $aff \ -u $uval -b 0 -z 1 \ -o [${prefix}_L,${prefix}_L_aff.nii.gz] $reg -d $dim -r ${prefix}_L0GenericAffine.mat \ -m mattes[ $imgs , 1 , 32 ] \ -t $dtx \ -c [${synits},1.e-8,10] \ -s 3x2x1x0 \ -f 8x4x2x1 \ -u $uval -b 0 -z 1 \ -o [${prefix}_L,${prefix}_L.nii.gz] $reg -d $dim -r ${prefix}_reflection.mat -r ${prefix}_L0GenericAffine.mat \ -m mattes[ $imgs , 1 , 32 ] \ -t $dtx \ -c [${synits},1.e-8,10] \ -s 3x2x1x0 \ -f 8x4x2x1 \ -u $uval -b 0 -z 1 \ -o [${prefix}_R,${prefix}_R.nii.gz] ########################################################## ANTSJacobian $dim ${prefix}_R1Warp.nii.gz ${prefix}_R 1 ANTSJacobian $dim ${prefix}_L1Warp.nii.gz ${prefix}_L 1 ImageMath $dim ${prefix}_asym.nii.gz - ${prefix}_Llogjacobian.nii.gz ${prefix}_Rlogjacobian.nii.gz ants-2.2.0/Scripts/basic_ants_example.sh000077500000000000000000000130371311104306400202640ustar00rootroot00000000000000#!/bin/bash # # if [ $# -lt 5 ] ; then echo this script will allow you to run \( Reg \) a pairwise segmentation, \( RegSegPSE or RegSegMSQ \) or run segmentation, then use segmentation output in registration or \( BTP \) build a template. echo you call the script like this echo $0 ImageDimensionality Opt OutputPrefix r16slice.nii.gz r64slice.nii.gz echo where Opt is Reg , RegSeg , BTP , RegSegPSE echo echo should work for 2D or 3D images. echo echo these are BASIC examples --- not intended to illustrate optimal usage but provide general, reasonable settings. exit fi # if [ ${#ANTSPATH} -le 3 ] ; then echo we guess at your ants path export ANTSPATH=${ANTSPATH:="$HOME/bin/ants/"} # EDIT THIS fi if [ ! -s ${ANTSPATH}/ANTS ] ; then echo we cant find the ANTS program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi # # in general, you should call ANTS -h for help # DIM=$1 OPT=$2 OUTPUTNAME=$3 # choose the output name II=$4 # change to your own images JJ=$5 if [[ ! -s $II ]] ; then echo cannot find $II --- please change the filename by editing this script. exit fi if [[ ! -s $JJ ]] ; then echo cannot find $JJ --- please change the filename by editing this script. exit fi ITS=" -i 100x100x30 " # 3 optimization levels # different transformation models you can choose TSYNWITHTIME=" -t SyN[0.25,5,0.01] -r DMFFD[10x10,0,3] " # spatiotemporal (full) diffeomorphism GGREEDYSYN=" -t SyN[0.15] -r Gauss[3,0] " # fast symmetric normalization gaussian regularization BGREEDYSYN=" -t SyN[0.15] -r DMFFD[4x4,0,3] " # fast symmetric normalization dmffd regularization TELAST=" -t Elast[1] -r Gauss[0.5,3] " # elastic TEXP=" -t Exp[0.001,100] -r DMFFD[3,0] " # exponential # different metric choices for the user INTMSQ=" -m MSQ[${II},${JJ},1,0] " INTMI=" -m MI[${II},${JJ},1,16] " INTCC=" -m CC[${II},${JJ},1,4] " # # # these are the forward and backward warps. # INVW=" -i ${OUTPUTNAME}Affine.txt ${OUTPUTNAME}InverseWarp.nii.gz " FWDW=" ${OUTPUTNAME}Warp.nii.gz ${OUTPUTNAME}Affine.txt " # INT=$INTCC # choose a metric ( here , cross-correlation ) TRAN=$BGREEDYSYN # choose a transformation # TRAN=$TEXP # choose a transformation if [[ $OPT == "Reg" ]] ; then # run the registration ${ANTSPATH}/ANTS $DIM -o $OUTPUTNAME $ITS $TRAN $INT # this is how you apply the output transformation ${ANTSPATH}/WarpImageMultiTransform $DIM ${II} ${OUTPUTNAME}IItoJJ.nii.gz -R ${JJ} $INVW ${ANTSPATH}/WarpImageMultiTransform $DIM ${JJ} ${OUTPUTNAME}JJtoII.nii.gz -R ${II} $FWDW elif [[ $OPT == "RegSegPSE" ]] ; then # try something different -- segment the images and use them in ANTS # to run multivariate registration. IISEG=${OUTPUTNAME}A_seg.nii.gz JJSEG=${OUTPUTNAME}B_seg.nii.gz # # get masks for atropos # ${ANTSPATH}/ThresholdImage $DIM $II $IISEG 1 1.e9 ${ANTSPATH}/ThresholdImage $DIM $JJ $JJSEG 1 1.e9 AtroposParams=" -d $DIM -m [0.1,1x1] -c [5,0] -i kmeans[3] " ${ANTSPATH}/Atropos $AtroposParams -a $II -o $IISEG -x $IISEG ${ANTSPATH}/Atropos $AtroposParams -a $JJ -o $JJSEG -x $JJSEG # compute some segmentations and use them in labelguided mapping LABELGUIDED=" -m PSE[${II},${JJ},${IISEG},${JJSEG},0.75,0.1,25,0,10] " # ${ANTSPATH}/ANTS $DIM -o ${OUTPUTNAME} $ITS $TRAN $INT $LABELGUIDED ${ANTSPATH}/WarpImageMultiTransform $DIM ${II} ${OUTPUTNAME}IItoJJ.nii.gz -R ${JJ} $INVW ${ANTSPATH}/WarpImageMultiTransform $DIM ${JJ} ${OUTPUTNAME}JJtoII.nii.gz -R ${II} $FWDW # now warp the labels in both directions ${ANTSPATH}/WarpImageMultiTransform $DIM ${IISEG} ${OUTPUTNAME}IIsegtoJJseg.nii.gz -R ${JJ} $INVW --use-NN ${ANTSPATH}/WarpImageMultiTransform $DIM ${JJSEG} ${OUTPUTNAME}JJsegtoIIseg.nii.gz -R ${II} $FWDW --use-NN elif [[ $OPT == "RegSegMSQ" ]] ; then # try something different -- segment the images and use them in ANTS # to run multivariate registration. IISEG=${OUTPUTNAME}A_seg.nii.gz JJSEG=${OUTPUTNAME}B_seg.nii.gz # # get masks for atropos # ${ANTSPATH}/ThresholdImage $DIM $II $IISEG 1 1.e9 ${ANTSPATH}/ThresholdImage $DIM $JJ $JJSEG 1 1.e9 AtroposParams=" -d $DIM -m [0.1,1x1] -c [5,0] -i kmeans[3] " ${ANTSPATH}/Atropos $AtroposParams -a $II -o $IISEG -x $IISEG ${ANTSPATH}/Atropos $AtroposParams -a $JJ -o $JJSEG -x $JJSEG # compute some segmentations and use them in labelguided mapping LABELGUIDED=" -m MSQ[${IISEG},${JJSEG},0.75] " # ${ANTSPATH}/ANTS $DIM -o ${OUTPUTNAME} $ITS $TRAN $INT $LABELGUIDED ${ANTSPATH}/WarpImageMultiTransform $DIM ${II} ${OUTPUTNAME}IItoJJ.nii.gz -R ${JJ} $INVW ${ANTSPATH}/WarpImageMultiTransform $DIM ${JJ} ${OUTPUTNAME}JJtoII.nii.gz -R ${II} $FWDW # now warp the labels in both directions ${ANTSPATH}/WarpImageMultiTransform $DIM ${IISEG} ${OUTPUTNAME}IIsegtoJJseg.nii.gz -R ${JJ} $INVW --use-NN ${ANTSPATH}/WarpImageMultiTransform $DIM ${JJSEG} ${OUTPUTNAME}JJsegtoIIseg.nii.gz -R ${II} $FWDW --use-NN elif [[ $OPT == "BTP" ]] ; then # # now build a template from the two input images . # in a real application, you would modify the call to btp # such that it's more appropriate for your data. # call buildtemplateparallel.sh -h to get usage. # ${ANTSPATH}/buildtemplateparallel.sh -d 2 -o ${OUTPUTNAME}BTP -c 0 $II $JJ rm -r GR* *cfg TEM=${OUTPUTNAME}BTPtemplate.nii.gz NM1=` echo $JJ | cut -d '.' -f 1 ` INVW=" -i ${OUTPUTNAME}BTP${NM1}Affine.txt ${OUTPUTNAME}BTP${NM1}InverseWarp.nii.gz " FWDW=" ${OUTPUTNAME}BTP${NM1}Warp.nii.gz ${OUTPUTNAME}BTP${NM1}Affine.txt " ${ANTSPATH}/WarpImageMultiTransform $DIM ${JJ} ${OUTPUTNAME}JJtoTemplate.nii.gz -R $TEM $FWDW ${ANTSPATH}/WarpImageMultiTransform $DIM $TEM ${OUTPUTNAME}TemplatetoJJ.nii.gz -R ${JJ} $INVW else echo unrecognized option $OPT fi ants-2.2.0/Scripts/buildtemplateparallel.sh000077500000000000000000001424211311104306400210130ustar00rootroot00000000000000#!/bin/bash VERSION="0.0.14 test" afftype=".txt" # trap keyboard interrupt (control-c) trap control_c SIGINT function setPath { cat <&2 fi if [ ! -s ${ANTSPATH}/ANTS ] ; then echo "ANTS program can't be found. Please (re)define \$ANTSPATH in your environment." exit fi # Test availability of helper scripts. # No need to test this more than once. Can reside outside of the main loop. ANTSSCRIPTNAME=${ANTSPATH}/antsIntroduction.sh PEXEC=${ANTSPATH}/ANTSpexec.sh SGE=${ANTSPATH}/waitForSGEQJobs.pl PBS=${ANTSPATH}/waitForPBSQJobs.pl XGRID=${ANTSPATH}/waitForXGridJobs.pl SLURM=${ANTSPATH}/waitForSlurmJobs.pl fle_error=0 for FLE in $ANTSSCRIPTNAME $PEXEC $SGE $XGRID $SLURM do if [ ! -x $FLE ] ; then echo echo "--------------------------------------------------------------------------------------" echo " FILE $FLE DOES NOT EXIST -- OR -- IS NOT EXECUTABLE !!! $0 will terminate." echo "--------------------------------------------------------------------------------------" echo " if the file is not executable, please change its permissions. " fle_error=1 fi done if [ $fle_error = 1 ] ; then exit 1 fi #assuming .nii.gz as default file type. This is the case for ANTS 1.7 and up function Usage { cat < Compulsory arguments (minimal command line requires SGE cluster, otherwise use -c & -j options): -d: ImageDimension: 2 or 3 (for 2 or 3 dimensional registration of single volume) ImageDimension: 4 (for template generation of time-series data) -o: OUTPREFIX; A prefix that is prepended to all output files. List of images in the current directory, eg *_t1.nii.gz. Should be at the end of the command. NB: All images to be added to the template should be in the same directory, and this script should be invoked from that directory. Optional arguments: -c: Control for parallel computation (default 1) -- 0 == run serially, 1 == SGE qsub, 2 == use PEXEC (localhost), 3 == Apple XGrid, 4 == PBS qsub, 5 == SLURM -q: Set default queue for PBS jobs (default: nopreempt) -g: Gradient step size (default 0.25) for template update. Does not affect the step size of individual registrations. The default of 0.25 should not be increased, smaller numbers result in more cautious template update steps. 0.25 is an upper (aggressive) limit for this parameter. -i: Iteration limit (default 4) -- iterations of the template construction (Iteration limit)*NumImages registrations. -j: Number of cpu cores to use (default: 2; -- requires "-c 2") -m: Max-iterations in each registration, eg 30x90x30 -n: N4BiasFieldCorrection of moving image (default 1) -- 0 == off, 1 == on. If 1, will run N4 before each registration. It is more efficient to run N4BiasFieldCorrection on the input images once, then build a template from the corrected images. -p: Commands to prepend to job scripts (e.g., change into appropriate directory, set paths, etc) -r: Do rigid-body registration of inputs before creating template (default 0) -- 0 == off 1 == on. Only useful when you do not have an initial template -s: Type of similarity metric used for registration. -t: Type of transformation model used for registration. -x: XGrid arguments (e.g., -x "-p password -h controlhost") -z: Use this this volume as the target of all inputs. When not used, the script will create an unbiased starting point by averaging all inputs. Use the full path! If you do not have an initial template, it is advisible to run a few iterations with affine normalization only (-m 1x0x0) to get a sensible initial template, then pass this with -z to run full deformable registration. Example: `basename $0` -d 3 -m 30x50x20 -t GR -s CC -c 1 -o MY -z InitialTemplate.nii.gz *RF*T1x.nii.gz - In this example 30x50x20 iterations per registration are used for template creation (that is the default) - Greedy-SyN and CC are the metrics to guide the mapping. - Output is prepended with MY and the initial template is InitialTemplate.nii.gz (optional). - The -c option is set to 1, which will result in using the Sun Grid Engine (SGE) to distribute the computation. - if you do not have SGE, read the help for multi-core computation on the local machine, or Apple X-grid options. -------------------------------------------------------------------------------------- ANTS was created by: -------------------------------------------------------------------------------------- Brian B. Avants, Nick Tustison and Gang Song Penn Image Computing And Science Laboratory University of Pennsylvania Please reference http://www.ncbi.nlm.nih.gov/pubmed/20851191 when employing this script in your studies. A reproducible evaluation of ANTs similarity metric performance in brain image registration: * Avants BB, Tustison NJ, Song G, Cook PA, Klein A, Gee JC. Neuroimage, 2011. Also see http://www.ncbi.nlm.nih.gov/pubmed/19818860 for more details. The script has been updated and improved since this publication. -------------------------------------------------------------------------------------- script adapted by N.M. van Strien, http://www.mri-tutorial.com | NTNU MR-Center -------------------------------------------------------------------------------------- Apple XGrid support by Craig Stark -------------------------------------------------------------------------------------- USAGE exit 1 } function Help { cat < Example Case: bash `basename $0` -d 3 -m 30x50x20 -t GR -s CC -c 1 -o MY -z InitialTemplate.nii.gz *RF*T1x.nii.gz - In this case you use 30x50x20 iterations per registration - 4 iterations over template creation (that is the default) - With Greedy-SyN and CC metrics to guide the mapping. - Output is prepended with MY and the initial template is InitialTemplate.nii.gz (optional). - The -c option is set to 1 which will try to use SGE to distribute the computation. - If you do not have SGE, use -c 0 or -c 2 combined with -j. - Continue reading this help file if things are not yet clear. Compulsory arguments (minimal command line requires SGE cluster, otherwise use -c & -j options):: -d: ImageDimension: 2 or 3 (for 2 or 3 dimensional registration of single volume) ImageDimension: 4 (for template generation of time-series data) -o: OUTPREFIX; A prefix that is prepended to all output files. List of images in the current directory, eg *_t1.nii.gz. Should be at the end of the command. NB: All files to be added to the template should be in the same directory. Optional arguments: -c: Control for parallel computation (default 1) -- 0 == run serially, 1 == SGE qsub, 2 == use PEXEC (localhost), 3 == Apple XGrid, 4 == PBS Grid, 5 == SLURM -g: Gradient step size; smaller in magnitude results in more cautious steps (default 0.25). This does not affect the step size of individual registrations; it lets you update the template more cautiously after each iteration by reducing the template update step size from 0.25 to a smaller positive number. -i: Iteration limit (default = 4) for template construction. requires 4*NumImages registrations. -j: Number of cpu cores to use (default: 2; --- set -c option to 2 to use this . The optimal number of cpu cores to use for template generation depends on the availability of cores, the amount of free working memory (RAM) and the resolution of the data. High resolution datasets typically require more RAM during processing. Running out of RAM during a calculation will slow down all processing on your computer. -m: Max-iterations Max-Iterations in form: JxKxL where J = max iterations at coarsest resolution (here, reduce by power of 2^2) K = middle resolution iterations (here,reduce by power of 2) L = fine resolution iterations (here, full resolution) !!this level takes much more time per iteration!! Adding an extra value before JxKxL (i.e. resulting in IxJxKxL) would add another iteration level. -n: N4BiasFieldCorrection of moving image ( 0 = off; 1 = on (default) ) -p: Commands to prepend to job scripts (e.g., change into appropriate directory, set paths, etc) -r: Do rigid-body registration of inputs before creating template (default 0) -- 0 == off 1 == on. Only useful when you do not have an initial template In case a template is specified (-z option), all inputs are registered to that template. If no template is specified, the inputs will be registered to the averaged input. -s: Type of similarity metric used for registration. For intramodal image registration, use: CC = cross-correlation MI = mutual information PR = probability mapping (default) MSQ = mean square difference (Demons-like) SSD = sum of squared differences For intermodal image registration, use: MI = mutual information PR = probability mapping (default) -t: Type of transformation model used for registration. For rigid image registration, use: RI = Purely rigid RA = Affine rigid For elastic image registration, use: EL = elastic transformation model (less deformation possible) For diffeomorphic image registration, use: SY = SyN with time (default) with arbitrary number of time points in time discretization S2 = SyN with time optimized specifically for 2 time points in the time discretization GR = Greedy SyN EX = Exponential DD = Diffeomorphic Demons style exponential mapping -x: XGrid arguments (e.g., -x "-p password -h controlhost") -z: Use this this volume as the target of all inputs. When not used, the script will create an unbiased starting point by averaging all inputs. Use the full path! If you do not have an initial template, it is advisible to run a few iterations with affine normalization only (-m 1x0x0) to get a sensible initial template, then pass this with -z to run full deformable registration. Requirements: This scripts relies on the following scripts in your $ANTSPATH directory. The script will terminate prematurely if these files are not present or are not executable. - antsIntroduction.sh - pexec.sh - waitForSGEQJobs.pl (only for use with Sun Grid Engine) - ANTSpexec.sh (only for use with localhost parallel execution) - waitForXGridJobs.pl (only for use with Apple XGrid) - waitForSlurmJobs.pl (only for use with SLURM) -------------------------------------------------------------------------------------- Get the latest ANTS version at: -------------------------------------------------------------------------------------- http://sourceforge.net/projects/advants/ -------------------------------------------------------------------------------------- Read the ANTS documentation at: -------------------------------------------------------------------------------------- http://picsl.upenn.edu/ANTS/ -------------------------------------------------------------------------------------- ANTS was created by: -------------------------------------------------------------------------------------- Brian B. Avants, Nick Tustison and Gang Song Penn Image Computing And Science Laboratory University of Pennsylvania Please reference http://www.ncbi.nlm.nih.gov/pubmed/20851191 when employing this script in your studies. A reproducible evaluation of ANTs similarity metric performance in brain image registration: * Avants BB, Tustison NJ, Song G, Cook PA, Klein A, Gee JC. Neuroimage, 2011. Also see http://www.ncbi.nlm.nih.gov/pubmed/19818860 for more details. The script has been updated and improved since this publication. -------------------------------------------------------------------------------------- script adapted by N.M. van Strien, http://www.mri-tutorial.com | NTNU MR-Center -------------------------------------------------------------------------------------- Apple XGrid support by Craig Stark -------------------------------------------------------------------------------------- HELP exit 1 } function reportMappingParameters { cat < $OUTNM echo "# Transform 0 " >> $OUTNM echo "Transform: MatrixOffsetTransformBase_double_2_2 " >> $OUTNM echo "Parameters: $PARAM1 $PARAM2 $PARAM3 $PARAM4 $PARAM5 $PARAM6 " >> $OUTNM echo "FixedParameters: $PARAM7 $PARAM8 " >> $OUTNM } function ANTSAverage3DAffine { OUTNM=${templatename}Affine${afftype} FLIST=${outputname}*Affine${afftype} NFILES=0 PARAM1=0 PARAM2=0 PARAM3=0 PARAM4=0 PARAM5=0 PARAM6=0 PARAM7=0 PARAM8=0 PARAM9=0 PARAM10=0 PARAM11=0 PARAM12=0 PARAM13=0 PARAM14=0 PARAM15=0 LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 2 ` for x in $LL ; do PARAM1=` awk -v a=$PARAM1 -v b=$x 'BEGIN{print (a + b)}' ` ; let NFILES=$NFILES+1 ; done PARAM1=` awk -v a=$PARAM1 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 3 ` for x in $LL ; do PARAM2=` awk -v a=$PARAM2 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM2=` awk -v a=$PARAM2 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 4 ` for x in $LL ; do PARAM3=` awk -v a=$PARAM3 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM3=` awk -v a=$PARAM3 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 5 ` for x in $LL ; do PARAM4=` awk -v a=$PARAM4 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM4=` awk -v a=$PARAM4 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 6 ` for x in $LL ; do PARAM5=` awk -v a=$PARAM5 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM5=` awk -v a=$PARAM5 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 7 ` for x in $LL ; do PARAM6=` awk -v a=$PARAM6 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM6=` awk -v a=$PARAM6 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 8 ` for x in $LL ; do PARAM7=` awk -v a=$PARAM7 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM7=` awk -v a=$PARAM7 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 9 ` for x in $LL ; do PARAM8=` awk -v a=$PARAM8 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM8=` awk -v a=$PARAM8 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 10 ` for x in $LL ; do PARAM9=` awk -v a=$PARAM9 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM9=` awk -v a=$PARAM9 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 11 ` for x in $LL ; do PARAM10=` awk -v a=$PARAM10 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM10=0 # ` awk -v a=$PARAM10 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 12 ` for x in $LL ; do PARAM11=` awk -v a=$PARAM11 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM11=0 # ` awk -v a=$PARAM11 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` head -n 4 $FLIST | grep Paramet | cut -d ' ' -f 13 ` for x in $LL ; do PARAM12=` awk -v a=$PARAM12 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM12=0 # ` awk -v a=$PARAM12 -v b=$NFILES 'BEGIN{print (a / b)}' ` # origin params below LL=` cat $FLIST | grep FixedParamet | cut -d ' ' -f 2 ` for x in $LL ; do PARAM13=` awk -v a=$PARAM13 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM13=` awk -v a=$PARAM13 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` cat $FLIST | grep FixedParamet | cut -d ' ' -f 3 ` for x in $LL ; do PARAM14=` awk -v a=$PARAM14 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM14=` awk -v a=$PARAM14 -v b=$NFILES 'BEGIN{print (a / b)}' ` LL=` cat $FLIST | grep FixedParamet | cut -d ' ' -f 4 ` for x in $LL ; do PARAM15=` awk -v a=$PARAM15 -v b=$x 'BEGIN{print (a + b)}' ` ; done PARAM15=` awk -v a=$PARAM15 -v b=$NFILES 'BEGIN{print (a / b)}' ` echo "# Insight Transform File V1.0 " > $OUTNM echo "# Transform 0 " >> $OUTNM echo "Transform: MatrixOffsetTransformBase_double_3_3 " >> $OUTNM echo "Parameters: $PARAM1 $PARAM2 $PARAM3 $PARAM4 $PARAM5 $PARAM6 $PARAM7 $PARAM8 $PARAM9 $PARAM10 $PARAM11 $PARAM12 " >> $OUTNM echo "FixedParameters: $PARAM13 $PARAM14 $PARAM15 " >> $OUTNM } function jobfnamepadding { files=`ls job*.sh` BASENAME1=`echo $files[1] | cut -d 'b' -f 1` for file in ${files} do if [ "${#file}" -eq "9" ] then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_000${BASENAME2}" elif [ "${#file}" -eq "10" ] then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_00${BASENAME2}" elif [ "${#file}" -eq "11" ] then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_0${BASENAME2}" fi done } cleanup() { echo "\n*** Performing cleanup, please wait ***\n" runningANTSpids=$( ps --ppid $$ -o pid= ) for thePID in $runningANTSpids do echo "killing: ${thePID}" kill ${thePID} done return $? } control_c() # run if user hits control-c { echo -en "\n*** User pressed CTRL + C ***\n" cleanup if [ $DOQSUB -eq 1 ] ; then qdel $jobIDs elif [ $DOQSUB -eq 5 ]; then scancel $jobIDs fi exit $? echo -en "\n*** Script cancelled by user ***\n" } #initializing variables with global scope time_start=`date +%s` currentdir=`pwd` nargs=$# MAXITERATIONS=30x90x20 LABELIMAGE=0 # initialize optional parameter METRICTYPE=CC # initialize optional parameter TRANSFORMATIONTYPE="GR" # initialize optional parameter if [[ $dim == 4 ]] ; then # we use a more constrained regularization for 4D mapping b/c we expect deformations to be relatively small and local TRANSFORMATIONTYPE="GR_Constrained" fi N4CORRECT=1 # initialize optional parameter DEFQUEUE=nopreempt DOQSUB=1 # By default, buildtemplateparallel tries to do things in parallel GRADIENTSTEP=0.25 # Gradient step size, smaller in magnitude means more smaller (more cautious) steps ITERATIONLIMIT=4 CORES=2 TDIM=0 RIGID=0 RIGIDTYPE=" --do-rigid" # set to an empty string to use affine initialization range=0 REGTEMPLATE=target XGRIDOPTS="" SCRIPTPREPEND="" # System specific queue options, eg "-q name" to submit to a specific queue # It can be set to an empty string if you do not need any special cluster options QSUBOPTS="" # EDIT THIS OUTPUTNAME=antsBTP ##Getting system info from linux can be done with these variables. # RAM=`cat /proc/meminfo | sed -n -e '/MemTotal/p' | awk '{ printf "%s %s\n", $2, $3 ; }' | cut -d " " -f 1` # RAMfree=`cat /proc/meminfo | sed -n -e '/MemFree/p' | awk '{ printf "%s %s\n", $2, $3 ; }' | cut -d " " -f 1` # cpu_free_ram=$((${RAMfree}/${cpu_count})) if [ ${OSTYPE:0:6} == 'darwin' ] then cpu_count=`sysctl -n hw.physicalcpu` else cpu_count=`cat /proc/cpuinfo | grep processor | wc -l` fi # Provide output for Help if [ "$1" == "-h" ] then Help >&2 fi # reading command line arguments while getopts "c:q:d:g:i:j:h:m:n:o:p:s:r:t:x:z:" OPT do case $OPT in h) #help echo "$USAGE" exit 0 ;; c) #use SGE cluster DOQSUB=$OPTARG if [[ ${#DOQSUB} -gt 2 ]] ; then echo " DOQSUB must be an integer value (0=serial, 1=SGE qsub, 2=try pexec, 3=XGrid, 4=PBS qsub, 5=SLURM) you passed -c $DOQSUB " exit 1 fi ;; q) #override default qsub queue DEFQUEUE=$OPTARG ;; d) #dimensions DIM=$OPTARG if [[ ${DIM} -eq 4 ]] ; then DIM=3 TDIM=4 fi ;; g) #gradient stepsize (default = 0.25) GRADIENTSTEP=$OPTARG ;; i) #iteration limit (default = 3) ITERATIONLIMIT=$OPTARG ;; j) #number of cpu cores to use (default = 2) CORES=$OPTARG ;; m) #max iterations other than default MAXITERATIONS=$OPTARG ;; n) #apply bias field correction N4CORRECT=$OPTARG ;; o) #output name prefix OUTPUTNAME=$OPTARG TEMPLATENAME=${OUTPUTNAME}template TEMPLATE=${TEMPLATENAME}.nii.gz ;; p) #Script prepend SCRIPTPREPEND=$OPTARG ;; s) #similarity model METRICTYPE=$OPTARG ;; r) #start with rigid-body registration RIGID=$OPTARG ;; t) #transformation model TRANSFORMATIONTYPE=$OPTARG ;; x) #initialization template XGRIDOPTS=$XGRIDOPTS ;; z) #initialization template REGTEMPLATE=$OPTARG ;; \?) # getopts issues an error message echo "$USAGE" >&2 exit 1 ;; esac done # Provide different output for Usage and Help if [ ${TDIM} -eq 4 ] && [ $nargs -lt 5 ] then Usage >&2 elif [ ${TDIM} -eq 4 ] && [ $nargs -eq 5 ] then echo "" # This option is required to run 4D template creation on SGE with a minimal command line elif [ $nargs -lt 6 ] then Usage >&2 fi if [[ $DOQSUB -eq 1 || $DOQSUB -eq 4 ]] ; then qq=`which qsub` if [ ${#qq} -lt 1 ] ; then echo do you have qsub? if not, then choose another c option ... if so, then check where the qsub alias points ... exit fi fi if [[ $DOQSUB -eq 5 ]]; then qq=`which sbatch` if [[ ${#qq} -lt 1 ]]; then echo "do you have sbatch? if not, then choose another c option ... if so, then check where the sbatch alias points ..." exit fi fi # Creating the file list of images to make a template from. # Shiftsize is calculated because a variable amount of arguments can be used on the command line. # The shiftsize variable will give the correct number of arguments to skip. Issuing shift $shiftsize will # result in skipping that number of arguments on the command line, so that only the input images remain. shiftsize=$(($OPTIND - 1)) shift $shiftsize # The invocation of $* will now read all remaining arguments into the variable IMAGESETVARIABLE IMAGESETVARIABLE=$* NINFILES=$(($nargs - $shiftsize)) # FSL not needed anymore, all dependent on ImageMath # #test if FSL is available in case of 4D, exit if not # if [ ${TDIM} -eq 4 ] && [ ${#FSLDIR} -le 0 ] # then # setFSLPath >&2 # fi if [ ${NINFILES} -eq 0 ] then echo "Please provide at least 2 filenames for the template." echo "Use `basename $0` -h for help" exit 1 elif [[ ${NINFILES} -eq 1 ]] then range=`${ANTSPATH}/ImageMath $TDIM abs nvols ${IMAGESETVARIABLE} | tail -1 | cut -d "," -f 4 | cut -d " " -f 2 | cut -d "]" -f 1 ` if [ ${range} -eq 1 ] && [ ${TDIM} -ne 4 ] then echo "Please provide at least 2 filenames for the template." echo "Use `basename $0` -h for help" exit 1 elif [ ${range} -gt 1 ] && [ ${TDIM} -ne 4 ] then echo "This is a multivolume file. Use -d 4" echo "Use `basename $0` -h for help" exit 1 elif [ ${range} -gt 1 ] && [ ${TDIM} -eq 4 ] then echo echo "--------------------------------------------------------------------------------------" echo " Creating template of 4D input. " echo "--------------------------------------------------------------------------------------" #splitting volume #setting up working dirs tmpdir=${currentdir}/tmp_${RANDOM}_${RANDOM}_${RANDOM}_$$ (umask 077 && mkdir ${tmpdir}) || { echo "Could not create temporary directory! Exiting." 1>&2 exit 1 } mkdir ${tmpdir}/selection #split the 4D file into 3D elements cp ${IMAGESETVARIABLE} ${tmpdir}/ cd ${tmpdir}/ # ${ANTSPATH}/ImageMath $TDIM vol0.nii.gz TimeSeriesSubset ${IMAGESETVARIABLE} ${range} # rm -f ${IMAGESETVARIABLE} # selecting 16 volumes randomly from the timeseries for averaging, placing them in tmp/selection folder. # the script will automatically divide timeseries into $total_volumes/16 bins from wich to take the random volumes; # if there are more than 32 volumes in the time-series (in case they are smaller nfmribins=2 let if [ ${range} -gt 31 ] ; then BINSIZE=$((${range} / ${nfmribins})) j=1 # initialize counter j for ((i = 0; i < ${nfmribins} ; i++)) do FLOOR=$((${i} * ${BINSIZE})) BINrange=$((${j} * ${BINSIZE})) # Retrieve random number between two limits. number=0 #initialize while [ "$number" -le $FLOOR ] do number=$RANDOM if [ $i -lt 15 ] then let "number %= $BINrange" # Scales $number down within $range. elif [ $i -eq 15 ] then let "number %= $range" # Scales $number down within $range. fi done #debug only echo echo "Random number between $FLOOR and $BINrange --- $number" # echo "Random number between $FLOOR and $range --- $number" if [ ${number} -lt 10 ] then ${ANTSPATH}/ImageMath $TDIM selection/vol000${number}.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol000${number}.nii.gz selection/ elif [ ${number} -ge 10 ] && [ ${number} -lt 100 ] then ${ANTSPATH}/ImageMath $TDIM selection/vol00${number}.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol00${number}.nii.gz selection/ elif [ ${number} -ge 100 ] && [ ${number} -lt 1000 ] then ${ANTSPATH}/ImageMath $TDIM selection/vol0${number}.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol0${number}.nii.gz selection/ fi let j++ done elif [ ${range} -gt ${nfmribins} ] && [ ${range} -lt 32 ] then for ((i = 0; i < ${nfmribins} ; i++)) do number=$RANDOM let "number %= $range" if [ ${number} -lt 10 ] then ${ANTSPATH}/ImageMath $TDIM selection/vol0.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol000${number}.nii.gz selection/ elif [ ${number} -ge 10 ] && [ ${number} -lt 100 ] then ${ANTSPATH}/ImageMath $TDIM selection/vol0.nii.gz ExtractSlice ${IMAGESETVARIABLE} ${number} # cp vol00${number}.nii.gz selection/ fi done elif [ ${range} -le ${nfmribins} ] then ${ANTSPATH}/ImageMath selection/$TDIM vol0.nii.gz TimeSeriesSubset ${IMAGESETVARIABLE} ${range} # cp *.nii.gz selection/ fi # set filelist variable rm -f ${IMAGESETVARIABLE} cd selection/ IMAGESETVARIABLE=`ls *.nii.gz` fi fi # exit # check for an initial template image and perform rigid body registration if requested if [ ! -s $REGTEMPLATE ] then echo echo "--------------------------------------------------------------------------------------" echo " No initial template exists. Creating a population average image from the inputs." echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/AverageImages $DIM $TEMPLATE 1 $IMAGESETVARIABLE else echo echo "--------------------------------------------------------------------------------------" echo " Initial template found. This will be used for guiding the registration. use : $REGTEMPLATE and $TEMPLATE " echo "--------------------------------------------------------------------------------------" # now move the initial registration template to OUTPUTNAME, otherwise this input gets overwritten. cp ${REGTEMPLATE} ${TEMPLATE} fi if [ ! -s $TEMPLATE ] ; then echo Your template : $TEMPLATE was not created. This indicates trouble! You may want to check correctness of your input parameters. exiting. exit fi # remove old job bash scripts rm -f job*.sh if [ "$RIGID" -eq 1 ] ; then count=0 jobIDs="" RIGID_IMAGESET="" for IMG in $IMAGESETVARIABLE do RIGID_IMAGESET="$RIGID_IMAGESET rigid_${IMG}" BASENAME=` echo ${IMG} | cut -d '.' -f 1 ` exe=" ${ANTSPATH}/ANTS $DIM -m MI[${TEMPLATE},${IMG},1,32] -o rigid_${IMG} -i 0 --use-Histogram-Matching --number-of-affine-iterations 10000x10000x10000x10000x10000 $RIGIDTYPE" exe2="${ANTSPATH}/WarpImageMultiTransform $DIM ${IMG} rigid_${IMG} rigid_${BASENAME}Affine${afftype} -R ${TEMPLATE}" pexe=" $exe >> job_${count}_metriclog.txt " qscript="job_${count}_qsub.sh" rm -f $qscript if [[ $DOQSUB -eq 5 ]]; then # SLURM job scripts must start with a shebang echo '#!/bin/sh' > $qscript fi echo "$SCRIPTPREPEND" >> $qscript echo "$exe" >> $qscript echo "$exe2" >> $qscript if [ $DOQSUB -eq 1 ] ; then id=`qsub -cwd -S /bin/bash -N antsBuildTemplate_rigid -v ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1,LD_LIBRARY_PATH=$LD_LIBRARY_PATH,ANTSPATH=$ANTSPATH $QSUBOPTS $qscript | awk '{print $3}'` jobIDs="$jobIDs $id" sleep 0.5 elif [ $DOQSUB -eq 4 ]; then echo "cp -R /jobtmp/pbstmp.\$PBS_JOBID/* ${currentdir}" >> $qscript; id=`qsub -N antsrigid -v ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1,LD_LIBRARY_PATH=$LD_LIBRARY_PATH,ANTSPATH=$ANTSPATH $QSUBOPTS -q $DEFQUEUE -l nodes=1:ppn=1 -l walltime=4:00:00 $qscript | awk '{print $1}'` jobIDs="$jobIDs $id" sleep 0.5 elif [ $DOQSUB -eq 2 ] ; then # Send pexe and exe2 to same job file so that they execute in series echo $pexe >> job${count}_r.sh echo $exe2 >> job${count}_r.sh elif [ $DOQSUB -eq 3 ] ; then id=`xgrid $XGRIDOPTS -job submit /bin/bash $qscript | awk '{sub(/;/,"");print $3}' | tr '\n' ' ' | sed 's: *: :g'` #echo "xgrid $XGRIDOPTS -job submit /bin/bash $qscript" jobIDs="$jobIDs $id" elif [[ $DOQSUB -eq 5 ]]; then id=`sbatch --job-name=antsrigid --export=ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1,LD_LIBRARY_PATH=$LD_LIBRARY_PATH,ANTSPATH=$ANTSPATH $QSUBOPTS --nodes=1 --cpus-per-task=1 --time=4:00:00 $qscript | rev | cut -f1 -d\ | rev` jobIDs="$jobIDs $id" sleep 0.5 elif [ $DOQSUB -eq 0 ] ; then # execute jobs in series $exe $exe2 fi ((count++)) done if [ $DOQSUB -eq 1 ]; then # Run jobs on SGE and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on SGE cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForSGEQJobs.pl 1 60 $jobIDs # Returns 1 if there are errors if [ ! $? -eq 0 ]; then echo "qsub submission failed - jobs went into error state" exit 1; fi fi if [ $DOQSUB -eq 4 ]; then # Run jobs on PBS and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on PBS cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForPBSQJobs.pl 1 60 $jobIDs # Returns 1 if there are errors if [ ! $? -eq 0 ]; then echo "qsub submission failed - jobs went into error state" exit 1; fi fi # Run jobs on localhost and wait to finish if [ $DOQSUB -eq 2 ]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on max ${CORES} cpucores. " echo " Progress can be viewed in job*_metriclog.txt" echo "--------------------------------------------------------------------------------------" jobfnamepadding #adds leading zeros to the jobnames, so they are carried out chronologically chmod +x job*.sh $PEXEC -j ${CORES} "sh" job*.sh fi if [ $DOQSUB -eq 3 ]; then # Run jobs on XGrid and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on XGrid cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForXGridJobs.pl -xgridflags "$XGRIDOPTS" -verbose -delay 30 $jobIDs # Returns 1 if there are errors if [ ! $? -eq 0 ]; then echo "XGrid submission failed - jobs went into error state" exit 1; fi fi if [ $DOQSUB -eq 5 ]; then # Run jobs on SLURM and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS rigid registration on SLURM cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. Rigid registration is quick, so poll queue every 60 seconds ${ANTSPATH}/waitForSlurmJobs.pl 1 60 $jobIDs # Returns 1 if there are errors if [ ! $? -eq 0 ]; then echo "SLURM submission failed - jobs went into error state" exit 1; fi fi # Update template ${ANTSPATH}/AverageImages $DIM $TEMPLATE 1 $RIGID_IMAGESET # cleanup and save output in seperate folder mkdir rigid mv *.cfg rigid*.nii.gz *Affine${afftype} rigid/ # backup logs if [ $DOQSUB -eq 1 ]; then mv antsBuildTemplate_rigid* rigid/ # Remove qsub scripts rm -f job_${count}_qsub.sh elif [ $DOQSUB -eq 4 ]; then mv antsrigid* rigid/ # Remove qsub scripts rm -f job_${count}_qsub.sh elif [ $DOQSUB -eq 2 ]; then mv job*.txt rigid/ elif [ $DOQSUB -eq 3 ]; then rm -f job_*_qsub.sh elif [[ $DOQSUB -eq 5 ]]; then mv slurm-*.out rigid/ mv job*.txt rigid/ # Remove qsub scripts rm -f ${outdir}/job_${count}_qsub.sh fi fi # endif RIGID # Begin Main Loop ITERATLEVEL=(` echo $MAXITERATIONS | tr 'x' ' ' `) NUMLEVELS=${#ITERATLEVEL[@]} # debugging only #echo $ITERATLEVEL #echo $NUMLEVELS #echo ${ITERATIONLIMIT} echo echo "--------------------------------------------------------------------------------------" echo " Start to build template: ${TEMPLATE}" echo "--------------------------------------------------------------------------------------" reportMappingParameters i=0 while [ $i -lt ${ITERATIONLIMIT} ] do itdisplay=$((i+1)) rm -f ${OUTPUTNAME}*Warp*.nii* rm -f job*.sh # Used to save time by only running coarse registration for the first couple of iterations # But with decent initialization, this is probably not worthwhile. # If you uncomment this, replace MAXITERATIONS with ITERATIONS in the call to ants below # # # For the first couple of iterations, use high-level registration only # # eg if MAXITERATIONS=30x90x20, then for iteration 0, do 30x0x0 # # for iteration 1 do 30x90x0, then do 30x90x20 on subsequent iterations # if [ $i -gt $((NUMLEVELS - 1)) ] # then # ITERATIONS=$MAXITERATIONS # else # # ITERATIONS=${ITERATLEVEL[0]} # # for (( n = 1 ; n < ${NUMLEVELS}; n++ )) # do # ITERATIONS=${ITERATIONS}x$((${ITERATLEVEL[n]} * $((n <= i)) )) # done # fi # Job IDs of jobs submitted to queue in loop below jobIDs="" # Reinitialize count to 0 count=0 # Submit registration of each input to volume template to SGE or run locally. for IMG in $IMAGESETVARIABLE do # 1 determine working dir dir=`pwd` # 2 determine new filename POO=${OUTPUTNAME}${IMG} # 3 Make variable OUTFILENAME and remove anything behind . ; for example .nii.gz.gz OUTFN=${POO%.*.*} # 4 Test if outputfilename has only a single extention and remove that if [ ${#OUTFN} -eq ${#POO} ] then OUTFN=${OUTPUTNAME}${IMG%.*} fi # 5 prepare registration command exe="${ANTSSCRIPTNAME} -d ${DIM} -r ${dir}/${TEMPLATE} -i ${dir}/${IMG} -o ${dir}/${OUTFN} -m ${MAXITERATIONS} -n ${N4CORRECT} -s ${METRICTYPE} -t ${TRANSFORMATIONTYPE} -f 1 " pexe=" $exe >> job_${count}_${i}_metriclog.txt " # 6 submit to SGE (DOQSUB=1), PBS (DOQSUB=4), PEXEC (DOQSUB=2), XGrid (DOQSUB=3) or else run locally (DOQSUB=0) if [ $DOQSUB -eq 1 ]; then id=`qsub -cwd -N antsBuildTemplate_deformable_${i} -S /bin/bash -v ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1,LD_LIBRARY_PATH=$LD_LIBRARY_PATH,ANTSPATH=$ANTSPATH $QSUBOPTS $exe | awk '{print $3}'` jobIDs="$jobIDs $id" sleep 0.5 elif [ $DOQSUB -eq 4 ]; then qscript="job_${count}_${i}.sh" echo "$SCRIPTPREPEND" > $qscript echo "$exe" >> $qscript echo "cp -R /jobtmp/pbstmp.\$PBS_JOBID/* ${currentdir}" >> $qscript; id=`qsub -N antsdef${i} -v ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1,LD_LIBRARY_PATH=$LD_LIBRARY_PATH,ANTSPATH=$ANTSPATH -q $DEFQUEUE -l nodes=1:ppn=1 -l walltime=4:00:00 $QSUBOPTS $qscript | awk '{print $1}'` jobIDs="$jobIDs $id" sleep 0.5 elif [ $DOQSUB -eq 2 ] ; then echo $pexe echo $pexe >> job${count}_${i}.sh elif [ $DOQSUB -eq 3 ] ; then qscript="job_${count}_${i}.sh" #exe="${ANTSSCRIPTNAME} -d ${DIM} -r ./${TEMPLATE} -i ./${IMG} -o ./${OUTFN} -m ${MAXITERATIONS} -n ${N4CORRECT} -s ${METRICTYPE} -t ${TRANSFORMATIONTYPE} " echo "$SCRIPTPREPEND" > $qscript echo "$exe" >> $qscript id=`xgrid $XGRIDOPTS -job submit /bin/bash $qscript | awk '{sub(/;/,"");print $3}' | tr '\n' ' ' | sed 's: *: :g'` jobIDs="$jobIDs $id" qscript="job_${count}_${i}.sh" elif [[ $DOQSUB -eq 5 ]] ; then qscript="job_${count}_${i}.sh" echo '#!/bin/sh' > $qscript echo -e "$SCRIPTPREPEND" >> $qscript echo -e "$exe" >> $qscript id=`sbatch --mem-per-cpu=32768M --job-name=antsdef${i} --export=ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1,LD_LIBRARY_PATH=$LD_LIBRARY_PATH,ANTSPATH=$ANTSPATH --nodes=1 --cpus-per-task=1 --time=4:00:00 $QSUBOPTS $qscript | rev | cut -f1 -d\ | rev` jobIDs="$jobIDs $id" sleep 0.5 elif [ $DOQSUB -eq 0 ] ; then bash $exe fi # counter updated, but not directly used in this loop count=`expr $count + 1`; # echo " submitting job number $count " # for debugging only done # SGE wait for script to finish if [ $DOQSUB -eq 1 ]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on SGE cluster. Iteration: $itdisplay of $ITERATIONLIMIT" echo "--------------------------------------------------------------------------------------" # now wait for the stuff to finish - this will take a while so poll queue every 10 mins ${ANTSPATH}/waitForSGEQJobs.pl 1 600 $jobIDs if [ ! $? -eq 0 ]; then echo "qsub submission failed - jobs went into error state" exit 1; fi elif [ $DOQSUB -eq 4 ]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on PBS cluster. Iteration: $itdisplay of $ITERATIONLIMIT" echo "--------------------------------------------------------------------------------------" # now wait for the stuff to finish - this will take a while so poll queue every 10 mins ${ANTSPATH}/waitForPBSQJobs.pl 1 600 $jobIDs if [ ! $? -eq 0 ]; then echo "qsub submission failed - jobs went into error state" exit 1; fi fi # Run jobs on localhost and wait to finish if [ $DOQSUB -eq 2 ]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on max ${CORES} cpucores. Iteration: $itdisplay of $ITERATIONLIMIT" echo " Progress can be viewed in job*_${i}_metriclog.txt" echo "--------------------------------------------------------------------------------------" jobfnamepadding #adds leading zeros to the jobnames, so they are carried out chronologically chmod +x job*.sh $PEXEC -j ${CORES} sh job*.sh fi if [ $DOQSUB -eq 3 ]; then # Run jobs on XGrid and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on XGrid cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the jobs to finish. This is slow, so poll less often ${ANTSPATH}/waitForXGridJobs.pl -xgridflags "$XGRIDOPTS" -verbose -delay 300 $jobIDs # Returns 1 if there are errors if [ ! $? -eq 0 ]; then echo "XGrid submission failed - jobs went into error state" exit 1; fi fi if [[ $DOQSUB -eq 5 ]]; then # Run jobs on SLURM and wait to finish echo echo "--------------------------------------------------------------------------------------" echo " Starting ANTS registration on SLURM cluster. Submitted $count jobs " echo "--------------------------------------------------------------------------------------" # now wait for the stuff to finish - this will take a while so poll queue every 10 mins ${ANTSPATH}/waitForSlurmJobs.pl 1 600 $jobIDs if [[ ! $? -eq 0 ]]; then echo "SLURM submission failed - jobs went into error state" exit 1; fi fi shapeupdatetotemplate ${DIM} ${TEMPLATE} ${TEMPLATENAME} ${OUTPUTNAME} ${GRADIENTSTEP} echo echo "--------------------------------------------------------------------------------------" echo " Backing up results from iteration $itdisplay" echo "--------------------------------------------------------------------------------------" mkdir ${TRANSFORMATIONTYPE}_iteration_${i} cp ${TEMPLATENAME}warp*log.txt *.cfg *${OUTPUTNAME}*.nii.gz ${TRANSFORMATIONTYPE}_iteration_${i}/ # backup logs if [ $DOQSUB -eq 1 ]; then mv antsBuildTemplate_deformable_* ${TRANSFORMATIONTYPE}_iteration_${i} elif [ $DOQSUB -eq 4 ]; then mv antsdef* ${TRANSFORMATIONTYPE}_iteration_${i} elif [ $DOQSUB -eq 2 ]; then mv job*.txt ${TRANSFORMATIONTYPE}_iteration_${i} elif [ $DOQSUB -eq 3 ]; then rm -f job_*.sh elif [[ $DOQSUB -eq 5 ]]; then mv slurm-*.out ${TRANSFORMATIONTYPE}_iteration_${i} mv job*.txt ${TRANSFORMATIONTYPE}_iteration_${i} fi ((i++)) done # end main loop rm -f job*.sh #cleanup of 4D files if [ "${range}" -gt 1 ] && [ "${TDIM}" -eq 4 ] then mv ${tmpdir}/selection/${TEMPLATE} ${currentdir}/ cd ${currentdir} rm -rf ${tmpdir}/ fi time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo echo "--------------------------------------------------------------------------------------" echo " Done creating: ${TEMPLATE}" echo " Script executed in $time_elapsed seconds" echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" echo "--------------------------------------------------------------------------------------" exit 0 ants-2.2.0/Scripts/cbf_pasl_robust_batch.R000077500000000000000000000011751311104306400205420ustar00rootroot00000000000000#!/usr/bin/env Rscript library( "ANTsR" ) library("extremevalues" ) args<-commandArgs(TRUE) aslImg <- args[1] m0Img <- args[2] outname <- args[3] #init <- cbf_pasl_robust( asl_img, m0_img ) #cbf <- cbf_pasl_quantify( init$delta, init$m0 ) pasl <- antsImageRead( aslImg, 4 ) m0 <- antsImageRead( m0Img, 3 ) pasl.processing <- aslPerfusion(pasl, moreaccurate=FALSE, m0=m0, interpolation="sinc" ) pasl.parameters <- list( sequence="pasl", m0=m0 ) cbf <- quantifyCBF( pasl.processing$perfusion, pasl.processing$mask, pasl.parameters ) meancbf <- cbf$kmeancbf cbfOverTime <- cbf$timecbf print(outname) antsImageWrite( meancbf, outname ) ants-2.2.0/Scripts/cbf_pcasl_robust_batch.R000077500000000000000000000007501311104306400207030ustar00rootroot00000000000000#!/usr/bin/env Rscript library( "ANTsR" ) library("extremevalues" ) args<-commandArgs(TRUE) aslImg <- args[1] outname <- args[2] pasl <- antsImageRead( aslImg, 4 ) pcasl.processing <- aslPerfusion(pasl, moreaccurate=FALSE) pcasl.parameters <- list( sequence="pcasl", m0=pcasl.processing$m0 ) cbf <- quantifyCBF( pcasl.processing$perfusion, pcasl.processing$mask, pcasl.parameters ) meancbf <- cbf$kmeancbf cbfOverTime <- cbf$timecbf print(outname) antsImageWrite( meancbf, outname ) ants-2.2.0/Scripts/directlabels.sh000077500000000000000000000327641311104306400171100ustar00rootroot00000000000000#!/bin/bash # Usage: $0 imageDimension segmentationImage grayMatterProbabilityImage # whiteMatterProbabilityImage outputImage VERSION="0.0" # trap keyboard interrupt (control-c) trap control_c SIGINT # Uncomment the line below in case you have not set the ANTSPATH variable in your environment. if [ ${#ANTSPATH} -le 3 ] ; then echo we guess at your ants path export ANTSPATH=${ANTSPATH:="$HOME/bin/ants/"} # EDIT THIS fi if [ ! -s ${ANTSPATH}/ANTS ] ; then echo we cant find the ANTS program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi function Usage { cat < -o OutputImage Example Case: echo " bash $0 -d 3 -s brainSegmentation.nii.gz -l corticalLabels.nii.gz -o thickness.nii.gz" Compulsory arguments (minimal command line requires SGE cluster, otherwise use -c & -j options): -d: ImageDimension: 2 or 3 (for 2 or 3 dimensional single image) -s: InputSegmentationImage: label image which has label = 2 for gray matter regions and label = 3 for white matter regions. -l: CorticalLabelImage: subdivided cortical label image. -o: OutputImage: thickness output image file. Optional arguments: -c: Control for parallel computation (default=0) -- 0 == run serially, 1 == SGE qsub, 2 == use PEXEC (localhost) -j: Number of cpu cores to use (default: 2; -- requires "-c 2") -g: Gray matter probability image -w: White matter probability image -t: Thickness prior estimate (could be a .csv file, e.g. for label 3 with a thickness of 4.5 -> 3,4.5) -m: Smoothing sigma -r: Gradient step size (default=0.025) -- smaller in magnitude results in more cautious steps -i: number of iterations (default=50) -------------------------------------------------------------------------------------- ANTS was created by: -------------------------------------------------------------------------------------- Brian B. Avants, Nick Tustison and Gang Song Penn Image Computing And Science Laboratory University of Pennsylvania USAGE exit 1 } echoParameters() { cat < ${LABELS[@]}) output image = ${OUTPUT_IMAGE} wm probability image = ${WMPROB_IMAGE} gm probability image = ${GMPROB_IMAGE} smoothing sigma = ${SMOOTHING_SIGMA} thicknes prior estimate = ${THICKNESS_PRIOR_ESTIMATE} do qsub = ${DOQSUB} number of cores = ${CORES} iteration limit = ${ITERATION_LIMIT} gradient step = ${GRADIENT_STEP} PARAMETERS } getLabelsAndBoundingBoxes() { OUTPUT=(`${ANTSPATH}/LabelGeometryMeasures $DIMENSION $LABEL_IMAGE`); ## Get labels begin=10 increment=`expr 5 + $DIMENSION + $DIMENSION + $DIMENSION + $DIMENSION` count=0; for (( i=begin; i<=${#OUTPUT[@]}; i+=$increment )); do LABELS[$count]=${OUTPUT[$i]} count=`expr $count + 1` done ## Get bounding boxes begin=`expr 15 + $DIMENSION + $DIMENSION` increment=`expr 5 + $DIMENSION + $DIMENSION + $DIMENSION + $DIMENSION` count=0; for (( i=begin; i<=${#OUTPUT[@]}; i+=$increment )); do for (( j=0; j<`expr $DIMENSION + $DIMENSION`; j++ )); do BOUNDING_BOXES[$count]=${BOUNDING_BOXES[$count]}${OUTPUT[`expr $i+$j`]} done count=`expr $count + 1`; done ## read thickness .csv file if it exists extension=`echo ${THICKNESS_PRIOR_ESTIMATE#*.}` if [[ $extension = 'csv' ]] || [[ $extension = 'txt' ]] ; then echo ${LABELS[@]} while read line do bar=(`echo $line | tr ',' ' '`) for (( i=0; i<${#LABELS[@]}; i++ )); do if [[ ${bar[0]} -eq ${LABELS[$i]} ]]; then LABEL_THICKNESSES[$i]=${bar[1]} fi done done < $THICKNESS_PRIOR_ESTIMATE else for (( count = 0; count<${#LABELS[@]}; count++ )); do LABEL_THICKNESSES[$count]=$THICKNESS_PRIOR_ESTIMATE done fi } writeSubimages() { if [ ! -d "$TMPDIR" ]; then mkdir $TMPDIR chmod ugo+rw $TMPDIR fi OUTFN=${POO%.*.*} for (( i=0; i<${#BOUNDING_BOXES[@]}; i++ )); do bbox=$(echo ${BOUNDING_BOXES[$i]}|sed 's/,/ /g') bbox=$(echo $bbox|sed 's/\[//') bbox=$(echo $bbox|sed 's/\]//') elements=( $bbox ) minIndex="" maxIndex="" count=0 for (( j=0; j<${#elements[@]}; j+=2 )); do minValue=${elements[$j]} jp1=`expr $j + 1` maxValue=${elements[$jp1]} minIndex=${minIndex}x${minValue} maxIndex=${maxIndex}x${maxValue} count=`expr $count + 1` done minIndex=${minIndex:1}; maxIndex=${maxIndex:1}; grayMatterMask=${TMPDIR}/grayMatterMask.nii.gz whiteMatterMask=${TMPDIR}/whiteMatterMask.nii.gz gmWmMask=${TMPDIR}/gmWmMask.nii.gz # For each label, we perform the following steps: # 1. Threshold out everything but the label region # 2. Threshold out everything but the white matter # 3. Combine the result from 1) and 2) to have an image with only # the white matter and the current label (as the grey matter). # OUTPUT=(`${ANTSPATH}/ThresholdImage $DIMENSION $LABEL_IMAGE $grayMatterMask ${LABELS[$i]} ${LABELS[$i]} 1 0`) OUTPUT=(`${ANTSPATH}/ThresholdImage $DIMENSION $SEG_IMAGE $whiteMatterMask 3 3 3 0`) OUTPUT=(`${ANTSPATH}/ImageMath $DIMENSION $grayMatterMask m $grayMatterMask $SEG_IMAGE`) OUTPUT=(`${ANTSPATH}/ImageMath $DIMENSION $gmWmMask + $grayMatterMask $whiteMatterMask`) OUTPUT=(`${ANTSPATH}/ExtractRegionFromImage $DIMENSION $gmWmMask ${TMPDIR}seg_${LABELS[$i]}.nii.gz $minIndex $maxIndex`) OUTPUT=(`${ANTSPATH}/ImageMath $DIMENSION ${TMPDIR}seg_${LABELS[$i]}.nii.gz PadImage ${TMPDIR}seg_${LABELS[$i]}.nii.gz $PADDING`) if [ -f $WMPROB_IMAGE ]; then OUTPUT=(`${ANTSPATH}/ImageMath $DIMENSION $whiteMatterMask m $whiteMatterMask $WMPROB_IMAGE`) OUTPUT=(`${ANTSPATH}/ExtractRegionFromImage $DIMENSION $whiteMatterMask ${TMPDIR}wm_${LABELS[$i]}.nii.gz $minIndex $maxIndex`); OUTPUT=(`${ANTSPATH}/ImageMath $DIMENSION ${TMPDIR}wm_${LABELS[$i]}.nii.gz PadImage ${TMPDIR}wm_${LABELS[$i]}.nii.gz $PADDING`); fi if [ -f $GMPROB_IMAGE ]; then OUTPUT=(`${ANTSPATH}/ImageMath $DIMENSION $grayMatterMask m $grayMatterMask $GMPROB_IMAGE`) OUTPUT=(`${ANTSPATH}/ExtractRegionFromImage $DIMENSION $grayMatterMask ${TMPDIR}gm_${LABELS[$i]}.nii.gz $minIndex $maxIndex`); OUTPUT=(`${ANTSPATH}/ImageMath $DIMENSION ${TMPDIR}gm_${LABELS[$i]}.nii.gz PadImage ${TMPDIR}gm_${LABELS[$i]}.nii.gz $PADDING`); fi rm -rf $grayMatterMask rm -rf $whiteMatterMask rm -rf $gmWmMask done } function jobfnamepadding { files=`ls ${TMPDIR}job*.sh` BASENAME1=`echo $files[1] | cut -d 'b' -f 1` for file in ${files} do if [ "${#file}" -eq "9" ] then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_000${BASENAME2}" elif [ "${#file}" -eq "10" ] then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_00${BASENAME2}" elif [ "${#file}" -eq "11" ] then BASENAME2=`echo $file | cut -d 'b' -f 2 ` mv "$file" "${BASENAME1}b_0${BASENAME2}" fi done } function pasteImages { output=( `${ANTSPATH}/CreateImage $DIMENSION $SEG_IMAGE $OUTPUT_IMAGE 0` ); for (( i=0; i<${#BOUNDING_BOXES[@]}; i++ )); do bbox=$(echo ${BOUNDING_BOXES[$i]}|sed 's/,/ /g') bbox=$(echo $bbox|sed 's/\[//') bbox=$(echo $bbox|sed 's/\]//') elements=( $bbox ) minIndex="" count=0 for (( j=0; j<${#elements[@]}; j+=2 )); do minValue=${elements[$j]} minValue=`expr $minValue - $PADDING`; minIndex=${minIndex}x${minValue} count=`expr $count + 1` done minIndex=${minIndex:1}; output=(`${ANTSPATH}/PasteImageIntoImage $DIMENSION $OUTPUT_IMAGE $TMPDIR/direct_${LABELS[$i]}.nii.gz $OUTPUT_IMAGE $minIndex 0 2 -1`); done } cleanup() { rm -rf ${TMPDIR} } ################################################################################ # # Main routine # ################################################################################ time_start=`date +%s` CURRENTDIR=`pwd`/ TMPDIR=${CURRENTDIR}/tmp$RANDOM/ DIRECT=${ANTSPATH}/KellyKapowski DIMENSION=3 SEG_IMAGE="" GMPROB_IMAGE="" WMPROB_IMAGE="" OUTPUT_IMAGE="" LABEL_IMAGE="" LABELS=() LABEL_THICKNESSES=() BOUNDING_BOXES=() PADDING=5; GRADIENT_STEP=0.025 SMOOTHING_SIGMA=1.5 THICKNESS_PRIOR_ESTIMATE=8.0 ITERATION_LIMIT=50 DOQSUB=0 CORES=2 # System specific queue options, eg "-q name" to submit to a specific queue # It can be set to an empty string if you do not need any special cluster options QSUBOPTS="" # EDIT THIS PEXEC=${ANTSPATH}/ANTSpexec.sh SGE=${ANTSPATH}/waitForSGEQJobs.pl for FLE in $PEXEC $SGE do if [ ! -x $FLE ] ; then echo echo "--------------------------------------------------------------------------------------" echo " FILE $FLE DOES NOT EXIST -- OR -- IS NOT EXECUTABLE !!! $0 will terminate." echo "--------------------------------------------------------------------------------------" echo " if the file is not executable, please change its permissions. " exit 1 fi done if [[ $# -eq 0 ]] ; then Usage >&2 else while getopts "c:d:g:i:j:w:m:o:s:r:t:h:l:" OPT do case $OPT in h) #help Usage >&2 exit 0 ;; c) #use SGE cluster DOQSUB=$OPTARG if [[ ${#DOQSUB} -gt 2 ]] ; then echo " DOQSUB must be an integer value (0=serial, 1=SGE qsub, 2=try pexec ) you passed -c $DOQSUB " exit 1 fi ;; d) #dimensions DIMENSION=$OPTARG if [[ ${DIMENSION} -gt 3 || ${DIMENSION} -lt 2 ]] ; then echo " Error: ImageDimension must be 2 or 3 " exit 1 fi ;; r) #gradient stepsize (default = 0.025) GRADIENT_STEP=$OPTARG ;; i) #iteration limit (default = 100) ITERATION_LIMIT=$OPTARG ;; j) #number of cpu cores to use (default = 2) CORES=$OPTARG ;; o) #output name prefix OUTPUT_IMAGE=$OPTARG ;; s) #segmentation image SEG_IMAGE=$OPTARG ;; g) #gray matter probability image GMPROB_IMAGE=$OPTARG ;; w) #white matter probability image WMPROB_IMAGE=$OPTARG ;; l) #label image LABEL_IMAGE=$OPTARG ;; m) #smoothing sigma SMOOTHING_SIGMA=$OPTARG ;; t) #thickness prior estimate THICKNESS_PRIOR_ESTIMATE=$OPTARG ;; *) # getopts issues an error message Usage >&2 exit 1 ;; esac done fi # Get label information and subimages getLabelsAndBoundingBoxes echoParameters >&2 writeSubimages # Job IDs of jobs submitted to queue in loop below jobIDs="" # Reinitialize count to 0 count=0 # Submit registration of each input to volume template to SGE or run locally. for LABEL in ${LABELS[@]} do # prepare DiReCT command segLabelImage=${TMPDIR}seg_${LABEL}.nii.gz gmLabelImage=${TMPDIR}gm_${LABEL}.nii.gz wmLabelImage=${TMPDIR}wm_${LABEL}.nii.gz exe="${DIRECT} -d ${DIMENSION} -c [$ITERATION_LIMIT,0.000001,10] -t ${LABEL_THICKNESSES[$count]} -r $GRADIENT_STEP -m $SMOOTHING_SIGMA -s $segLabelImage -o ${TMPDIR}direct_${LABEL}.nii.gz" if [[ -f "$gmLabelImage" ]]; then exe=${exe}" -g $gmLabelImage" fi if [[ -f "$wmLabelImage" ]]; then exe=${exe}" -w $wmLabelImage" fi pexe=" $exe >> ${TMPDIR}job_label_${LABEL}_metriclog.txt " # 6 submit to SGE or else run locally if [ $DOQSUB -eq 1 ]; then id=`qsub -cwd -N DiReCT_${LABEL} -S /bin/bash -v ANTSPATH=$ANTSPATH $QSUBOPTS $exe | awk '{print $3}'` jobIDs="$jobIDs $id" sleep 0.5 elif [ $DOQSUB -eq 2 ] ; then echo $pexe echo $pexe >> ${TMPDIR}job_label_${LABEL}.sh elif [ $DOQSUB -eq 0 ] ; then echo " Performing DiReCT for label ${LABEL}." output=(`$exe`) fi # SGE wait for script to finish if [ $DOQSUB -eq 1 ]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting DiReCT on SGE cluster. Label: $LABEL of ${#LABELS[@]} total labels." echo "--------------------------------------------------------------------------------------" # now wait for the stuff to finish - this will take a while so poll queue every 5 mins $SGE 1 300 $jobIDs if [ ! $? -eq 0 ]; then echo "qsub submission failed - jobs went into error state" exit 1; fi fi count=`expr $count + 1` done # Run jobs on localhost and wait to finish if [ $DOQSUB -eq 2 ]; then echo echo "--------------------------------------------------------------------------------------" echo " Starting DiReCT on max ${CORES} cpucores." echo " Progress can be viewed in ${TMPDIR}job_label*_metriclog.txt" echo "--------------------------------------------------------------------------------------" jobfnamepadding #adds leading zeros to the jobnames, so they are carried out chronologically chmod +x ${TMPDIR}job*.sh $PEXEC -j ${CORES} sh ${TMPDIR}job*.sh fi pasteImages cleanup time_end=`date +%s` time_elapsed=$((time_end - time_start)) echo echo "--------------------------------------------------------------------------------------" echo " Done with DiReCT" echo " Script executed in $time_elapsed seconds" echo " $(( time_elapsed / 3600 ))h $(( time_elapsed %3600 / 60 ))m $(( time_elapsed % 60 ))s" echo "--------------------------------------------------------------------------------------" exit 0 ants-2.2.0/Scripts/geodesicinterpolation.sh000077500000000000000000000155271311104306400210430ustar00rootroot00000000000000#!/bin/bash NUMPARAMS=$# if [ $NUMPARAMS -lt 2 ] then echo " USAGE :: " echo " sh geodesicinterpolation.sh image1 image2 N-interpolation-points N-step-size Use-Affine-Initialization? Mask-Image Auxiliary-Template " echo " N-interpolation-points -- if you choose 10 points and step-size of 1 then you will get 8 interpolated/morphed images -- end-points are the original images " echo " if you choose 10 points and step-size of 2 then you will get 4 interpolated/morphed images -- end-points are the original images " echo " if you use affine initialization - sometimes it could cause unpleasantness, if the affine mapping is large -- sometimes it may be needed, though " echo " the mask image restricts matching to an ROI " echo " The Auxiliary-Template is another image through which the morph images (image1 and image2) are mapped -- this often helps, if the auxiliary template is an average face image " echo " Be sure to set the ANTSPATH path variable " echo " " echo " You may need to tune the ANTS parameters coded within this script for your application " exit fi # should we use the generative model to find landmarks then # drive individual mappings by the LMs? TEMPLATE=$1 TARGET=$2 NUMSTEPS=10 STEP=1 OUTNAME=ANTSMORPH if [ $NUMPARAMS -gt 2 ] then NUMSTEPS=$3 fi if [ $NUMPARAMS -gt 3 ] then STEP=$4 fi USEAFF=0 if [ $NUMPARAMS -gt 4 ] then USEAFF=$5 fi MASK=0 if [ $NUMPARAMS -gt 5 ] then MASK=$6 fi TEMPLATEB=0 if [ $NUMPARAMS -gt 6 ] then TEMPLATEB=$7 fi echo " Morphing in total $NUMSTEPS time-points by $STEP steps -- end-points are original images " echo " Use-Affine ? $USEAFF " echo " templateB $TEMPLATEB " for (( n = 0 ; n <= ${NUMSTEPS}; n=n+${STEP} )) do BLENDINGA=$(echo "scale=2; ${n}/${NUMSTEPS}" | bc ) BLENDINGB=$(echo "scale=2; 1-$BLENDINGA" | bc ) BLENDNAME=$(echo "scale=2; 100*$BLENDINGA" | bc ) echo " Blending values: $BLENDINGA and $BLENDINGB" done ITS=100x100x10 TRAN=SyN[0.5] REG=Gauss[3,1] RADIUS=6 if [ $NUMPARAMS -le 5 ] then if [ $USEAFF -eq 0 ] then ${ANTSPATH}/ANTS 2 -m PR[$TEMPLATE,$TARGET,1,${RADIUS}] -t $TRAN -r $REG -o $OUTNAME -i $ITS --number-of-affine-iterations 0 -x $MASK fi if [ $USEAFF -ne 0 ] then ${ANTSPATH}/ANTS 2 -m PR[$TEMPLATE,$TARGET,1,${RADIUS}] -t $TRAN -r $REG -o $OUTNAME -i $ITS -x $MASK #--MI-option 16x2000 fi ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}Warp.nii -R $TEMPLATE ${OUTNAME}Warp.nii ${OUTNAME}Affine.txt ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}InverseWarp.nii -R $TARGET -i ${OUTNAME}Affine.txt ${OUTNAME}InverseWarp.nii fi if [ $NUMPARAMS -gt 5 ] then if [ $USEAFF -eq 0 ] then #echo " Pseudo-Morphing " #echo " method 1 " ${ANTSPATH}/ANTS 2 -m PR[$TEMPLATEB,$TARGET,1,${RADIUS}] -t $TRAN -r $REG -o ${OUTNAME}B -i $ITS --number-of-affine-iterations 0 -x $MASK ${ANTSPATH}/ANTS 2 -m PR[$TEMPLATE,$TEMPLATEB,1,${RADIUS}] -t $TRAN -r $REG -o ${OUTNAME}A -i $ITS --number-of-affine-iterations 0 -x $MASK ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}AWarp.nii -R $TEMPLATE ${OUTNAME}AWarp.nii ${OUTNAME}AAffine.txt ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}AInverseWarp.nii -R $TEMPLATEB -i ${OUTNAME}AAffine.txt ${OUTNAME}AInverseWarp.nii ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}BWarp.nii -R $TEMPLATEB ${OUTNAME}BWarp.nii ${OUTNAME}BAffine.txt ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}BInverseWarp.nii -R $TARGET -i ${OUTNAME}BAffine.txt ${OUTNAME}BInverseWarp.nii ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}Warp.nii -R $TEMPLATE ${OUTNAME}AWarp.nii ${OUTNAME}BWarp.nii ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}InverseWarp.nii -R $TARGET ${OUTNAME}BInverseWarp.nii ${OUTNAME}AInverseWarp.nii # #echo " method 2 " #${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}Warp.nii -R $TEMPLATE ${OUTNAME}AWarp.nii ${OUTNAME}AAffine.txt ${OUTNAME}BWarp.nii ${OUTNAME}BAffine.txt #${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}InverseWarp.nii -R $TARGET -i ${OUTNAME}BAffine.txt ${OUTNAME}BInverseWarp.nii -i ${OUTNAME}AAffine.txt ${OUTNAME}AInverseWarp.nii # fi if [ $USEAFF -ne 0 ] then #echo " Pseudo-Morphing " #echo " method 1 " ${ANTSPATH}/ANTS 2 -m PR[$TEMPLATEB,$TARGET,1,${RADIUS}] -t $TRAN -r $REG -o ${OUTNAME}B -i $ITS -x $MASK ${ANTSPATH}/ANTS 2 -m PR[$TEMPLATE,$TEMPLATEB,1,${RADIUS}] -t $TRAN -r $REG -o ${OUTNAME}A -i $ITS -x $MASK ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}AWarp.nii -R $TEMPLATE ${OUTNAME}AWarp.nii ${OUTNAME}AAffine.txt ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}AInverseWarp.nii -R $TEMPLATEB -i ${OUTNAME}AAffine.txt ${OUTNAME}AInverseWarp.nii ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}BWarp.nii -R $TEMPLATEB ${OUTNAME}BWarp.nii ${OUTNAME}BAffine.txt ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}BInverseWarp.nii -R $TARGET -i ${OUTNAME}BAffine.txt ${OUTNAME}BInverseWarp.nii ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}Warp.nii -R $TEMPLATE ${OUTNAME}AWarp.nii ${OUTNAME}BWarp.nii ${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}InverseWarp.nii -R $TARGET ${OUTNAME}BInverseWarp.nii ${OUTNAME}AInverseWarp.nii # #echo " method 2 " #${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}Warp.nii -R $TEMPLATE ${OUTNAME}AWarp.nii ${OUTNAME}AAffine.txt ${OUTNAME}BWarp.nii ${OUTNAME}BAffine.txt #${ANTSPATH}/ComposeMultiTransform 2 ${OUTNAME}InverseWarp.nii -R $TARGET -i ${OUTNAME}BAffine.txt ${OUTNAME}BInverseWarp.nii -i ${OUTNAME}AAffine.txt ${OUTNAME}AInverseWarp.nii # fi fi for (( n = 0 ; n <= ${NUMSTEPS}; n=n+${STEP} )) do BLENDINGA=$(echo "scale=2; ${n}/${NUMSTEPS}" | bc ) BLENDINGB=$(echo "scale=2; 1-$BLENDINGA" | bc ) BLENDNAME=$(echo "scale=2; 100*$BLENDINGA" | bc ) echo " Blending values: $BLENDINGA and $BLENDINGB" BASEA=${TEMPLATE%.*} BASEB=${TARGET%.*} ${ANTSPATH}/MultiplyImages 2 ${OUTNAME}InverseWarpxvec.nii $BLENDINGA SM${OUTNAME}InverseWarpxvec.nii ${ANTSPATH}/MultiplyImages 2 ${OUTNAME}InverseWarpyvec.nii $BLENDINGA SM${OUTNAME}InverseWarpyvec.nii ${ANTSPATH}/MultiplyImages 2 ${OUTNAME}Warpxvec.nii $BLENDINGB SM${OUTNAME}Warpxvec.nii ${ANTSPATH}/MultiplyImages 2 ${OUTNAME}Warpyvec.nii $BLENDINGB SM${OUTNAME}Warpyvec.nii ${ANTSPATH}/WarpImageMultiTransform 2 $TARGET temp.nii SM${OUTNAME}Warp.nii -R $TEMPLATE ${ANTSPATH}/ImageMath 2 temp.nii Normalize temp.nii 1 ${ANTSPATH}/ImageMath 2 temp.nii m temp.nii 1. #$BLENDINGA ${ANTSPATH}/WarpImageMultiTransform 2 $TEMPLATE temp2.nii SM${OUTNAME}InverseWarp.nii -R $TEMPLATE ${ANTSPATH}/ImageMath 2 temp2.nii Normalize temp2.nii 1 ${ANTSPATH}/ImageMath 2 temp2.nii m temp2.nii 0 #$BLENDINGB echo " ImageMath 2 ${BASEA}${BASEB}${BLENDNAME}morph.nii + temp2.nii temp.nii " ${ANTSPATH}/ImageMath 2 ${BASEA}${BASEB}${BLENDNAME}morph.nii + temp2.nii temp.nii ${ANTSPATH}/ConvertToJpg ${BASEA}${BASEB}${BLENDNAME}morph.nii ${BASEA}${BASEB}${BLENDNAME}morph.jpg rm -f ${BASEA}${BASEB}${BLENDNAME}morph.nii done rm -f ${OUTNAME}* SM${OUTNAME}* exit ants-2.2.0/Scripts/guidedregistration.sh000077500000000000000000000045131311104306400203360ustar00rootroot00000000000000#!/bin/bash if [ $# -lt 7 ] then echo " USAGE \n sh command.sh fixed.nii fixedhipp.nii moving.nii movinghipp.nii outputname iterations DIM " echo " the template = fixed.nii , the individual = moving.nii " echo " iterations should be of the form 100x100x10 " exit fi if [ ${#ANTSPATH} -le 3 ] ; then echo we guess at your ants path export ANTSPATH=${ANTSPATH:="$HOME/bin/ants/"} # EDIT THIS fi if [ ! -s ${ANTSPATH}/ANTS ] ; then echo we cant find the ANTS program -- does not seem to exist. please \(re\)define \$ANTSPATH in your environment. exit fi FIX=$1 FIXH=$2 MOV=$3 MOVH=$4 OUT=$5 ITS=$6 DIM=$7 #ANTSPATH='/Users/stnava/Code/bin/ants/' if [ ${#FIX} -lt 1 -o ! -f $FIX ] then echo " Problem with specified Fixed Image => User Specified Value = $FIX " exit fi if [ ${#MOV} -lt 1 -o ! -f $MOV ] then echo " Problem with specified Moving Image => User Specified Value = $MOV " exit fi if [ ${#FIXH} -lt 1 -o ! -f $FIXH ] then echo " Problem with specified Fixed Label Image => User Specified Value = $FIXH " exit fi if [ ${#MOVH} -lt 1 -o ! -f $MOVH ] then echo " Problem with specified Moving Label Image => User Specified Value = $MOVH " exit fi # == Important Parameters Begin == LMWT=$9 # weight on landmarks INTWT=$8 # weight on intensity -- twice the landmarks # PSE/point-set-expectation/PointSetExpectation[fixedImage,movingImage,fixedPoints,movingPoints,weight,pointSetPercentage,pointSetSigma,boundaryPointsOnly,kNeighborhood, PartialMatchingIterations=100000] # the partial matching option assumes the complete labeling is in the first set of label parameters ... # more iterations leads to more symmetry in the matching - 0 iterations means full asymmetry PCT=0.1 # percent of labeled voxels to use PARZ=100 # PARZEN sigma LM=PSE[${FIX},${MOV},$FIXH,$MOVH,${LMWT},${PCT},${PARZ},0,25,100] INTENSITY=CC[$FIX,${MOV},${INTWT},4] # == Important Parameters end? == ${ANTSPATH}ANTS $DIM -o $OUT -i $ITS -t SyN[0.25] -r Gauss[3,0] -m $INTENSITY -m $LM ${ANTSPATH}WarpImageMultiTransform $DIM $MOV ${OUT}toTemplate.nii.gz ${OUT}Warp.nii.gz ${OUT}Affine.txt -R $FIX ${ANTSPATH}WarpImageMultiTransform $DIM $FIX ${OUT}toMov.nii.gz -i ${OUT}Affine.txt ${OUT}InverseWarp.nii.gz -R $MOV ${ANTSPATH}WarpImageMultiTransform $DIM $FIXH ${OUT}hipp.nii.gz -i ${OUT}Affine.txt ${OUT}InverseWarp.nii.gz -R $MOV --UseNN ants-2.2.0/Scripts/landmarkmatch.sh000077500000000000000000000050501311104306400172450ustar00rootroot00000000000000#!/bin/bash if [ $# -lt 6 ] then echo " USAGE \n sh $0 fixed.nii fixedhipp.nii moving.nii movinghipp.nii ITERATIONS LandmarkWeight " echo " you should set LandmarkWeight yourself --- the intensity weight is 1. so the landmark weight should be in that range (e.g. 0.5 => 1 ) " echo " the usage indicates .nii but you can use whatever image type you like (as long as itk can read it). also, the fixedhipp.nii can contain either the full hippocampus or the landmarks for the hippocampus --- this would be your template. the moving.nii can contain either one as well. if the moving contains the landmarks and the fixed contains the full hippocampus, then this is known as "partial matching" as in Pluta, et al Hippocampus 2009. " exit fi #ANTSPATH="/mnt/aibs1/avants/bin/ants/" ITS=$5 LMWT=$6 INTWT=1 FIX=$1 FIXH=$2 MOV=$3 MOVH=$4 OUT=${MOV%.*} NN=${#OUT} MM=${#MOV} let DD=MM-NN if [ $DD -eq 3 ] then OUT=${MOV%.*.*} fi OUT2=${FIX%.*} NN=${#OUT2} MM=${#FIX} let DD=MM-NN if [ $DD -eq 3 ] then OUT2=${FIX%.*.*} fi dothis=0 if [ $dothis -eq 1 ] then # here, we select a subset of the labeled regions FIXHMOD=${OUT2}locmod.nii.gz MOVHMOD=${OUT}locmod.nii.gz for i in $MOVH do PRE=$OUT TOUT=${PRE}locmod.nii.gz echo " DOING $TOUT " ${ANTSPATH}ThresholdImage 3 $i ${PRE}tempb.nii.gz 30.5 32.5 ${ANTSPATH}MultiplyImages 3 $i ${PRE}tempb.nii.gz $TOUT ${ANTSPATH}ThresholdImage 3 $i ${PRE}tempb.nii.gz 20.5 22.5 ${ANTSPATH}MultiplyImages 3 $i ${PRE}tempb.nii.gz ${PRE}tempb.nii ${ANTSPATH}ImageMath 3 $TOUT + $TOUT ${PRE}tempb.nii.gz ${ANTSPATH}ThresholdImage 3 $i ${PRE}tempb.nii.gz 8.5 10.5 ${ANTSPATH}MultiplyImages 3 $i ${PRE}tempb.nii.gz ${PRE}tempb.nii.gz ${ANTSPATH}ImageMath 3 $TOUT + $TOUT ${PRE}tempb.nii.gz done else FIXHMOD=$FIXH MOVHMOD=$MOVH fi KNN=10 PERCENTtoUSE=0.5 PARZSIG=10 LM=PSE[${FIX},${MOV},${FIXHMOD},${MOVHMOD},${LMWT},${PERCENTtoUSE},${PARZSIG},0,${KNN}] echo $LM if [ $# -gt 6 ] then OUT=$7 fi STEPL=0.25 INTENSITY=PR[$FIX,${MOV},${INTWT},4] if [ $LMWT -le 0 ] then exe="${ANTSPATH}ANTS 3 -o $OUT -i $ITS -t SyN[${STEPL}] -r Gauss[3,0] -m $INTENSITY " else exe="${ANTSPATH}ANTS 3 -o $OUT -i $ITS -t SyN[${STEPL}] -r Gauss[3,0] -m $LM -m $INTENSITY " fi echo " $exe " $exe ${ANTSPATH}WarpImageMultiTransform 3 $MOV ${OUT}deformed.nii.gz ${OUT}Warp.nii.gz ${OUT}Affine.txt -R $FIX ${ANTSPATH}WarpImageMultiTransform 3 $MOVH ${OUT}label.nii.gz ${OUT}Warp.nii.gz ${OUT}Affine.txt -R $FIX --use-NN ${ANTSPATH}WarpImageMultiTransform 3 $FIXH ${OUT}labelinv.nii.gz -i ${OUT}Affine.txt ${OUT}InverseWarp.nii.gz -R $MOV --use-NN ants-2.2.0/Scripts/lohmann.sh000077500000000000000000000056171311104306400161040ustar00rootroot00000000000000#!/bin/bash NUMPARAMS=$# MAXITERATIONS=30x90x20 if [ $NUMPARAMS -lt 2 ] then echo " USAGE :: " echo " sh $0 ImageDimension image.ext " echo " be sure to set ANTSPATH environment variable " exit fi ANTSPATH=/mnt/aibs1/avants/bin/ants/ if [ ${#ANTSPATH} -le 0 ] then echo " Please set ANTSPATH=LocationOfYourAntsBinaries " echo " Either set this in your profile or directly, here in the script. " echo " For example : " echo " ANTSPATH=/home/yourname/bin/ants/ " exit else echo " ANTSPATH is $ANTSPATH " fi #initialization, here, is unbiased DIM=$1 if [ ${#DIM} -gt 1 ] then echo " Problem with specified ImageDimension => User Specified Value = $DIM " exit fi FIXED=$2 if [ ${#FIXED} -lt 1 -o ! -f $FIXED ] then echo " Problem with specified Fixed Image => User Specified Value = $FIXED " exit fi OUTPUTNAME=` echo $FIXED | cut -d '.' -f 1 ` if [[ ! -s ${OUTPUTNAME}repaired.nii.gz ]] ; then ${ANTSPATH}/N3BiasFieldCorrection $DIM $FIXED ${OUTPUTNAME}repaired.nii.gz 4 fi ${ANTSPATH}/ThresholdImage $DIM ${OUTPUTNAME}repaired.nii.gz ${OUTPUTNAME}mask.nii.gz 0.1 99999 WM=${OUTPUTNAME}seg.nii.gz if [[ ! -s $WM ]] ; then ${ANTSPATH}/Apocrita $DIM -x ${OUTPUTNAME}mask.nii.gz -m [0.5,1,0,0] -o [${OUTPUTNAME}seg.nii.gz] -i Otsu[${OUTPUTNAME}repaired.nii.gz,3] -h 0 ${ANTSPATH}/ThresholdImage $DIM $WM $WM 3 3 ${ANTSPATH}/ImageMath $DIM $WM GetLargestComponent $WM fi OUT=${OUTPUTNAME} if [[ ! -s ${OUT}max.nii.gz ]] ; then # the closing operation - arachnoid surface ${ANTSPATH}/ImageMath 3 ${OUT}max.nii.gz MD $WM 15 ${ANTSPATH}/ImageMath 3 ${OUT}max.nii.gz ME ${OUT}max.nii.gz 15 # unfolded or lissencephalic surface # we would prefer to do this in a topology preserving way ${ANTSPATH}/ImageMath 3 ${OUT}min.nii.gz ME $WM 5 ${ANTSPATH}/ImageMath 3 ${OUT}min.nii.gz MD ${OUT}min.nii.gz 5 # distance transform from arachnoid surface to inside of brain # perhaps should be a geodesic distance transform ${ANTSPATH}/ImageMath 3 ${OUT}max.nii.gz Neg ${OUT}max.nii ${ANTSPATH}/ImageMath 3 ${OUT}dist.nii.gz D ${OUT}max.nii fi # multiply the distance transform by the "unfolded" surface # this gives the "feature" surface/image that could be input # to an image registration similarity metric ${ANTSPATH}/ImageMath 3 ${OUT}lohmann.nii.gz m ${OUT}dist.nii.gz ${OUT}min.nii.gz # surface stuff for visualization ${ANTSPATH}/ImageMath 3 ${OUT}surf.nii.gz ME ${OUT}min.nii.gz 2 ${ANTSPATH}/ImageMath 3 ${OUT}surf.nii.gz - ${OUT}min.nii.gz ${OUT}surf.nii.gz ${ANTSPATH}/ImageMath 3 ${OUT}maxvals.nii.gz m ${OUT}lohmann.nii.gz ${OUT}surf.nii.gz # find sulci using curvature of the WM/lohmann surface ${ANTSPATH}/SurfaceCurvature ${OUT}lohmann.nii.gz ${OUT}lohmannk.nii.gz 1.5 ${ANTSPATH}/ThresholdImage 3 ${OUT}lohmann.nii.gz ${OUT}lohmann.nii.gz 0.001 9999 ${ANTSPATH}/ImageMath 3 ${OUT}lohmannk.nii.gz Normalize ${OUT}lohmannk.nii.gz ${ANTSPATH}/ImageMath 3 ${OUT}lohmannk.nii.gz m ${OUT}lohmannk.nii.gz ${OUT}lohmann.nii.gz exit ants-2.2.0/Scripts/multi_template_script.sh000077500000000000000000000020631311104306400210510ustar00rootroot00000000000000#! /bin/bash ANTSPATH=$1 if [ ${#ANTSPATH} -lt 3 ] ; then echo usage echo $0 PATH_TO_ANTS_BINARIES image_to_label.nii.gz exit fi ANTSPATH=${ANTSPATH}/ if [ ! -s ${ANTSPATH}/ants.sh ] ; then echo you need the file ${ANTSPATH}/ants.sh - exiting exit fi Image_To_Be_Labeled=$2 if [[ ! -s $Image_To_Be_Labeled ]] || [[ ${#Image_To_Be_Labeled} -lt 3 ]] ; then echo you need to pass an image you want to label as the 2nd argument echo " you tried to pass $Image_To_Be_Labeled with name length ${#Image_To_Be_Labeled} --- sure that's right? " exit fi LONGITERATIONS=30x50x30 FASTITERATIONS=10x0x0 ct=0 LIST_OF_IMAGES=" image1.nii.gz image2.nii.gz " LIST_OF_LABELS=( image1_labels.nii.gz image2_labels.nii.gz ) LIST_OF_OUTPUT=( image1_output image2_output ) for labeled_img in $LIST_OF_IMAGES ; do sh ${ANTSPATH}/ants.sh 3 $labeled_img $Image_To_Be_Labeled ${LIST_OF_OUTPUT[${ct}]} $FASTITERATIONS ${LIST_OF_LABELS[${ct}]} let ct=$ct+1 done ls *labeled.nii.gz > labeled_list.txt ${ANTSPATH}/ImageSetStatistics 3 labeled_list.txt new_subject_labels.nii.gz 1 ants-2.2.0/Scripts/newAntsExample.sh000077500000000000000000000044061311104306400173760ustar00rootroot00000000000000#!/bin/bash # dim=3 # image dimensionality AP="" # /home/yourself/code/ANTS/bin/bin/ # path to ANTs binaries ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=2 # controls multi-threading export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS f=$1 ; m=$2 # fixed and moving image file names mysetting=$3 if [[ ! -s $f ]] ; then echo no fixed $f ; exit; fi if [[ ! -s $m ]] ; then echo no moving $m ;exit; fi if [[ ${#mysetting} -eq 0 ]] ; then echo usage is echo $0 fixed.nii.gz moving.nii.gz mysetting echo where mysetting is either forproduction or fastfortesting exit fi nm1=` basename $f | cut -d '.' -f 1 ` nm2=` basename $m | cut -d '.' -f 1 ` reg=${AP}antsRegistration # path to antsRegistration if [[ $mysetting == "fastfortesting" ]] ; then its=10000x0x0 percentage=0.1 syn="100x0x0,0,5" else its=10000x111110x11110 percentage=0.3 syn="100x100x50,-0.01,5" mysetting=forproduction fi echo affine $m $f outname is $nm am using setting $mysetting nm=${D}${nm1}_fixed_${nm2}_moving_setting_is_${mysetting} # construct output prefix $reg -d $dim -r [ $f, $m ,1] \ -m mattes[ $f, $m , 1 , 32, regular, $percentage ] \ -t translation[ 0.1 ] \ -c [$its,1.e-8,20] \ -s 4x2x1vox \ -f 6x4x2 -l 1 \ -m mattes[ $f, $m , 1 , 32, regular, $percentage ] \ -t rigid[ 0.1 ] \ -c [$its,1.e-8,20] \ -s 4x2x1vox \ -f 3x2x1 -l 1 \ -m mattes[ $f, $m , 1 , 32, regular, $percentage ] \ -t affine[ 0.1 ] \ -c [$its,1.e-8,20] \ -s 4x2x1vox \ -f 3x2x1 -l 1 \ -m mattes[ $f, $m , 0.5 , 32 ] \ -m cc[ $f, $m , 0.5 , 4 ] \ -t SyN[ .20, 3, 0 ] \ -c [ $syn ] \ -s 1x0.5x0vox \ -f 4x2x1 -l 1 -u 1 -z 1 \ -o [${nm},${nm}_diff.nii.gz,${nm}_inv.nii.gz] ${AP}antsApplyTransforms -d $dim -i $m -r $f -n linear -t ${nm}1Warp.nii.gz -t ${nm}0GenericAffine.mat -o ${nm}_warped.nii.gz ants-2.2.0/Scripts/optimalsmooth.sh000077500000000000000000000005771311104306400173470ustar00rootroot00000000000000#!/bin/bash echo " sh $0 outputprefix " if [ $# -lt 1 ] ; then exit fi export ANTSPATH=${ANTSPATH:="$HOME/bin/ants/"} SUB=$1 TH=${SUB}thicknorm.nii.gz JA=${SUB}logjacobian.nii MK=${SUB}mask.nii.gz OUTT=${SUB}smooththick.nii.gz OUTJ=${SUB}smoothjac.nii.gz REPS=5 ${ANTSPATH}SurfaceBasedSmoothing $TH 1.5 $MK $OUTT $REPS ${ANTSPATH}SurfaceBasedSmoothing $JA 1.5 $MK $OUTJ $REPS ants-2.2.0/Scripts/phantomstudy.sh000077500000000000000000000027331311104306400172030ustar00rootroot00000000000000#!/bin/bash ANTSPATH="/Users/stnava/Code/bin/ants/" #initialization, here, is unbiased count=0 for x in phantomAwmgm.jpg phantomBwmgm.jpg phantomCwmgm.jpg phantomDwmgm.jpg phantomEwmgm.jpg phantomFwmgm.jpg phantomGwmgm.jpg phantomHwmgm.jpg do count=`expr $count + 1` # Increment the counter echo " count is $count and file is $x " ${ANTSPATH}/ANTS 2 -m PR[phantomtemplate.jpg,$x,1,4] -i 20x161x161x161 -o TEST{$count} -t SyN[2] -r Gauss[3,0.] # # -t SyN[1,2,0.05] -r Gauss[3,0.25] ${ANTSPATH}/WarpImageMultiTransform 2 $x TEST{$count}registered.nii TEST{$count}Warp.nii TEST{$count}Affine.txt done # to look at the stack of results, do this: ${ANTSPATH}/StackSlices volo.hdr -1 -1 0 *$1 ${ANTSPATH}/StackSlices volr.hdr -1 -1 0 *registered.nii count=0 for x in phantomAwmgm.jpg phantomBwmgm.jpg phantomCwmgm.jpg phantomDwmgm.jpg phantomEwmgm.jpg phantomFwmgm.jpg phantomGwmgm.jpg phantomHwmgm.jpg do count=`expr $count + 1` # Increment the counter echo " count is $count and file is $x " echo " CreateJacobianDeterminantImage 2 TEST{$count}Warp.nii TEST{$count}logjac.nii 1 " ${ANTSPATH}/CreateJacobianDeterminantImage 2 TEST{$count}Warp.nii TEST{$count}logjac.nii 1 # ${ANTSPATH}/SmoothImage 2 TEST{$count}logjac.nii 1 TEST{$count}logjac.nii done ${ANTSPATH}/ThresholdImage 2 phantomtemplate.jpg mask.nii 100 200 # use the GLM with mask ${ANTSPATH}/GLM 2 mask.nii designmat.txt contrast.txt Result.nii 1000 TEST*logjac.nii ants-2.2.0/Scripts/registerimages.pl000077500000000000000000000033401311104306400174520ustar00rootroot00000000000000#!/usr/bin/perl -w use File::Basename; if ($#ARGV >= 0) { $who = join(' ', $ARGV[0]); } if ($#ARGV >= 1) { $template = join(' ', $ARGV[1]); } if ($#ARGV >= 2) { $params = join(' ', $ARGV[2]); } else { $params=0; } if ($#ARGV >= 3) { $outnaming = join(' ', $ARGV[3]); } else { $outnaming=0; } if ($#ARGV >= 4) { $metric= join(' ', $ARGV[4]); } else { $metric="PR[".$template; } if ($#ARGV >= 5) { $metricpar= join(' ', $ARGV[5]); } else { $metricpar=",1,4]"; } opendir(DIR, $who); @filelist = glob("$who"); # @filelist2 = glob("$who2"); $ct=100; print " Expecting Normalization params ".$params." \n \n \n doing \n $who \n "; $dir = `pwd`; $dir=substr($dir,0,length($dir)-1)."/"; $pathpre=""; $qsname="superfresh.sh"; foreach $name (@filelist) { $pre=$outnaming.$name; $pre=~s/\.[^.]*$//; print $pre." \n "; $prog=$params; $exe=$prog." -m ".$metric.",".$name.",".$metricpar." -o ".$pre." \n "; $exe2 = "/mnt/pkg/sge-root/bin/lx24-x86/qsub -q mac ".$qsname." ".$dir." ".$exe." "; print $exe."\n"; system $exe2; # USE qstat TO VERIFY THE JOB IS SUBMITTED $ct=$ct+1; } print " DONE WITH LOOP \n "; $user=`whoami`; chomp($user); print " you are ".$user." = surf-king "; $atcfn=0; if ($atcfn) { $waitcmd="vq -s | grep ".$user; } else { $waitcmd=" qstat -u ".$user." | grep ".substr($qsname,0,4); } $continuewaiting=1; if ($continuewaiting) { system "sleep 40"; } # Now Wait for the jobs to complete while($continuewaiting) { system $waitcmd; $output = `$waitcmd`; if ( $output eq "" ) { $continuewaiting=0; print "Jobs all finished!\n"; } else { print " wait ";system "sleep 30"; } } closedir(DIR); ants-2.2.0/Scripts/runprogramonimageset.pl000077500000000000000000000012501311104306400207060ustar00rootroot00000000000000#!/usr/bin/perl -w if ($#ARGV >= 0) { $who = join(' ', $ARGV[0]); } opendir(DIR, $who); @filelist = glob("$who"); # @filelist2 = glob("$who2"); $ct=0; $dir = `pwd`; $dir=substr($dir,0,length($dir)-1)."/"; $pathpre="/mnt/aibs1/avants/bin/ants/"; foreach $name (@filelist) { $exe = $pathpre."ANTS 3 -m PR[templatecontrol.nii,".$name.",1,2] -o ".$name." -t SyN[3] -r Gauss[2,0] -i 100x100x30 "; $exe2 = "/mnt/pkg/sge-root/bin/lx24-x86/qsub -q mac /mnt/data1/avants/Data/code/qsub3.sh ".$dir." ".$exe." "; print "$exe\n"; system $exe2; # USE qstat TO VERIFY THE JOB IS SUBMITTED $ct=$ct+1; } closedir(DIR); ants-2.2.0/Scripts/sccan_tests000066400000000000000000000064261311104306400163440ustar00rootroot00000000000000#!/bin/bash PATHTOSCCAN=${1}/ PATHTODATA=${2}/ VERBOSE=$3 if [[ ${#1} -lt 2 ]] || [[ ${#2} -lt 2 ]] ; then echo define the path to the sccan executable echo usage echo $0 path_to_sccan_bin_directory path_to_data echo path_to_data is the sccan/scripts directory exit 1 fi if [[ ! -s ${1}/sccan ]] ; then echo ${1}/sccan does not exist exit 1 fi if [[ ! -s ${PATHTODATA}/myview1.csv ]] ; then echo ${PATHTODATA}/myview1.csv test data does not exist exit 1 fi OUT=${PATHTOSCCAN}/SCCAN_TEST params=" -e 0 -n 2 -i 20 -r 0 -p 0 " # most common parameters t1=`${PATHTOSCCAN}/sccan --scca two-view[ ${PATHTODATA}myview1.csv , ${PATHTODATA}myview_mismatch.csv , na , na , 0.5 , 0.5 ] -o ${OUT}.nii.gz $params ` test1=$? t2=`${PATHTOSCCAN}/sccan --svd sparse[ ${PATHTODATA}myview3.csv , na , -0.15 ] -o ${OUT}.nii.gz -n 15 -i 500 ` # ${PATHTOSCCAN}/sccan --svd sparse[ ${PATHTODATA}myview3.csv , na , -0.15 ] -o ${OUT}.nii.gz -n 15 -i 500 test2=$? paramsb=" -e 0 -n 2 -i 50 -r 0 -p 0 --partial-scca-option PQminusR " # most common parameters t3=`${PATHTOSCCAN}/sccan --scca partial[ ${PATHTODATA}myview2.csv , ${PATHTODATA}myview3.csv , ${PATHTODATA}myview4.csv , na , na , na , -0.4 , -0.9 , 1] -o ${OUT}.nii.gz $paramsb ` test3=$? t4=`${PATHTOSCCAN}/sccan --svd sparse[ ${PATHTODATA}myview1.csv , na , 0.5 ] -o ${OUT}.nii.gz ` # ${PATHTOSCCAN}/sccan --sparse-svd [ ${PATHTODATA}myview1.csv , na , -1] -o ${OUT}.nii.gz -n 25 -i 1000 # exit test4=$? t5a=`${PATHTOSCCAN}/sccan --scca partial[ ${PATHTODATA}myview2.csv , ${PATHTODATA}myview3.csv , ${PATHTODATA}myview4.csv , na , na , na , -0.05 , 0.1 , 1] -o ${OUT}.nii.gz $paramsb` t5b=`${PATHTOSCCAN}/sccan --scca partial[ ${PATHTODATA}myview2.csv , ${PATHTODATA}myview3.csv , ${PATHTODATA}myview4.csv , na , na , na , -0.05 , 0.1 , 1] -o ${OUT}.nii.gz $paramsb` echo $t5a > tempa.txt echo $t5b > tempb.txt if diff tempa.txt tempb.txt >/dev/null ; then test5=0 # they are the same and should be !! else test5=1 fi rm tempa.txt tempb.txt t5a=`${PATHTOSCCAN}/sccan --scca partial[ ${PATHTODATA}myview2.csv , ${PATHTODATA}myview3.csv , ${PATHTODATA}myview4.csv , na , na , na , 0.05 , 0.1 , 1] -o ${OUT}.nii.gz $paramsb` t5b=`${PATHTOSCCAN}/sccan --scca partial[ ${PATHTODATA}myview2.csv , ${PATHTODATA}myview3.csv , ${PATHTODATA}myview4.csv , na , na , na , -0.05 , 0.1 , 1] -o ${OUT}.nii.gz $paramsb` echo $t5a > tempa.txt echo $t5b > tempb.txt if diff tempa.txt tempb.txt >/dev/null ; then test6=1 else test6=0 # they are different and should be !! fi rm tempa.txt tempb.txt if [[ $test1 != 1 ]] ; then echo $test1 echo test1 failed --- two-view scca exit 1 fi if [[ $test2 != 0 ]] ; then echo $t2 echo test2 failed --- sparse svd no sparseness exit 1 fi if [[ $test3 != 0 ]] ; then echo $t3 echo test3 failed --- partial scca exit 1 fi if [[ $test4 != 0 ]] ; then echo $t4 echo test4 failed --- sparse svd exit 1 fi if [[ $test5 != 0 ]] ; then echo test5 failed --- this test verifies that two runs of pscca should produce the same numerical results exit 1 fi if [[ $test6 != 0 ]] ; then echo test6 failed --- this test verifies that two different parameter sets for pscca should have different results exit 1 fi if [[ $VERBOSE -eq 1 ]] ; then echo $t1 echo $t2 echo $t3 echo $t4 fi echo all tests passed rm ${OUT}* exit 0 ants-2.2.0/Scripts/shapeupdatetotemplate.sh000077500000000000000000000202711311104306400210430ustar00rootroot00000000000000#!/bin/bash # trap keyboard interrupt (control-c) trap control_c SIGINT function setPath { cat <&2 fi WARP=${ANTSPATH}/antsApplyTransforms AVERAGE_AFFINE_PROGRAM=${ANTSPATH}/AverageAffineTransform # NoRigid if [[ ! -s ${WARP} ]]; then echo "antsApplyTransforms program can't be found. Please (re)define \$ANTSPATH in your environment." exit fi if [[ ! -s ${AVERAGE_AFFINE_PROGRAM} ]]; then echo "AverageAffineTransform* program can't be found. Please (re)define \$ANTSPATH in your environment." exit fi DIM=3 TEMPLATE=template0.nii.gz OUTPUTNAME=T_ GRADIENTSTEP=0.25 whichtemplate=0 statsmethod=1 useaff=0 USAGE="$0 -d 3 -t template0.nii.gz -o T_ -g 0.25 -s 1 -w 0 -y 0" while getopts "d:t:o:g:w:s:y:h:" OPT do case $OPT in h) #help echo $USAGE exit 0 ;; d) # dimensions DIM=$OPTARG ;; t) # name of image TEMPLATE=$OPTARG ;; o) # output prefix OUTPUTNAME=$OPTARG ;; g) # moving image GRADIENTSTEP=$OPTARG ;; w) # for multivar templates whichtemplate=$OPTARG ;; y) # affine only in update? useaff=$OPTARG ;; s) # median, mean, etc statsmethod=$OPTARG ;; \?) # getopts issues an error message echo "$USAGE" >&2 exit 1 ;; esac done if [[ $useaff -eq 1 ]] ; then AVERAGE_AFFINE_PROGRAM=${ANTSPATH}/AverageAffineTransformNoRigid fi function summarizeimageset() { local dim=$1 shift local output=$1 shift local method=$1 shift local images=( "${@}" "" ) case $method in 0) #mean AverageImages $dim $output 0 ${images[*]} ;; 1) #mean of normalized images AverageImages $dim $output 1 ${images[*]} ;; 2) #median for i in "${images[@]}"; do echo $i >> ${output}_list.txt done ImageSetStatistics $dim ${output}_list.txt ${output} 0 rm ${output}_list.txt ;; 3) #median+sharpen for i in "${images[@]}"; do echo $i >> ${output}_list.txt done ImageSetStatistics $dim ${output}_list.txt ${output} 0 ImageMath $dim ${output} Sharpen ${output} rm ${output}_list.txt ;; 4) #mean+sharpen AverageImages $dim $output 1 ${images[*]} ImageMath $dim ${output} Sharpen ${output} ;; esac } function shapeupdatetotemplate() { echo "shapeupdatetotemplate()" # local declaration of values dim=$1 template=$2 templatename=$3 outputname=$4 gradientstep=-$5 whichtemplate=$6 statsmethod=$7 # debug only # echo $dim # echo ${template} # echo ${templatename} # echo ${outputname} # echo ${outputname}*WarpedToTemplate.nii* # echo ${gradientstep} # We find the average warp to the template and apply its inverse to the template image # This keeps the template shape stable over multiple iterations of template building echo echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---voxel-wise averaging of the warped images to the current template" date #echo " ${ANTSPATH}/AverageImages $dim ${template} 1 ${templatename}${whichtemplate}*WarpedToTemplate.nii.gz " #echo " ${ANTSPATH}/ImageSetStatistics $dim ${whichtemplate}WarpedToTemplateList.txt ${template} 0" echo "--------------------------------------------------------------------------------------" imagelist=(`ls ${outputname}template${whichtemplate}*WarpedToTemplate.nii.gz`) if [[ ${#imagelist[@]} -eq 0 ]] ; then echo ERROR shapeupdatedtotemplate - imagelist length is 0 exit 1 fi summarizeimageset $dim $template $statsmethod ${imagelist[@]} WARPLIST=( `ls ${outputname}*[0-9]Warp.nii.gz 2> /dev/null` ) NWARPS=${#WARPLIST[*]} echo "number of warps = $NWARPS" echo "$WARPLIST" if [[ $whichtemplate -eq 0 ]]; then if [[ $NWARPS -ne 0 ]]; then echo "$NWARPS does not equal 0" echo echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---voxel-wise averaging of the inverse warp fields (from subject to template)" echo " ${ANTSPATH}/AverageImages $dim ${templatename}${whichtemplate}warp.nii.gz 0 `ls ${outputname}*Warp.nii.gz | grep -v "InverseWarp"`" date echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/AverageImages $dim ${templatename}${whichtemplate}warp.nii.gz 0 `ls ${outputname}*Warp.nii.gz | grep -v "InverseWarp"` echo echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---scale the averaged inverse warp field by the gradient step" echo " ${ANTSPATH}/MultiplyImages $dim ${templatename}${whichtemplate}warp.nii.gz ${gradientstep} ${templatename}${whichtemplate}warp.nii.gz" date echo "--------------------------------------------------------------------------------------" ${ANTSPATH}/MultiplyImages $dim ${templatename}${whichtemplate}warp.nii.gz ${gradientstep} ${templatename}${whichtemplate}warp.nii.gz fi echo echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---average the affine transforms (template <-> subject)" echo " ---transform the inverse field by the resulting average affine transform" echo " ${AVERAGE_AFFINE_PROGRAM} ${dim} ${templatename}0GenericAffine.mat ${outputname}*GenericAffine.mat" echo " ${WARP} -d ${dim} -e vector -i ${templatename}0warp.nii.gz -o ${templatename}0warp.nii.gz -t [${templatename}0GenericAffine.mat,1] -r ${template}" echo "--------------------------------------------------------------------------------------" ${AVERAGE_AFFINE_PROGRAM} ${dim} ${templatename}0GenericAffine.mat ${outputname}*GenericAffine.mat if [[ $NWARPS -ne 0 ]]; then ${WARP} -d ${dim} -e vector -i ${templatename}0warp.nii.gz -o ${templatename}0warp.nii.gz -t [${templatename}0GenericAffine.mat,1] -r ${template} ${ANTSPATH}/MeasureMinMaxMean ${dim} ${templatename}0warp.nii.gz ${templatename}warplog.txt 1 fi fi echo "--------------------------------------------------------------------------------------" echo " shapeupdatetotemplate---warp each template by the resulting transforms" echo " ${WARP} -d ${dim} --float $USEFLOAT -i ${template} -o ${template} -t [${templatename}0GenericAffine.mat,1] -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -r ${template}" echo "--------------------------------------------------------------------------------------" if [ -f "${templatename}0warp.nii.gz" ]; then ${WARP} -d ${dim} --float $USEFLOAT -i ${template} -o ${template} -t [${templatename}0GenericAffine.mat,1] -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -t ${templatename}0warp.nii.gz -r ${template} else ${WARP} -d ${dim} --float $USEFLOAT -i ${template} -o ${template} -t [${templatename}0GenericAffine.mat,1] -r ${template} fi } TEMPLATENAME=$OUTPUTNAME shapeupdatetotemplate ${DIM} ${TEMPLATE} ${TEMPLATENAME} ${OUTPUTNAME} ${GRADIENTSTEP} ${whichtemplate} $statsmethod ants-2.2.0/Scripts/skel.sh000077500000000000000000000017161311104306400154020ustar00rootroot00000000000000#!/bin/bash if [[ $# -lt 3 ]] ; then echo read script before using echo $0 segmentationimage outputname thresholdlevel echo e.g. $0 seg.nii.gz gm 2 exit fi TEM=$1 nm=$2 ThresholdImage 3 $TEM ${nm}_topo.nii.gz $3 $3 ImageMath 3 ${nm}_topo.nii.gz MD ${nm}_topo.nii.gz 1 ImageMath 3 ${nm}_topo.nii.gz ME ${nm}_topo.nii.gz 1 ImageMath 3 ${nm}_topo.nii.gz Neg ${nm}_topo.nii.gz ImageMath 3 ${nm}_topod.nii.gz D ${nm}_topo.nii.gz ImageMath 3 ${nm}_topodl.nii.gz Laplacian ${nm}_topod.nii.gz 1.0 1 ThresholdImage 3 ${nm}_topodl.nii.gz speed.nii.gz 0.0 0.25 ImageMath 3 speed.nii.gz Neg speed.nii.gz ImageMath 3 ${nm}_topo.nii.gz GetLargestComponent ${nm}_topo.nii.gz ImageMath 3 ${nm}_topo_skel.nii.gz PropagateLabelsThroughMask speed.nii.gz ${nm}_topo.nii.gz 20000 1 ImageMath 3 ${nm}_topo_skel.nii.gz Neg ${nm}_topo_skel.nii.gz ThresholdImage 3 ${nm}_topo_skel.nii.gz ${nm}_topo_skel.nii.gz 1 10000 MultiplyImages 3 ${nm}_topo_skel.nii.gz $3 ${nm}_topo_skel.nii.gz ants-2.2.0/Scripts/sliceBySliceOperation.sh000077500000000000000000000126001311104306400206710ustar00rootroot00000000000000#!/bin/bash VERSION="0.0.0 test" # trap keyboard interrupt (control-c) trap control_c SIGINT function setPath { cat <&2 fi PROGRAMS[0]=ExtractSliceFromImage PROGRAMS[1]=PrintHeader PROGRAMS[2]=TileImages PROGRAMS[3]=DenoiseImage PROGRAMS[4]=ImageMath PROGRAMS[5]=N4BiasFieldCorrection for (( i = 0; i < ${#PROGRAMS[@]}; i++ )) do if [[ ! -s "${ANTSPATH}/${PROGRAMS[$i]}" ]]; then echo "The ${PROGRAMS[$i]} program can't be found. Please (re)define \$ANTSPATH in your environment." exit fi done function Usage { cat <&2 fi if [ $# -lt 4 ]; then echo "Illegal number of parameters ($#)" Usage >&2 fi OUTPUT_IMAGE=$1 OPERATION=$2 WHICH_DIRECTION=$3 INPUT_IMAGE=$4 PARAMETERS[0]=$5 PARAMETERS[1]=$6 PARAMETERS[2]=$7 PARAMETERS[3]=$8 PARAMETERS[4]=$9 PARAMETERS[5]=$10 PARAMETERS[6]=$11 SIZE_STRING=$( ${ANTSPATH}/PrintHeader $INPUT_IMAGE 2 ) SIZE=( ${SIZE_STRING//x/ } ) if [[ ${#SIZE[@]} -ne 3 ]]; then echo "Error: The input image, $INPUT_IMAGE, is not 3-D." exit fi if [[ ${WHICH_DIRECTION} -gt 2 || ${WHICH_DIRECTION} -lt 0 ]]; then echo "Error: Direction must be an integer in [0,2]" exit fi NUMBER_OF_SLICES=${SIZE[$WHICH_DIRECTION]} TMP_OUTPUT_DIR=$( mktemp -d ) TMP_OUTPUT_FILE=${TMP_OUTPUT_DIR}/tmp${RANDOM}.nii.gz ALL_OUTPUT_SLICES=() for (( i = 0; i < $NUMBER_OF_SLICES; i++ )) do echo "$OPERATION (direction $WHICH_DIRECTION, slice $i of $NUMBER_OF_SLICES)" OUTPUT_DIR=`dirname $OUTPUT_IMAGE` OUTPUT_SLICE=`basename $OUTPUT_IMAGE` OUTPUT_SLICE=${OUTPUT_SLICE/\.mha/} OUTPUT_SLICE=${OUTPUT_SLICE/\.nii\.gz/} OUTPUT_SLICE=${OUTPUT_SLICE/\.nii/} OUTPUT_SLICE=${OUTPUT_SLICE/\.nrrd/} OUTPUT_SLICE="${TMP_OUTPUT_DIR}/${OUTPUT_SLICE}Slice${i}.nii.gz" ${ANTSPATH}/ExtractSliceFromImage 3 $INPUT_IMAGE $OUTPUT_SLICE $WHICH_DIRECTION $i ALL_OUTPUT_SLICES[$i]=$OUTPUT_SLICE case "$OPERATION" in "ExtractAllSlices") mv ${OUTPUT_SLICE} ${OUTPUT_DIR} ;; "Convolve") ${ANTSPATH}/ImageMath 2 $OUTPUT_SLICE Convolve $OUTPUT_SLICE ${PARAMETERS[0]} ${PARAMETERS[1]} ;; "DenoiseImage") ${ANTSPATH}/DenoiseImage -d 2 -i $OUTPUT_SLICE -o $OUTPUT_SLICE -v 0 ;; "N4BiasFieldCorrection") ${ANTSPATH}/N4BiasFieldCorrection -d 2 -i $OUTPUT_SLICE -o $OUTPUT_SLICE -v 0 -s 2 ;; "RescaleImage") ${ANTSPATH}/ImageMath 2 $OUTPUT_SLICE RescaleImage $OUTPUT_SLICE ${PARAMETERS[0]} ${PARAMETERS[1]} ;; "TruncateImageIntensity") ${ANTSPATH}/ImageMath 2 $OUTPUT_SLICE TruncateImageIntensity $OUTPUT_SLICE ${PARAMETERS[0]} ${PARAMETERS[1]} ${PARAMETERS[2]} ${PARAMETERS[3]} ;; *) echo "The operation '$OPERATION' is not an option. See usage: '$0 -h 1'" exit ;; esac done PERMUTATION_ORDER[0]='2 0 1' PERMUTATION_ORDER[1]='0 2 1' PERMUTATION_ORDER[2]='0 1 2' if [[ $OPERATION != "ExtractAllSlices" ]]; then ${ANTSPATH}/TileImages 3 $TMP_OUTPUT_FILE 1x1x0 ${ALL_OUTPUT_SLICES[@]} ${ANTSPATH}/PermuteFlipImageOrientationAxes 3 $TMP_OUTPUT_FILE $TMP_OUTPUT_FILE ${PERMUTATION_ORDER[$WHICH_DIRECTION]} 0 0 0 0 ${ANTSPATH}/CopyImageHeaderInformation $INPUT_IMAGE $TMP_OUTPUT_FILE $OUTPUT_IMAGE 1 1 1 fi ants-2.2.0/Scripts/submitexperimentalbuild.sh000077500000000000000000000005231311104306400214000ustar00rootroot00000000000000#!/bin/bash # request Bourne shell as shell for job ctest -D ExperimentalStart ctest -D ExperimentalUpdate ctest -D ExperimentalConfigure ctest -D ExperimentalBuild ctest -D ExperimentalSubmit ctest -D ExperimentalTest #ctest -D ExperimentalCoverage ctest -D ExperimentalSubmit #ctest -D ExperimentalMemCheck #ctest -D ExperimentalSubmit ants-2.2.0/Scripts/sygnccavg.sh000077500000000000000000000010051311104306400164170ustar00rootroot00000000000000#!/bin/bash TNAME=$1 GRADSTEP=$2 shift 2 FLIST2=$* ImageMath 2 $TNAME Normalize $TNAME for x in $(seq 1 5 ) ; do for y in $FLIST2 ; do ImageMath 2 turdx${y} Normalize $y ImageMath 2 turdx${y} CorrelationUpdate $TNAME turdx${y} 4 # MeasureImageSimilarity 2 1 $y $TNAME SmoothImage 2 turdx${y} 0.5 turdx${y} # MeasureMinMaxMean 2 turdx$y done AverageImages 2 turdu.nii.gz 0 turdx** MultiplyImages 2 turdu.nii.gz $GRADSTEP turdu.nii.gz ImageMath 2 $TNAME + $TNAME turdu.nii.gz done ants-2.2.0/Scripts/thickstudy.sh000077500000000000000000000017401311104306400166340ustar00rootroot00000000000000#!/bin/bash count=0 for x in phantomA phantomB phantomC phantomD phantomE phantomF phantomG phantomH do count=`expr $count + 1` # Increment the counter echo " $x " echo " LaplacianThickness ${x}wm.hdr ${x}gm.hdr 15 TEST{$count}thick.hdr 1 0.5 " ThresholdImage 2 ${x}wmgm.jpg ${x}wm.nii 245 290 ThresholdImage 2 ${x}wmgm.jpg ${x}gm.nii 111 133 LaplacianThickness ${x}wm.nii ${x}gm.nii 15 TEST{$count}thick.hdr 5 3 WarpImageMultiTransform 2 TEST{$count}thick.hdr TEST{$count}thickreg.hdr TEST{$count}Warp.nii TEST{$count}Affine.txt # SmoothImage 2 TEST{$count}thickreg.hdr 1 TEST{$count}thickreg.hdr done StudentsTestOnImages 2 THICK2.hdr 4 4 ~/Code/bin/ants/GLM 2 mask.nii designmat.txt contrastvec.txt temp.hdr 1000 TEST{1}thickreg.hdr TEST{2}thickreg.hdr TEST{3}thickreg.hdr TEST{4}thickreg.hdr TEST{5}thickreg.hdr TEST{6}thickreg.hdr TEST{7}thickreg.hdr TEST{8}thickreg.hdr MultiplyImages 2 THICK2.hdr phantomtemplate.jpg THICK2.hdr ants-2.2.0/Scripts/unbiased_longitudinal_map000066400000000000000000000030711311104306400212240ustar00rootroot00000000000000#!/bin/bash # # an example of unbiased longitudinal registration performed with ANTs. # DIM=2 # set the image dimensionality # # the images below are available in ANTS/Examples/Data/ # you should link them to a local directory and then run this script from that directory. # REFSPACE=B2.tiff I=B3.tiff J=B4.tiff K=B5.tiff AFFITS=" --number-of-affine-iterations 10000x10000x10000x10000 " ANTS $DIM -m MI[$REFSPACE,$I,1,16] -o I_init -i 0 $AFFITS WarpImageMultiTransform $DIM $I temp.nii.gz -R $REFSPACE I_initAffine.txt ANTS $DIM -m MI[temp.nii.gz,$J,1,16] -o J_init -i 0 $AFFITS TRAN=" -i 100x100 -t SyN[0.1] " TRAN=" -i 100x100x1000 -t SyN[0.1] " # this will initialize the registration with the affine maps computed above # gaussian # ANTS $DIM -m CC[$I,$J,1,4] -o J_to_I_diff $TRAN -r Gauss[3,0] $AFFITS -F I_initAffine.txt -a J_initAffine.txt # dmffd ANTS $DIM -m CC[$I,$J,1,4] -o J_to_I_diff $TRAN -r DMFFD[10x10x10,0,3] $AFFITS --fixed-image-initial-affine $REFSPACE -F I_initAffine.txt -a J_initAffine.txt --fixed-image-initial-affine $REFSPACE # now warp J to I just to check correctness WarpImageMultiTransform $DIM $J J_to_I.nii.gz -R $I -i I_initAffine.txt J_to_I_diffWarp.nii.gz J_initAffine.txt # # note that the reference space is also the space where the warp is computed. # if you want to analyze the mapping in another space, then you would have to warp it there. e.g. # this will deform the map to the space of I. WarpImageMultiTransform $DIM J_to_I_diffWarp.nii.gz J_to_I_diffWarp_in_I_space.nii.gz -R $I -i I_initAffine.txt # # cleanup rm temp.nii.gz ants-2.2.0/Scripts/unbiased_pairwise_registration.sh000077500000000000000000000113241311104306400227270ustar00rootroot00000000000000#!/bin/bash usage=" $0 -d 3 -f fixed.nii.gz -m moving.nii.gz -o output_prefix " :< A which gives Aff_A AB => B which gives Aff_B and , finally A( Aff_A ) <=> B( Aff_ B ) where <=> is SyN. so briefly, this implements: A => midAffA => midWarpAtoMID * MID * midWarpBtoMID <= midAffB <= B i don't worry about the header bias too much or the fact that my first transform depends on mapping B to A --- the interpolation is still symmetric ( i think ) with this approach. would be nice to use the CompositeTransformUtil to convert the output of this to just a fwd/inv tx. if this turns out to be biased, i suppose we need header tricks. supercalifragilisticexpialidocious A=A ; B=B ; prefix=J ; dim=3 if [[ $# -eq 0 ]] ; then echo $usage ; exit 0 ; fi while getopts ":d:f:m:o:t:h:" opt; do case $opt in d) echo "-d $OPTARG" >&2 dim=$OPTARG ;; f) echo "-f $OPTARG" >&2 A=$OPTARG ;; m) echo "-m $OPTARG" >&2 B=$OPTARG ;; o) echo "-o $OPTARG " >&2 prefix=$OPTARG ;; h) echo "Usage: $usage " >&2 exit 0 ;; \?) echo "Usage: $usage " >&2 exit 0 ;; esac done echo inputs: $A $B $prefix $dim if [[ ${#dim} -lt 1 ]] ; then echo must provide input dimension $dim ; echo $usage ; exit 0 ; fi if [[ ${#prefix} -lt 3 ]] ; then echo must provide output prefix $prefix ; echo $usage ; exit 0 ; fi if [[ ! -s $A ]] || [[ ! -s $B ]] ; then echo inputs: $A $B $prefix ; echo $usage ; exit 1 ; fi reg=antsRegistration uval=0 aff=" -t affine[ 0.25 ] -c [1009x200x20,1.e-8,20] -s 4x2x0 -f 4x2x1 " synits=20x20x10 ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=2 export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS nmA=${prefix}_A_norm nmB=${prefix}_B_norm nm=${prefix}_B_to_A nminv=${prefix}_A_to_B initAmat=${prefix}_initA0GenericAffine.mat initBmat=${prefix}_initB0GenericAffine.mat # register in both directions, then average the result initA=${prefix}_initA $reg -d $dim -r [ $A, $B, 1 ] \ -m mattes[ $A, $B , 1 , 32, regular , 0.25 ] $aff -z 1 \ -o [${initA}] initB=${prefix}_initB $reg -d $dim -r [ $B, $A, 1 ] \ -m mattes[ $B, $A , 1 , 32, regular , 0.25 ] $aff -z 1 \ -o [${initB}] # get the identity map ComposeMultiTransform $dim ${initA}_id.mat -R ${initA}0GenericAffine.mat ${initA}0GenericAffine.mat -i ${initA}0GenericAffine.mat # invert the 2nd affine registration map ComposeMultiTransform $dim ${initB}_inv.mat -R ${initA}0GenericAffine.mat -i ${initB}0GenericAffine.mat # get the average affine map AverageAffineTransform $dim ${prefix}_avg.mat ${initB}_inv.mat ${initA}0GenericAffine.mat # get the midpoint affine map AverageAffineTransform $dim ${prefix}_mid.mat ${initA}_id.mat ${prefix}_avg.mat #.........# # this applies, to B, a map from B to midpoint(B,A) antsApplyTransforms -d $dim -i $B -o ${prefix}_mid.nii.gz -t ${prefix}_mid.mat -r $A # compute the map from A to midpoint(B,A) --- "fair" interpolation $reg -d $dim -r [ ${prefix}_mid.nii.gz, $A, 1 ] \ -m mattes[ ${prefix}_mid.nii.gz, $A, 1 , 32, random , 0.25 ] $aff \ -o [${nmA},${nmA}_aff.nii.gz] # compute the map from B to midpoint(B,A) --- "fair" interpolation $reg -d $dim -r [ ${nmA}_aff.nii.gz, $B, 1] \ -m mattes[ ${nmA}_aff.nii.gz, $B, 1 , 32, random , 0.25 ] $aff \ -o [${nmB},${nmB}_aff.nii.gz] midaaff=${nmA}0GenericAffine.mat midbaff=${nmB}0GenericAffine.mat # now we can do a symmetric deformable mapping - not sure echo now do deformable expecting $initB and $initA to exist $reg -d $dim --initial-fixed-transform $midaaff --initial-moving-transform $midbaff \ -m mattes[ $A, $B , 1 , 32 ] \ -t syn[ 0.25, 3, 0.0 ] \ -c [${synits},1.e-8,10] \ -s 2x1x0 \ -f 4x2x1 \ -o [${nm},${nm}_diff_symm.nii.gz] # this is the composite mapping - via the mid-point - to the A image antsApplyTransforms -d $dim -i $B -o ${nm}_diffX.nii.gz -t [ $midaaff, 1 ] -t ${nm}1Warp.nii.gz -t $midbaff -r $A # one can gain the inverse in a similar way antsApplyTransforms -d $dim -i $A -o ${nminv}_diffX.nii.gz -t [ $midbaff, 1 ] -t ${nm}1InverseWarp.nii.gz -t $midaaff -r $B ants-2.2.0/Scripts/unbiased_pairwise_registration_with_aux_images.sh000077500000000000000000000427651311104306400262010ustar00rootroot00000000000000#!/bin/bash usage=" $0 -d 3 -f fixed.nii.gz -m moving.nii.gz -t grouptemplate.nii.gz -b templatebrainmask.nii.gz -g f_auximage_tensor -n m_auximage_tensor -o output_prefix -h f_auximage_scalar -k m_auximage_scalar " :< A which gives Aff_A AB => B which gives Aff_B and , finally A( Aff_A ) <=> B( Aff_ B ) where <=> is SyN. i don't worry about the header bias too much or the fact that my first transform depends on mapping B to A --- the interpolation is still symmetric ( i think ) with this approach. would be nice to use the CompositeTransformUtil to convert the output of this to just a fwd/inv tx. if this turns out to be biased, i suppose we need header tricks. supercalifragilisticexpialidocious A=A ; B=B ; prefix=J ; dim=3 if [[ $# -eq 0 ]] ; then echo $usage ; exit 0 ; fi while getopts ":d:f:m:b:o:t:g:h:n:k:h:" opt; do case $opt in d) echo "-d $OPTARG" >&2 dim=$OPTARG ;; f) echo "-f $OPTARG" >&2 A=$OPTARG ;; g) echo "-g $OPTARG" >&2 G=$OPTARG ;; h) echo "-h $OPTARG" >&2 H=$OPTARG ;; k) echo "-k $OPTARG" >&2 K=$OPTARG ;; m) echo "-m $OPTARG" >&2 B=$OPTARG ;; n) echo "-n $OPTARG" >&2 N=$OPTARG ;; o) echo "-o $OPTARG " >&2 prefix=$OPTARG ;; t) echo "-t $OPTARG " >&2 template=$OPTARG ;; b) echo "-b $OPTARG " >&2 templatebm=$OPTARG ;; h) echo "Usage: $usage " >&2 exit 0 ;; \?) echo "Usage: $usage " >&2 exit 0 ;; esac done echo inputs: $A $B $prefix $dim echo THIS SCRIPT MAY OR MAY NOT WORK - IT HAS NOT BEEN TESTED RECENTLY echo USE AT OWN RISK BY COMMENTING OUT THE EXIT CALL BELOW exit 0 if [[ ${#dim} -lt 1 ]] ; then echo must provide input dimension $dim ; echo $usage ; exit 0 ; fi if [[ ${#prefix} -lt 3 ]] ; then echo must provide output prefix $prefix ; echo $usage ; exit 0 ; fi if [[ ! -s $A ]] || [[ ! -s $B ]] ; then echo inputs: $A $B $prefix ; echo $usage ; exit 1 ; fi reg=antsRegistration uval=0 affits=999x550x20 aff=" -t affine[ 0.2 ] -c [ $affits ,1.e-7,20] -s 3x2x0 -f 4x2x1 -u $uval -l 0 " metparams=" 1 , 32, random , 0.25 " synits=100x50 #BA ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=2 export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS nmA=${prefix}_A_norm nmB=${prefix}_B_norm nm=${prefix}_Long nminv=${prefix}_ILong inA=${prefix}_A.nii.gz inB=${prefix}_B.nii.gz if [[ ! -s $inA ]] && [[ ! -s $inB ]] ; then cp $A $inA cp $B $inB A=$inA B=$inB # map the input images to a similar intensity space ImageMath $dim $A Normalize $A ImageMath $dim $B Normalize $B if [[ -s $template ]] ; then ImageMath $dim $A HistogramMatch $A $template ImageMath $dim $B HistogramMatch $B $template fi N3BiasFieldCorrection $dim $A $A 4 N3BiasFieldCorrection $dim $B $B 4 fi A=$inA B=$inB ################ # T1 Mapping # ################ initAmat=${prefix}_A_norm0GenericAffine.mat initBmat=${prefix}_B_norm0GenericAffine.mat initA=${prefix}_initA initB=${prefix}_initB ##################### if [[ ! -s ${nm}1Warp.nii.gz ]] ; then $reg -d $dim -r [ $A, $B, 1 ] \ -m mattes[ $A, $B , $metparams ] $aff -z 1 \ -o [${initA}] $reg -d $dim -r [ $B, $A, 1 ] \ -m mattes[ $B, $A , $metparams ] $aff -z 1 \ -o [${initB}] # get the identity map ComposeMultiTransform $dim ${initA}_id.mat -R ${initA}0GenericAffine.mat ${initA}0GenericAffine.mat -i ${initA}0GenericAffine.mat # invert the 2nd affine registration map ComposeMultiTransform $dim ${initB}_inv.mat -R ${initA}0GenericAffine.mat -i ${initB}0GenericAffine.mat # get the average affine map AverageAffineTransform $dim ${prefix}_avg.mat ${initB}_inv.mat ${initA}0GenericAffine.mat # get the midpoint affine map AverageAffineTransform $dim ${prefix}_mid.mat ${initA}_id.mat ${prefix}_avg.mat #.........# # this applies, to B, a map from B to midpoint(B,A) ImageMath $dim ${prefix}_mid.nii.gz PadImage $A 10 antsApplyTransforms -d $dim -i $B -o ${prefix}_mid.nii.gz -t ${prefix}_mid.mat -r $A # compute the map from A to midpoint(B,A) --- "fair" interpolation $reg -d $dim \ -m mattes[ ${prefix}_mid.nii.gz, $A, $metparams ] $aff \ -o [${nmA},${nmA}_aff.nii.gz] # compute the map from B to midpoint(B,A) --- "fair" interpolation $reg -d $dim \ -m mattes[ ${nmA}_aff.nii.gz, $B, $metparams ] $aff \ -o [${nmB},${nmB}_aff.nii.gz] # now we can do a symmetric deformable mapping N3BiasFieldCorrection $dim $A ${nm}_n3_a.nii.gz 4 N3BiasFieldCorrection $dim ${nm}_n3_a.nii.gz ${nm}_n3_a.nii.gz 2 N3BiasFieldCorrection $dim $B ${nm}_n3_b.nii.gz 4 N3BiasFieldCorrection $dim ${nm}_n3_b.nii.gz ${nm}_n3_b.nii.gz 2 echo now do deformable expecting $initB and $initA to exist if [[ -s $initAmat ]] && [[ -s $initBmat ]] ; then $reg -d $dim --initial-fixed-transform $initAmat --initial-moving-transform $initBmat \ -m mattes[ ${nm}_n3_a.nii.gz, ${nm}_n3_b.nii.gz , 1 , 32 ] \ -t syn[ 0.25, 3, 0.5 ] \ -c [${synits},1.e-8,10] \ -s 1x0 \ -f 2x1 \ -u $uval -b 0 -z 1 \ -o [${nm},${nm}_diff_symm.nii.gz] # $reg -d $dim --initial-fixed-transform $initBmat --initial-moving-transform $initAmat \ # -m mattes[ ${nm}_n3_a.nii.gz, ${nm}_n3_b.nii.gz , 1 , 32 ] \ # -t syn[ 0.25, 3, 0.5 ] \ # -c [${synits},1.e-8,10] \ # -s 1x0 \ # -f 2x1 \ # -u $uval -b 0 -z 1 \ # -o [${nminv},${nminv}_diff_symm.nii.gz] else echo $initBmat and $initAmat DO NOT exist exit fi fi AverageImages $dim ${nm}_avg.nii.gz 0 ${nmA}_aff.nii.gz ${nm}_diff_symm.nii.gz MultiplyImages $dim ${nm}1InverseWarp.nii.gz 0.5 ${nm}tempWarp.nii.gz antsApplyTransforms -d $dim -i ${nm}_avg.nii.gz -o ${nm}_avg.nii.gz -t ${nm}tempWarp.nii.gz -r ${nm}_avg.nii.gz rm ${nm}tempWarp.nii.gz # recompute the mappings if [[ -s $initAmat ]] && [[ -s $initBmat ]] ; then $reg -d $dim --initial-moving-transform $initBmat \ -m mattes[ ${nm}_avg.nii.gz, ${nm}_n3_b.nii.gz , 1 , 32 ] \ -t syn[ 0.25, 3, 0.5 ] \ -c [${synits},1.e-8,10] \ -s 1x0 \ -f 2x1 \ -u $uval -b 0 -z 1 \ -o [${nm}_B,${nm}_B_symm.nii.gz] $reg -d $dim --initial-moving-transform $initAmat \ -m mattes[ ${nm}_avg.nii.gz, ${nm}_n3_a.nii.gz , 1 , 32 ] \ -t syn[ 0.25, 3, 0.5 ] \ -c [${synits},1.e-8,10] \ -s 1x0 \ -f 2x1 \ -u $uval -b 0 -z 1 \ -o [${nm}_A,${nm}_A_symm.nii.gz] else echo $initBmat and $initAmat DO NOT exist exit fi initafffn=${nm}_init_aff.mat if [[ -s $template ]] && [[ ! -s ${nm}_gt_0GenericAffine.mat ]] ; then imgsmall=${nm}_diffsmall.nii.gz temsmall=${nm}_temsmall.nii.gz ResampleImageBySpacing $dim ${nm}_avg.nii.gz $imgsmall 4 4 4 ResampleImageBySpacing $dim $template $temsmall 4 4 4 antsAffineInitializer $dim $temsmall $imgsmall $initafffn 15 0.1 0 10 gf=$template gm=${nm}_avg.nii.gz imgs=" $gf, $gm " $reg -d $dim -r $initafffn \ -m mattes[ $imgs , 1 , 32, regular, 0.25 ] \ -t affine[ 0.1 ] \ -c [ $affits ,1.e-7,20] \ -s 4x2x1vox \ -f 4x2x1 -l 1 \ -m cc[ $imgs , 1 , 4 ] \ -t syn[ .2, 3, 0.0 ] \ -c [100x50x20,1.e-8,20] \ -s 2x1x0vox \ -f 4x2x1 -l 1 -u 0 -z 1 \ -o [${nm}_gt_,${nm}_gt.nii.gz] # map brain mask to subject space T1 trans=" -t [ $initAmat, 1 ] -t ${nm}_A1InverseWarp.nii.gz -t [ ${nm}_gt_0GenericAffine.mat, 1] -t ${nm}_gt_1InverseWarp.nii.gz " antsApplyTransforms -d $dim -i $templatebm -o ${nm}_bm_A.nii.gz -n NearestNeighbor -r $A $trans trans=" -t [ $initBmat, 1 ] -t ${nm}_B1InverseWarp.nii.gz -t [ ${nm}_gt_0GenericAffine.mat, 1] -t ${nm}_gt_1InverseWarp.nii.gz " antsApplyTransforms -d $dim -i $templatebm -o ${nm}_bm_B.nii.gz -n NearestNeighbor -r $B $trans MultiplyImages $dim ${nm}_bm_A.nii.gz $A ${nm}_A_brain.nii.gz MultiplyImages $dim ${nm}_bm_B.nii.gz $B ${nm}_B_brain.nii.gz fi echo done with brain extraction if [[ -s $G ]] && [[ -s $N ]] && [[ ! -s ${nm}_fadiff.nii.gz ]] ; then # map brain mask to subject space T1 trans=" -t [ $initAmat, 1 ] -t [ ${nm}_gt_0GenericAffine.mat, 1] -t ${nm}_gt_1InverseWarp.nii.gz " antsApplyTransforms -d $dim -i $templatebm -o ${nm}_bm_A.nii.gz -n NearestNeighbor -r $A $trans trans=" -t [ $initBmat, 1 ] -t ${nm}1InverseWarp.nii.gz -t [ ${nm}_gt_0GenericAffine.mat, 1] -t ${nm}_gt_1InverseWarp.nii.gz " antsApplyTransforms -d $dim -i $templatebm -o ${nm}_bm_B.nii.gz -n NearestNeighbor -r $B $trans MultiplyImages $dim ${nm}_bm_A.nii.gz $A ${nm}_A_brain.nii.gz MultiplyImages $dim ${nm}_bm_B.nii.gz $B ${nm}_B_brain.nii.gz ######### now redo bias correction & syn ######### N3BiasFieldCorrection $dim ${nm}_A_brain.nii.gz ${nm}_n3_a.nii.gz 4 N3BiasFieldCorrection $dim ${nm}_n3_a.nii.gz ${nm}_n3_a.nii.gz 2 N3BiasFieldCorrection $dim ${nm}_n3_a.nii.gz ${nm}_n3_a.nii.gz 2 N3BiasFieldCorrection $dim ${nm}_B_brain.nii.gz ${nm}_n3_b.nii.gz 4 N3BiasFieldCorrection $dim ${nm}_n3_b.nii.gz ${nm}_n3_b.nii.gz 2 N3BiasFieldCorrection $dim ${nm}_n3_b.nii.gz ${nm}_n3_b.nii.gz 2 echo now do deformable expecting $initB and $initA to exist if [[ -s $initAmat ]] && [[ -s $initBmat ]] ; then $reg -d $dim --initial-fixed-transform $initAmat --initial-moving-transform $initBmat \ -m mattes[ ${nm}_n3_a.nii.gz, ${nm}_n3_b.nii.gz , 1 , 32 ] \ -t syn[ 0.25, 3, 0.5 ] \ -c [${synits},1.e-8,10] \ -s 1x0 \ -f 2x1 \ -u $uval -b 0 -z 1 \ -o [${nm},${nm}_diff_symm.nii.gz] else echo $initBmat and $initAmat DO NOT exist exit fi antsApplyTransforms -d $dim -i $B -o ${nm}_diff.nii.gz -t [ $initAmat, 1 ] -t ${nm}1Warp.nii.gz -t $initBmat -r $A ANTSJacobian $dim ${nm}1Warp.nii.gz ${nm} 1 no 0 fi if [[ -s $G ]] && [[ -s $N ]] && [[ ! -s ${nm}_fadiff.nii.gz ]] ; then echo deal with auxiliary images ... here DTI ffa=${nm}_ffa.nii.gz mfa=${nm}_mfa.nii.gz ImageMath 3 $ffa TensorFA $G ImageMath 3 $mfa TensorFA $N synits=15x25x3 imgs=" ${nm}_A_brain.nii.gz, $ffa " $reg -d $dim \ -m mattes[ $imgs , 1 , 32, regular, 0.2 ] \ -t rigid[ 0.1 ] \ -c [1000x1000x5,1.e-7,20] \ -s 4x2x1mm -x [${nm}_bm_A.nii.gz] \ -f 4x2x1 -l 1 -u 1 -z 1 \ -m mattes[ $imgs , 1 , 32, regular, 0.2 ] \ -t affine[ 0.1 ] \ -c [1000x25,1.e-7,20] \ -s 2x1mm -x [${nm}_bm_A.nii.gz] \ -f 2x1 -l 1 -u 1 -z 1 \ -m mattes[ $imgs , 1 , 32 ] \ -m cc[ $imgs , 1 , 2 ] \ -t SyN[ 0.2, 3, 0.5 ] \ -c [$synits,1.e-7,20] \ -s 4x2x1mm -x [${nm}_bm_A.nii.gz] \ -f 4x2x1 -l 1 -u 1 -z 1 \ -o [${nm}_ffa,${nm}_ffa_distcorr.nii.gz] imgs=" ${nm}_B_brain.nii.gz, $mfa " $reg -d $dim \ -m mattes[ $imgs , 1 , 32, regular, 0.2 ] \ -t rigid[ 0.1 ] \ -c [1000x1000x5,1.e-7,20] \ -s 4x2x1mm -x [${nm}_bm_A.nii.gz] \ -f 4x2x1 -l 1 -u 1 -z 1 \ -m mattes[ $imgs , 1 , 32, regular, 0.2 ] \ -t affine[ 0.1 ] \ -c [1000x25,1.e-7,20] \ -s 2x1mm -x [${nm}_bm_B.nii.gz] \ -f 2x1 -l 1 -u 1 -z 1 \ -m mattes[ $imgs , 1 , 32 ] \ -m cc[ $imgs , 1 , 2 ] \ -t SyN[ 0.2, 3, 0.5 ] \ -c [$synits,1.e-7,20] \ -s 4x2x1mm -x [${nm}_bm_B.nii.gz] \ -f 4x2x1 -l 1 -u 1 -z 1 \ -o [${nm}_mfa,${nm}_mfa_distcorr.nii.gz] # trans=" -t ${nm}_gt_1Warp.nii.gz -t ${nm}_gt_0GenericAffine.mat -t ${nm}_A1Warp.nii.gz -t $initAmat -t ${nm}_ffa1Warp.nii.gz -t ${nm}_ffa0GenericAffine.mat " antsApplyTransforms -d $dim -i $ffa -o ${nm}_ffanorm.nii.gz -r $template $trans # trans=" -t ${nm}_gt_1Warp.nii.gz -t ${nm}_gt_0GenericAffine.mat -t ${nm}_B1Warp.nii.gz -t $initBmat -t ${nm}_mfa1Warp.nii.gz -t ${nm}_mfa0GenericAffine.mat " antsApplyTransforms -d $dim -i $mfa -o ${nm}_mfanorm.nii.gz -r $template $trans ImageMath $dim ${nm}_fadiff.nii.gz - ${nm}_ffanorm.nii.gz ${nm}_mfanorm.nii.gz fi echo done with aux images --- now get final jacobians if [[ -s $H ]] && [[ -s $K ]] && [[ ! -s ${nm}_cbfdiff.nii.gz ]] ; then echo deal with auxiliary images ... here a scalar image pair # if [[ -s $H ]] && [[ -s $K ]] && [[ ! -s ${nm}_cbfdiff.nii.gz ]] ; then # echo deal with auxiliary images ... here DTI fcbf=${nm}_fcbf.nii.gz mcbf=${nm}_mcbf.nii.gz ImageMath $dim $fcbf Normalize $H ImageMath $dim $mcbf Normalize $K synits=15x25x3 imgs=" ${nm}_A_brain.nii.gz, $fcbf " $reg -d $dim \ -m mattes[ $imgs , 1 , 32, regular, 0.2 ] \ -t rigid[ 0.1 ] \ -c [1000x1000x5,1.e-7,20] \ -s 4x2x1mm -x [${nm}_bm_A.nii.gz] \ -f 4x2x1 -l 1 -u 1 -z 1 \ -m mattes[ $imgs , 1 , 32, regular, 0.2 ] \ -t affine[ 0.1 ] \ -c [1000x25,1.e-7,20] \ -s 2x1mm -x [${nm}_bm_A.nii.gz] \ -f 2x1 -l 1 -u 1 -z 1 \ -m mattes[ $imgs , 1 , 32 ] \ -m cc[ $imgs , 1 , 2 ] \ -t SyN[ 0.2, 3, 0.5 ] \ -c [$synits,1.e-7,20] \ -s 4x2x1mm -x [${nm}_bm_A.nii.gz] \ -f 4x2x1 -l 1 -u 1 -z 1 \ -o [${nm}_fcbf,${nm}_fcbf_distcorr.nii.gz] imgs=" ${nm}_B_brain.nii.gz, $mcbf " $reg -d $dim \ -m mattes[ $imgs , 1 , 32, regular, 0.2 ] \ -t rigid[ 0.1 ] \ -c [1000x1000x5,1.e-7,20] \ -s 4x2x1mm -x [${nm}_bm_A.nii.gz] \ -f 4x2x1 -l 1 -u 1 -z 1 \ -m mattes[ $imgs , 1 , 32, regular, 0.2 ] \ -t affine[ 0.1 ] \ -c [1000x25,1.e-7,20] \ -s 2x1mm -x [${nm}_bm_B.nii.gz] \ -f 2x1 -l 1 -u 1 -z 1 \ -m mattes[ $imgs , 1 , 32 ] \ -m cc[ $imgs , 1 , 2 ] \ -t SyN[ 0.2, 3, 0.5 ] \ -c [$synits,1.e-7,20] \ -s 4x2x1mm -x [${nm}_bm_B.nii.gz] \ -f 4x2x1 -l 1 -u 1 -z 1 \ -o [${nm}_mcbf,${nm}_mcbf_distcorr.nii.gz] # trans=" -t ${nm}_gt_1Warp.nii.gz -t ${nm}_gt_0GenericAffine.mat -t ${nm}_A1Warp.nii.gz -t $initAmat -t ${nm}_fcbf1Warp.nii.gz -t ${nm}_fcbf0GenericAffine.mat " exe1="antsApplyTransforms -d $dim -i $fcbf -o ${nm}_fcbfnorm.nii.gz -r $template $trans" $exe1 ##### trans=" -t ${nm}_gt_1Warp.nii.gz -t ${nm}_gt_0GenericAffine.mat -t ${nm}_B1Warp.nii.gz -t $initBmat -t ${nm}_mcbf1Warp.nii.gz -t ${nm}_mcbf0GenericAffine.mat " exe2="antsApplyTransforms -d $dim -i $mcbf -o ${nm}_mcbfnorm.nii.gz -r $template $trans" $exe2 echo $exe1 > ${nm}_cbf_map.txt echo $exe2 >> ${nm}_cbf_map.txt ##### ImageMath $dim ${nm}_cbfdiff.nii.gz - ${nm}_fcbfnorm.nii.gz ${nm}_mcbfnorm.nii.gz fi echo done with 2nd aux images --- now get final jacobians # get final jacobian values # trans=" -t ${nm}_gt_1Warp.nii.gz -t ${nm}_gt_0GenericAffine.mat -t ${nm}_A1Warp.nii.gz -t $initAmat" trans=" -t ${nm}_gt_1Warp.nii.gz -t ${nm}_gt_0GenericAffine.mat -t $initAmat" antsApplyTransforms -d $dim -i ${nm}_A_brain.nii.gz -o [${nm}_A_fullWarp.nii.gz, 1 ] -r $template $trans ANTSJacobian $dim ${nm}_A_fullWarp.nii.gz ${nm}_A_full 1 no 0 # trans=" -t ${nm}_gt_1Warp.nii.gz -t ${nm}_gt_0GenericAffine.mat -t ${nm}_B1Warp.nii.gz -t $initBmat" antsApplyTransforms -d $dim -i ${nm}_B_brain.nii.gz -o [${nm}_B_fullWarp.nii.gz, 1 ] -r $template $trans ANTSJacobian $dim ${nm}_B_fullWarp.nii.gz ${nm}_B_full 1 no 0 ants-2.2.0/Scripts/waitForPBSQJobs.pl000077500000000000000000000072311311104306400173620ustar00rootroot00000000000000#!/usr/bin/perl -w use strict; # Usage: waitForSGEQJobs.pl [job IDs] # # # Takes as args a string of qsub job IDs and periodically monitors them. Once they all finish, it returns 0 # # If any of the jobs go into error state, an error is printed to stderr and the program waits for the non-error # jobs to finish, then returns 1 # # Usual qstat format - check this at run time # job-ID prior name user state submit/start at queue slots ja-task-ID # First thing to do is parse our input my ( $verbose, $delay, @jobIDs ) = @ARGV; # Check for user stupidity if( $delay < 10 ) { print STDERR "Sleep period is too short, will poll queue once every 10 seconds\n"; $delay = 10; } elsif( $delay > 3600 ) { print STDERR "Sleep period is too long, will poll queue once every 60 minutes\n"; $delay = 3600; } print " Waiting for " . scalar( @jobIDs ) . " jobs: @jobIDs\n"; my $user=`whoami`; my $qstatOutput = `qstat -u $user`; if( !scalar(@jobIDs) || !$qstatOutput ) { # Nothing to do exit 0; } my @qstatLines = split("\n", $qstatOutput); # my @header = split('\s+', trim($qstatLines[0])); # Position in qstat output of tokens we want # Here we hardcode the values that work at UVa my $jobID_Pos = 0; my $statePos = 9; # foreach my $i (0..$#header) # { # if ( $header[$i] eq "Job ID" ) # { # $jobID_Pos = $i; # } # elsif ($header[$i] eq "state") # { # $statePos = $i; # } # } # If we can't parse the job IDs, something is very wrong # if ($jobID_Pos < 0 || $statePos < 0) # { # die "Cannot find job-ID and state field in qstat output, cannot monitor jobs\n"; # } # Now check on all of our jobs my $jobsIncomplete = 1; # Set to 1 for any job in an error state my $haveErrors = 0; while( $jobsIncomplete ) { # Jobs that are still showing up in qstat $jobsIncomplete = 0; foreach my $job (@jobIDs) { # iterate over all user jobs in the queue qstatLine: foreach my $line ( @qstatLines ) { # trim string for trailing white space so that the tokens are in the correct sequence # We are being paranoid by matching tokens to job-IDs this way. Less elegant than a # match but also less chance of a false-positive match my @tokens = split( '\s+', trim( $line ) ); # The qstat command only prints the first 15 characters of the job # so we only compare the first 15 characters my $job_short = substr( $job, 0, 15 ); if( @tokens > 0 && ( $tokens[$jobID_Pos] =~ m/$job_short/ ) ) { # Check status - there's no error state in PBS # so we simply skip over this check # if( $tokens[$statePos] =~ m/E/ ) # { # $haveErrors = 1; # } # else # { $jobsIncomplete = $jobsIncomplete + 1; # } if( $verbose ) { print " Job $job is in state $tokens[$statePos]\n"; } } last qstatLine if ( @tokens > 0 && ( $tokens[$jobID_Pos] =~ m/$job_short/ ) ); } } if( $jobsIncomplete ) { if( $verbose ) { my $timestamp = `date`; chomp $timestamp; print " ($timestamp) Still waiting for $jobsIncomplete jobs\n\n"; } # Use of backticks rather than system permits a ctrl+c to work `sleep $delay`; $qstatOutput = `qstat -u $user`; @qstatLines = split("\n", $qstatOutput); } } if ($haveErrors) { print " No more jobs to run - some jobs had errors\n\n"; exit 1; } else { print " No more jobs in queue\n\n"; exit 0; } sub trim { my ($string) = @_; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } ants-2.2.0/Scripts/waitForSGEQJobs.pl000077500000000000000000000065471311104306400173650ustar00rootroot00000000000000#!/usr/bin/perl -w use strict; $SIG{INT}=\&myCleanup; # Usage: waitForSGEQJobs.pl [job IDs] # # # Takes as args a string of qsub job IDs and periodically monitors them. Once they all finish, it returns 0 # # If any of the jobs go into error state, an error is printed to stderr and the program waits for the non-error # jobs to finish, then returns 1 # # Usual qstat format - check this at run time # job-ID prior name user state submit/start at queue slots ja-task-ID # First thing to do is parse our input my ($verbose, $delay, @jobIDs) = @ARGV; # Check for user stupidity if ($delay < 10) { print STDERR "Sleep period is too short, will poll queue once every 10 seconds\n"; $delay = 10; } elsif ($delay > 3600) { print STDERR "Sleep period is too long, will poll queue once every 60 minutes\n"; $delay = 3600; } print " Waiting for " . scalar(@jobIDs) . " jobs: @jobIDs\n"; my $user=`whoami`; my $qstatOutput = `qstat -u $user`; if (!scalar(@jobIDs) || !$qstatOutput) { # Nothing to do exit 0; } my @qstatLines = split("\n", $qstatOutput); my @header = split('\s+', trim($qstatLines[0])); # Position in qstat output of tokens we want my $jobID_Pos = -1; my $statePos = -1; foreach my $i (0..$#header) { if ($header[$i] eq "job-ID") { $jobID_Pos = $i; } elsif ($header[$i] eq "state") { $statePos = $i; } } # If we can't parse the job IDs, something is very wrong if ($jobID_Pos < 0 || $statePos < 0) { die "Cannot find job-ID and state field in qstat output, cannot monitor jobs\n"; } # Now check on all of our jobs my $jobsIncomplete = 1; # Set to 1 for any job in an error state my $haveErrors = 0; while ($jobsIncomplete) { # Jobs that are still showing up in qstat $jobsIncomplete = 0; foreach my $job (@jobIDs) { # iterate over all user jobs in the queue qstatLine: foreach my $line (@qstatLines) { # trim string for trailing white space so that the tokens are in the correct sequence # We are being paranoid by matching tokens to job-IDs this way. Less elegant than a # match but also less chance of a false-positive match my @tokens = split('\s+', trim($line)); if ( $tokens[$jobID_Pos] eq $job) { # Check status if ($tokens[$statePos] =~ m/E/) { $haveErrors = 1; } else { $jobsIncomplete = $jobsIncomplete + 1; } if ($verbose > 1) { print " Job $job is in state $tokens[$statePos]\n"; } } last qstatLine if ( $tokens[$jobID_Pos] eq $job ); } } if ($jobsIncomplete) { if ($verbose > 0) { my $timestamp = `date`; chomp $timestamp; print " ($timestamp) Still waiting for $jobsIncomplete jobs\n\n"; } # Use of backticks rather than system permits a ctrl+c to work `sleep $delay`; $qstatOutput = `qstat -u $user`; @qstatLines = split("\n", $qstatOutput); } } if ($haveErrors) { print " No more jobs to run - some jobs had errors\n\n"; exit 1; } else { print " No more jobs in queue\n\n"; exit 0; } sub trim { my ($string) = @_; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } sub myCleanup { print " *** CTRL-C pressed, deleting remaining jobs ***\n\n"; my $cmd= "qdel " . join(" ", @jobIDs); `$cmd`; exit(1); } ants-2.2.0/Scripts/waitForSlurmJobs.pl000077500000000000000000000061671311104306400177260ustar00rootroot00000000000000#!/usr/bin/perl -w use strict; # Usage: waitForSlurmJobs.pl [job IDs] # # # Takes as args a string of sbatch job IDs and periodically monitors them. Once they all finish, it returns 0 # # If any of the jobs go into error state, an error is printed to stderr and the program waits for the non-error # jobs to finish, then returns 1 my ( $verbose, $delay, @jobIDs ) = @ARGV; my %COMPLETION_STATES = map { $_ => 1 } qw( COMPLETED ); my %FAILURE_STATES = map { $_ => 1 } qw( CANCELLED FAILED NODE_FAIL PREEMPTED TIMEOUT ); # Validate that the delay is within the acceptable range if ($delay < 10) { print STDERR "Sleep period is too short, will poll queue once every 10 seconds\n"; $delay = 10; } elsif ($delay > 3600) { print STDERR "Sleep period is too long, will poll queue once every 60 minutes\n"; $delay = 3600; } print " Waiting for " . scalar( @jobIDs ) . " jobs: @jobIDs\n"; my $errorsEncountered = 0; wait_for_all_jobs_to_complete(@jobIDs); if ($errorsEncountered) { print " No more jobs to run - some jobs had errors\n\n"; exit 1; } else { print " No more jobs in queue\n\n"; exit 0; } sub wait_for_all_jobs_to_complete { my @pendingJobs = update_pending_jobs(@_); while (@pendingJobs) { if ($verbose) { my $timestamp = `date`; chomp $timestamp; printf " ($timestamp) Still waiting for %d jobs\n\n", scalar(@pendingJobs); } # Use of backticks rather than system permits a ctrl+c to work `sleep $delay`; @pendingJobs = update_pending_jobs(@pendingJobs); }; } sub update_pending_jobs { my (@jobsToQuery) = @_; my %jobStatuses = query_job_statuses(@jobsToQuery); if (!scalar(@jobsToQuery) || !%jobStatuses) { # No more jobs remain return (); } if ($verbose) { while (my ($job, $status) = each(%jobStatuses)) { print(" Job $job is in state $status\n"); } } my @terminatedJobs = grep { !exists($jobStatuses{$jobsToQuery[$_]}) || exists($COMPLETION_STATES{$jobStatuses{$jobsToQuery[$_]}}) } 0..$#jobsToQuery; my @failedJobs = grep { exists($jobStatuses{$jobsToQuery[$_]}) && exists($FAILURE_STATES{$jobStatuses{$jobsToQuery[$_]}}) } 0..$#jobsToQuery; if (@failedJobs) { $errorsEncountered = 1; } push @terminatedJobs, @failedJobs; foreach my $index (reverse(@terminatedJobs)) { splice @jobsToQuery, $index, 1; } return @jobsToQuery; } sub query_job_statuses { my (@jobsToQuery) = @_; my $user = trim(`whoami`); my $squeueOutput = qx/squeue --noheader --user="$user" --format="%i,%T" --jobs=${\join(',', @jobsToQuery)}/; my $exitcode = $? >> 8; my %jobStatuses = (); if ($exitcode == 0) { %jobStatuses = map { my @statusParts = split(",", $_); $statusParts[0] => $statusParts[1]; } split("\n", trim($squeueOutput)); } return %jobStatuses; } sub trim { my ($string) = @_; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } ants-2.2.0/Scripts/waitForXGridJobs.pl000077500000000000000000000063451311104306400176370ustar00rootroot00000000000000#!/usr/bin/perl # # Usage: waitForXGridJobs.pl [-verbose] [-delay N] [-xgridflags string] job_IDs # # Minimal call would be (e.g., to wait for a single job ID #200): # waitForXGridJobs.pl 200 # # delay: Poll interval in seconds (default is 30) # verbose: Be verbose # xgridflags: Flags to pass to XGrid. Odds are you are going to want to put this # string in quotes as you'll be passing in several flags as this one string. # # More complex call: # waitForXGridJobs.pl -delay 60 -xgridflags "-p password -h hostname.local" 100 101 102 103 104 # # # Craig Stark, June 2010 # # Code based loosly on waitForSGEJobs.pl use Getopt::Long; if ($ARGV[0] =~ /help/) { die "syntax: waitForXGridJobs.pl [-verbose] [-delay N] [-xgridflags string] job_IDs\n"; } $result = GetOptions("verbose" => \$verbose, "delay:n" => \$delay, "xgridflags=s" => \$xgridflags); if (!$result) { die "Invalid command line options. Try waitForXGridJobs.pl -help\n"; } if (!$delay) { $delay = 30; } if (!$xgridflags) { $xgridflags = ''; } $njobs = scalar(@ARGV); if ($njobs == 0) { die "No jobs specified. Try waitForXGridJobs.pl -help\n"; } @jobIDs = @ARGV; if ($verbose) { print " Poll interval is $delay, and xgrid flags are: $xgridflags\n"; print " Waiting for " . scalar(@jobIDs) . " jobs: @jobIDs\n"; } # Check for user stupidity if ($delay < 10) { print STDERR "Sleep period is too short, will poll queue once every 10 seconds\n"; $delay = 10; } elsif ($delay > 3600) { print STDERR "Sleep period is too long, will poll queue once every 60 minutes\n"; $delay = 3600; } # Now check on all of our jobs my $jobsIncomplete = 1; # Set to 1 for any job in an error state my $haveErrors = 0; # Find the line number in the output we'll use #$statline = 0; #@result = `xgrid $xgridflags -job attributes -id $jobIDs[i]`; #for ($i=0; $i= 0) { $who = join(' ', $ARGV[0]); } if ($#ARGV >= 1) { $template = join(' ', $ARGV[1]); } if ($#ARGV >= 2) { $dim = join(' ', $ARGV[2]); } else { $dim=0; } if ($#ARGV >= 3) { $outnaming = join(' ', $ARGV[3]); } else { $outnaming=""; } opendir(DIR, $who); @filelist = glob("$who"); # @filelist2 = glob("$who2"); $ct=0; print " EXPECTING IMAGE DIMENSION ".$dim." \n \n \n "; $dir = `pwd`; $dir=substr($dir,0,length($dir)-1)."/"; $pathpre="/mnt/aibs1/avants/bin/ants/"; $qsname="superfresh.sh"; foreach $name (@filelist) { $pre=$outnaming.$name; $pre=~s/\.[^.]*$//; print $pre." \n "; $prog=$pathpre."WarpImageMultiTransform ".$dim; $exe=$prog." ".$name." ".$pre."deformed.nii ".$pre."Warp.nii ".$pre."Affine.txt -R ".$template; $exe2 = "/mnt/pkg/sge-root/bin/lx24-x86/qsub -q mac ".$qsname." ".$dir." ".$exe." "; print $exe."\n"; system $exe2; # USE qstat TO VERIFY THE JOB IS SUBMITTED $ct=$ct+1; } $user=`whoami`; chomp($user); print " you are ".$user." = a jingle-jangle-jungle "; $atcfn=0; if ($atcfn) { $waitcmd="vq -s | grep ".$user; } else { $waitcmd=" qstat -u ".$user." | grep ".substr($qsname,0,4); } $continuewaiting=1; if ($continuewaiting) { system "sleep 40"; } while($continuewaiting) { system $waitcmd; $output = `$waitcmd`; if ( $output eq "" ) { $continuewaiting=0; print "Jobs all finished!\n"; } else { print " wait ";system "sleep 30"; } } closedir(DIR); ants-2.2.0/Scripts/weightedaverage.pl000077500000000000000000000021251311104306400175730ustar00rootroot00000000000000#!/usr/bin/perl -w if ($#ARGV >= 0) { $who = join(' ', $ARGV[0]); } if ($#ARGV >= 1) { $imagedimension = join(' ', $ARGV[1]); } if ($#ARGV >= 2) { $outputname = join(' ', $ARGV[2]); } if ($#ARGV < 2) { print " usage: \n perl weightedaverage.pl *files.jpg imagedimension weight1 ... weightN \n "; exit(1); } opendir(DIR, $who); @filelist = glob("$who"); $basect=3; $ct=$basect; $dir = `pwd`; $dir=substr($dir,0,length($dir)-1)."/"; foreach $name (@filelist) { $weight=0; if ($#ARGV >= $ct) { $weight = join(' ', $ARGV[$ct]); } print " count ".$ct." & w= ".$weight." \n "; $tempname=" temp.nii "; $exe = " ImageMath ".$imagedimension." ".$tempname." m ".$name." ".$weight." \n "; system $exe; $exe = " ImageMath ".$imagedimension." ".$outputname." + ".$tempname." ".$outputname." \n "; if ($ct > $basect ) { system $exe; } else { $exe=" ImageMath ".$imagedimension." ".$outputname." m ".$name." ".$weight." \n "; print " YEE \n"; system $exe; } $ct=$ct+1; } closedir(DIR); ants-2.2.0/Scripts/weightedaverage.sh000077500000000000000000000015611311104306400175750ustar00rootroot00000000000000#!/bin/bash echo "Usage: \n sh weightedaverage.sh \"Faces*tiff\" \n " count=9 for x in `ls -tr $1 ` do count=`expr $count + 1` # Increment the counter echo " count is $count and file is $x at it $i " ANTS 2 -m PR[template.nii,$x,1,8,-0.9] -t SyN[3] -r Gauss[3,1.5] -o ROBU{$count} -i 22x21x10 --MI-option 16x8000 #-a InitAffine.txt --continue-affine 0 WarpImageMultiTransform 2 $x ROBU{$count}{$i}registered.nii ROBU{$count}Warp.nii ROBU{$count}Affine.txt ComposeMultiTransform 2 ROBU{$count}Warp.nii -R template.nii ROBU{$count}Warp.nii ROBU{$count}Affine.txt ComposeMaps 2 ROBU{$count}Warp.nii ROBU{$count}Affine.txt ROBU{$count}Warp.nii 2 ConvertToJpg ROBU{$count}{$i}registered.nii ROBU{$count}{$i}registered.jpg MeasureImageSimilarity 2 2 template.nii ROBU{$count}{$i}registered.nii metriclog.txt done # end loop ants-2.2.0/SuperBuild.cmake000066400000000000000000000256771311104306400155520ustar00rootroot00000000000000 #----------------------------------------------------------------------------- set(verbose FALSE) #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- enable_language(C) enable_language(CXX) #----------------------------------------------------------------------------- enable_testing() include(CTest) #----------------------------------------------------------------------------- include(${CMAKE_CURRENT_SOURCE_DIR}/Common.cmake) #----------------------------------------------------------------------------- # Git protocol option #----------------------------------------------------------------------------- option(${CMAKE_PROJECT_NAME}_USE_GIT_PROTOCOL "If behind a firewall turn this off to use http instead." ON) set(git_protocol "git") if(NOT ${CMAKE_PROJECT_NAME}_USE_GIT_PROTOCOL) set(git_protocol "https") endif() find_package(Git REQUIRED) # I don't know who removed the Find_Package for QT, but it needs to be here # in order to build VTK if ${LOCAL_PROJECT_NAME}_USE_QT is set. if(${LOCAL_PROJECT_NAME}_USE_QT) find_package(Qt4 REQUIRED) endif() #----------------------------------------------------------------------------- # Enable and setup External project global properties #----------------------------------------------------------------------------- include(ExternalProject) include(SlicerMacroEmptyExternalProject) include(SlicerMacroCheckExternalProjectDependency) # Compute -G arg for configuring external projects with the same CMake generator: if(CMAKE_EXTRA_GENERATOR) set(gen "${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}") else() set(gen "${CMAKE_GENERATOR}") endif() # With CMake 2.8.9 or later, the UPDATE_COMMAND is required for updates to occur. # For earlier versions, we nullify the update state to prevent updates and # undesirable rebuild. option(FORCE_EXTERNAL_BUILDS "Force rebuilding of external project (if they are updated)" OFF) if(CMAKE_VERSION VERSION_LESS 2.8.9 OR NOT FORCE_EXTERNAL_BUILDS) set(cmakeversion_external_update UPDATE_COMMAND) set(cmakeversion_external_update_value "" ) else() set(cmakeversion_external_update LOG_UPDATE ) set(cmakeversion_external_update_value 1) endif() #----------------------------------------------------------------------------- # Platform check #----------------------------------------------------------------------------- set(PLATFORM_CHECK true) if(PLATFORM_CHECK) # See CMake/Modules/Platform/Darwin.cmake) # 6.x == Mac OSX 10.2 (Jaguar) # 7.x == Mac OSX 10.3 (Panther) # 8.x == Mac OSX 10.4 (Tiger) # 9.x == Mac OSX 10.5 (Leopard) # 10.x == Mac OSX 10.6 (Snow Leopard) if (DARWIN_MAJOR_VERSION LESS "9") message(FATAL_ERROR "Only Mac OSX >= 10.5 are supported !") endif() endif() #----------------------------------------------------------------------------- # Superbuild option(s) #----------------------------------------------------------------------------- option(BUILD_STYLE_UTILS "Build uncrustify, cppcheck, & KWStyle" OFF) CMAKE_DEPENDENT_OPTION( USE_SYSTEM_Uncrustify "Use system Uncrustify program" OFF "BUILD_STYLE_UTILS" OFF ) CMAKE_DEPENDENT_OPTION( USE_SYSTEM_KWStyle "Use system KWStyle program" OFF "BUILD_STYLE_UTILS" OFF ) CMAKE_DEPENDENT_OPTION( USE_SYSTEM_Cppcheck "Use system Cppcheck program" OFF "BUILD_STYLE_UTILS" OFF ) option(ITK_BUILD_MINC_SUPPORT "Build support for MINC2" OFF) set(EXTERNAL_PROJECT_BUILD_TYPE "Release" CACHE STRING "Default build type for support libraries") option(USE_SYSTEM_ITK "Build using an externally defined version of ITK" OFF) option(USE_SYSTEM_SlicerExecutionModel "Build using an externally defined version of SlicerExecutionModel" OFF) option(USE_VTK "Build tools that depend on VTK" OFF) CMAKE_DEPENDENT_OPTION( USE_SYSTEM_VTK "Build using an externally defined version of VTK" OFF "USE_VTK" OFF ) option(BUILD_ALL_ANTS_APPS "Build all ANTs apps" ON) option(RUN_SHORT_TESTS "Run the quick unit tests." ON ) option(RUN_LONG_TESTS "Run the time consuming tests. i.e. real world registrations" ON ) option(OLD_BASELINE_TESTS "Use reported metrics from old tests" OFF ) #------------------------------------------------------------------------------ # ${LOCAL_PROJECT_NAME} dependency list #------------------------------------------------------------------------------ set(ITK_EXTERNAL_NAME ITKv${ITK_VERSION_MAJOR}) set(${LOCAL_PROJECT_NAME}_DEPENDENCIES ${ITK_EXTERNAL_NAME} ) if(USE_VTK) set(${LOCAL_PROJECT_NAME}_DEPENDENCIES VTK ) list(APPEND ${LOCAL_PROJECT_NAME}_DEPENDENCIES ${ITK_EXTERNAL_NAME} ) endif() if(BUILD_STYLE_UTILS) list(APPEND ${LOCAL_PROJECT_NAME}_DEPENDENCIES Cppcheck KWStyle Uncrustify) endif() #----------------------------------------------------------------------------- # Define Superbuild global variables #----------------------------------------------------------------------------- # This variable will contain the list of CMake variable specific to each external project # that should passed to ${CMAKE_PROJECT_NAME}. # The item of this list should have the following form: : # where '' is an external project variable and TYPE is either BOOL, STRING, PATH or FILEPATH. # TODO Variable appended to this list will be automatically exported in ${LOCAL_PROJECT_NAME}Config.cmake, # prefix '${LOCAL_PROJECT_NAME}_' will be prepended if it applies. set(${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS) # The macro '_expand_external_project_vars' can be used to expand the list of . set(${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_ARGS) # List of CMake args to configure BRAINS set(${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARNAMES) # List of CMake variable names # Convenient macro allowing to expand the list of EP_VAR listed in ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS # The expanded arguments will be appended to the list ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_ARGS # Similarly the name of the EP_VARs will be appended to the list ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARNAMES. macro(_expand_external_project_vars) set(${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_ARGS "") set(${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARNAMES "") foreach(arg ${${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS}) string(REPLACE ":" ";" varname_and_vartype ${arg}) set(target_info_list ${target_info_list}) list(GET varname_and_vartype 0 _varname) list(GET varname_and_vartype 1 _vartype) list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_ARGS -D${_varname}:${_vartype}=${${_varname}}) list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARNAMES ${_varname}) endforeach() endmacro() #----------------------------------------------------------------------------- # Common external projects CMake variables #----------------------------------------------------------------------------- list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS USE_VTK:BOOL CMAKE_BUILD_TYPE:PATH MAKECOMMAND:STRING CMAKE_SKIP_RPATH:BOOL CMAKE_BUILD_TYPE:STRING BUILD_SHARED_LIBS:BOOL CMAKE_CXX_COMPILER:PATH CMAKE_CXX_FLAGS_RELEASE:STRING CMAKE_CXX_FLAGS_DEBUG:STRING CMAKE_CXX_FLAGS:STRING CMAKE_C_COMPILER:PATH CMAKE_C_FLAGS_RELEASE:STRING CMAKE_C_FLAGS_DEBUG:STRING CMAKE_C_FLAGS:STRING CMAKE_SHARED_LINKER_FLAGS:STRING CMAKE_EXE_LINKER_FLAGS:STRING CMAKE_MODULE_LINKER_FLAGS:STRING CMAKE_GENERATOR:STRING CMAKE_EXTRA_GENERATOR:STRING CMAKE_INSTALL_PREFIX:PATH CMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH CMAKE_ARCHIVE_OUTPUT_DIRECTORY:PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH CMAKE_BUNDLE_OUTPUT_DIRECTORY:PATH CTEST_NEW_FORMAT:BOOL MEMORYCHECK_COMMAND_OPTIONS:STRING MEMORYCHECK_COMMAND:PATH CMAKE_SHARED_LINKER_FLAGS:STRING CMAKE_EXE_LINKER_FLAGS:STRING CMAKE_MODULE_LINKER_FLAGS:STRING SITE:STRING BUILDNAME:STRING ) _expand_external_project_vars() set(COMMON_EXTERNAL_PROJECT_ARGS ${${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_ARGS}) set(extProjName ${LOCAL_PROJECT_NAME}) set(proj ${LOCAL_PROJECT_NAME}) SlicerMacroCheckExternalProjectDependency(${proj}) #----------------------------------------------------------------------------- # Set CMake OSX variable to pass down the external project #----------------------------------------------------------------------------- set(CMAKE_OSX_EXTERNAL_PROJECT_ARGS) if(APPLE) list(APPEND CMAKE_OSX_EXTERNAL_PROJECT_ARGS -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}) endif() #----------------------------------------------------------------------------- # Add external project CMake args #----------------------------------------------------------------------------- if(USE_VTK) list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS USE_VTK:BOOL VTK_DIR:PATH ) endif() list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS BUILD_EXAMPLES:BOOL BUILD_TESTING:BOOL ITK_VERSION_MAJOR:STRING ITK_DIR:PATH BUILD_ALL_ANTS_APPS:BOOL RUN_SHORT_TESTS:BOOL RUN_LONG_TESTS:BOOL OLD_BASELINE_TESTS:BOOL ${LOCAL_PROJECT_NAME}_CLI_LIBRARY_OUTPUT_DIRECTORY:PATH ${LOCAL_PROJECT_NAME}_CLI_ARCHIVE_OUTPUT_DIRECTORY:PATH ${LOCAL_PROJECT_NAME}_CLI_RUNTIME_OUTPUT_DIRECTORY:PATH ${LOCAL_PROJECT_NAME}_CLI_INSTALL_LIBRARY_DESTINATION:PATH ${LOCAL_PROJECT_NAME}_CLI_INSTALL_ARCHIVE_DESTINATION:PATH ${LOCAL_PROJECT_NAME}_CLI_INSTALL_RUNTIME_DESTINATION:PATH INSTALL_RUNTIME_DESTINATION:STRING INSTALL_LIBRARY_DESTINATION:STRING INSTALL_ARCHIVE_DESTINATION:STRING ) _expand_external_project_vars() set(COMMON_EXTERNAL_PROJECT_ARGS ${${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_ARGS}) if(verbose) message("Inner external project args:") foreach(arg ${${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_ARGS}) message(" ${arg}") endforeach() endif() string(REPLACE ";" "^" ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARNAMES "${${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARNAMES}") if(verbose) message("Inner external project argnames:") foreach(argname ${${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARNAMES}) message(" ${argname}") endforeach() endif() #------------------------------------------------------------------------------ # Configure and build #------------------------------------------------------------------------------ set(proj ${LOCAL_PROJECT_NAME}) ExternalProject_Add(${proj} DEPENDS ${${LOCAL_PROJECT_NAME}_DEPENDENCIES} DOWNLOAD_COMMAND "" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} BINARY_DIR ${LOCAL_PROJECT_NAME}-build CMAKE_GENERATOR ${gen} CMAKE_ARGS --no-warn-unused-cli # HACK Only expected variables should be passed down. ${CMAKE_OSX_EXTERNAL_PROJECT_ARGS} ${COMMON_EXTERNAL_PROJECT_ARGS} -D${LOCAL_PROJECT_NAME}_SUPERBUILD:BOOL=OFF INSTALL_COMMAND "" ) ## Force rebuilding of the main subproject every time building from super structure ExternalProject_Add_Step(${proj} forcebuild COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BUILD_DIR}/${proj}-prefix/src/${proj}-stamp/${proj}-build DEPENDEES configure DEPENDERS build ALWAYS 1 ) ants-2.2.0/SuperBuild/000077500000000000000000000000001311104306400145275ustar00rootroot00000000000000ants-2.2.0/SuperBuild/External_Cppcheck.cmake000066400000000000000000000074401311104306400211200ustar00rootroot00000000000000# Make sure this file is included only once by creating globally unique varibles # based on the name of this included file. get_filename_component(CMAKE_CURRENT_LIST_FILENAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) if(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED) return() endif() set(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED 1) ## External_${extProjName}.cmake files can be recurisvely included, ## and cmake variables are global, so when including sub projects it ## is important make the extProjName and proj variables ## appear to stay constant in one of these files. ## Store global variables before overwriting (then restore at end of this file.) ProjectDependancyPush(CACHED_extProjName ${extProjName}) ProjectDependancyPush(CACHED_proj ${proj}) # Make sure that the ExtProjName/IntProjName variables are unique globally # even if other External_${ExtProjName}.cmake files are sourced by # SlicerMacroCheckExternalProjectDependency set(extProjName Cppcheck ) #The find_package known name set(proj Cppcheck ) #This local name set(${extProjName}_REQUIRED_VERSION "") #If a required version is necessary, then set this, else leave blank #if(${USE_SYSTEM_${extProjName}}) # unset(${extProjName}_DIR CACHE) #endif() # Sanity checks if(DEFINED ${extProjName}_DIR AND NOT EXISTS ${${extProjName}_DIR}) message(FATAL_ERROR "${extProjName}_DIR variable is defined but corresponds to non-existing directory (${${extProjName}_DIR})") endif() # Set dependency list set(${proj}_DEPENDENCIES "") # Include dependent projects if any SlicerMacroCheckExternalProjectDependency(${proj}) if(NOT ( DEFINED "${extProjName}_DIR" OR ( DEFINED "${USE_SYSTEM_${extProjName}}" AND NOT "${USE_SYSTEM_${extProjName}}" ) ) ) #message(STATUS "${__indent}Adding project ${proj}") # Set CMake OSX variable to pass down the external project set(CMAKE_OSX_EXTERNAL_PROJECT_ARGS) if(APPLE) list(APPEND CMAKE_OSX_EXTERNAL_PROJECT_ARGS -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}) endif() ### --- Project specific additions here ### --- End Project specific additions set(${proj}_REPOSITORY ${git_protocol}://github.com/danmar/cppcheck.git) set(${proj}_GIT_TAG origin/master) ExternalProject_Add(${proj} GIT_REPOSITORY ${${proj}_REPOSITORY} GIT_TAG ${${proj}_GIT_TAG} SOURCE_DIR ${proj} BUILD_IN_SOURCE 1 LOG_CONFIGURE 0 # Wrap configure in script to ignore log output from dashboards LOG_BUILD 0 # Wrap build in script to to ignore log output from dashboards LOG_TEST 0 # Wrap test in script to to ignore log output from dashboards LOG_INSTALL 0 # Wrap install in script to to ignore log output from dashboards ${cmakeversion_external_update} "${cmakeversion_external_update_value}" CONFIGURE_COMMAND "" BUILD_COMMAND HAVE_RULES=no CC=${CMAKE_C_COMPILER} CXX=$CMAKE_CXX_COMPILER{} ${CMAKE_MAKE_PROGRAM} INSTALL_COMMAND HAVE_RULES=no DESTDIR=${CMAKE_BINARY_DIR}/ PREFIX=Utils ${CMAKE_MAKE_PROGRAM} install DEPENDS ${${proj}_DEPENDENCIES} ) set(${extProjName}_EXE ${CMAKE_BINARY_DIR}/Utils/bin/cppcheck) else() if(${USE_SYSTEM_${extProjName}}) find_program(${proj}_EXE cppcheck DOC "Path of Cppcheck program") if(NOT ${proj}_EXE) message(FATAL_ERROR "To use the system ${extProjName}, set ${extProjName}_EXE") endif() message("USING the system ${extProjName}, set ${extProjName}_EXE=${${extProjName}_EXE}") endif() # The project is provided using ${extProjName}_EXE, nevertheless since other # project may depend on ${extProjName}, let's add an 'empty' one SlicerMacroEmptyExternalProject(${proj} "${${proj}_DEPENDENCIES}") endif() list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS ${extProjName}_EXE:FILEPATH) ants-2.2.0/SuperBuild/External_ITKv4.cmake000066400000000000000000000173051311104306400203020ustar00rootroot00000000000000 # Make sure this file is included only once by creating globally unique varibles # based on the name of this included file. get_filename_component(CMAKE_CURRENT_LIST_FILENAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) if(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED) return() endif() set(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED 1) ## External_${extProjName}.cmake files can be recurisvely included, ## and cmake variables are global, so when including sub projects it ## is important make the extProjName and proj variables ## appear to stay constant in one of these files. ## Store global variables before overwriting (then restore at end of this file.) ProjectDependancyPush(CACHED_extProjName ${extProjName}) ProjectDependancyPush(CACHED_proj ${proj}) # Make sure that the ExtProjName/IntProjName variables are unique globally # even if other External_${ExtProjName}.cmake files are sourced by # SlicerMacroCheckExternalProjectDependency set(extProjName ITK) #The find_package known name set(proj ITKv4) #This local name set(${extProjName}_REQUIRED_VERSION ${${extProjName}_VERSION_MAJOR}) #If a required version is necessary, then set this, else leave blank #if(${USE_SYSTEM_${extProjName}}) # unset(${extProjName}_DIR CACHE) #endif() # Sanity checks if(DEFINED ${extProjName}_DIR AND NOT EXISTS ${${extProjName}_DIR}) message(FATAL_ERROR "${extProjName}_DIR variable is defined but corresponds to non-existing directory (${${extProjName}_DIR})") endif() # Set dependency list set(${proj}_DEPENDENCIES "") if(${PROJECT_NAME}_BUILD_DICOM_SUPPORT) list(APPEND ${proj}_DEPENDENCIES DCMTK JPEG TIFF) endif() # Include dependent projects if any SlicerMacroCheckExternalProjectDependency(${proj}) if(NOT DEFINED ${extProjName}_DIR AND NOT ${USE_SYSTEM_${extProjName}}) #message(STATUS "${__indent}Adding project ${proj}") # Set CMake OSX variable to pass down the external project set(CMAKE_OSX_EXTERNAL_PROJECT_ARGS) if(APPLE) list(APPEND CMAKE_OSX_EXTERNAL_PROJECT_ARGS -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}) endif() ### --- Project specific additions here set(${proj}_DCMTK_ARGS) if(${PROJECT_NAME}_BUILD_DICOM_SUPPORT) set(${proj}_DCMTK_ARGS -DITK_USE_SYSTEM_DCMTK:BOOL=ON -DDCMTK_DIR:PATH=${DCMTK_DIR} -DModule_ITKDCMTK:BOOL=ON -DModule_ITKIODCMTK:BOOL=ON ) endif() if(${PROJECT_NAME}_BUILD_FFTWF_SUPPORT) set(${proj}_FFTWF_ARGS -DITK_USE_FFTWF:BOOL=ON ) endif() if(${PROJECT_NAME}_BUILD_FFTWD_SUPPORT) set(${proj}_FFTWD_ARGS -DITK_USE_FFTWD:BOOL=ON ) endif() if(${extProjName}_BUILD_MINC_SUPPORT) set(${proj}_MINC_ARGS -DModule_ITKIOMINC:BOOL=ON -DModule_ITKIOTransformMINC:BOOL=ON -DModule_ITKMINC:BOOL=ON ) endif() set(${proj}_WRAP_ARGS) #if(foo) #set(${proj}_WRAP_ARGS # -DINSTALL_WRAP_ITK_COMPATIBILITY:BOOL=OFF # -DWRAP_float:BOOL=ON # -DWRAP_unsigned_char:BOOL=ON # -DWRAP_signed_short:BOOL=ON # -DWRAP_unsigned_short:BOOL=ON # -DWRAP_complex_float:BOOL=ON # -DWRAP_vector_float:BOOL=ON # -DWRAP_covariant_vector_float:BOOL=ON # -DWRAP_rgb_signed_short:BOOL=ON # -DWRAP_rgb_unsigned_char:BOOL=ON # -DWRAP_rgb_unsigned_short:BOOL=ON # -DWRAP_ITK_TCL:BOOL=OFF # -DWRAP_ITK_JAVA:BOOL=OFF # -DWRAP_ITK_PYTHON:BOOL=ON # -DPYTHON_EXECUTABLE:PATH=${${CMAKE_PROJECT_NAME}_PYTHON_EXECUTABLE} # -DPYTHON_INCLUDE_DIR:PATH=${${CMAKE_PROJECT_NAME}_PYTHON_INCLUDE} # -DPYTHON_LIBRARY:FILEPATH=${${CMAKE_PROJECT_NAME}_PYTHON_LIBRARY} # ) #endif() # HACK This code fixes a loony problem with HDF5 -- it doesn't # link properly if -fopenmp is used. string(REPLACE "-fopenmp" "" ITK_CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") string(REPLACE "-fopenmp" "" ITK_CMAKE_CXX_FLAGS "${CMAKE_CX_FLAGS}") find_package(ZLIB REQUIRED) set(${proj}_CMAKE_OPTIONS -DBUILD_TESTING:BOOL=OFF -DBUILD_EXAMPLES:BOOL=OFF -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/${proj}-install -DITK_LEGACY_REMOVE:BOOL=OFF -DITK_FUTURE_LEGACY_REMOVE:=BOOL=ON -DITKV3_COMPATIBILITY:BOOL=ON -DITK_BUILD_DEFAULT_MODULES:BOOL=ON # -DITK_MODULE_Core:BOOL=ON # -DITK_MODULE_IO:BOOL=ON # -DITK_MODULE_Filtering:BOOL=ON # -DITK_MODULE_Registration:BOOL=ON #-DITK_INSTALL_NO_DEVELOPMENT:BOOL=ON -DKWSYS_USE_MD5:BOOL=ON # Required by SlicerExecutionModel -DITK_WRAPPING:BOOL=OFF #${BUILD_SHARED_LIBS} ## HACK: QUICK CHANGE -DModule_MGHIO:BOOL=ON -DModule_ITKReview:BOOL=ON -DModule_ITKVtkGlue:BOOL=OFF ${${proj}_DCMTK_ARGS} ${${proj}_WRAP_ARGS} ${${proj}_FFTWF_ARGS} ${${proj}_FFTWD_ARGS} ${${proj}_MINC_ARGS} ) if( USE_VTK STREQUAL "ON" ) set(${proj}_CMAKE_OPTIONS -DBUILD_TESTING:BOOL=OFF -DBUILD_EXAMPLES:BOOL=OFF -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/${proj}-install -DITK_LEGACY_REMOVE:BOOL=OFF -DITK_FUTURE_LEGACY_REMOVE:=BOOL=ON -DITKV3_COMPATIBILITY:BOOL=ON -DITK_BUILD_DEFAULT_MODULES:BOOL=ON #-DITK_INSTALL_NO_DEVELOPMENT:BOOL=ON -DKWSYS_USE_MD5:BOOL=ON # Required by SlicerExecutionModel -DITK_WRAPPING:BOOL=OFF #${BUILD_SHARED_LIBS} ## HACK: QUICK CHANGE -DModule_MGHIO:BOOL=ON -DModule_ITKReview:BOOL=ON -DModule_ITKVtkGlue:BOOL=ON ${${proj}_DCMTK_ARGS} ${${proj}_WRAP_ARGS} ${${proj}_FFTWF_ARGS} ${${proj}_FFTWD_ARGS} ${${proj}_MINC_ARGS} ) endif() ### --- End Project specific additions set(${proj}_REPOSITORY ${git_protocol}://github.com/InsightSoftwareConsortium/ITK.git) set(${proj}_GIT_TAG f60bbd3aadc4b9fbf2cff1cf7227cef1e92b979a) ## set(ITK_VERSION_ID ITK-4.10) ### NOTE: When updating GIT_TAG, also update ITK_VERSION_ID set(${proj}_GIT_TAG 5aad8ac86968a12be74a31ef6493811c0ea13d05) ## set(ITK_VERSION_ID ITK-4.11) ### NOTE: When updating GIT_TAG, also update ITK_VERSION_ID ExternalProject_Add(${proj} GIT_REPOSITORY ${${proj}_REPOSITORY} GIT_TAG ${${proj}_GIT_TAG} SOURCE_DIR ${proj} BINARY_DIR ${proj}-build LOG_CONFIGURE 0 # Wrap configure in script to ignore log output from dashboards LOG_BUILD 0 # Wrap build in script to to ignore log output from dashboards LOG_TEST 0 # Wrap test in script to to ignore log output from dashboards LOG_INSTALL 0 # Wrap install in script to to ignore log output from dashboards ${cmakeversion_external_update} "${cmakeversion_external_update_value}" CMAKE_GENERATOR ${gen} CMAKE_ARGS -Wno-dev --no-warn-unused-cli ${CMAKE_OSX_EXTERNAL_PROJECT_ARGS} ${COMMON_EXTERNAL_PROJECT_ARGS} ${${proj}_CMAKE_OPTIONS} ## We really do want to install in order to limit # of include paths INSTALL_COMMAND "" DEPENDS ${${proj}_DEPENDENCIES} ) set(${extProjName}_DIR ${CMAKE_BINARY_DIR}/${proj}-install/lib/cmake/${ITK_VERSION_ID}) else() if(${USE_SYSTEM_${extProjName}}) find_package(${extProjName} ${ITK_VERSION_MAJOR} REQUIRED) if(NOT ${extProjName}_DIR) message(FATAL_ERROR "To use the system ${extProjName}, set ${extProjName}_DIR") endif() message("USING the system ${extProjName}, set ${extProjName}_DIR=${${extProjName}_DIR}") endif() # The project is provided using ${extProjName}_DIR, nevertheless since other # project may depend on ${extProjName}, let's add an 'empty' one SlicerMacroEmptyExternalProject(${proj} "${${proj}_DEPENDENCIES}") endif() list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS ${extProjName}_DIR:PATH) ProjectDependancyPop(CACHED_extProjName extProjName) ProjectDependancyPop(CACHED_proj proj) ants-2.2.0/SuperBuild/External_KWStyle.cmake000066400000000000000000000073271311104306400207460ustar00rootroot00000000000000# Make sure this file is included only once by creating globally unique varibles # based on the name of this included file. get_filename_component(CMAKE_CURRENT_LIST_FILENAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) if(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED) return() endif() set(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED 1) ## External_${extProjName}.cmake files can be recurisvely included, ## and cmake variables are global, so when including sub projects it ## is important make the extProjName and proj variables ## appear to stay constant in one of these files. ## Store global variables before overwriting (then restore at end of this file.) ProjectDependancyPush(CACHED_extProjName ${extProjName}) ProjectDependancyPush(CACHED_proj ${proj}) # Make sure that the ExtProjName/IntProjName variables are unique globally # even if other External_${ExtProjName}.cmake files are sourced by # SlicerMacroCheckExternalProjectDependency set(extProjName KWStyle) #The find_package known name set(proj KWStyle) #This local name set(${extProjName}_REQUIRED_VERSION "") #If a required version is necessary, then set this, else leave blank #if(${USE_SYSTEM_${extProjName}}) # unset(${extProjName}_DIR CACHE) #endif() # Sanity checks if(DEFINED ${extProjName}_EXE AND NOT EXISTS ${${extProjName}_EXE}) message(FATAL_ERROR "${extProjName}_EXE variable is defined but corresponds to non-existing file") endif() # Set dependency list set(${proj}_DEPENDENCIES "") # Include dependent projects if any SlicerMacroCheckExternalProjectDependency(${proj}) if(NOT DEFINED ${extProjName}_EXE AND NOT ${USE_SYSTEM_${extProjName}}) #message(STATUS "${__indent}Adding project ${proj}") # Set CMake OSX variable to pass down the external project set(CMAKE_OSX_EXTERNAL_PROJECT_ARGS) if(APPLE) list(APPEND CMAKE_OSX_EXTERNAL_PROJECT_ARGS -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}) endif() ### --- Project specific additions here set(${proj}_CMAKE_OPTIONS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR}/Utils ) ### --- End Project specific additions ExternalProject_Add(${proj} CVS_REPOSITORY :pserver:anoncvs@public.kitware.com:/cvsroot/KWStyle CVS_MODULE KWStyle SOURCE_DIR ${proj} BINARY_DIR ${proj}-build LOG_CONFIGURE 0 # Wrap configure in script to ignore log output from dashboards LOG_BUILD 0 # Wrap build in script to to ignore log output from dashboards LOG_TEST 0 # Wrap test in script to to ignore log output from dashboards LOG_INSTALL 0 # Wrap install in script to to ignore log output from dashboards ${cmakeversion_external_update} "${cmakeversion_external_update_value}" CMAKE_GENERATOR ${gen} CMAKE_ARGS -Wno-dev --no-warn-unused-cli ${CMAKE_OSX_EXTERNAL_PROJECT_ARGS} ${COMMON_EXTERNAL_PROJECT_ARGS} ${${proj}_CMAKE_OPTIONS} INSTALL_COMMAND "" DEPENDS ${${proj}_DEPENDENCIES} ) set(${extProjName}_EXE ${CMAKE_BINARY_DIR}/Utils/bin/KWStyle) else() if(${USE_SYSTEM_${extProjName}}) find_program(${extProjName}_EXE ${extProjName} DOC "Path of ${extProjName} program") if(NOT ${extProjName}_EXE) message(FATAL_ERROR "To use the system ${extProjName}, set ${extProjName}_EXE") endif() message("USING the system ${extProjName}, set ${extProjName}_EXE=${${extProjName}_EXE}") endif() # The project is provided using ${extProjName}_EXE, nevertheless since other # project may depend on ${extProjName}, let's add an 'empty' one SlicerMacroEmptyExternalProject(${proj} "${${proj}_DEPENDENCIES}") endif() list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS ${extProjName}_EXE:FILEPATH) ants-2.2.0/SuperBuild/External_SlicerExecutionModel.cmake000066400000000000000000000122421311104306400234620ustar00rootroot00000000000000# Make sure this file is included only once by creating globally unique varibles # based on the name of this included file. get_filename_component(CMAKE_CURRENT_LIST_FILENAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) if(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED) return() endif() set(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED 1) ## External_${extProjName}.cmake files can be recurisvely included, ## and cmake variables are global, so when including sub projects it ## is important make the extProjName and proj variables ## appear to stay constant in one of these files. ## Store global variables before overwriting (then restore at end of this file.) ProjectDependancyPush(CACHED_extProjName ${extProjName}) ProjectDependancyPush(CACHED_proj ${proj}) # Make sure that the ExtProjName/IntProjName variables are unique globally # even if other External_${ExtProjName}.cmake files are sourced by # SlicerMacroCheckExternalProjectDependency set(extProjName SlicerExecutionModel) #The find_package known name set(proj SlicerExecutionModel) #This local name set(${extProjName}_REQUIRED_VERSION "") #If a required version is necessary, then set this, else leave blank #if(${USE_SYSTEM_${extProjName}}) # unset(${extProjName}_DIR CACHE) #endif() # Sanity checks if(DEFINED ${extProjName}_DIR AND NOT EXISTS ${${extProjName}_DIR}) message(FATAL_ERROR "${extProjName}_DIR variable is defined but corresponds to non-existing directory (${${extProjName}_DIR})") endif() # Set dependency list set(${proj}_DEPENDENCIES ITKv4) # Include dependent projects if any SlicerMacroCheckExternalProjectDependency(${proj}) if(NOT ( DEFINED "${extProjName}_DIR" OR ( DEFINED "${USE_SYSTEM_${extProjName}}" AND NOT "${USE_SYSTEM_${extProjName}}" ) ) ) #message(STATUS "${__indent}Adding project ${proj}") # Set CMake OSX variable to pass down the external project set(CMAKE_OSX_EXTERNAL_PROJECT_ARGS) if(APPLE) list(APPEND CMAKE_OSX_EXTERNAL_PROJECT_ARGS -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}) endif() ### --- Project specific additions here set(${proj}_CMAKE_OPTIONS -DBUILD_EXAMPLES:BOOL=OFF -DBUILD_TESTING:BOOL=OFF -DITK_DIR:PATH=${ITK_DIR} -DSlicerExecutionModel_DEFAULT_CLI_RUNTIME_OUTPUT_DIRECTORY:PATH=${${PROJECT_NAME}_CLI_RUNTIME_OUTPUT_DIRECTORY} -DSlicerExecutionModel_DEFAULT_CLI_LIBRARY_OUTPUT_DIRECTORY:PATH=${${PROJECT_NAME}_CLI_LIBRARY_OUTPUT_DIRECTORY} -DSlicerExecutionModel_DEFAULT_CLI_ARCHIVE_OUTPUT_DIRECTORY:PATH=${${PROJECT_NAME}_CLI_ARCHIVE_OUTPUT_DIRECTORY} -DSlicerExecutionModel_DEFAULT_CLI_INSTALL_RUNTIME_DESTINATION:STRING=${${PROJECT_NAME}_CLI_INSTALL_RUNTIME_DESTINATION} -DSlicerExecutionModel_DEFAULT_CLI_INSTALL_LIBRARY_DESTINATION:STRING=${${PROJECT_NAME}_CLI_INSTALL_LIBRARY_DESTINATION} -DSlicerExecutionModel_DEFAULT_CLI_INSTALL_ARCHIVE_DESTINATION:STRING=${${PROJECT_NAME}_CLI_INSTALL_ARCHIVE_DESTINATION} #-DSlicerExecutionModel_LIBRARY_PROPERTIES:STRING=${Slicer_LIBRARY_PROPERTIES} #-DSlicerExecutionModel_INSTALL_BIN_DIR:PATH=bin #-DSlicerExecutionModel_INSTALL_LIB_DIR:PATH=lib #-DSlicerExecutionModel_INSTALL_SHARE_DIR:PATH=${Slicer_INSTALL_ROOT}share/${SlicerExecutionModel} #-DSlicerExecutionModel_INSTALL_NO_DEVELOPMENT:BOOL=${Slicer_INSTALL_NO_DEVELOPMENT} ) ### --- End Project specific additions set(${proj}_REPOSITORY "${git_protocol}://github.com/Slicer/SlicerExecutionModel.git") set(${proj}_GIT_TAG "9202673b809fb7df0890a2b01c288dae0b02c598") ExternalProject_Add(${proj} GIT_REPOSITORY ${${proj}_REPOSITORY} GIT_TAG ${${proj}_GIT_TAG} SOURCE_DIR ${proj} BINARY_DIR ${proj}-build LOG_CONFIGURE 0 # Wrap configure in script to ignore log output from dashboards LOG_BUILD 0 # Wrap build in script to to ignore log output from dashboards LOG_TEST 0 # Wrap test in script to to ignore log output from dashboards LOG_INSTALL 0 # Wrap install in script to to ignore log output from dashboards ${cmakeversion_external_update} "${cmakeversion_external_update_value}" CMAKE_GENERATOR ${gen} CMAKE_ARGS -Wno-dev --no-warn-unused-cli ${CMAKE_OSX_EXTERNAL_PROJECT_ARGS} ${COMMON_EXTERNAL_PROJECT_ARGS} ${${proj}_CMAKE_OPTIONS} INSTALL_COMMAND "" DEPENDS ${${proj}_DEPENDENCIES} ) set(${extProjName}_DIR ${CMAKE_BINARY_DIR}/${proj}-build) else() if(${USE_SYSTEM_${extProjName}}) find_package(${extProjName} ${${extProjName}_REQUIRED_VERSION} REQUIRED) if(NOT ${extProjName}_DIR) message(FATAL_ERROR "To use the system ${extProjName}, set ${extProjName}_DIR") endif() message("USING the system ${extProjName}, set ${extProjName}_DIR=${${extProjName}_DIR}") endif() # The project is provided using ${extProjName}_DIR, nevertheless since other # project may depend on ${extProjName}, let's add an 'empty' one SlicerMacroEmptyExternalProject(${proj} "${${proj}_DEPENDENCIES}") endif() list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS ${extProjName}_DIR:PATH) ProjectDependancyPop(CACHED_extProjName extProjName) ProjectDependancyPop(CACHED_proj proj) ants-2.2.0/SuperBuild/External_Uncrustify.cmake000066400000000000000000000075211311104306400215530ustar00rootroot00000000000000# Make sure this file is included only once by creating globally unique varibles # based on the name of this included file. get_filename_component(CMAKE_CURRENT_LIST_FILENAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) if(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED) return() endif() set(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED 1) ## External_${extProjName}.cmake files can be recurisvely included, ## and cmake variables are global, so when including sub projects it ## is important make the extProjName and proj variables ## appear to stay constant in one of these files. ## Store global variables before overwriting (then restore at end of this file.) ProjectDependancyPush(CACHED_extProjName ${extProjName}) ProjectDependancyPush(CACHED_proj ${proj}) # Make sure that the ExtProjName/IntProjName variables are unique globally # even if other External_${ExtProjName}.cmake files are sourced by # SlicerMacroCheckExternalProjectDependency set(extProjName Uncrustify) #The find_package known name set(proj Uncrustify) #This local name set(${extProjName}_REQUIRED_VERSION "") #If a required version is necessary, then set this, else leave blank #if(${USE_SYSTEM_${extProjName}}) # unset(${extProjName}_EXE CACHE) #endif() # Sanity checks if(DEFINED ${extProjName}_EXE AND NOT EXISTS ${${extProjName}_EXE}) message(FATAL_ERROR "${extProjName}_EXE variable is defined but corresponds to non-existing file") endif() # Set dependency list set(${proj}_DEPENDENCIES "") # Include dependent projects if any SlicerMacroCheckExternalProjectDependency(${proj}) if(NOT ( DEFINED "${extProjName}_EXE" OR ( DEFINED "${USE_SYSTEM_${extProjName}}" AND NOT "${USE_SYSTEM_${extProjName}}" ) ) ) #message(STATUS "${__indent}Adding project ${proj}") # Set CMake OSX variable to pass down the external project set(CMAKE_OSX_EXTERNAL_PROJECT_ARGS) if(APPLE) list(APPEND CMAKE_OSX_EXTERNAL_PROJECT_ARGS -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}) endif() ### --- Project specific additions here set(${proj}_CMAKE_OPTIONS ) ### --- End Project specific additions set(${proj}_REPOSITORY "${git_protocol}://github.com/bengardner/uncrustify.git") set(${proj}_GIT_TAG "60f3681da60462eda539b78e0c6c3eea823481e5") ExternalProject_Add(${proj} GIT_REPOSITORY ${${proj}_REPOSITORY} GIT_TAG ${${proj}_GIT_TAG} SOURCE_DIR ${proj} BINARY_DIR ${proj}-build LOG_CONFIGURE 0 # Wrap configure in script to ignore log output from dashboards LOG_BUILD 0 # Wrap build in script to to ignore log output from dashboards LOG_TEST 0 # Wrap test in script to to ignore log output from dashboards LOG_INSTALL 0 # Wrap install in script to to ignore log output from dashboards ${cmakeversion_external_update} "${cmakeversion_external_update_value}" SOURCE_DIR ${proj} BINARY_DIR ${proj}-build CONFIGURE_COMMAND /configure --prefix=${CMAKE_BINARY_DIR}/Utils DEPENDS ${${proj}_DEPENDENCIES} ) set(${extProjName}_EXE ${CMAKE_BINARY_DIR}/Utils/bin/uncrustify) else() if(${USE_SYSTEM_${extProjName}}) find_package(${extProjName} ${${extProjName}_REQUIRED_VERSION} REQUIRED) if(NOT ${extProjName}_EXE) message(FATAL_ERROR "To use the system ${extProjName}, set ${extProjName}_EXE") endif() message("USING the system ${extProjName}, set ${extProjName}_EXE=${${extProjName}_EXE}") endif() # The project is provided using ${extProjName}_EXE, nevertheless since other # project may depend on ${extProjName}, let's add an 'empty' one SlicerMacroEmptyExternalProject(${proj} "${${proj}_DEPENDENCIES}") endif() list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS ${extProjName}_EXE:PATH) ProjectDependancyPop(CACHED_extProjName extProjName) ProjectDependancyPop(CACHED_proj proj) ants-2.2.0/SuperBuild/External_VTK.cmake000066400000000000000000000173741311104306400200530ustar00rootroot00000000000000# Make sure this file is included only once by creating globally unique varibles # based on the name of this included file. get_filename_component(CMAKE_CURRENT_LIST_FILENAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) if(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED) return() endif() set(${CMAKE_CURRENT_LIST_FILENAME}_FILE_INCLUDED 1) ## External_${extProjName}.cmake files can be recurisvely included, ## and cmake variables are global, so when including sub projects it ## is important make the extProjName and proj variables ## appear to stay constant in one of these files. ## Store global variables before overwriting (then restore at end of this file.) ProjectDependancyPush(CACHED_extProjName ${extProjName}) ProjectDependancyPush(CACHED_proj ${proj}) # Make sure that the ExtProjName/IntProjName variables are unique globally # even if other External_${ExtProjName}.cmake files are sourced by # SlicerMacroCheckExternalProjectDependency set(extProjName VTK) #The find_package known name set(proj VTK) #This local name # set(${extProjName}_REQUIRED_VERSION "6.2") #If a required version is necessary, then set this, else leave blank #if(${USE_SYSTEM_${extProjName}}) # unset(${extProjName}_DIR CACHE) #endif() # Sanity checks if(DEFINED ${extProjName}_DIR AND NOT EXISTS ${${extProjName}_DIR}) message(FATAL_ERROR "${extProjName}_DIR variable is defined but corresponds to non-existing directory (${${extProjName}_DIR})") endif() # Set dependency list set(${proj}_DEPENDENCIES "") if (${PROJECT_NAME}_USE_PYTHONQT) list(APPEND ${proj}_DEPENDENCIES python) endif() # Include dependent projects if any SlicerMacroCheckExternalProjectDependency(${proj}) if(NOT ( DEFINED "USE_SYSTEM_${extProjName}" AND "${USE_SYSTEM_${extProjName}}" ) ) #message(STATUS "${__indent}Adding project ${proj}") # Set CMake OSX variable to pass down the external project set(CMAKE_OSX_EXTERNAL_PROJECT_ARGS) if(APPLE) list(APPEND CMAKE_OSX_EXTERNAL_PROJECT_ARGS -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} -DVTK_REQUIRED_OBJCXX_FLAGS:STRING="") endif() ### --- Project specific additions here set(VTK_WRAP_TCL OFF) set(VTK_WRAP_PYTHON OFF) if (${PROJECT_NAME}_USE_PYTHONQT) set(VTK_WRAP_PYTHON ON) endif() set(VTK_PYTHON_ARGS) if(${PROJECT_NAME}_USE_PYTHONQT) set(VTK_PYTHON_ARGS -DVTK_INSTALL_PYTHON_USING_CMAKE:BOOL=ON -DPYTHON_EXECUTABLE:PATH=${slicer_PYTHON_EXECUTABLE} -DPYTHON_INCLUDE_DIR:PATH=${slicer_PYTHON_INCLUDE} -DPYTHON_LIBRARY:FILEPATH=${slicer_PYTHON_LIBRARY} ) endif() set(VTK_QT_ARGS) if(${PRIMARY_PROJECT_NAME}_USE_QT) if(NOT APPLE) set(VTK_QT_ARGS #-DDESIRED_QT_VERSION:STRING=4 # Unused -DVTK_USE_GUISUPPORT:BOOL=ON -DVTK_USE_QVTK_QTOPENGL:BOOL=ON -DVTK_USE_QT:BOOL=ON -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} ) else() set(VTK_QT_ARGS -DVTK_USE_CARBON:BOOL=OFF # Default to Cocoa, VTK/CMakeLists.txt will enable Carbon and disable cocoa if needed -DVTK_USE_COCOA:BOOL=ON -DVTK_USE_X:BOOL=OFF #-DVTK_USE_RPATH:BOOL=ON # Unused #-DDESIRED_QT_VERSION:STRING=4 # Unused -DVTK_USE_GUISUPPORT:BOOL=ON -DVTK_USE_QVTK_QTOPENGL:BOOL=ON -DVTK_USE_QT:BOOL=ON -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} ) endif() find_package(Qt4 REQUIRED) else() set(VTK_QT_ARGS -DVTK_USE_GUISUPPORT:BOOL=OFF -DVTK_USE_QT:BOOL=OFF ) endif() # Disable Tk when Python wrapping is enabled if (${PROJECT_NAME}_USE_PYTHONQT) list(APPEND VTK_QT_ARGS -DVTK_USE_TK:BOOL=OFF) endif() set(slicer_TCL_LIB) set(slicer_TK_LIB) set(slicer_TCLSH) set(VTK_TCL_ARGS) if(VTK_WRAP_TCL) if(WIN32) set(slicer_TCL_LIB ${CMAKE_BINARY_DIR}/tcl-build/lib/tcl84.lib) set(slicer_TK_LIB ${CMAKE_BINARY_DIR}/tcl-build/lib/tk84.lib) set(slicer_TCLSH ${CMAKE_BINARY_DIR}/tcl-build/bin/tclsh.exe) elseif(APPLE) set(slicer_TCL_LIB ${CMAKE_BINARY_DIR}/tcl-build/lib/libtcl8.4.dylib) set(slicer_TK_LIB ${CMAKE_BINARY_DIR}/tcl-build/lib/libtk8.4.dylib) set(slicer_TCLSH ${CMAKE_BINARY_DIR}/tcl-build/bin/tclsh84) else() set(slicer_TCL_LIB ${CMAKE_BINARY_DIR}/tcl-build/lib/libtcl8.4.so) set(slicer_TK_LIB ${CMAKE_BINARY_DIR}/tcl-build/lib/libtk8.4.so) set(slicer_TCLSH ${CMAKE_BINARY_DIR}/tcl-build/bin/tclsh84) endif() set(VTK_TCL_ARGS -DTCL_INCLUDE_PATH:PATH=${CMAKE_BINARY_DIR}/tcl-build/include -DTK_INCLUDE_PATH:PATH=${CMAKE_BINARY_DIR}/tcl-build/include -DTCL_LIBRARY:FILEPATH=${slicer_TCL_LIB} -DTK_LIBRARY:FILEPATH=${slicer_TK_LIB} -DTCL_TCLSH:FILEPATH=${slicer_TCLSH} ) endif() set(VTK_BUILD_STEP "") if(UNIX) configure_file(SuperBuild/External_VTK_build_step.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/External_VTK_build_step.cmake @ONLY) set(VTK_BUILD_STEP ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/External_VTK_build_step.cmake) endif() set(${proj}_CMAKE_OPTIONS -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/${proj}-install -DBUILD_EXAMPLES:BOOL=OFF -DBUILD_TESTING:BOOL=OFF -DVTK_USE_PARALLEL:BOOL=ON -DBUILD_SHARED_LIBS:BOOL=OFF -DVTK_DEBUG_LEAKS:BOOL=${${PROJECT_NAME}_USE_VTK_DEBUG_LEAKS} -DVTK_LEGACY_REMOVE:BOOL=OFF -DVTK_WRAP_TCL:BOOL=${VTK_WRAP_TCL} #-DVTK_USE_RPATH:BOOL=ON # Unused ${VTK_TCL_ARGS} -DVTK_WRAP_PYTHON:BOOL=${VTK_WRAP_PYTHON} -DVTK_INSTALL_LIB_DIR:PATH=${${PROJECT_NAME}_INSTALL_LIB_DIR} ${VTK_PYTHON_ARGS} ${VTK_QT_ARGS} ${VTK_MAC_ARGS} ) ### --- End Project specific additions set(${proj}_REPOSITORY ${git_protocol}://github.com/Kitware/VTK.git) set(${proj}_GIT_TAG acc5f269186e3571fb2a10af4448076ecac75e8e ) ExternalProject_Add(${proj} GIT_REPOSITORY ${${proj}_REPOSITORY} GIT_TAG ${${proj}_GIT_TAG} SOURCE_DIR ${proj} BINARY_DIR ${proj}-build BUILD_COMMAND ${VTK_BUILD_STEP} LOG_CONFIGURE 0 # Wrap configure in script to ignore log output from dashboards LOG_BUILD 0 # Wrap build in script to to ignore log output from dashboards LOG_TEST 0 # Wrap test in script to to ignore log output from dashboards LOG_INSTALL 0 # Wrap install in script to to ignore log output from dashboards ${cmakeversion_external_update} "${cmakeversion_external_update_value}" CMAKE_GENERATOR ${gen} CMAKE_ARGS -Wno-dev --no-warn-unused-cli ${CMAKE_OSX_EXTERNAL_PROJECT_ARGS} ${COMMON_EXTERNAL_PROJECT_ARGS} ${${proj}_CMAKE_OPTIONS} ## We really do want to install in order to limit # of include paths INSTALL_COMMAND "" DEPENDS ${${proj}_DEPENDENCIES} ) # set(VTKPatchScript ${CMAKE_CURRENT_LIST_DIR}/External_VTK_patch.cmake) # ExternalProject_Add_Step(${proj} VTKPatch # COMMENT "get rid of obsolete C/CXX flags" # DEPENDEES download # DEPENDERS configure # COMMAND ${CMAKE_COMMAND} # -DVTKSource= # -P ${VTKPatchScript} # ) set(${extProjName}_DIR ${CMAKE_BINARY_DIR}/${proj}-install) else() if(${USE_SYSTEM_${extProjName}}) find_package(${extProjName} ${${extProjName}_REQUIRED_VERSION} REQUIRED) message("USING the system ${extProjName}, set ${extProjName}_DIR=${${extProjName}_DIR}") endif() # The project is provided using ${extProjName}_DIR, nevertheless since other # project may depend on ${extProjName}, let's add an 'empty' one SlicerMacroEmptyExternalProject(${proj} "${${proj}_DEPENDENCIES}") endif() list(APPEND ${CMAKE_PROJECT_NAME}_SUPERBUILD_EP_VARS ${extProjName}_DIR:PATH) ProjectDependancyPop(CACHED_extProjName extProjName) ProjectDependancyPop(CACHED_proj proj) ants-2.2.0/SuperBuild/External_VTK_build_step.cmake.in000066400000000000000000000004701311104306400226570ustar00rootroot00000000000000 if(UNIX) if(APPLE) set(ENV{DYLD_LIBRARY_PATH} "@CMAKE_CURRENT_BINARY_DIR@/python-build/lib") else() set(ENV{LD_LIBRARY_PATH} "@CMAKE_CURRENT_BINARY_DIR@/python-build/lib") endif() execute_process( COMMAND make WORKING_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@/@proj@-build" ) endif() ants-2.2.0/SuperBuild/External_VTK_patch.cmake000066400000000000000000000016601311104306400212210ustar00rootroot00000000000000set(vtkDetCFLAGS ${VTKSource}/CMake/vtkDetermineCompilerFlags.cmake) file(READ ${vtkDetCFLAGS} code) string(REPLACE "SET(VTK_REQUIRED_C_FLAGS \"\${VTK_REQUIRED_C_FLAGS} -mlong-branch\")" "" code "${code}") string(REPLACE "SET(VTK_REQUIRED_CXX_FLAGS \"\${VTK_REQUIRED_CXX_FLAGS} -mlong-branch\")" "" code "${code}") file(WRITE ${vtkDetCFLAGS} "${code}" ) set(ftglCMakeLists_txt ${VTKSource}/Utilities/ftgl/CMakeLists.txt) file(READ ${ftglCMakeLists_txt} code) string(REPLACE " -fpascal-strings" "" code "${code}") file(WRITE ${ftglCMakeLists_txt} "${code}") find_file(vtkVRMLImporter vtkVRMLImporter.cxx HINTS ${VTKSource}/Hybrid ${VTKSource}/IO/IMPORT ) file(READ ${vtkVRMLImporter} code) string(REPLACE "#ifdef __GNUC__ #undef alloca #define alloca __builtin_alloca " "#ifdef __GNUC__ #ifndef __clang__ #undef alloca #define alloca __builtin_alloca #endif " code "${code}") file(WRITE ${vtkVRMLImporter} "${code}" ) ants-2.2.0/Temporary/000077500000000000000000000000001311104306400144335ustar00rootroot00000000000000ants-2.2.0/Temporary/antsFastMarchingImageFilter.h000066400000000000000000000407171311104306400221620ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsFMarchingImageFilter_h #define __antsFMarchingImageFilter_h #include "itkArray.h" #include "itkImageToImageFilter.h" #include "itkIndex.h" #include "itkLevelSet.h" #include "itkNeighborhoodIterator.h" #include "vnl/vnl_math.h" #include #include namespace itk { /** \class FMarchingImageFilter * \brief Solve an Eikonal equation using Fast Marching * * Fast marching solves an Eikonal equation where the speed is always * non-negative and depends on the position only. Starting from an * initial position on the front, fast marching systematically moves the * front forward one grid point at a time. * * Updates are preformed using an entropy satisfy scheme where only * "upwind" neighborhoods are used. This implementation of Fast Marching * uses a std::priority_queue to locate the next proper grid position to * update. * * Fast Marching sweeps through N grid points in (N log N) steps to obtain * the arrival time value as the front propagates through the grid. * * Implementation of this class is based on Chapter 8 of * "Level Set Methods and Fast Marching Methods", J.A. Sethian, * Cambridge Press, Second edition, 1999. * * This class is templated over the level set image type and the speed * image type. The initial front is specified by two containers: one * containing the known points and one containing the trial * points. Alive points are those that are already part of the * object, and trial points are considered for inclusion. * In order for the filter to evolve, at least some trial * points must be specified. These can for instance be specified as the layer of * pixels around the alive points. * The speed function can be specified as a speed image or a * speed constant. The speed image is set using the method * SetInput(). If the speed image is NULL, a constant speed function * is used and is specified using method the SetSpeedConstant(). * * If the speed function is constant and of value one, fast marching results * in an approximate distance function from the initial alive points. * FMarchingImageFilter is used in the ReinitializeLevelSetImageFilter * object to create a signed distance function from the zero level set. * * The algorithm can be terminated early by setting an appropriate stopping * value. The algorithm terminates when the current arrival time being * processed is greater than the stopping value. * * There are two ways to specify the output image information * ( LargestPossibleRegion, Spacing, Origin): (a) it is copied directly from * the input speed image or (b) it is specified by the user. Default values * are used if the user does not specify all the information. * * The output information is computed as follows. * If the speed image is NULL or if the OverrideOutputInformation is set to * true, the output information is set from user specified parameters. These * parameters can be specified using methods SetOutputRegion(), SetOutputSpacing(), SetOutputDirection(), * and SetOutputOrigin(). Else if the speed image is not NULL, the output information * is copied from the input speed image. * * Possible Improvements: * In the current implemenation, std::priority_queue only allows * taking nodes out from the front and putting nodes in from the back. * To update a value already on the heap, a new node is added to the heap. * The defunct old node is left on the heap. When it is removed from the * top, it will be recognized as invalid and not used. * Future implementations can implement the heap in a different way * allowing the values to be updated. This will generally require * some sift-up and sift-down functions and * an image of back-pointers going from the image to heap in order * to locate the node which is to be updated. * * \sa LevelSetTypeDefault * \ingroup LevelSetSegmentation */ template < class TLevelSet, class TSpeedImage = Image > class FMarchingImageFilter : public ImageToImageFilter { public: /** Standard class typdedefs. */ typedef FMarchingImageFilter Self; typedef ImageSource Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(FMarchingImageFilter, ImageSource); /** Typedef support of level set method types. */ typedef LevelSetTypeDefault LevelSetType; typedef typename LevelSetType::LevelSetImageType LevelSetImageType; typedef typename LevelSetType::LevelSetPointer LevelSetPointer; typedef typename LevelSetType::PixelType PixelType; typedef typename LevelSetType::NodeType NodeType; typedef typename LevelSetType::NodeContainer NodeContainer; typedef typename LevelSetType::NodeContainerPointer NodeContainerPointer; typedef typename LevelSetImageType::SizeType OutputSizeType; typedef typename LevelSetImageType::RegionType OutputRegionType; typedef typename LevelSetImageType::SpacingType OutputSpacingType; typedef typename LevelSetImageType::DirectionType OutputDirectionType; typedef typename LevelSetImageType::PointType OutputPointType; class AxisNodeType : public NodeType { public: AxisNodeType() { this->m_Axis = 0; } int GetAxis() const { return m_Axis; } void SetAxis( int axis ) { m_Axis = axis; } const AxisNodeType & operator=(const NodeType & node) { this->NodeType::operator=(node); return *this; } private: int m_Axis; }; /** SpeedImage typedef support. */ typedef TSpeedImage SpeedImageType; /** SpeedImagePointer typedef support. */ typedef typename SpeedImageType::Pointer SpeedImagePointer; typedef typename SpeedImageType::ConstPointer SpeedImageConstPointer; /** Dimension of the level set and the speed image. */ itkStaticConstMacro(SetDimension, unsigned int, LevelSetType::SetDimension); itkStaticConstMacro(SpeedImageDimension, unsigned int, SpeedImageType::ImageDimension); /** Index typedef support. */ typedef Index IndexType; /** Enum of Fast Marching algorithm point types. FarPoints represent far * away points; TrialPoints represent points within a narrowband of the * propagating front; and AlivePoints represent points which have already * been processed. Topology points were trial points but their inclusion * would have violated topology checks. */ enum LabelType { FarPoint, AlivePoint, TrialPoint, InitialTrialPoint, TopologyPoint }; /** LabelImage typedef support. */ typedef Image LabelImageType; typedef NeighborhoodIterator NeighborhoodIteratorType; /** LabelImagePointer typedef support. */ typedef typename LabelImageType::Pointer LabelImagePointer; /** ConnectedComponentImage typedef support. */ typedef Image ConnectedComponentImageType; /** ConnectedComponentImagePointer typedef support. */ typedef typename ConnectedComponentImageType::Pointer ConnectedComponentImagePointer; /** Set the container of Alive Points representing the initial front. * Alive points are represented as a VectorContainer of LevelSetNodes. */ void SetAlivePoints( NodeContainer * points ) { m_AlivePoints = points; this->Modified(); } /** Get the container of Alive Points representing the initial front. */ NodeContainerPointer GetAlivePoints() { return m_AlivePoints; } /** Set the container of Trial Points representing the initial front. * Trial points are represented as a VectorContainer of LevelSetNodes. */ void SetTrialPoints( NodeContainer * points ) { m_TrialPoints = points; this->Modified(); } /** Get the container of Trial Points representing the initial front. */ NodeContainerPointer GetTrialPoints() { return m_TrialPoints; } /** Get the point type label image. */ LabelImagePointer GetLabelImage() const { return m_LabelImage; } /** Get the point type label image. */ ConnectedComponentImagePointer GetConnectedComponentImage() const { return m_ConnectedComponentImage; } /** Set the Speed Constant. If the Speed Image is NULL, * the SpeedConstant value is used for the whole level set. * By default, the SpeedConstant is set to 1.0. */ void SetSpeedConstant( double value ) { m_SpeedConstant = value; m_InverseSpeed = -1.0 * vnl_math_sqr( 1.0 / m_SpeedConstant ); this->Modified(); } /** Get the Speed Constant. */ itkGetConstReferenceMacro( SpeedConstant, double ); /** Set/Get the Normalization Factor for the Speed Image. The values in the Speed Image is divided by this factor. This allows the use of images with integer pixel types to represent the speed. */ itkSetMacro( NormalizationFactor, double ); itkGetConstMacro( NormalizationFactor, double ); /** Set the Fast Marching algorithm Stopping Value. The Fast Marching * algorithm is terminated when the value of the smallest trial point * is greater than the stopping value. */ itkSetMacro( StoppingValue, double ); /** Get the Fast Marching algorithm Stopping Value. */ itkGetConstReferenceMacro( StoppingValue, double ); /** Set the Collect Points flag. Instrument the algorithm to collect * a container of all nodes which it has visited. Useful for * creating Narrowbands for level set algorithms that supports * narrow banding. */ itkSetMacro( CollectPoints, bool ); /** Get thConste Collect Points flag. */ itkGetConstReferenceMacro( CollectPoints, bool ); itkBooleanMacro( CollectPoints ); enum TopologyCheckType { None, NoHandles, Strict }; /** Set/Get boolean macro indicating whether the user wants to check topology. */ itkSetMacro( TopologyCheck, TopologyCheckType ); itkGetConstReferenceMacro( TopologyCheck, TopologyCheckType ); /** Get the container of Processed Points. If the CollectPoints flag * is set, the algorithm collects a container of all processed nodes. * This is useful for defining creating Narrowbands for level * set algorithms that supports narrow banding. */ NodeContainerPointer GetProcessedPoints() const { return m_ProcessedPoints; } /** The output largeset possible, spacing and origin is computed as follows. * If the speed image is NULL or if the OverrideOutputInformation is true, * the output information is set from user specified parameters. These * parameters can be specified using methods SetOutputRegion(), SetOutputSpacing(), SetOutputDirection(), * and SetOutputOrigin(). Else if the speed image is not NULL, the output information * is copied from the input speed image. */ virtual void SetOutputSize( const OutputSizeType& size ) { m_OutputRegion = size; } virtual OutputSizeType GetOutputSize() const { return m_OutputRegion.GetSize(); } itkSetMacro( OutputRegion, OutputRegionType ); itkGetConstReferenceMacro( OutputRegion, OutputRegionType ); itkSetMacro( OutputSpacing, OutputSpacingType ); itkGetConstReferenceMacro( OutputSpacing, OutputSpacingType ); itkSetMacro( OutputDirection, OutputDirectionType ); itkGetConstReferenceMacro( OutputDirection, OutputDirectionType ); itkSetMacro( OutputOrigin, OutputPointType ); itkGetConstReferenceMacro( OutputOrigin, OutputPointType ); itkSetMacro( OverrideOutputInformation, bool ); itkGetConstReferenceMacro( OverrideOutputInformation, bool ); itkBooleanMacro( OverrideOutputInformation ); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(SameDimensionCheck, (Concept::SameDimension ) ); itkConceptMacro(SpeedConvertibleToDoubleCheck, (Concept::Convertible ) ); itkConceptMacro(DoubleConvertibleToLevelSetCheck, (Concept::Convertible ) ); itkConceptMacro(LevelSetOStreamWritableCheck, (Concept::OStreamWritable ) ); /** End concept checking */ #endif protected: FMarchingImageFilter(); ~FMarchingImageFilter() { }; void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; virtual void Initialize( LevelSetImageType * ); virtual void UpdateNeighbors( const IndexType& index, const SpeedImageType *, LevelSetImageType * ); virtual double UpdateValue( const IndexType& index, const SpeedImageType *, LevelSetImageType * ); const AxisNodeType & GetNodeUsedInCalculation(unsigned int idx) const { return m_NodesUsed[idx]; } void GenerateData() ITK_OVERRIDE; /** Generate the output image meta information. */ virtual void GenerateOutputInformation() ITK_OVERRIDE; virtual void EnlargeOutputRequestedRegion(DataObject *output) ITK_OVERRIDE; /** Get Large Value. This value is used to represent the concept of infinity for the time assigned to pixels that have not been visited. This value is set by default to half the max() of the pixel type used to represent the time-crossing map. */ itkGetConstReferenceMacro( LargeValue, PixelType ); OutputRegionType m_BufferedRegion; typedef typename LevelSetImageType::IndexType LevelSetIndexType; LevelSetIndexType m_StartIndex; LevelSetIndexType m_LastIndex; itkGetConstReferenceMacro( StartIndex, LevelSetIndexType ); itkGetConstReferenceMacro( LastIndex, LevelSetIndexType ); private: FMarchingImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented NodeContainerPointer m_AlivePoints; NodeContainerPointer m_TrialPoints; LabelImagePointer m_LabelImage; ConnectedComponentImagePointer m_ConnectedComponentImage; double m_SpeedConstant; double m_InverseSpeed; double m_StoppingValue; bool m_CollectPoints; NodeContainerPointer m_ProcessedPoints; OutputRegionType m_OutputRegion; OutputPointType m_OutputOrigin; OutputSpacingType m_OutputSpacing; OutputDirectionType m_OutputDirection; bool m_OverrideOutputInformation; typename LevelSetImageType::PixelType m_LargeValue; AxisNodeType m_NodesUsed[SetDimension]; /** Trial points are stored in a min-heap. This allow efficient access * to the trial point with minimum value which is the next grid point * the algorithm processes. */ typedef std::vector HeapContainer; typedef std::greater NodeComparer; typedef std::priority_queue HeapType; HeapType m_TrialHeap; double m_NormalizationFactor; /** * Functions and variables to check for topology changes (2D/3D only). */ TopologyCheckType m_TopologyCheck; // Functions/data for the 2-D case void InitializeIndices2D(); bool IsChangeWellComposed2D( IndexType ); bool IsCriticalC1Configuration2D( Array ); bool IsCriticalC2Configuration2D( Array ); bool IsCriticalC3Configuration2D( Array ); bool IsCriticalC4Configuration2D( Array ); bool IsSpecialCaseOfC4Configuration2D( PixelType, IndexType, IndexType, IndexType ); Array m_RotationIndices[4]; Array m_ReflectionIndices[2]; // Functions/data for the 3-D case void InitializeIndices3D(); bool IsCriticalC1Configuration3D( Array ); unsigned int IsCriticalC2Configuration3D( Array ); bool IsChangeWellComposed3D( IndexType ); Array m_C1Indices[12]; Array m_C2Indices[8]; // Functions for both 2D/3D cases bool DoesVoxelChangeViolateWellComposedness( IndexType ); bool DoesVoxelChangeViolateStrictTopology( IndexType ); }; } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsFastMarchingImageFilter.hxx" #endif #endif ants-2.2.0/Temporary/antsFastMarchingImageFilter.hxx000066400000000000000000001004511311104306400225320ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsFMarchingImageFilter_hxx #define __antsFMarchingImageFilter_hxx #include "antsAllocImage.h" #include "antsFastMarchingImageFilter.h" #include "itkConnectedComponentImageFilter.h" #include "itkImageRegionIterator.h" #include "itkNumericTraits.h" #include "itkRelabelComponentImageFilter.h" #include "vnl/vnl_math.h" #include namespace itk { template FMarchingImageFilter ::FMarchingImageFilter() : m_TrialHeap() { this->ProcessObject::SetNumberOfRequiredInputs(0); OutputSizeType outputSize; outputSize.Fill( 16 ); typename LevelSetImageType::IndexType outputIndex; outputIndex.Fill( 0 ); this->m_OutputRegion.SetSize( outputSize ); this->m_OutputRegion.SetIndex( outputIndex ); this->m_OutputOrigin.Fill( 0.0 ); this->m_OutputSpacing.Fill( 1.0 ); this->m_OutputDirection.SetIdentity(); this->m_OverrideOutputInformation = false; this->m_AlivePoints = ITK_NULLPTR; this->m_TrialPoints = ITK_NULLPTR; this->m_ProcessedPoints = ITK_NULLPTR; this->m_SpeedConstant = 1.0; this->m_InverseSpeed = -1.0; this->m_LabelImage = LabelImageType::New(); this->m_LargeValue = static_cast( NumericTraits::max() / 2.0 ); this->m_StoppingValue = static_cast( this->m_LargeValue ); this->m_CollectPoints = false; this->m_NormalizationFactor = 1.0; this->m_TopologyCheck = None; } template void FMarchingImageFilter ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Alive points: " << this->m_AlivePoints.GetPointer() << std::endl; os << indent << "Trial points: " << this->m_TrialPoints.GetPointer() << std::endl; os << indent << "Speed constant: " << this->m_SpeedConstant << std::endl; os << indent << "Stopping value: " << this->m_StoppingValue << std::endl; os << indent << "Large Value: " << static_cast::PrintType>( this->m_LargeValue ) << std::endl; os << indent << "Normalization Factor: " << this->m_NormalizationFactor << std::endl; os << indent << "Topology check: "; switch( this->m_TopologyCheck ) { case None: { os << "None" << std::endl; } case NoHandles: { os << "No handles" << std::endl; } case Strict: os << "Strict" << std::endl; } os << indent << "Collect points: " << this->m_CollectPoints << std::endl; os << indent << "OverrideOutputInformation: "; os << this->m_OverrideOutputInformation << std::endl; os << indent << "OutputRegion: " << this->m_OutputRegion << std::endl; os << indent << "OutputOrigin: " << this->m_OutputOrigin << std::endl; os << indent << "OutputSpacing: " << this->m_OutputSpacing << std::endl; os << indent << "OutputDirection: " << this->m_OutputDirection << std::endl; } template void FMarchingImageFilter ::GenerateOutputInformation() { // copy output information from input image Superclass::GenerateOutputInformation(); // use user-specified output information if( this->GetInput() == ITK_NULLPTR || this->m_OverrideOutputInformation ) { LevelSetPointer output = this->GetOutput(); output->SetLargestPossibleRegion( this->m_OutputRegion ); output->SetOrigin( this->m_OutputOrigin ); output->SetSpacing( this->m_OutputSpacing ); output->SetDirection( this->m_OutputDirection ); } } template void FMarchingImageFilter ::EnlargeOutputRequestedRegion( DataObject *output ) { // enlarge the requested region of the output // to the whole data set TLevelSet * imgData; imgData = dynamic_cast( output ); if( imgData ) { imgData->SetRequestedRegionToLargestPossibleRegion(); } else { // Pointer could not be cast to TLevelSet * itkWarningMacro(<< "itk::FMarchingImageFilter" << "::EnlargeOutputRequestedRegion cannot cast " << typeid(output).name() << " to " << typeid(TLevelSet *).name() ); } } template void FMarchingImageFilter ::Initialize( LevelSetImageType * output ) { // allocate memory for the output buffer output->SetBufferedRegion( output->GetRequestedRegion() ); output->Allocate(); // cache some buffered region information this->m_BufferedRegion = output->GetBufferedRegion(); this->m_StartIndex = this->m_BufferedRegion.GetIndex(); this->m_LastIndex = this->m_StartIndex + this->m_BufferedRegion.GetSize(); typename LevelSetImageType::OffsetType offset; offset.Fill( 1 ); this->m_LastIndex -= offset; // allocate memory for the LabelImage this->m_LabelImage->CopyInformation( output ); this->m_LabelImage->SetBufferedRegion( output->GetBufferedRegion() ); this->m_LabelImage->Allocate(); // Checking for handles only requires an image to keep track of // connected components. if( this->m_TopologyCheck == NoHandles ) { this->m_ConnectedComponentImage = AllocImage(output->GetBufferedRegion(), 0); this->m_ConnectedComponentImage->SetOrigin( output->GetOrigin() ); this->m_ConnectedComponentImage->SetSpacing( output->GetSpacing() ); this->m_ConnectedComponentImage->SetDirection( output->GetDirection() ); } // set all output value to infinity typedef ImageRegionIterator OutputIterator; OutputIterator outIt( output, output->GetBufferedRegion() ); PixelType outputPixel; outputPixel = this->m_LargeValue; for( outIt.GoToBegin(); !outIt.IsAtEnd(); ++outIt ) { outIt.Set( outputPixel ); } // set all points type to FarPoint typedef ImageRegionIterator LabelIterator; LabelIterator typeIt( this->m_LabelImage, this->m_LabelImage->GetBufferedRegion() ); for( typeIt.GoToBegin(); !typeIt.IsAtEnd(); ++typeIt ) { typeIt.Set( FarPoint ); } // process input alive points AxisNodeType node; if( this->m_AlivePoints ) { typename NodeContainer::ConstIterator pointsIter = this->m_AlivePoints->Begin(); typename NodeContainer::ConstIterator pointsEnd = this->m_AlivePoints->End(); for( ; pointsIter != pointsEnd; ++pointsIter ) { // get node from alive points container node = pointsIter.Value(); // check if node index is within the output level set if( !this->m_BufferedRegion.IsInside( node.GetIndex() ) ) { continue; } // make this an alive point this->m_LabelImage->SetPixel( node.GetIndex(), AlivePoint ); // if( this->m_TopologyCheck == NoHandles ) { this->m_ConnectedComponentImage->SetPixel( node.GetIndex(), NumericTraits::OneValue() ); } outputPixel = node.GetValue(); output->SetPixel( node.GetIndex(), outputPixel ); } } if( this->m_TopologyCheck == NoHandles ) { // Now create the connected component image and relabel such that labels // are 1, 2, 3, ... typedef ConnectedComponentImageFilter ConnectedComponentFilterType; typename ConnectedComponentFilterType::Pointer connecter = ConnectedComponentFilterType::New(); connecter->SetInput( this->m_ConnectedComponentImage ); typedef RelabelComponentImageFilter RelabelerType; typename RelabelerType::Pointer relabeler = RelabelerType::New(); relabeler->SetInput( connecter->GetOutput() ); relabeler->Update(); this->m_ConnectedComponentImage = relabeler->GetOutput(); } // make sure the heap is empty while( !this->m_TrialHeap.empty() ) { this->m_TrialHeap.pop(); } // process the input trial points if( this->m_TrialPoints ) { typename NodeContainer::ConstIterator pointsIter = this->m_TrialPoints->Begin(); typename NodeContainer::ConstIterator pointsEnd = this->m_TrialPoints->End(); for( ; pointsIter != pointsEnd; ++pointsIter ) { // get node from trial points container node = pointsIter.Value(); // check if node index is within the output level set if( !this->m_BufferedRegion.IsInside( node.GetIndex() ) ) { continue; } #ifdef ITK_USE_DEPRECATED_FAST_MARCHING // make this a trial point m_LabelImage->SetPixel( node.GetIndex(), TrialPoint ); #else // make this an initial trial point m_LabelImage->SetPixel( node.GetIndex(), InitialTrialPoint ); #endif outputPixel = node.GetValue(); output->SetPixel( node.GetIndex(), outputPixel ); this->m_TrialHeap.push( node ); } } // initialize indices if this->m_TopologyCheck is activated if( this->m_TopologyCheck != None ) { if( SetDimension == 2 ) { this->InitializeIndices2D(); } else if( SetDimension == 3 ) { this->InitializeIndices3D(); } else { itkExceptionMacro( "Topology checking is only valid for level set dimensions of 2 and 3" ); } } } template void FMarchingImageFilter ::GenerateData() { LevelSetPointer output = this->GetOutput(); SpeedImageConstPointer speedImage = this->GetInput(); this->Initialize( output ); if( this->m_CollectPoints ) { this->m_ProcessedPoints = NodeContainer::New(); } // process points on the heap AxisNodeType node; double currentValue; double oldProgress = 0; this->UpdateProgress( 0.0 ); // Send first progress event while( !this->m_TrialHeap.empty() ) { // get the node with the smallest value node = this->m_TrialHeap.top(); this->m_TrialHeap.pop(); // does this node contain the current value ? currentValue = (double) output->GetPixel( node.GetIndex() ); if( node.GetValue() != currentValue ) { continue; } // is this node already alive ? #ifdef ITK_USE_DEPRECATED_FAST_MARCHING if( m_LabelImage->GetPixel( node.GetIndex() ) != TrialPoint ) #else if( m_LabelImage->GetPixel( node.GetIndex() ) == AlivePoint ) #endif { continue; } if( this->m_TopologyCheck != None ) { bool wellComposednessViolation = this->DoesVoxelChangeViolateWellComposedness( node.GetIndex() ); bool strictTopologyViolation = this->DoesVoxelChangeViolateStrictTopology( node.GetIndex() ); if( this->m_TopologyCheck == Strict && ( wellComposednessViolation || strictTopologyViolation ) ) { output->SetPixel( node.GetIndex(), -0.00000001 ); this->m_LabelImage->SetPixel( node.GetIndex(), TopologyPoint ); continue; } if( this->m_TopologyCheck == NoHandles ) { if( wellComposednessViolation ) { output->SetPixel( node.GetIndex(), -0.00000001 ); this->m_LabelImage->SetPixel( node.GetIndex(), TopologyPoint ); continue; } if( strictTopologyViolation ) { // check for handles typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( 1 ); NeighborhoodIteratorType ItL( radius, this->m_LabelImage, this->m_LabelImage->GetBufferedRegion() ); ItL.SetLocation( node.GetIndex() ); NeighborhoodIterator ItC( radius, this->m_ConnectedComponentImage, this->m_ConnectedComponentImage->GetBufferedRegion() ); ItC.SetLocation( node.GetIndex() ); typename ConnectedComponentImageType::PixelType minLabel = NumericTraits::ZeroValue(); typename ConnectedComponentImageType::PixelType otherLabel = NumericTraits::ZeroValue(); bool doesChangeCreateHandle = false; for( unsigned int d = 0; d < SetDimension; d++ ) { if( ItL.GetNext( d ) == AlivePoint && ItL.GetPrevious( d ) == AlivePoint ) { if( ItC.GetNext( d ) == ItC.GetPrevious( d ) ) { doesChangeCreateHandle = true; } else { minLabel = vnl_math_min( ItC.GetNext( d ), ItC.GetPrevious( d ) ); otherLabel = vnl_math_max( ItC.GetNext( d ), ItC.GetPrevious( d ) ); } break; } } if( doesChangeCreateHandle ) { output->SetPixel( node.GetIndex(), -0.0001 ); this->m_LabelImage->SetPixel( node.GetIndex(), TopologyPoint ); continue; } else { for( ItC.GoToBegin(); !ItC.IsAtEnd(); ++ItC ) { if( ItC.GetCenterPixel() == otherLabel ) { ItC.SetCenterPixel( minLabel ); } } } } } } if( currentValue > this->m_StoppingValue ) { break; } if( this->m_CollectPoints ) { this->m_ProcessedPoints->InsertElement( this->m_ProcessedPoints->Size(), node ); } // set this node as alive this->m_LabelImage->SetPixel( node.GetIndex(), AlivePoint ); // for topology handle checks, we need to update the connected // component image at the current node with the appropriate label. if( this->m_TopologyCheck == NoHandles ) { typename ConnectedComponentImageType::PixelType neighborhoodLabel = NumericTraits::ZeroValue(); typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( 1 ); NeighborhoodIterator ItC( radius, this->m_ConnectedComponentImage, this->m_ConnectedComponentImage->GetBufferedRegion() ); ItC.SetLocation( node.GetIndex() ); for( unsigned int n = 0; n < ItC.Size(); n++ ) { if( n == static_cast( 0.5 * ItC.Size() ) ) { continue; } typename ConnectedComponentImageType::PixelType c = ItC.GetPixel( n ); if( c > 0 ) { neighborhoodLabel = c; break; } } if( neighborhoodLabel > 0 ) { ItC.SetCenterPixel( neighborhoodLabel ); } } // update its neighbors this->UpdateNeighbors( node.GetIndex(), speedImage, output ); // Send events every certain number of points. const double newProgress = currentValue / this->m_StoppingValue; if( newProgress - oldProgress > 0.01 ) // update every 1% { this->UpdateProgress( newProgress ); oldProgress = newProgress; if( this->GetAbortGenerateData() ) { this->InvokeEvent( AbortEvent() ); this->ResetPipeline(); ProcessAborted e(__FILE__, __LINE__); e.SetDescription("Process aborted."); e.SetLocation(ITK_LOCATION); throw e; } } } } template void FMarchingImageFilter ::UpdateNeighbors( const IndexType& index, const SpeedImageType * speedImage, LevelSetImageType * output ) { IndexType neighIndex = index; for( unsigned int j = 0; j < SetDimension; j++ ) { // update left neighbor if( index[j] > this->m_StartIndex[j] ) { neighIndex[j] = index[j] - 1; } #ifdef ITK_USE_DEPRECATED_FAST_MARCHING if( m_LabelImage->GetPixel( neighIndex ) != AlivePoint ) #else unsigned char label = m_LabelImage->GetPixel( neighIndex ); if( label != AlivePoint && label != InitialTrialPoint ) #endif { this->UpdateValue( neighIndex, speedImage, output ); } // update right neighbor if( index[j] < this->m_LastIndex[j] ) { neighIndex[j] = index[j] + 1; } #ifdef ITK_USE_DEPRECATED_FAST_MARCHING if( m_LabelImage->GetPixel( neighIndex ) != AlivePoint ) #else label = m_LabelImage->GetPixel( neighIndex ); if( label != AlivePoint && label != InitialTrialPoint ) #endif { this->UpdateValue( neighIndex, speedImage, output ); } // reset neighIndex neighIndex[j] = index[j]; } } template double FMarchingImageFilter ::UpdateValue( const IndexType& index, const SpeedImageType * speedImage, LevelSetImageType * output ) { IndexType neighIndex = index; typename TLevelSet::PixelType neighValue; PixelType outputPixel; AxisNodeType node; for( unsigned int j = 0; j < SetDimension; j++ ) { node.SetValue( this->m_LargeValue ); // find smallest valued neighbor in this dimension for( int s = -1; s < 2; s = s + 2 ) { neighIndex[j] = index[j] + s; if( neighIndex[j] > this->m_LastIndex[j] || neighIndex[j] < this->m_StartIndex[j] ) { continue; } if( this->m_LabelImage->GetPixel( neighIndex ) == AlivePoint ) { outputPixel = output->GetPixel( neighIndex ); neighValue = outputPixel; if( node.GetValue() > neighValue ) { node.SetValue( neighValue ); node.SetIndex( neighIndex ); } } } // put the minimum neighbor onto the heap this->m_NodesUsed[j] = node; this->m_NodesUsed[j].SetAxis(j); // reset neighIndex neighIndex[j] = index[j]; } // sort the local list std::sort( this->m_NodesUsed, this->m_NodesUsed + SetDimension ); // solve quadratic equation double aa, bb, cc; double solution = this->m_LargeValue; aa = 0.0; bb = 0.0; if( speedImage ) { cc = (double) speedImage->GetPixel( index ) / this->m_NormalizationFactor; cc = -1.0 * vnl_math_sqr( 1.0 / cc ); } else { cc = this->m_InverseSpeed; } OutputSpacingType spacing = this->GetOutput()->GetSpacing(); double discrim; for( unsigned int j = 0; j < SetDimension; j++ ) { node = this->m_NodesUsed[j]; if( solution >= node.GetValue() ) { const int axis = node.GetAxis(); const double spaceFactor = vnl_math_sqr( 1.0 / spacing[axis] ); const double value = double(node.GetValue() ); aa += spaceFactor; bb += value * spaceFactor; cc += vnl_math_sqr( value ) * spaceFactor; discrim = vnl_math_sqr( bb ) - aa * cc; if( discrim < 0.0 ) { // Discriminant of quadratic eqn. is negative ExceptionObject err(__FILE__, __LINE__); err.SetLocation( ITK_LOCATION ); err.SetDescription( "Discriminant of quadratic equation is negative" ); throw err; } solution = ( std::sqrt( discrim ) + bb ) / aa; } else { break; } } if( solution < this->m_LargeValue ) { // write solution to this->m_OutputLevelSet outputPixel = static_cast( solution ); output->SetPixel( index, outputPixel ); // insert point into trial heap this->m_LabelImage->SetPixel( index, TrialPoint ); node.SetValue( static_cast( solution ) ); node.SetIndex( index ); this->m_TrialHeap.push( node ); } return solution; } /** * Topology check functions */ template bool FMarchingImageFilter ::DoesVoxelChangeViolateWellComposedness( IndexType idx ) { bool isChangeWellComposed = false; if( SetDimension == 2 ) { isChangeWellComposed = this->IsChangeWellComposed2D( idx ); } else // SetDimension == 3 { isChangeWellComposed = this->IsChangeWellComposed3D( idx ); } return !isChangeWellComposed; } template bool FMarchingImageFilter ::DoesVoxelChangeViolateStrictTopology( IndexType idx ) { typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( 1 ); NeighborhoodIteratorType It( radius, this->m_LabelImage, this->m_LabelImage->GetBufferedRegion() ); It.SetLocation( idx ); unsigned int numberOfCriticalC3Configurations = 0; unsigned int numberOfFaces = 0; for( unsigned int d = 0; d < SetDimension; d++ ) { if( It.GetNext( d ) == AlivePoint ) { numberOfFaces++; } if( It.GetPrevious( d ) == AlivePoint ) { numberOfFaces++; } if( It.GetNext( d ) == AlivePoint && It.GetPrevious( d ) == AlivePoint ) { numberOfCriticalC3Configurations++; } } if( numberOfCriticalC3Configurations > 0 && numberOfFaces % 2 == 0 && numberOfCriticalC3Configurations * 2 == numberOfFaces ) { return true; } return false; } template bool FMarchingImageFilter ::IsChangeWellComposed2D( IndexType idx ) { typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( 1 ); NeighborhoodIteratorType It( radius, this->m_LabelImage, this->m_LabelImage->GetBufferedRegion() ); It.SetLocation( idx ); Array neighborhoodPixels( 9 ); // Check for critical configurations: 4 90-degree rotations for( unsigned int i = 0; i < 4; i++ ) { for( unsigned int j = 0; j < 9; j++ ) { neighborhoodPixels[j] = ( It.GetPixel( this->m_RotationIndices[i][j] ) != AlivePoint ); if( this->m_RotationIndices[i][j] == 4 ) { neighborhoodPixels[j] = !neighborhoodPixels[j]; } } if( this->IsCriticalC1Configuration2D( neighborhoodPixels ) || this->IsCriticalC2Configuration2D( neighborhoodPixels ) || this->IsCriticalC3Configuration2D( neighborhoodPixels ) || this->IsCriticalC4Configuration2D( neighborhoodPixels ) ) { return false; } } // Check for critical configurations: 2 reflections // Note that the reflections for the C1 and C2 cases // are covered by the rotation cases above (except // in the case of FullInvariance == false. for( unsigned int i = 0; i < 2; i++ ) { for( unsigned int j = 0; j < 9; j++ ) { neighborhoodPixels[j] = ( It.GetPixel( this->m_ReflectionIndices[i][j] ) != AlivePoint ); if( this->m_ReflectionIndices[i][j] == 4 ) { neighborhoodPixels[j] = !neighborhoodPixels[j]; } } // if( !this->m_FullInvariance // && ( this->IsCriticalC1Configuration2D( neighborhoodPixels ) // || this->IsCriticalC2Configuration2D( neighborhoodPixels ) ) ) // { // return false; // } if( this->IsCriticalC3Configuration2D( neighborhoodPixels ) || this->IsCriticalC4Configuration2D( neighborhoodPixels ) ) { return false; } } return true; } template bool FMarchingImageFilter ::IsCriticalC1Configuration2D( Array neighborhood ) { return !neighborhood[0] && neighborhood[1] && neighborhood[3] && !neighborhood[4] && !neighborhood[8]; } template bool FMarchingImageFilter ::IsCriticalC2Configuration2D( Array neighborhood ) { return !neighborhood[0] && neighborhood[1] && neighborhood[3] && !neighborhood[4] && neighborhood[8] && ( neighborhood[5] || neighborhood[7] ); } template bool FMarchingImageFilter ::IsCriticalC3Configuration2D( Array neighborhood ) { return !neighborhood[0] && neighborhood[1] && neighborhood[3] && !neighborhood[4] && !neighborhood[5] && neighborhood[6] && !neighborhood[7] && neighborhood[8]; } template bool FMarchingImageFilter ::IsCriticalC4Configuration2D( Array neighborhood ) { return !neighborhood[0] && neighborhood[1] && neighborhood[3] && !neighborhood[4] && !neighborhood[5] && !neighborhood[6] && !neighborhood[7] && neighborhood[8]; } template void FMarchingImageFilter ::InitializeIndices2D() { this->m_RotationIndices[0].SetSize( 9 ); this->m_RotationIndices[1].SetSize( 9 ); this->m_RotationIndices[2].SetSize( 9 ); this->m_RotationIndices[3].SetSize( 9 ); this->m_RotationIndices[0][0] = 0; this->m_RotationIndices[0][1] = 1; this->m_RotationIndices[0][2] = 2; this->m_RotationIndices[0][3] = 3; this->m_RotationIndices[0][4] = 4; this->m_RotationIndices[0][5] = 5; this->m_RotationIndices[0][6] = 6; this->m_RotationIndices[0][7] = 7; this->m_RotationIndices[0][8] = 8; this->m_RotationIndices[1][0] = 2; this->m_RotationIndices[1][1] = 5; this->m_RotationIndices[1][2] = 8; this->m_RotationIndices[1][3] = 1; this->m_RotationIndices[1][4] = 4; this->m_RotationIndices[1][5] = 7; this->m_RotationIndices[1][6] = 0; this->m_RotationIndices[1][7] = 3; this->m_RotationIndices[1][8] = 6; this->m_RotationIndices[2][0] = 8; this->m_RotationIndices[2][1] = 7; this->m_RotationIndices[2][2] = 6; this->m_RotationIndices[2][3] = 5; this->m_RotationIndices[2][4] = 4; this->m_RotationIndices[2][5] = 3; this->m_RotationIndices[2][6] = 2; this->m_RotationIndices[2][7] = 1; this->m_RotationIndices[2][8] = 0; this->m_RotationIndices[3][0] = 6; this->m_RotationIndices[3][1] = 3; this->m_RotationIndices[3][2] = 0; this->m_RotationIndices[3][3] = 7; this->m_RotationIndices[3][4] = 4; this->m_RotationIndices[3][5] = 1; this->m_RotationIndices[3][6] = 8; this->m_RotationIndices[3][7] = 5; this->m_RotationIndices[3][8] = 2; this->m_ReflectionIndices[0].SetSize( 9 ); this->m_ReflectionIndices[1].SetSize( 9 ); this->m_ReflectionIndices[0][0] = 6; this->m_ReflectionIndices[0][1] = 7; this->m_ReflectionIndices[0][2] = 8; this->m_ReflectionIndices[0][3] = 3; this->m_ReflectionIndices[0][4] = 4; this->m_ReflectionIndices[0][5] = 5; this->m_ReflectionIndices[0][6] = 0; this->m_ReflectionIndices[0][7] = 1; this->m_ReflectionIndices[0][8] = 2; this->m_ReflectionIndices[1][0] = 2; this->m_ReflectionIndices[1][1] = 1; this->m_ReflectionIndices[1][2] = 0; this->m_ReflectionIndices[1][3] = 5; this->m_ReflectionIndices[1][4] = 4; this->m_ReflectionIndices[1][5] = 3; this->m_ReflectionIndices[1][6] = 8; this->m_ReflectionIndices[1][7] = 7; this->m_ReflectionIndices[1][8] = 6; } template bool FMarchingImageFilter ::IsChangeWellComposed3D( IndexType idx ) { Array neighborhoodPixels( 8 ); typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( 1 ); NeighborhoodIteratorType It( radius, this->m_LabelImage, this->m_LabelImage->GetRequestedRegion() ); It.SetLocation( idx ); // Check for C1 critical configurations for( unsigned int i = 0; i < 12; i++ ) { for( unsigned int j = 0; j < 4; j++ ) { neighborhoodPixels[j] = ( It.GetPixel( this->m_C1Indices[i][j] ) == AlivePoint ); if( this->m_C1Indices[i][j] == 13 ) { neighborhoodPixels[j] = !neighborhoodPixels[j]; } } if( this->IsCriticalC1Configuration3D( neighborhoodPixels ) ) { return false; } } // Check for C2 critical configurations for( unsigned int i = 0; i < 8; i++ ) { for( unsigned int j = 0; j < 8; j++ ) { neighborhoodPixels[j] = ( It.GetPixel( this->m_C2Indices[i][j] ) == AlivePoint ); if( this->m_C2Indices[i][j] == 13 ) { neighborhoodPixels[j] = !neighborhoodPixels[j]; } } if( this->IsCriticalC2Configuration3D( neighborhoodPixels ) ) { return false; } } return true; } template bool FMarchingImageFilter ::IsCriticalC1Configuration3D( Array neighborhood ) { return ( neighborhood[0] && neighborhood[1] && !neighborhood[2] && !neighborhood[3] ) || ( !neighborhood[0] && !neighborhood[1] && neighborhood[2] && neighborhood[3] ); } template unsigned int FMarchingImageFilter ::IsCriticalC2Configuration3D( Array neighborhood ) { // Check if Type 1 or Type 2 for( unsigned int i = 0; i < 4; i++ ) { bool isC2 = false; if( neighborhood[2 * i] == neighborhood[2 * i + 1] ) { isC2 = true; for( unsigned int j = 0; j < 8; j++ ) { if( neighborhood[j] == neighborhood[2 * i] && j != 2 * i && j != 2 * i + 1 ) { isC2 = false; } } } if( isC2 ) { if( neighborhood[2 * i] ) { return 1; } else { return 2; } } } return 0; } template void FMarchingImageFilter ::InitializeIndices3D() { for( unsigned int i = 0; i < 12; i++ ) { this->m_C1Indices[i].SetSize( 4 ); } for( unsigned int i = 0; i < 8; i++ ) { this->m_C2Indices[i].SetSize( 8 ); } this->m_C1Indices[0][0] = 1; this->m_C1Indices[0][1] = 13; this->m_C1Indices[0][2] = 4; this->m_C1Indices[0][3] = 10; this->m_C1Indices[1][0] = 9; this->m_C1Indices[1][1] = 13; this->m_C1Indices[1][2] = 10; this->m_C1Indices[1][3] = 12; this->m_C1Indices[2][0] = 3; this->m_C1Indices[2][1] = 13; this->m_C1Indices[2][2] = 4; this->m_C1Indices[2][3] = 12; this->m_C1Indices[3][0] = 4; this->m_C1Indices[3][1] = 14; this->m_C1Indices[3][2] = 5; this->m_C1Indices[3][3] = 13; this->m_C1Indices[4][0] = 12; this->m_C1Indices[4][1] = 22; this->m_C1Indices[4][2] = 13; this->m_C1Indices[4][3] = 21; this->m_C1Indices[5][0] = 13; this->m_C1Indices[5][1] = 23; this->m_C1Indices[5][2] = 14; this->m_C1Indices[5][3] = 22; this->m_C1Indices[6][0] = 4; this->m_C1Indices[6][1] = 16; this->m_C1Indices[6][2] = 7; this->m_C1Indices[6][3] = 13; this->m_C1Indices[7][0] = 13; this->m_C1Indices[7][1] = 25; this->m_C1Indices[7][2] = 16; this->m_C1Indices[7][3] = 22; this->m_C1Indices[8][0] = 10; this->m_C1Indices[8][1] = 22; this->m_C1Indices[8][2] = 13; this->m_C1Indices[8][3] = 19; this->m_C1Indices[9][0] = 12; this->m_C1Indices[9][1] = 16; this->m_C1Indices[9][2] = 13; this->m_C1Indices[9][3] = 15; this->m_C1Indices[10][0] = 13; this->m_C1Indices[10][1] = 17; this->m_C1Indices[10][2] = 14; this->m_C1Indices[10][3] = 16; this->m_C1Indices[11][0] = 10; this->m_C1Indices[11][1] = 14; this->m_C1Indices[11][2] = 11; this->m_C1Indices[11][3] = 13; this->m_C2Indices[0][0] = 0; this->m_C2Indices[0][1] = 13; this->m_C2Indices[0][2] = 1; this->m_C2Indices[0][3] = 12; this->m_C2Indices[0][4] = 3; this->m_C2Indices[0][5] = 10; this->m_C2Indices[0][6] = 4; this->m_C2Indices[0][7] = 9; this->m_C2Indices[4][0] = 9; this->m_C2Indices[4][1] = 22; this->m_C2Indices[4][2] = 10; this->m_C2Indices[4][3] = 21; this->m_C2Indices[4][4] = 12; this->m_C2Indices[4][5] = 19; this->m_C2Indices[4][6] = 13; this->m_C2Indices[4][7] = 18; for( unsigned int i = 1; i < 4; i++ ) { int addend; if( i == 2 ) { addend = 2; } else { addend = 1; } for( unsigned int j = 0; j < 8; j++ ) { this->m_C2Indices[i][j] = this->m_C2Indices[i - 1][j] + addend; this->m_C2Indices[i + 4][j] = this->m_C2Indices[i + 3][j] + addend; } } } } // namespace itk #endif ants-2.2.0/Temporary/deprecate_itkFEMElement3DC0LinearTriangular.cxx000066400000000000000000000234261311104306400253710ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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. =========================================================================*/ // disable debug warnings in MS compiler #ifdef _MSC_VER #pragma warning(disable: 4786) #endif #include "itkFEMElement3DC0LinearTriangular.h" #include "vnl/vnl_math.h" #include "vnl/algo/vnl_svd.h" #include "vnl/algo/vnl_qr.h" namespace itk { namespace fem { const Element3DC0LinearTriangular::Float Element3DC0LinearTriangular ::trigGaussRuleInfo[6][7][4] = { // order=0, never used { { 0.0 } }, // order=1 // <-------------------------- point ---------------------------> <-------weight-----> { { 0.33333333333333333, 0.33333333333333333, 0.33333333333333333, 1.00000000000000000 } }, // order=2 { { 0.66666666666666667, 0.16666666666666667, 0.16666666666666667, 0.33333333333333333 }, { 0.16666666666666667, 0.66666666666666667, 0.16666666666666667, 0.33333333333333333 }, { 0.16666666666666667, 0.16666666666666667, 0.66666666666666667, 0.33333333333333333 } }, // order=3, p=-3 in the book { { 0.00000000000000000, 0.50000000000000000, 0.50000000000000000, 0.33333333333333333 }, { 0.50000000000000000, 0.00000000000000000, 0.50000000000000000, 0.33333333333333333 }, { 0.50000000000000000, 0.50000000000000000, 0.00000000000000000, 0.33333333333333333 } }, // order=4, p=6 in the book { { 0.10810301816807023, 0.44594849091596489, 0.44594849091596489, 0.22338158967801147 }, { 0.44594849091596489, 0.10810301816807023, 0.44594849091596489, 0.22338158967801147 }, { 0.44594849091596489, 0.44594849091596489, 0.10810301816807023, 0.22338158967801147 }, { 0.81684757298045851, 0.09157621350977074, 0.09157621350977074, 0.10995174365532187 }, { 0.09157621350977074, 0.81684757298045851, 0.09157621350977074, 0.10995174365532187 }, { 0.09157621350977074, 0.09157621350977074, 0.81684757298045851, 0.10995174365532187 } }, // order=5, p=7 in the book { { 0.33333333333333333, 0.33333333333333333, 0.33333333333333333, 0.22500000000000000 }, { 0.79742698535308732, 0.10128650732345634, 0.10128650732345634, 0.12593918054482715 }, { 0.10128650732345634, 0.79742698535308732, 0.10128650732345634, 0.12593918054482715 }, { 0.10128650732345634, 0.10128650732345634, 0.79742698535308732, 0.12593918054482715 }, { 0.05971587178976982, 0.47014206410511509, 0.47014206410511509, 0.13239415278850618 }, { 0.47014206410511509, 0.05971587178976982, 0.47014206410511509, 0.13239415278850618 }, { 0.47014206410511509, 0.47014206410511509, 0.05971587178976982, 0.13239415278850618 } } }; const unsigned int Element3DC0LinearTriangular ::Nip[6] = { 0, 1, 3, 3, 6, 7 }; void Element3DC0LinearTriangular ::GetIntegrationPointAndWeight(unsigned int i, VectorType& pt, Float& w, unsigned int order) const { // FIXME: range checking // default integration order if( order == 0 || order > 5 ) { order = DefaultIntegrationOrder; } pt.set_size(3); /* * We provide implementation for 5 different integration rules * as defined in chapter 24 - Implementation of Iso-P Truangular * Elements, of http://titan.colorado.edu/courses.d/IFEM.d/. * * Note that the order parameter here does not correspond to the * actual order of integration, but rather the degree of polynomials * that are exactly integrated. In addition, there are two integration * rules for polynomials of 2nd degree. In order to allow using both of * them, we assign the index number 3 to the second one. Note that this * does not mean that the rule is capable of integrating the polynomials * of 3rd degree. It's just an index of a rule. */ pt.copy_in(trigGaussRuleInfo[order][i]); // We scale the weight by 0.5, to take into account // the factor that must be applied when integrating. w = 0.5 * trigGaussRuleInfo[order][i][3]; } unsigned int Element3DC0LinearTriangular ::GetNumberOfIntegrationPoints(unsigned int order) const { // FIXME: range checking // default integration order if( order == 0 ) { order = DefaultIntegrationOrder; } return Nip[order]; } Element3DC0LinearTriangular::VectorType Element3DC0LinearTriangular ::ShapeFunctions( const VectorType& pt ) const { // Linear triangular element has 3 shape functions VectorType shapeF(3); // Shape functions are equal to coordinates shapeF = pt; return shapeF; } void Element3DC0LinearTriangular ::ShapeFunctionDerivatives( const VectorType &, MatrixType& shapeD ) const { // Matrix of shape functions derivatives is an // identity matrix for linear triangular element. shapeD.set_size(3, 3); shapeD.fill(0.0); shapeD[0][0] = 1.0; shapeD[1][1] = 1.0; shapeD[2][2] = 1.0; } bool Element3DC0LinearTriangular ::GetLocalFromGlobalCoordinates( const VectorType& globalPt, VectorType& localPt) const { Float x, x1, x2, x3, y, y1, y2, y3, z, z1, z2, z3, A; localPt.set_size(3); x = globalPt[0]; y = globalPt[1]; z = globalPt[2]; x1 = this->m_node[0]->GetCoordinates()[0]; y1 = this->m_node[0]->GetCoordinates()[1]; x2 = this->m_node[1]->GetCoordinates()[0]; y2 = this->m_node[1]->GetCoordinates()[1]; x3 = this->m_node[2]->GetCoordinates()[0]; y3 = this->m_node[2]->GetCoordinates()[1]; z1 = this->m_node[0]->GetCoordinates()[2]; z2 = this->m_node[1]->GetCoordinates()[2]; z3 = this->m_node[2]->GetCoordinates()[2]; // FIXME! A = x1 * y2 - x2 * y1 + x3 * y1 - x1 * y3 + x2 * y3 - x3 * y2; // localPt[0]=((y2 - y3)*x + (x3 - x2)*y + x2*y3 - x3*y2)/A; // localPt[1]=((y3 - y1)*x + (x1 - x3)*y + x3*y1 - x1*y3)/A; // localPt[2]=((y1 - y2)*x + (x2 - x1)*y + x1*y2 - x2*y1)/A; if( localPt[0] < 0.0 || localPt[0] > 1.0 || localPt[1] < 0.0 || localPt[1] > 1.0 || localPt[2] < 0.0 || localPt[2] > 1.0 ) { return false; } else { return true; } } Element3DC0LinearTriangular::Float Element3DC0LinearTriangular ::JacobianDeterminant( const VectorType& pt, const MatrixType* pJ ) const { // use heron's formula int na = 0; int nb = 1; int nc = 2; VectorType A = this->GetNode(na)->GetCoordinates(); VectorType B = this->GetNode(nb)->GetCoordinates(); VectorType C = this->GetNode(nc)->GetCoordinates(); VectorType BA = B - A; VectorType CA = C - A; VectorType CB = C - B; float L1 = CB.magnitude(); float L2 = CA.magnitude(); float L3 = BA.magnitude(); float s = (L1 + L2 + L3) * .5; Float det = sqrt( s * (s - L1) * (s - L2) * (s - L3) ); /* // use the formula for tri pqr, area is mag( vec(pq) cross vec(pr) ) VectorType a=this->GetNode(2)->GetCoordinates()-this->GetNode(0)->GetCoordinates(); VectorType b=this->GetNode(1)->GetCoordinates()-this->GetNode(0)->GetCoordinates(); VectorType c; c.set_size(3); c[0] = a[1] * b[2] - a[2] * b[1]; c[1] = a[2] * b[0] - a[0] * b[2]; c[2] = a[0] * b[1] - a[1] * b[0]; Float det=0.5*c.magnitude(); */ // ::std::cout << " area " << det << std::endl; return det; } void Element3DC0LinearTriangular ::JacobianInverse( const VectorType& pt, MatrixType& invJ, const MatrixType* pJ ) const { MatrixType* pJlocal = 0; // If Jacobian was not provided, we // need to compute it here if( pJ == 0 ) { pJlocal = new MatrixType(); this->Jacobian( pt, *pJlocal ); pJ = pJlocal; } // invJ=vnl_svd_inverse(*pJ); invJ = vnl_qr(*pJ).inverse(); /* // Note that inverse of Jacobian is not quadratic matrix MatrixType invJ2; invJ2.set_size(3,3); invJ2.fill(0); Float idet=1.0/this->JacobianDeterminant( pt, pJ ); invJ2[0][0]=idet*((*pJ)[1][1]-(*pJ)[2][1]); invJ2[0][1]=idet*((*pJ)[2][1]-(*pJ)[0][1]); invJ2[0][2]=idet*((*pJ)[0][1]-(*pJ)[1][1]); invJ2[1][0]=idet*((*pJ)[2][0]-(*pJ)[1][0]); invJ2[1][1]=idet*((*pJ)[0][0]-(*pJ)[2][0]); invJ2[1][2]=idet*((*pJ)[1][0]-(*pJ)[0][0]); ::std::cout << " pJ " << std::endl; ::std::cout << (*pJ) << std::endl; ::std::cout << " invJ " << std::endl; ::std::cout << (invJ) << std::endl; ::std::cout << " invJ2 " << std::endl; ::std::cout << (invJ2) << std::endl;*/ delete pJlocal; } /* * Draw the element on device context pDC. */ #ifdef FEM_BUILD_VISUALIZATION void Element3DC0LinearTriangular ::Draw(CDC* pDC, Solution::ConstPointer sol) const { int x1 = m_node[0]->GetCoordinates()[0] * DC_Scale; int y1 = m_node[0]->GetCoordinates()[1] * DC_Scale; int x2 = m_node[1]->GetCoordinates()[0] * DC_Scale; int y2 = m_node[1]->GetCoordinates()[1] * DC_Scale; int x3 = m_node[2]->GetCoordinates()[0] * DC_Scale; int y3 = m_node[2]->GetCoordinates()[1] * DC_Scale; x1 += sol->GetSolutionValue(this->m_node[0]->GetDegreeOfFreedom(0) ) * DC_Scale; y1 += sol->GetSolutionValue(this->m_node[0]->GetDegreeOfFreedom(1) ) * DC_Scale; x2 += sol->GetSolutionValue(this->m_node[1]->GetDegreeOfFreedom(0) ) * DC_Scale; y2 += sol->GetSolutionValue(this->m_node[1]->GetDegreeOfFreedom(1) ) * DC_Scale; x3 += sol->GetSolutionValue(this->m_node[2]->GetDegreeOfFreedom(0) ) * DC_Scale; y3 += sol->GetSolutionValue(this->m_node[2]->GetDegreeOfFreedom(1) ) * DC_Scale; pDC->MoveTo(x1, y1); pDC->LineTo(x2, y2); pDC->LineTo(x3, y3); pDC->LineTo(x1, y1); } #endif } } // end namespace itk::fem ants-2.2.0/Temporary/deprecate_itkFEMElement3DC0LinearTriangular.h000066400000000000000000000047441311104306400250200ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 __itkFEMElement3DC0LinearTriangular_h #define __itkFEMElement3DC0LinearTriangular_h #include "itkFEMElementStd.h" namespace itk { namespace fem { /** * \class Element3DC0LinearTriangular * \brief 3-noded, linear, C0 continuous finite element in 2D space. */ class Element3DC0LinearTriangular : public ElementStd<3, 3> { typedef ElementStd<3, 3> TemplatedParentClass; FEM_ABSTRACT_CLASS( Element3DC0LinearTriangular, TemplatedParentClass ) public: // //////////////////////////////////////////////////////////////////////// /* * Methods related to numeric integration */ enum { DefaultIntegrationOrder = 1 }; virtual void GetIntegrationPointAndWeight(unsigned int i, VectorType& pt, Float& w, unsigned int order) const; virtual unsigned int GetNumberOfIntegrationPoints(unsigned int order) const; // //////////////////////////////////////////////////////////////////////// /* * Methods related to the geometry of an element */ virtual VectorType ShapeFunctions( const VectorType& pt ) const; virtual void ShapeFunctionDerivatives( const VectorType& pt, MatrixType& shapeD ) const; // FIXME: Write a proper implementation virtual bool GetLocalFromGlobalCoordinates( const VectorType& globalPt, VectorType& localPt) const; virtual Float JacobianDeterminant( const VectorType& pt, const MatrixType* pJ = 0 ) const; virtual void JacobianInverse( const VectorType& pt, MatrixType& invJ, const MatrixType* pJ = 0 ) const; /** * Draw the element on the specified device context */ #ifdef FEM_BUILD_VISUALIZATION void Draw(CDC* pDC, Solution::ConstPointer sol) const; #endif /** * Constants for integration rules. */ static const Float trigGaussRuleInfo[6][7][4]; /** * Array that holds number of integration point for each order * of numerical integration. */ static const unsigned int Nip[6]; }; } } // end namespace itk::fem #endif // #ifndef __itkFEMElement3DC0LinearTriangular_h ants-2.2.0/Temporary/deprecate_itkFEMElement3DC0LinearTriangularLaplaceBeltrami.cxx000066400000000000000000000166651311104306400303420ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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. =========================================================================*/ // disable debug warnings in MS compiler #ifdef _MSC_VER #pragma warning(disable: 4786) #endif #include #include "itkFEMElement3DC0LinearTriangularLaplaceBeltrami.h" namespace itk { namespace fem { Element3DC0LinearTriangularLaplaceBeltrami ::Element3DC0LinearTriangularLaplaceBeltrami() : Superclass() { } Element3DC0LinearTriangularLaplaceBeltrami ::Element3DC0LinearTriangularLaplaceBeltrami(NodeIDType n1_, NodeIDType n2_, NodeIDType n3_, Material::ConstPointer m_) : Superclass() { // Set the geometrical points this->SetNode( 0, n1_ ); this->SetNode( 1, n2_ ); this->SetNode( 2, n3_ ); /* * Initialize the pointer to material object and check that * we were given the pointer to the right class. * If the material class was incorrect an exception is thrown. */ if( (m_mat = dynamic_cast(&*m_) ) == 0 ) { throw FEMExceptionWrongClass( __FILE__, __LINE__, "Element3DC0LinearTriangularLaplaceBeltrami::Element3DC0LinearTriangularLaplaceBeltrami()"); } } void Element3DC0LinearTriangularLaplaceBeltrami::GetStiffnessMatrix(MatrixType& Ke) const { MatrixType cot, D, BB; this->GetMaterialMatrix( D ); // // ::std::cout<< " Nip " << Nip << " w " << w << std::endl; this->GetMaterialMatrix(D); cot.set_size(3, 3); cot.fill(0.); int na = 0; int nb = 1; int nc = 2; VectorType A = this->GetNode(na)->GetCoordinates(); VectorType B = this->GetNode(nb)->GetCoordinates(); VectorType C = this->GetNode(nc)->GetCoordinates(); VectorType BA = B - A; VectorType CA = C - A; VectorType CB = C - B; float L1 = CB.magnitude(); float L2 = CA.magnitude(); float L3 = BA.magnitude(); float s = (L1 + L2 + L3) * .5; Float Area = sqrt( s * (s - L1) * (s - L2) * (s - L3) ); cot[0][0] = (2.0 * L1 * L1) * D[0][0]; cot[1][1] = (2.0 * L2 * L2) * D[0][0]; cot[2][2] = (2.0 * L3 * L3) * D[0][0]; cot[0][1] = (L3 * L3 - L1 * L1 - L2 * L2) * D[0][0]; cot[0][2] = (L2 * L2 - L1 * L1 - L3 * L3) * D[0][0]; cot[1][2] = (L1 * L1 - L3 * L3 - L2 * L2) * D[0][0]; cot[1][0] = (L3 * L3 - L1 * L1 - L2 * L2) * D[0][0]; cot[2][0] = (L2 * L2 - L1 * L1 - L3 * L3) * D[0][0]; cot[2][1] = (L1 * L1 - L3 * L3 - L2 * L2) * D[0][0]; cot = cot * 1.0 / (8.0 * Area); /* if ( this->GetNode(0)->GetDegreeOfFreedom(0)==53 || this->GetNode(1)->GetDegreeOfFreedom(0)==53 || this->GetNode(2)->GetDegreeOfFreedom(0)==53 ) { ::std::cout << " cot " << this->GetNode(0)->GetDegreeOfFreedom(0) << " " <GetNode(1)->GetDegreeOfFreedom(0) << " " <GetNode(2)->GetDegreeOfFreedom(0) <GetMaterialMatrix( D ); VectorType ip; Float w; MatrixType J; MatrixType shapeDgl; MatrixType shapeD; // //::std::cout<< " Nip " << Nip << " w " << w << std::endl; this->GetMaterialMatrix(D); cot.set_size(3,3); cot.fill(0.); int na=0; int nb=1; int nc=2; { VectorType A=this->GetNode(na)->GetCoordinates(); VectorType B=this->GetNode(nb)->GetCoordinates(); VectorType C=this->GetNode(nc)->GetCoordinates(); VectorType BA =B-A; VectorType AC =A-C; VectorType CB =C-B; float bamag=BA.magnitude(); float cbmag=CB.magnitude(); float acmag=AC.magnitude(); if (bamag > cbmag && bamag > acmag) { na=0; nb=1; nc=2; } if (cbmag > bamag && cbmag > acmag) { na=1; nb=2; nc=0; } if (acmag > bamag && acmag > cbmag) { na=2; nb=0; nc=1; } } VectorType A=this->GetNode(na)->GetCoordinates(); VectorType B=this->GetNode(nb)->GetCoordinates(); VectorType C=this->GetNode(nc)->GetCoordinates(); VectorType BA =B-A; VectorType CA =C-A; VectorType CB =C-B; float bamag=BA.magnitude(); float cbmag=CB.magnitude(); float acmag=CA.magnitude(); float t=(CA[0]*BA[0]+CA[1]*BA[1]+CA[2]*BA[2])/bamag*bamag; VectorType E = A+BA*t; VectorType CE =C-E; VectorType BE =B-E; VectorType AE =A-E; float cemag=CE.magnitude(); float bemag=CE.magnitude(); float aemag=AE.magnitude(); float h1; if (acmag > aemag) h1=acmag; else h1=aemag; float theta1=asin(cemag/h1); float h2; if (cbmag > bemag) h2=cbmag; else h2=bemag; float theta2=asin(cemag/h2); float theta3=acos(-1.0)-theta1-theta2; float cottheta1=cemag/aemag; // if (aemag == 0) cottheta1=1.0/tan(3.14159/2.0); float cottheta2=cemag/bemag; float cottheta3=1.0/tan(theta3); // if (fabs(cottheta1-1) < 1.e-6 && fabs(cottheta2-1) < 1.e-6) cottheta3=1.0; // ::std::cout <<" ct0 " << cottheta1 <<" ct1 " << cottheta2 <<" ct2 " << cottheta3 << std::endl; cot[na][na]=(cottheta3+cottheta2)*D[0][0]; cot[nb][nb]=(cottheta3+cottheta1)*D[0][0]; cot[nc][nc]=(cottheta1+cottheta2)*D[0][0]; cot[na][na]=-cottheta3*D[0][0]; cot[na][nc]=-cottheta2*D[0][0]; cot[nb][nc]=-cottheta1*D[0][0]; cot[nc][nb]=cot[nb][nc]*D[0][0]; cot[nc][na]=cot[na][nc]*D[0][0]; cot[nb][na]=cot[na][nb]*D[0][0]; cot=cot*0.5; Ke=cot; if ( this->GetNode(0)->GetDegreeOfFreedom(0)==909 || this->GetNode(1)->GetDegreeOfFreedom(0)==909 || this->GetNode(2)->GetDegreeOfFreedom(0)==909 ) { ::std::cout << " cot " << std::endl; ::std::cout << cot < { FEM_CLASS(Element3DC0LinearTriangularLaplaceBeltrami, Element3DMembrane1DOF ) public: HANDLE_ELEMENT_LOADS(); /** * Default constructor only clears the internal storage */ Element3DC0LinearTriangularLaplaceBeltrami(); /** * Construct an element by specifying pointers to * 3 points and a material. */ Element3DC0LinearTriangularLaplaceBeltrami(NodeIDType n1_, NodeIDType n2_, NodeIDType n3_, Material::ConstPointer p_ ); virtual unsigned int GetNumberOfDegreesOfFreedomPerNode( void ) const { return 1; } virtual void GetStiffnessMatrix( MatrixType& Ke ) const; // void Read( std::istream&, void* info ); }; // class Element3DC0LinearTriangularLaplaceBeltrami FEM_CLASS_INIT(Element3DC0LinearTriangularLaplaceBeltrami) } } // end namespace itk::fem #endif // #ifndef __itkFEMElement3DC0LinearTriangularLaplaceBeltrami_h ants-2.2.0/Temporary/deprecate_itkFEMElement3DC0LinearTriangularMembrane.cxx000066400000000000000000000076131311104306400270400ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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. =========================================================================*/ // disable debug warnings in MS compiler #ifdef _MSC_VER #pragma warning(disable: 4786) #endif #include #include "itkFEMElement3DC0LinearTriangularMembrane.h" namespace itk { namespace fem { Element3DC0LinearTriangularMembrane ::Element3DC0LinearTriangularMembrane() : Superclass() { } Element3DC0LinearTriangularMembrane ::Element3DC0LinearTriangularMembrane(NodeIDType n1_, NodeIDType n2_, NodeIDType n3_, Material::ConstPointer m_) : Superclass() { // Set the geometrical points this->SetNode( 0, n1_ ); this->SetNode( 1, n2_ ); this->SetNode( 2, n3_ ); /* * Initialize the pointer to material object and check that * we were given the pointer to the right class. * If the material class was incorrect an exception is thrown. */ if( (m_mat = dynamic_cast(&*m_) ) == 0 ) { throw FEMExceptionWrongClass(__FILE__, __LINE__, "Element3DC0LinearTriangularMembrane::Element3DC0LinearTriangularMembrane()"); } } /* void Element3DC0LinearTriangularMembrane::GetStiffnessMatrix(MatrixType& Ke) const { MatrixType D; unsigned int Nip=this->GetNumberOfIntegrationPoints(0); VectorType ip; Float w; this->GetIntegrationPointAndWeight(0,ip,w,0); // //::std::cout<< " Nip " << Nip << " w " << w << std::endl; this->GetMaterialMatrix(D); Ke.set_size(3,3); int na=0; int nb=1; int nc=2; { VectorType A=this->GetNode(na)->GetCoordinates(); VectorType B=this->GetNode(nb)->GetCoordinates(); VectorType C=this->GetNode(nc)->GetCoordinates(); VectorType BA =B-A; VectorType AC =A-C; VectorType CB =C-B; float bamag=BA.magnitude(); float cbmag=CB.magnitude(); float acmag=AC.magnitude(); if (bamag > cbmag && bamag > acmag) { na=0; nb=1; nc=2; } if (cbmag > bamag && cbmag > acmag) { na=1; nb=2; nc=0; } if (acmag > bamag && acmag > cbmag) { na=2; nb=0; nc=1; } } VectorType A=this->GetNode(na)->GetCoordinates(); VectorType B=this->GetNode(nb)->GetCoordinates(); VectorType C=this->GetNode(nc)->GetCoordinates(); VectorType BA =B-A; VectorType CA =C-A; VectorType CB =C-B; float bamag=BA.magnitude(); float cbmag=CB.magnitude(); float acmag=CA.magnitude(); float t=(CA[0]*BA[0]+CA[1]*BA[1]+CA[2]*BA[2])/bamag*bamag; VectorType E = A+BA*t; VectorType CE =C-E; VectorType BE =B-E; VectorType AE =A-E; float cemag=CE.magnitude(); float bemag=CE.magnitude(); float aemag=AE.magnitude(); float h1; if (acmag > aemag) h1=acmag; else h1=aemag; float theta1=asin(cemag/h1); float h2; if (cbmag > bemag) h2=cbmag; else h2=bemag; float theta2=asin(cemag/h2); float theta3=acos(-1.0)-theta1-theta2; float cottheta1=atan(theta1); float cottheta2=atan(theta2); float cottheta3=atan(theta3); Ke[0][0]=(cottheta3+cottheta2)*D[0][0]; Ke[1][1]=(cottheta3+cottheta1)*D[0][0]; Ke[2][2]=(cottheta1+cottheta2)*D[0][0]; Ke[0][1]=-cottheta3*D[0][0]; Ke[0][2]=-cottheta2*D[0][0]; Ke[1][2]=-cottheta1*D[0][0]; Ke[2][1]=Ke[1][2]*D[0][0]; Ke[2][0]=Ke[0][2]*D[0][0]; Ke[1][0]=Ke[0][1]*D[0][0]; // ::std::cout << " lapl belt " << std::endl; // ::std::cout << Ke << std::endl; } */ FEM_CLASS_REGISTER(Element3DC0LinearTriangularMembrane) } } // end namespace itk::fem ants-2.2.0/Temporary/deprecate_itkFEMElement3DC0LinearTriangularMembrane.h000066400000000000000000000037351311104306400264660ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 __itkFEMElement3DC0LinearTriangularMembrane_h #define __itkFEMElement3DC0LinearTriangularMembrane_h #include "itkFEMElement3DC0LinearTriangular.h" #include "itkFEMElement3DMembrane.h" #include "itkFEMElement3DMembrane1DOF.h" namespace itk { namespace fem { /** * \class Element3DC0LinearTriangularMembrane * \brief 3-noded finite element class in 3D space for surface membrane problem. * * This element is combined from Element3DC0LinearTriangular and Element3DMembrane. */ class Element3DC0LinearTriangularMembrane : public Element3DMembrane { FEM_CLASS(Element3DC0LinearTriangularMembrane, Element3DMembrane ) public: HANDLE_ELEMENT_LOADS(); /** * Default constructor only clears the internal storage */ Element3DC0LinearTriangularMembrane(); /** * Construct an element by specifying pointers to * 3 points and a material. */ Element3DC0LinearTriangularMembrane(NodeIDType n1_, NodeIDType n2_, NodeIDType n3_, Material::ConstPointer p_ ); // virtual unsigned int GetNumberOfDegreesOfFreedomPerNode( void ) const // { return 1; } // virtual void GetStiffnessMatrix( MatrixType& Ke ) const; // void Read( std::istream&, void* info ); }; // class Element3DC0LinearTriangularMembrane FEM_CLASS_INIT(Element3DC0LinearTriangularMembrane) } } // end namespace itk::fem #endif // #ifndef __itkFEMElement3DC0LinearTriangularMembrane_h ants-2.2.0/Temporary/itkAddConstantToImageFilter.h000066400000000000000000000104041311104306400221310ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkAddConstantToImageFilter_h #define __itkAddConstantToImageFilter_h #include "itkUnaryFunctorImageFilter.h" #include "itkNumericTraits.h" namespace itk { /** \class AddConstantToImageFilter * * \brief Add a constant to all input pixels. * * This filter is templated over the input image type * and the output image type. * * \author Tom Vercauteren, INRIA & Mauna Kea Technologies * * Based on filters from the Insight Journal paper: * http://hdl.handle.net/1926/510 * * \ingroup IntensityImageFilters Multithreaded * \sa UnaryFunctorImageFilter */ namespace Functor { template class AddConstantTo { public: AddConstantTo() : m_Constant(NumericTraits::OneValue()) { }; ~AddConstantTo() { }; bool operator!=( const AddConstantTo & other ) const { return !(*this == other); } bool operator==( const AddConstantTo & other ) const { return other.m_Constant == m_Constant; } inline TOutput operator()( const TInput & A ) const { // Because the user has to specify the constant we don't // check if the cte is not 0; return static_cast( A + m_Constant ); } void SetConstant(TConstant ct) { this->m_Constant = ct; } const TConstant & GetConstant() const { return m_Constant; } TConstant m_Constant; }; } template class AddConstantToImageFilter : public UnaryFunctorImageFilter > { public: /** Standard class typedefs. */ typedef AddConstantToImageFilter Self; typedef UnaryFunctorImageFilter< TInputImage, TOutputImage, Functor::AddConstantTo< typename TInputImage::PixelType, TConstant, typename TOutputImage::PixelType> > Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(AddConstantToImageFilter, UnaryFunctorImageFilter); /** Set the constant that will be used to multiply all the image * pixels */ void SetConstant(TConstant ct) { if( ct != this->GetFunctor().GetConstant() ) { this->GetFunctor().SetConstant(ct); this->Modified(); } } const TConstant & GetConstant() const { return this->GetFunctor().GetConstant(); } #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(InputConvertibleToOutputCheck, (Concept::Convertible ) ); itkConceptMacro(Input1Input2OutputAddOperatorCheck, (Concept::AdditiveOperators ) ); /** End concept checking */ #endif protected: AddConstantToImageFilter() { }; virtual ~AddConstantToImageFilter() { }; void PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Constant: " << static_cast::PrintType>(this->GetConstant() ) << std::endl; } private: AddConstantToImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #endif ants-2.2.0/Temporary/itkDijkstrasAlgorithm.cxx000066400000000000000000000200321311104306400214710ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit (ITK) =========================================================================*/ #ifndef _itkDijkstrasAlgorithm_cxx_ #define _itkDijkstrasAlgorithm_cxx_ #include "itkDijkstrasAlgorithm.h" namespace itk { template DijkstrasAlgorithm::DijkstrasAlgorithm() { m_Graph = GraphType::New(); m_QS = DijkstrasAlgorithmQueue::New(); m_MaxCost = vnl_huge_val(m_MaxCost); // not defined for unsigned char this->m_TotalCost = 0; }; template void DijkstrasAlgorithm::SetGraphSize(GraphSizeType Sz) { for( int i = 0; i < GraphDimension; i++ ) { m_GraphSize[i] = Sz[i]; } } template void DijkstrasAlgorithm::InitializeGraph() { m_GraphRegion.SetSize( m_GraphSize ); m_Graph->SetLargestPossibleRegion( m_GraphRegion ); m_Graph->SetRequestedRegion( m_GraphRegion ); m_Graph->SetBufferedRegion( m_GraphRegion ); m_Graph->Allocate(); GraphIteratorType GraphIterator( m_Graph, m_GraphRegion ); GraphIterator.GoToBegin(); NodeLocationType loc; while( !GraphIterator.IsAtEnd() ) { typename GraphSearchNode::Pointer G = NULL; GraphIterator.Set(G); ++GraphIterator; /* m_GraphIndex = GraphIterator.GetIndex(); //std::cout << " allocating " << m_GraphIndex << std::endl; ///GraphSearchNode::Pointer G= G=TGraphSearchNode::New(); G->SetUnVisited(); G->SetTotalCost(m_MaxCost); for (int i=0; iSetLocation(loc); G->SetPredecessor(NULL); m_Graph->SetPixel(m_GraphIndex,G);*/ m_Graph->SetPixel( GraphIterator.GetIndex(), NULL); // USE IF POINTER IMAGE defines visited } m_SearchFinished = false; } template void DijkstrasAlgorithm::InitializeQueue() { int n = m_QS->m_SourceNodes.size(); GraphIteratorType GraphIterator( m_Graph, m_GraphRegion ); m_GraphSize = m_Graph->GetLargestPossibleRegion().GetSize(); GraphIterator.GoToBegin(); m_GraphIndex = GraphIterator.GetIndex(); NodeLocationType loc; // make sure the graph contains the right pointers for( int i = 0; i < n; i++ ) { typename GraphSearchNode::Pointer G = m_QS->m_SourceNodes[i]; G->SetPredecessor(G); m_QS->m_Q.push(G); loc = G->GetLocation(); for( int d = 0; d < GraphDimension; d++ ) { m_GraphIndex[d] = (long int)(loc[d] + 0.5); } m_Graph->SetPixel(m_GraphIndex, G); } for( int i = 0; i < m_QS->m_SinkNodes.size(); i++ ) { typename GraphSearchNode::Pointer G = m_QS->m_SinkNodes[i]; G->SetPredecessor(NULL); loc = G->GetLocation(); for( int d = 0; d < GraphDimension; d++ ) { m_GraphIndex[d] = (long)loc[d]; } m_Graph->SetPixel(m_GraphIndex, G); } m_SearchFinished = false; if( m_EdgeTemplate.empty() ) { InitializeEdgeTemplate(); } } template void DijkstrasAlgorithm::InitializeEdgeTemplate (vector UserEdgeTemplate, unsigned int R) { for( int i = 0; i < GraphDimension; i++ ) { m_Radius[i] = R; } m_EdgeTemplate = UserEdgeTemplate; } template void DijkstrasAlgorithm::InitializeEdgeTemplate() { int MaxIndex = 1; for( int i = 0; i < GraphDimension; i++ ) { m_Radius[i] = 1; } for( int i = 0; i < GraphDimension; i++ ) { MaxIndex = MaxIndex * (2 * m_Radius[i] + 1); } MaxIndex = MaxIndex - 1; // int Center = MaxIndex/2; for( unsigned int i = 0; i <= MaxIndex; i++ ) { // if (i != Center) m_EdgeTemplate.insert(m_EdgeTemplate.end(), i); } } /** * Compute the local cost using Manhattan distance. */ template typename DijkstrasAlgorithm:: PixelType DijkstrasAlgorithm::LocalCost() { return 1.0; // manhattan distance }; template bool DijkstrasAlgorithm::TerminationCondition() { if( !m_QS->m_SinkNodes.empty() ) { if( m_NeighborNode == m_QS->m_SinkNodes[0] && !m_SearchFinished ) { m_SearchFinished = true; m_NeighborNode->SetPredecessor(m_CurrentNode); } } else { m_SearchFinished = true; } return m_SearchFinished; } template void DijkstrasAlgorithm::SearchEdgeSet() { // std::cout << " SES 0 " << std::endl; GraphNeighborhoodIteratorType GHood(m_Radius, m_Graph, m_Graph->GetRequestedRegion() ); GraphNeighborhoodIndexType GNI; // std::cout << " SES 1 " << std::endl; for( unsigned int i = 0; i < GraphDimension; i++ ) { // std::cout << " SES 2 " << std::endl; GNI[i] = (long)(m_CurrentNode->GetLocation()[i] + 0.5); } // std::cout << " SES 3 " << std::endl; GHood.SetLocation(GNI); for( unsigned int dd = 0; dd < GraphDimension; dd++ ) { if( GNI[dd] < 2 || GNI[dd] > (unsigned long)(m_GraphSize[dd] - 2) ) { return; } } for( unsigned int i = 0; i < m_EdgeTemplate.size(); i++ ) { // std::cout << " SES 4 " << std::endl; // std::cout << " ET " << m_EdgeTemplate[i] << " RAD " << m_Radius << " ind " << // GHood.GetIndex(m_EdgeTemplate[i]) // << std::endl; if( !GHood.GetPixel(m_EdgeTemplate[i]) ) // std::cout << " OK " << std::endl; // /else { // std::cout << " NOT OK " <SetUnVisited(); G->SetTotalCost(m_MaxCost); NodeLocationType loc; for( int j = 0; j < GraphDimension; j++ ) { loc[j] = ind[j]; } G->SetLocation(loc); G->SetPredecessor(m_CurrentNode); m_Graph->SetPixel(ind, G); } m_NeighborNode = GHood.GetPixel(m_EdgeTemplate[i]); // std::cout << "DA i " << i << " position " << m_NeighborNode->GetLocation() << endl; this->TerminationCondition(); if( !m_SearchFinished && m_CurrentNode != m_NeighborNode && !m_NeighborNode->GetDelivered() ) { m_NewCost = m_CurrentCost + LocalCost(); CheckNodeStatus(); } } } template void DijkstrasAlgorithm::CheckNodeStatus() // checks a graph neighbor's status { if( !m_NeighborNode->GetVisited() ) { // set the cost and put into the queue m_NeighborNode->SetTotalCost(m_NewCost); m_NeighborNode->SetPredecessor(m_CurrentNode); m_NeighborNode->SetVisited(); m_QS->m_Q.push(m_NeighborNode); } else if( m_NewCost < m_NeighborNode->GetTotalCost() ) { m_NeighborNode->SetTotalCost(m_NewCost); m_NeighborNode->SetPredecessor(m_CurrentNode); m_QS->m_Q.push(m_NeighborNode); } } template void DijkstrasAlgorithm::FindPath() { if( m_QS->m_SourceNodes.empty() ) { std::cout << "ERROR !! DID NOT SET SOURCE!!\n"; return; } while( !m_SearchFinished && !m_QS->m_Q.empty() ) { m_CurrentNode = m_QS->m_Q.top(); m_CurrentCost = m_CurrentNode->GetTotalCost(); m_QS->m_Q.pop(); if( !m_CurrentNode->GetDelivered() ) { m_QS->IncrementTimer(); this->SearchEdgeSet(); this->m_TotalCost += m_CurrentNode->GetTotalCost(); // if ( (m_CurrentNode->GetTimer() % 1.e5 ) == 0) // std::cout << " searched " << m_CurrentNode->GetTimer() << " \n"; } m_CurrentNode->SetDelivered(); } // end of while m_NumberSearched = (unsigned long) m_QS->GetTimer(); std::cout << "Done with find path " << " Q size " << m_QS->m_Q.size() << " num searched " << m_NumberSearched << " \n"; return; } } // end namespace itk #endif ants-2.2.0/Temporary/itkDijkstrasAlgorithm.h000066400000000000000000000454711311104306400211340ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit (ITK) Copyright (c) 2001 Insight Consortium All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of the Insight Consortium, nor the names of any consortium members, nor of any contributors, may be used to endorse or promote products derived from this software without specific prior written permission. * Modified source versions must be plainly marked as such, and must not be misrepresented as being the original software. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================================*/ #ifndef _itkDijkstrasAlgorithm_h_ #define _itkDijkstrasAlgorithm_h_ #include #include #include #include #include #include #include #include "vnl/vnl_math.h" // #include "vnl/vnl_matrix_fixed.h" // #include "vnl/vnl_vector_fixed.h" #include "itkImage.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkNeighborhoodIterator.h" #include "itkVector.h" using namespace std; namespace itk { /** The GraphSearchNode class defines a general shortest path graph node. * The algorithm requires * the node to have a pointer to itself and entry for the cumulative cost. * We also define an index to its location and a couple of booleans * for keeping track of the graph node's state. * We assume the connectivity between nodes is defined externally. * The class will also be useful for minimum spanning trees and * other graph search algorithms. Connectivity is defined externally. * May be worthwhile to implement arbitrary connectivity e.g. for random graphs. * One way to do this is to include a list of pointers which define * the neighbors of this node, similar to how the predecessor is defined. */ template class GraphSearchNode : public itk::LightObject { public: /* Standard typedefs.*/ typedef GraphSearchNode Self; typedef LightObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; itkTypeMacro(GraphSearchNode, LightObject); itkNewMacro(Self); /** Method for creation through the object factory. */ enum StateType { UnVisitedState, VisitedState, DeliveredState, UnVisitableState }; enum { GraphDimension = NGraphDimension }; typedef TPixelType PixelType; /** defines the cost data type */ typedef TCoordRep CoordRep; /** defines the location data type */ typedef itk::Vector NodeLocationType; // typedef typename itk::Image::IndexType NodeLocationType; typedef vector NodeListType; // typedef itk::Image::IndexType NodeLocationType; inline void SetLocation(NodeLocationType loc) { m_Location = loc; } inline NodeLocationType GetLocation() { return m_Location; } inline void SetTotalCost(TPixelType cost) { m_TotalCost = cost; } inline void SetValue(TPixelType cost, int which = 0) { if( which <= 0 ) { m_Value1 = cost; } if( which == 1 ) { m_Value2 = cost; } if( which == 2 ) { m_Value3 = cost; } if( which >= 3 ) { m_Value4 = cost; } } inline TPixelType GetValue(int which = 0) { if( which <= 0 ) { return m_Value1; } if( which == 1 ) { return m_Value2; } if( which == 2 ) { return m_Value3; } if( which >= 3 ) { return m_Value4; } return m_Value1; } inline void SetUnVisited() { m_State = UnVisitedState; } inline void SetUnVisitable() { m_State = UnVisitableState; } inline void SetVisited() { m_State = VisitedState; } inline void SetDelivered() { m_State = DeliveredState; } inline bool IsInQueue() { if( m_State == VisitedState ) { return true; } else { return false; } } inline bool WasVisited() { if( m_State == VisitedState ) { return true; } else if( m_State == DeliveredState ) { return true; } else { return false; } } inline TPixelType GetTotalCost() { return m_TotalCost; } inline void SetPredecessor(Pointer address) { m_PredecessorAddress = address; } inline Pointer GetPredecessor() { return m_PredecessorAddress; } inline void SetAncestor(Pointer address) { m_AncestorAddress = address; } inline Pointer GetAncestor() { return m_AncestorAddress; } inline bool GetUnVisited() { if( m_State == UnVisitedState ) { return true; } else { return false; } } inline bool GetUnVisitable() { if( m_State == UnVisitableState ) { return true; } else { return false; } } inline bool GetVisited() { if( m_State == VisitedState ) { return true; } else { return false; } } inline bool GetDelivered() { if( m_State == DeliveredState ) { return true; } else { return false; } } inline void SetState(StateType S) { m_State = S; } inline StateType GetState() { return m_State; } inline void SetIdentity(unsigned int i) { m_Identity = i; } inline unsigned int GetIdentity() { return m_Identity; } inline int GetNumberOfNeighbors() { return m_Neighbors.size(); } inline Pointer GetNeighbor(int i) { return m_Neighbors[i]; } void SetNeighborSize(int i) { m_Neighbors.resize(i); } NodeListType m_Neighbors; unsigned short m_NumberOfNeighbors; unsigned int m_Identity; protected: GraphSearchNode() { m_TotalCost = 0.0; m_Value1 = 0.0; m_Value2 = 0.0; m_Value3 = 0.0; m_Value4 = 0.0; m_PredecessorAddress = NULL; m_AncestorAddress = NULL; m_State = UnVisitedState; m_NumberOfNeighbors = 0; m_Identity = 0; } ~GraphSearchNode() { } private: TPixelType m_TotalCost; /** keeps track of the minimum accumulated cost. */ TPixelType m_Value1; /** keeps track of the minimum accumulated cost. */ TPixelType m_Value2; /** keeps track of the minimum accumulated cost. */ TPixelType m_Value3; /** keeps track of the minimum accumulated cost. */ TPixelType m_Value4; /** keeps track of the minimum accumulated cost. */ StateType m_State; NodeLocationType m_Location; /** provides the location in the graph. */ Pointer m_PredecessorAddress; /** provides the best predecessor address */ Pointer m_AncestorAddress; /** provides the best predecessor address */ GraphSearchNode(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; // Forward declaration of DijkstrasAlgorithm so it can be declared a friend template class DijkstrasAlgorithm; template class DijkstrasAlgorithmQueue : public itk::LightObject /** \class DijkstrasAlgorithmQueue the class containing the priority queue and associated data. */ { private: template class GraphSearchNodePriority /* defines the comparison operator for the prioritiy queue */ { public: bool operator()( typename G::Pointer N1, typename G::Pointer N2) { return N1->GetTotalCost() > N2->GetTotalCost(); } }; public: /* Standard typedefs.*/ typedef DijkstrasAlgorithmQueue Self; typedef LightObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; itkTypeMacro(DijkstrasAlgorithmQueue, LightObject); itkNewMacro(Self); /** Method for creation through the object factory. */ typedef typename TGraphSearchNode::Pointer TGraphSearchNodePointer; typedef typename TGraphSearchNode::PixelType PixelType; /** pixel type for the cost */ typedef typename TGraphSearchNode::CoordRep CoordRep; /** type for coordinates */ typedef typename std::priority_queue, GraphSearchNodePriority > QType; /** the queue we are using */ typedef vector NodeListType; inline QType GetQ() { return m_Q; } void AddToPath(TGraphSearchNodePointer G) { this->m_Path.push_back(G); } inline NodeListType GetPath() { return m_Path; } void EmptyPath() { m_Path.clear(); } inline NodeListType GetSourceNodes() { return m_SourceNodes; } inline NodeListType GetSinkNodes() { return m_SinkNodes; } inline void IncrementTimer() { m_timer++; } inline long GetTimer() { return m_timer; } inline void EmptyQ() { while( !m_Q.empty() ) { m_Q.pop(); } m_timer = 0; m_SourceNodes.clear(); m_SinkNodes.clear(); } NodeListType m_SinkNodes; NodeListType m_SourceNodes; QType m_Q; NodeListType m_Path; protected: friend class DijkstrasAlgorithm; // so it can access this data easily DijkstrasAlgorithmQueue() { m_timer = 0; } ~DijkstrasAlgorithmQueue() { } private: unsigned long m_timer; DijkstrasAlgorithmQueue(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; /** * \class DijkstrasAlgorithm * \brief General shortest path / greedy dynamic programming solver. * * This class uses Dijkstra's algorithm to solve the shortest path problem. * We use the stl priority queue which is not optimal for this problem, but which * works o.k. It's implemented to be used for general regularly connected * graphs with fixed costs, or for the variational solution of an integral * curve matching energy. * Note: we assume all edge weights are positive. * The class is implemented as a an abstract base class, with virtual functions for * LocalCost, SearchEdgeSet and FindPath. LocalCost must be implemented by derived classes. * The connectivity of the graph defined * here is always regular and is controlled by a set of neighborhood indices. * the default is a radius 1 neighborhood with all entries used. However, the * user may also supply her own regular connectivity by selecting the size of * the neighborhood and a subset of the indices which define the edges. If * the GraphSearchNode contains its edges, they may be used with minor modification * to the function SearchEdgeSet. * Another improvement would be to make the LocalCost function a pointer * to a function which could be set. */ template class DijkstrasAlgorithm : public itk::LightObject { public: typedef DijkstrasAlgorithm Self; typedef LightObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; itkTypeMacro(DijkstrasAlgorithm, LightObject); itkNewMacro(Self); // Computation Data typedef TGraphSearchNode SearchNode; /** dimension of the graph */ typedef typename SearchNode::Pointer SearchNodePointer; enum { GraphDimension = SearchNode::GraphDimension }; /** dimension of the graph */ typedef typename SearchNode::PixelType PixelType; /** pixel type for the cost */ typedef typename SearchNode::CoordRep CoordRep; /** coordinate type */ typedef Image GraphType; typedef typename GraphType::SizeType GraphSizeType; typedef ImageRegionIteratorWithIndex GraphIteratorType; typedef typename GraphType::RegionType GraphRegionType; typedef typename DijkstrasAlgorithmQueue::Pointer QType; typedef typename DijkstrasAlgorithmQueue::NodeListType NodeListType; typedef itk::NeighborhoodIterator GraphNeighborhoodIteratorType; typedef typename GraphNeighborhoodIteratorType::IndexType GraphNeighborhoodIndexType; typedef typename GraphNeighborhoodIteratorType::RadiusType RadiusType; typedef typename TGraphSearchNode::NodeLocationType NodeLocationType; typedef typename GraphType::IndexType IndexType; // FUNCTIONS void InitializeGraph(); /** initializes all graph values appropriately */ void InitializeQueue(); /** initializes all queue values appropriately call AFTER source and sink are set*/ void InitializeEdgeTemplate(); /** helper function initializes edge set appropriately */ void InitializeEdgeTemplate(vector, unsigned int); /** user supplied edge template */ void SetGraphSize(typename GraphType::SizeType Sz); /** the rectangular size of the graph */ inline void EmptyQ() { m_QS->EmptyQ(); this->m_TotalCost = 0; } /* adds a source to the source set */ void SetSource(typename TGraphSearchNode::Pointer G) { m_QS->m_SourceNodes.push_back(G); for( int i = 0; i < GraphDimension; i++ ) { m_GraphIndex[i] = (long int)(G->GetLocation()[i] + 0.5); // ::std::cout << " mgi " << m_GraphIndex[i]; } m_Graph->SetPixel(m_GraphIndex, G); }; typename TGraphSearchNode::Pointer GetGraphNode( IndexType index) { // ::std::cout << " get node " << index << std::endl; return m_Graph->GetPixel(index); }; // adds a sink to the sink set void SetSink(typename TGraphSearchNode::Pointer G) { m_QS->m_SinkNodes.push_back(G); } // Backtracks from the given node to its source node; void BackTrack(typename TGraphSearchNode::Pointer SinkNode) { m_QS->m_Path.clear(); typename TGraphSearchNode::Pointer G = SinkNode; typename TGraphSearchNode::Pointer P = SinkNode->GetPredecessor(); if( !P || !G ) { return; } float highcost = G->GetValue(); if( G->GetTotalCost() > P->GetValue() ) { P->SetAncestor(G); P->SetValue(G->GetTotalCost() ); highcost = G->GetTotalCost(); } while( P && G != P ) { m_QS->m_Path.push_back(G); G = P; P = G->GetPredecessor(); if( P->GetValue() < highcost ) { P->SetValue(highcost); P->SetAncestor(G); } } if( !P ) { cout << " null pred "; // else cout << " pred == self \n"; } return; } // Inverse of backtrack - from the given node to its sink node; void ForwardTrack(typename TGraphSearchNode::Pointer SinkNode) { typename TGraphSearchNode::Pointer G = SinkNode; typename TGraphSearchNode::Pointer P = SinkNode->GetAncestor(); while( P && G != P && G ) { if( P->GetValue() > G->GetValue() ) { G->SetValue(P->GetValue() ); } if( G->GetValue() > P->GetValue() ) { P->SetValue(G->GetValue() ); } G = P; P = G->GetAncestor(); } return; } virtual bool TerminationCondition(); /** decides when the algorithm stops */ virtual void SearchEdgeSet(); /** loops over the neighbors in the graph */ void CheckNodeStatus(); /** checks if the node has been explored already, its cost, etc. */ virtual PixelType LocalCost(); /* computes the local cost */ /* alternatively, we could pass the function as a template parameter or set a function pointer. the latter method is used in dijkstrasegment. */ virtual void FindPath(); /* runs the algorithm */ inline unsigned int GetPathSize() { return m_QS->m_Path.size(); } inline typename TGraphSearchNode::Pointer GetPathAtIndex(unsigned int i) { return m_QS->m_Path[i]; } inline typename TGraphSearchNode::Pointer GetNeighborNode() { return m_NeighborNode; } inline typename TGraphSearchNode::Pointer GetCurrentNode() { return m_CurrentNode; } void SetMaxCost(PixelType m) { m_MaxCost = m; } double GetTotalCost() { return m_TotalCost; } void SetSearchFinished(bool m) { m_SearchFinished = m; } /** sets the boolean that indicates if the algorithm is done */ protected: QType m_QS; vector m_EdgeTemplate; /** defines neighborhood connectivity */ RadiusType m_Radius; /** used by the neighborhood iterator */ typename TGraphSearchNode::Pointer m_PredecessorNode; /** holds the predecessor node */ typename TGraphSearchNode::Pointer m_CurrentNode; /** holds the current node */ typename TGraphSearchNode::Pointer m_NeighborNode; /** holds the current neighbor node */ typename GraphType::Pointer m_Graph; /** holds all the graph information */ GraphRegionType m_GraphRegion; GraphSizeType m_GraphSize; /** rectangular size of graph */ typename GraphType::IndexType m_GraphIndex; bool m_SearchFinished; PixelType m_NewCost; PixelType m_CurrentCost; PixelType m_MaxCost; // This creates an insurmountable barrier unless all costs are max double m_TotalCost; unsigned long m_NumberSearched; DijkstrasAlgorithm(); ~DijkstrasAlgorithm() { }; private: DijkstrasAlgorithm(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkDijkstrasAlgorithm.cxx" #endif #endif ants-2.2.0/Temporary/itkDivideByConstantImageFilter.h000066400000000000000000000111251311104306400226360ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkDivideByConstantImageFilter_h #define __itkDivideByConstantImageFilter_h #include "itkUnaryFunctorImageFilter.h" #include "itkNumericTraits.h" namespace itk { /** \class DivideByConstantImageFilter * * \brief Divide input pixels by a constant. * * This filter is templated over the input image type * and the output image type. * * \author Tom Vercauteren, INRIA & Mauna Kea Technologies * * This implementation was taken from the Insight Journal paper: * http://hdl.handle.net/1926/510 * * \ingroup IntensityImageFilters Multithreaded * \sa UnaryFunctorImageFilter */ namespace Functor { template class DivideByConstant { public: DivideByConstant() : m_Constant(NumericTraits::OneValue()) { }; ~DivideByConstant() { }; bool operator!=( const DivideByConstant & other ) const { return !(*this == other); } bool operator==( const DivideByConstant & other ) const { return other.m_Constant == m_Constant; } inline TOutput operator()( const TInput & A ) const { // Because the user has to specify the constant we don't // check if the constant is not too small (i.e. almost equal to zero); return static_cast( A / m_Constant ); } void SetConstant(TConstant ct) { if( ct == NumericTraits::ZeroValue() ) { itkGenericExceptionMacro( << "The constant value used as denominator should not be set to zero"); } this->m_Constant = ct; } const TConstant & GetConstant() const { return m_Constant; } TConstant m_Constant; }; } template class DivideByConstantImageFilter : public UnaryFunctorImageFilter > { public: /** Standard class typedefs. */ typedef DivideByConstantImageFilter Self; typedef UnaryFunctorImageFilter< TInputImage, TOutputImage, Functor::DivideByConstant< typename TInputImage::PixelType, TConstant, typename TOutputImage::PixelType> > Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(DivideByConstantImageFilter, UnaryFunctorImageFilter); /** Set the constant value that will be used for dividing all the image * pixels */ void SetConstant(TConstant ct) { if( ct != this->GetFunctor().GetConstant() ) { this->GetFunctor().SetConstant(ct); this->Modified(); } } const TConstant & GetConstant() const { return this->GetFunctor().GetConstant(); } #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(InputConvertibleToOutputCheck, (Concept::Convertible ) ); // The following concept check doesn't seem to work with vector immages // itkConceptMacro(Input1Input2OutputDivisionOperatorsCheck, // (Concept::DivisionOperators)); /** End concept checking */ #endif protected: DivideByConstantImageFilter() { }; virtual ~DivideByConstantImageFilter() { }; void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE { Superclass::PrintSelf(os, indent); os << indent << "Constant: " << static_cast::PrintType>(this->GetConstant() ) << std::endl; } private: DivideByConstantImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #endif ants-2.2.0/Temporary/itkFEMConformalMap.cxx000066400000000000000000001110061311104306400205740ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Copyright (c) 2002 Insight Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for detailm_Solver. 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 _FEMConformalMap_hxx #define _FEMConformalMap_hxx #include "vtkFeatureEdges.h" #include "vtkPointLocator.h" #include "vtkCellLocator.h" #include "vtkTriangleFilter.h" #include "vtkCleanPolyData.h" #include "vtkPolyDataConnectivityFilter.h" #include #include #include #include #include #include #include #include #include "itkFEMConformalMap.h" #include "itkFEMLoadNode.h" namespace itk { vtkPolyData * vtkGenSmoothMesh(vtkPolyData* vds) { float fai = 45; vtkSmoothPolyDataFilter *polySmoother = vtkSmoothPolyDataFilter::New(); // polySmoother->SetInput(decimator->GetOutput()); polySmoother->SetInput(vds); polySmoother->SetNumberOfIterations(100); polySmoother->SetRelaxationFactor(0.01); polySmoother->SetFeatureAngle(fai); polySmoother->FeatureEdgeSmoothingOff(); polySmoother->BoundarySmoothingOff(); polySmoother->Update(); return polySmoother->GetOutput(); } template FEMConformalMap ::FEMConformalMap() { m_Sigma = 2.0e-4; m_ParameterFileName = ""; m_NorthPole = 0; m_SouthPole = 1; m_VtkSurfaceMesh = NULL; m_Pi = 3.14159265358979323846; m_ReadFromFile = true; m_Debug = false; m_FindingRealSolution = true; m_SurfaceMesh = NULL; m_Image = NULL; m_SphereImage = NULL; for( int i = 0; i < 7; i++ ) { m_PoleElementsGN[i] = 0; } } template bool FEMConformalMap ::GenerateSystemFromVtkSurfaceMesh() { if( !m_VtkSurfaceMesh ) { ::std::cout << " NO MESH"; return false; } ::std::cout << " Generate system from surface mesh " << std::endl; vtkTriangleFilter* fltTriangle = vtkTriangleFilter::New(); fltTriangle->SetInput(m_VtkSurfaceMesh); cout << " converting mesh to triangles " << endl; fltTriangle->Update(); // Clean the data vtkCleanPolyData* fltCleaner = vtkCleanPolyData::New(); fltCleaner->SetInput(fltTriangle->GetOutput() ); fltCleaner->SetTolerance(0); fltCleaner->ConvertPolysToLinesOn(); cout << " cleaning up triangle mesh " << endl; fltCleaner->Update(); // Go through and delete the cells that are of the wrong type vtkPolyData* clean = fltCleaner->GetOutput(); for( vtkIdType i = clean->GetNumberOfCells(); i > 0; i-- ) { if( clean->GetCellType(i - 1) != VTK_TRIANGLE ) { clean->DeleteCell(i - 1); } } clean->BuildCells(); m_VtkSurfaceMesh = clean; // create a material // Choose the material properties typename MaterialType::Pointer m; m = MaterialType::New(); m->GN = 0; // Global number of the material /// m->E = m_Sigma; // Young modulus -- used in the membrane /// m->A = 0.0; // Crossection area /// m->h = 0.0; // Crossection area /// m->I = 0.0; // Moment of inertia /// m->nu = 0.; // .0; // poissons -- DONT CHOOSE 1.0!!/// m->RhoC = 0.0; // Create the element type ElementType::Pointer e1 = ElementType::New(); e1->m_mat = dynamic_cast( m ); vtkPoints* vtkpoints = m_VtkSurfaceMesh->GetPoints(); int numPoints = vtkpoints->GetNumberOfPoints(); int foundnum = 0; for( int i = 0; i < numPoints; i++ ) { double* pt = vtkpoints->GetPoint(i); typename NodeType::Pointer n; n = new NodeType(pt[0], pt[1], pt[2]); n->GN = i; m_Solver.node.push_back(itk::fem::FEMP(n) ); foundnum++; } ::std::cout << " foundnum " << foundnum << std::endl; typename NodeType::Pointer * narr = new NodeType::Pointer[numPoints]; for( int i = 0; i < numPoints; i++ ) { narr[i] = m_Solver.node.Find( i ); } vtkCellArray* vtkcells = m_VtkSurfaceMesh->GetPolys(); vtkIdType npts; vtkIdType* pts; unsigned long i = 0; unsigned long toti = vtkcells->GetNumberOfCells(); // ::std::cout << " progress "; ::std::cout << " DONE: NUMBER OF CELLS start " << toti << std::endl; for( vtkcells->InitTraversal(); vtkcells->GetNextCell(npts, pts); ) { ElementType::Pointer e; e = dynamic_cast(e1->Clone() ); e->SetNode(2, narr[pts[0]]); e->SetNode(1, narr[pts[1]]); e->SetNode(0, narr[pts[2]]); e->GN = i; m_Solver.el.push_back(itk::fem::FEMP(e) ); i++; } ::std::cout << " DONE: NUMBER OF CELLS " << i << std::endl; return true; } template bool FEMConformalMap ::GenerateSystemFromSurfaceMesh() { if( !m_SurfaceMesh ) { return false; } // create a material // Choose the material properties typename MaterialType::Pointer m; m = MaterialType::New(); m->GN = 0; // Global number of the material /// m->E = m_Sigma; // Young modulus -- used in the membrane /// m->A = 0.0; // Crossection area /// m->h = 0.0; // Crossection area /// m->I = 0.0; // Moment of inertia /// m->nu = 0.; // .0; // poissons -- DONT CHOOSE 1.0!!/// m->RhoC = 0.0; // 3.e-4 prolly good // ::std::cout << " input E "; std::cin >> m->E; // Create the element type ElementType::Pointer e1 = ElementType::New(); e1->m_mat = dynamic_cast( m ); PointType* pt_ptr; PointType pt; pt_ptr = &pt; for( int i = 0; i < m_SurfaceMesh->GetNumberOfPoints(); i++ ) { m_SurfaceMesh->GetPoint(i, pt_ptr); if( m_Debug ) { ::std::cout << "Point: " << i << " coords " << pt[0] << ", " << pt[1] << ", " << pt[2] << std::endl; } // turn the point into a node { // Create nodes typename NodeType::Pointer n; n = new NodeType(pt[0], pt[1], pt[2]); n->GN = i; m_Solver.node.push_back(itk::fem::FEMP(n) ); } } InputCellsContainerPointer cellList = m_SurfaceMesh->GetCells(); InputCellsContainerIterator cells = cellList->Begin(); for( int i = 0; i < m_SurfaceMesh->GetNumberOfCells(); i++ ) { const unsigned long *tp; typename SurfaceType::CellType * cellPtr = cells.Value(); tp = cellPtr->GetPointIds(); if( m_Debug ) { ::std::cout << " pt 1 id " << tp[0] << " pt 2 id " << tp[1] << " pt 3 id " << tp[2] << std::endl; } cells++; // turn the cell into an element { // Create elements ElementType::Pointer e; e = dynamic_cast(e1->Clone() ); e->SetNode(0, m_Solver.node.Find( tp[0] ) ); e->SetNode(1, m_Solver.node.Find( tp[1] ) ); e->SetNode(2, m_Solver.node.Find( tp[2] ) ); e->GN = i; m_Solver.el.push_back(itk::fem::FEMP(e) ); } } return true; } template void FEMConformalMap ::MapImageToSphere(TImage* img, float rad ) { typename ImageType::SizeType imageSize = img->GetLargestPossibleRegion().GetSize(); vnl_vector Pos; // solution at the point vnl_vector Sol; // solution at the local point vnl_vector Gpt; // global position given by local point Sol.set_size(ImageDimension); Gpt.set_size(ImageDimension); Pos.set_size(ImageDimension); VectorType disp; disp.set_size(3); disp.fill(0.0); float WIDTH = rad * 2.1; float HEIGHT = rad * 2.1; float DEPTH = rad * 2.1; typename ImageType::SizeType size; size[0] = WIDTH; size[1] = HEIGHT; size[2] = DEPTH; typename ImageType::IndexType start; start.Fill(0); typename ImageType::RegionType region; region.SetSize( size ); region.SetIndex( start ); m_SphereImage = ImageType::New(); m_SphereImage->SetRegions( region ); m_SphereImage->Allocate(); int center = (int)WIDTH / 2; typename ImageType::PixelType backgroundValue = 0; itk::ImageRegionIteratorWithIndex it( m_SphereImage, region ); it.GoToBegin(); typename ImageType::IndexType rindex = it.GetIndex(); typename ImageType::IndexType sindex = it.GetIndex(); while( !it.IsAtEnd() ) { rindex = it.GetIndex(); float dist = 0; for( int ii = 0; ii < ImageDimension; ii++ ) { dist += (float)(rindex[ii] - center) * (float)(rindex[ii] - center); } dist = sqrt(dist); if( dist > rad ) { it.Set( backgroundValue ); } else { it.Set( 1 ); } ++it; } PointType* pt_ptr; PointType pt; pt_ptr = &pt; bool inimage; for( itk::fem::Element::ArrayType::iterator elt = m_Solver.el.begin(); elt != m_Solver.el.end(); ++elt ) { unsigned int Nnodes = (*elt)->GetNumberOfNodes(); inimage = true; for( unsigned int nd = 0; nd < Nnodes; nd++ ) { for( unsigned int f = 0; f < ImageDimension; f++ ) { float x; if( (*elt)->GetNumberOfDegreesOfFreedomPerNode() == 3 ) { Sol[f] = m_Solver.GetLinearSystemWrapper()->GetSolutionValue( (*elt)->GetNode(nd)->GetDegreeOfFreedom(f), 0); x = (*elt)->GetNode(nd)->GetCoordinates()[f]; } else { Sol[f] = (*elt)->GetNode(nd)->GetCoordinates() (f); m_SurfaceMesh->GetPoint( (*elt)->GetNode(nd)->GN, pt_ptr); x = pt[f]; } // get img index long int temp; if( x != 0 ) { temp = (long int) ( (x) + 0.5); } else { temp = 0; // round } rindex[f] = temp; // get sphr index disp[f] = (float) ( (float)center) + rad * Sol[f]; x = disp[f]; if( x != 0 ) { temp = (long int) ( (x) + 0.5); } else { temp = 0; // round } sindex[f] = temp; } if( inimage ) { m_SphereImage->SetPixel(sindex, img->GetPixel(rindex) + 1.0); // *100. +110.0); } } } // end of elt array loop } template void FEMConformalMap ::MapCheckerboardToImage( float increment ) { typedef float Float; if( increment < 0 ) { increment = 0; } if( increment > 1 ) { increment = 1; } float phiinc = m_Pi * increment; float thetainc = m_Pi * increment; vnl_vector Sol; // solution at the local point typename ImageType::SpacingType spacing = m_Image->GetSpacing(); typename ImageType::SizeType imageSize = m_Image->GetLargestPossibleRegion().GetSize(); ::std::cout << " size " << imageSize << std::endl; VectorType disp; disp.set_size(3); disp.fill(0.0); // Float solval,posval; bool inimage; float phi; float theta; float s; float t; float gridval; ImageRegionIteratorWithIndex wimIter( m_Image, m_Image->GetLargestPossibleRegion() ); wimIter.GoToBegin(); for( ; !wimIter.IsAtEnd(); ++wimIter ) { wimIter.Set(0.0); } typename ImageType::IndexType rindex = wimIter.GetIndex(); Sol.set_size(ImageDimension); PointType* pt_ptr; PointType pt; pt_ptr = &pt; for( itk::fem::Element::ArrayType::iterator elt = m_Solver.el.begin(); elt != m_Solver.el.end(); ++elt ) { unsigned int Nnodes = (*elt)->GetNumberOfNodes(); inimage = true; for( unsigned int nd = 0; nd < Nnodes; nd++ ) { for( unsigned int f = 0; f < ImageDimension; f++ ) { Float x; Sol[f] = (*elt)->GetNode(nd)->GetCoordinates() (f); m_SurfaceMesh->GetPoint( (*elt)->GetNode(nd)->GN, pt_ptr); x = pt[f]; long int temp; if( x != 0 ) { temp = (long int) ( (x) + 0.5); } else { temp = 0; // round } rindex[f] = temp / spacing[f]; disp[f] = (Float) 1.0 * Sol[f]; if( temp < 0 || temp > (long int) imageSize[f] - 1 ) { inimage = false; } } // convert the cartesian coordinates to theta and phi spherical coords phi = acos(Sol[2]); theta = acos(Sol[0] / sin(phi) ); s = phi / phiinc; t = theta / thetainc; gridval; if( ( ( (int)t) + ( (int)s) ) % 2 == 0 ) { gridval = 100; } else { gridval = 200; } if( inimage ) { m_Image->SetPixel(rindex, gridval); // *100. +110.0); } } } // end of elt array loop } template void FEMConformalMap ::MapStereographicCoordinatesToImage(int cdim) { typedef float Float; vnl_vector Pos; // solution at the point vnl_vector Sol; // solution at the local point vnl_vector Gpt; // global position given by local point typename ImageType::SpacingType spacing = m_Image->GetSpacing(); typename ImageType::SizeType imageSize = m_Image->GetLargestPossibleRegion().GetSize(); VectorType disp; disp.set_size(3); disp.fill(0.0); bool inimage; ImageRegionIteratorWithIndex wimIter( m_Image, m_Image->GetLargestPossibleRegion() ); wimIter.GoToBegin(); for( ; !wimIter.IsAtEnd(); ++wimIter ) { wimIter.Set(0.0); } typename ImageType::IndexType rindex = wimIter.GetIndex(); Sol.set_size(ImageDimension); Gpt.set_size(ImageDimension); Pos.set_size(ImageDimension); PointType* pt_ptr; PointType pt; pt_ptr = &pt; for( itk::fem::Element::ArrayType::iterator elt = m_Solver.el.begin(); elt != m_Solver.el.end(); ++elt ) { unsigned int Nnodes = (*elt)->GetNumberOfNodes(); inimage = true; for( unsigned int nd = 0; nd < Nnodes; nd++ ) { for( unsigned int f = 0; f < ImageDimension; f++ ) { Float x; if( (*elt)->GetNumberOfDegreesOfFreedomPerNode() == 3 ) { Sol[f] = m_Solver.GetLinearSystemWrapper()->GetSolutionValue( (*elt)->GetNode(nd)->GetDegreeOfFreedom(f), 0); x = (*elt)->GetNode(nd)->GetCoordinates()[f]; } else { Sol[f] = (*elt)->GetNode(nd)->GetCoordinates() (f); m_SurfaceMesh->GetPoint( (*elt)->GetNode(nd)->GN, pt_ptr); x = pt[f]; } long int temp; if( x != 0 ) { temp = (long int) ( (x) + 0.5); } else { temp = 0; // round } rindex[f] = temp / spacing[f]; disp[f] = (Float) 1.0 * Sol[f]; if( temp < 0 || temp > (long int) imageSize[f] - 1 ) { inimage = false; } } if( inimage ) { m_Image->SetPixel(rindex, disp[cdim] + 2.0); // *100. +110.0); } } } // end of elt array loop } template void FEMConformalMap ::FindPoles(int dim) { // find node with min x coord and its elt -> use as north pole (apply forces there) // find node with max x coord and its elt -> use as south pole (fix solution to zero) if( dim < 0 ) { return; } vnl_vector minx(3, 9.e9); vnl_vector maxx(3, 0.0); itk::fem::Element::VectorType minv; itk::fem::Element::VectorType maxv; itk::fem::Element::ArrayType::iterator elt = m_Solver.el.begin(); unsigned int Nnodes = (*elt)->GetNumberOfNodes(); for( ; elt != m_Solver.el.end(); ++elt ) { for( unsigned int nd = 0; nd < Nnodes; nd++ ) { for( unsigned int dd = 0; dd < ImageDimension; dd++ ) { float x = ( (*elt)->GetNode(nd)->GetCoordinates()[dd]); if( x <= minx[dd] ) { // ::std::cout << " x " << x << " minx " << minx << std::endl; m_PoleElementsGN[dd * 2] = (*elt)->GN; minv = (*elt)->GetNode(nd)->GetCoordinates(); minx[dd] = x; } else if( x >= maxx[dd] ) { // ::std::cout << " x " << x << " maxx " << maxx << std::endl; m_PoleElementsGN[dd * 2 + 1] = (*elt)->GN; maxv = (*elt)->GetNode(nd)->GetCoordinates(); maxx[dd] = x; } } } } for( int jj = 0; jj < 3; jj++ ) { ::std::cout << " Element " << m_PoleElementsGN[jj * 2] << " is north " << jj << std::endl; ::std::cout << " Element " << m_PoleElementsGN[jj * 2 + 1] << " is south " << jj << std::endl; } } template void FEMConformalMap ::FixPoles(int dim) { // if (dim > 3 || dim < -3 || dim == 0 ) return; if( dim == m_NorthPole ) { return; } itk::fem::Element::ArrayType::iterator elt = m_Solver.el.begin(); unsigned int dofs = (*elt)->GetNumberOfDegreesOfFreedomPerNode(); { float fixval; itk::fem::LoadBC::Pointer l1; itk::fem::LoadBC::Pointer l2; itk::fem::LoadBC::Pointer l3; itk::fem::LoadBC::Pointer l4; itk::fem::LoadBC::Pointer l5; // itk::fem::LoadBC::Pointer l6; int absdim = abs(dim); float oc = 2.0; ::std::cout << " Fixing BC " << absdim << std::endl; switch( absdim ) { case 0: { for( int i = 0; i < dofs; i++ ) { fixval = 0; l1 = itk::fem::LoadBC::New(); l1->m_element = m_Solver.el[m_PoleElementsGN[0]]; l1->m_dof = i; l1->m_value = vnl_vector(1, fixval); m_Solver.load.push_back( itk::fem::FEMP(&*l1) ); } } break; case 1: { for( int i = 0; i < dofs; i++ ) { fixval = 0; l1 = itk::fem::LoadBC::New(); l1->m_element = m_Solver.el[m_PoleElementsGN[1]]; l1->m_dof = i; l1->m_value = vnl_vector(1, fixval); m_Solver.load.push_back( itk::fem::FEMP(&*l1) ); } } break; case 2: { for( int i = 0; i < dofs; i++ ) { if( m_FindingRealSolution ) { fixval = 0; } else { fixval = oc; } l2 = itk::fem::LoadBC::New(); l2->m_element = m_Solver.el[m_PoleElementsGN[2]]; l2->m_dof = i; l2->m_value = vnl_vector(1, fixval); m_Solver.load.push_back( itk::fem::FEMP(&*l2) ); } } break; case 3: { for( int i = 0; i < dofs; i++ ) { if( m_FindingRealSolution ) { fixval = 0; } else { fixval = -1.0 * oc; // was -2.0 } l3 = itk::fem::LoadBC::New(); l3->m_element = m_Solver.el[m_PoleElementsGN[3]]; l3->m_dof = i; l3->m_value = vnl_vector(1, fixval); m_Solver.load.push_back( itk::fem::FEMP(&*l3) ); } } break; case 4: { for( int i = 0; i < dofs; i++ ) { if( m_FindingRealSolution ) { fixval = oc; } else { fixval = 0.0; } l4 = itk::fem::LoadBC::New(); l4->m_element = m_Solver.el[m_PoleElementsGN[4]]; l4->m_dof = i; l4->m_value = vnl_vector(1, fixval); m_Solver.load.push_back( itk::fem::FEMP(&*l4) ); } } break; case 5: { for( int i = 0; i < dofs; i++ ) { if( m_FindingRealSolution ) { fixval = -1.0 * oc; } else { fixval = 0.0; } l5 = itk::fem::LoadBC::New(); l5->m_element = m_Solver.el[m_PoleElementsGN[5]]; l5->m_dof = i; l5->m_value = vnl_vector(1, fixval); m_Solver.load.push_back( itk::fem::FEMP(&*l5) ); } } break; } } } template void FEMConformalMap ::ApplyRealForces(int dim) { if( dim > 6 ) { dim = 6; } // loop over all elements, applying forces to the nodes 0 and 1 (real forces are zero on the 3rd node) // for( ::itk::fem::Solver::ElementArray::iterator e = m_Solver.el.begin(); e!=m_Solver.el.end(); e++) { itk::fem::Element::Pointer e = m_Solver.el[m_PoleElementsGN[dim]]; int na = 0; int nb = 1; int nc = 2; { VectorType A = (e)->GetNode(na)->GetCoordinates(); VectorType B = (e)->GetNode(nb)->GetCoordinates(); VectorType C = (e)->GetNode(nc)->GetCoordinates(); VectorType BA = B - A; VectorType AC = A - C; VectorType CB = C - B; float bamag = BA.magnitude(); float cbmag = CB.magnitude(); float acmag = AC.magnitude(); if( bamag > cbmag && bamag > acmag ) { na = 0; nb = 1; nc = 2; } if( cbmag > bamag && cbmag > acmag ) { na = 1; nb = 2; nc = 0; } if( acmag > bamag && acmag > cbmag ) { na = 2; nb = 0; nc = 1; } } VectorType A = (e)->GetNode(na)->GetCoordinates(); VectorType B = (e)->GetNode(nb)->GetCoordinates(); VectorType C = (e)->GetNode(nc)->GetCoordinates(); VectorType BA = B - A; VectorType CA = C - A; VectorType CB = C - B; float bamag = BA.magnitude(); float theta = (CA[0] * BA[0] + CA[1] * BA[1] + CA[2] * BA[2]) / bamag * bamag; if( theta > 1 ) { ::std::cout << " theta " << theta << std::endl; } VectorType E = A + BA * theta; VectorType CE = C - E; // load node 0 { itk::fem::LoadNode::Pointer load = itk::fem::LoadNode::New(); load->m_pt = na; load->F.set_size(3); load->F.fill(-1.0 / bamag); load->m_element = (e); m_Solver.load.push_back( itk::fem::FEMP(&*load) ); } // load node 1 { itk::fem::LoadNode::Pointer load = itk::fem::LoadNode::New(); load->m_pt = nb; load->F.set_size(3); load->F.fill(1.0 / bamag); load->m_element = (e); m_Solver.load.push_back( itk::fem::FEMP(&*load) ); } return; } } template void FEMConformalMap ::ApplyImaginaryForces(int dim) { if( dim > 6 ) { dim = 6; } // loop over all elements, applying forces to the nodes 0 and 1 (real forces are zero on the 3rd node) // for( ::itk::fem::Solver::ElementArray::iterator e = m_Solver.el.begin(); e!=m_Solver.el.end(); e++) { itk::fem::Element::Pointer e = m_Solver.el[m_PoleElementsGN[dim]]; /* VectorType A=(e)->GetNode(0)->GetCoordinates(); VectorType B=(e)->GetNode(1)->GetCoordinates(); VectorType C=(e)->GetNode(2)->GetCoordinates(); VectorType BA =B-A; VectorType CA =C-A; float bamag=BA.magnitude(); float theta=(CA[0]*BA[0]+CA[1]*BA[1]+CA[2]*BA[2])/bamag*bamag; if (theta > 1) ::std::cout << " theta " << theta << std::endl; VectorType E = A+BA*theta; VectorType CE =C-E; float cemag=CE.magnitude(); */ int na = 0; int nb = 1; int nc = 2; { VectorType A = (e)->GetNode(na)->GetCoordinates(); VectorType B = (e)->GetNode(nb)->GetCoordinates(); VectorType C = (e)->GetNode(nc)->GetCoordinates(); VectorType BA = B - A; VectorType AC = A - C; VectorType CB = C - B; float bamag = BA.magnitude(); float cbmag = CB.magnitude(); float acmag = AC.magnitude(); if( bamag > cbmag && bamag > acmag ) { na = 0; nb = 1; nc = 2; } if( cbmag > bamag && cbmag > acmag ) { na = 1; nb = 2; nc = 0; } if( acmag > bamag && acmag > cbmag ) { na = 2; nb = 0; nc = 1; } } VectorType A = (e)->GetNode(na)->GetCoordinates(); VectorType B = (e)->GetNode(nb)->GetCoordinates(); VectorType C = (e)->GetNode(nc)->GetCoordinates(); VectorType BA = B - A; VectorType CA = C - A; VectorType CB = C - B; float bamag = BA.magnitude(); float theta = (CA[0] * BA[0] + CA[1] * BA[1] + CA[2] * BA[2]) / bamag * bamag; if( theta > 1 || theta < 0 ) { // ::std::cout << " ERROR : theta from non-acute angle " << theta << std::endl; // throw; // return; } VectorType E = A + BA * theta; VectorType CE = C - E; float cemag = CE.magnitude(); // load node 0 { itk::fem::LoadNode::Pointer load = itk::fem::LoadNode::New(); load->m_pt = na; load->F.set_size(3); load->F.fill( (1.0 - theta) / cemag); load->m_element = (e); m_Solver.load.push_back( itk::fem::FEMP(&*load) ); } // load node 1 { itk::fem::LoadNode::Pointer load = itk::fem::LoadNode::New(); load->m_pt = nb; load->F.set_size(3); load->F.fill( (theta) / cemag); load->m_element = (e); m_Solver.load.push_back( itk::fem::FEMP(&*load) ); } // load node 2 { itk::fem::LoadNode::Pointer load = itk::fem::LoadNode::New(); load->m_pt = nc; load->F.set_size(3); load->F.fill(-1.0 / cemag); load->m_element = (e); m_Solver.load.push_back( itk::fem::FEMP(&*load) ); } return; } } template void FEMConformalMap ::ComputeStereographicCoordinates() { ::std::cout << " coords on unit sphere : " << std::endl; float radsq; // radius squared float c1, c2, c3; // stereographic coordinates unsigned int dof; for( ::itk::fem::Solver::NodeArray::iterator n = m_Solver.node.begin(); n != m_Solver.node.end(); ++n ) { dof = (*n)->GetDegreeOfFreedom(0); float x = m_RealSolution[dof]; float y = m_ImagSolution[dof]; radsq = x * x + y * y; c1 = 2.0 * x / (1.0 + radsq); c2 = 2.0 * y / (1.0 + radsq); c3 = 2.0 * radsq / (1.0 + radsq) - 1.0; // ::std::cout << c1 << " " << c2 << " " << c3 << " mag " << sqrt(c1*c1+c2*c2+c3*c3) << std::endl; VectorType coord = (*n)->GetCoordinates(); coord[0] = c1; coord[1] = c2; coord[2] = c3; // / (*n)->SetCoordinates(coord); } ::std::cout << " coords on unit sphere done " << std::endl; } template void FEMConformalMap ::ConformalMap() { m_Solver.load.clear(); m_Solver.node.clear(); m_Solver.el.clear(); /** * Open the file and assign it to stream object f */ if( m_ReadFromFile ) { const char* filename = m_ParameterFileName.c_str(); ::std::cout << "Reading FEM problem from file: " << std::string(filename) << "\n"; std::ifstream f; f.open(filename); if( !f ) { ::std::cout << "File " << filename << " not found!\n"; return; } try { m_Solver.Read(f); } catch( ::itk::fem::FEMException & e ) { ::std::cout << "Error reading FEM problem: " << filename << "!\n"; e.Print(::std::cout); return; } f.close(); } else // if (m_VtkSurfaceMesh) { this->GenerateSystemFromVtkSurfaceMesh(); } // else this->GenerateSystemFromSurfaceMesh(); int dir = m_NorthPole; // ::std::cout << " input dir to fix "; std::cin >> dir; this->FindPoles(dir); if( m_NorthPole != m_SouthPole ) { this->FixPoles(m_SouthPole); } // for (int oo=0; oo<6; oo++) // this->FixPoles(oo); /** * Assign a unique id (global freedom number - GFN) * to every degree of freedom (DOF) in a system. */ m_Solver.GenerateGFN(); m_ImagSolution.set_size(m_Solver.GetNumberOfDegreesOfFreedom() ); m_RealSolution.set_size(m_Solver.GetNumberOfDegreesOfFreedom() ); m_RealSolution.fill(0); m_ImagSolution.fill(0); if( m_Debug ) { for( ::itk::fem::Solver::NodeArray::iterator n = m_Solver.node.begin(); n != m_Solver.node.end(); ++n ) { ::std::cout << "Node#: " << (*n)->GN << ": "; ::std::cout << " coord " << (*n)->GetCoordinates() << std::endl; } for( ::itk::fem::Solver::ElementArray::iterator n = m_Solver.el.begin(); n != m_Solver.el.end(); ++n ) { ::std::cout << "Elt#: " << (*n)->GN << ": has " << (*n)->GetNumberOfNodes() << " nodes "; for( int i = 0; i < (*n)->GetNumberOfNodes(); i++ ) { ::std::cout << " coord " << (*n)->GetNode(i)->GetCoordinates() << std::endl; } } } unsigned int maxits = m_Solver.GetNumberOfDegreesOfFreedom(); // should be > twice ndofs // if (m_Debug) ::std::cout << " ndof " << maxits << std::endl; itpackWrapper.SetMaximumNumberIterations(maxits * 4); itpackWrapper.SetTolerance(1.e-5); itpackWrapper.JacobianConjugateGradient(); itpackWrapper.SetMaximumNonZeroValuesInMatrix(maxits * 100); m_Solver.SetLinearSystemWrapper(&itpackWrapper); /** * Assemble the master stiffness matrix. In order to do this * the GFN's should already be assigned to every DOF. */ ::std::cout << " assemble k " << std::endl; m_Solver.AssembleK(); ::std::cout << " assemble k done " << std::endl; if( m_Debug ) { for( int i = 0; i < m_Solver.GetNumberOfDegreesOfFreedom(); i++ ) { float sum = 0; for( int j = 0; j < m_Solver.GetNumberOfDegreesOfFreedom(); j++ ) { float val = m_Solver.GetLinearSystemWrapper()->GetMatrixValue(i, j); if( i != j ) { sum += val; } // if (val != 0) ::std::cout << " entry i,j " << i << " , " << j << " = " << // m_Solver.GetLinearSystemWrapper()->GetMatrixValue(i,j)<GN << ": "; } if( m_Debug ) { ::std::cout << " coord " << (*n)->GetCoordinates() << std::endl; } // For each DOF in the node... */ for( unsigned int d = 0, dof; (dof = (*n)->GetDegreeOfFreedom(d) ) != ::itk::fem::Element::InvalidDegreeOfFreedomID; d++ ) { if( m_Debug ) { ::std::cout << m_Solver.GetSolution(dof); } if( m_Debug ) { ::std::cout << ", "; } // if (d==0 && im == 0) {m_RealSolution[ct]=m_Solver.GetSolution(dof); ct++;} // if (d==0 && im == 1) {m_ImagSolution[ct]=m_Solver.GetSolution(dof); ct++;} if( im == 0 ) { m_RealSolution[dof] = m_Solver.GetSolution(dof); ct++; } if( im == 1 ) { m_ImagSolution[dof] = m_Solver.GetSolution(dof); ct++; } // ::std::cout << " dof " << dof << " ct " << ct << std::endl; } if( m_Debug ) { ::std::cout << "\b\b\b \b\n"; } m_Debug = false; } } return; } template void FEMConformalMap ::BuildOutputMeshes(typename TImage::Pointer image) { // Get the number of points in the mesh int numPoints = m_Solver.node.size(); // Create vtk polydata vtkPolyData* polydata1 = vtkPolyData::New(); vtkPolyData* polydata2 = vtkPolyData::New(); // Create the vtkPoints object and set the number of points vtkPoints* vpoints1 = vtkPoints::New(); vtkPoints* vpoints2 = vtkPoints::New(); vpoints1->SetNumberOfPoints(numPoints); vpoints2->SetNumberOfPoints(numPoints); vtkFloatArray* param = vtkFloatArray::New(); param->SetName("angle"); ::std::cout << " start pts "; int idx = 0; vtkDataArray* scs = NULL; if( m_VtkSurfaceMesh ) { vtkPointData *pd = m_VtkSurfaceMesh->GetPointData(); scs = pd->GetScalars(); } typename TImage::SpacingType spacing = image->GetSpacing(); for( ::itk::fem::Solver::NodeArray::iterator n = m_Solver.node.begin(); n != m_Solver.node.end(); ++n ) { // extrinisic coords VectorType loc = (*n)->GetCoordinates(); // spherical coords unsigned int dof = (*n)->GetDegreeOfFreedom(0); float x = m_RealSolution[dof]; float y = m_ImagSolution[dof]; float radsq = x * x + y * y; float c1 = 2.0 * x / (1.0 + radsq); float c2 = 2.0 * y / (1.0 + radsq); float c3 = 2.0 * radsq / (1.0 + radsq) - 1.0; if( idx % 1000 == 0 ) { ::std::cout << c1 << " " << c2 << " " << c3 << " mag " << sqrt(c1 * c1 + c2 * c2 + c3 * c3) << std::endl; } // float pt1[3]; pt1[0] = c1; pt1[1] = c2; pt1[2] = c3; float pt2[3]; pt2[0] = loc[0]; pt2[1] = loc[1]; pt2[2] = loc[2]; vpoints1->SetPoint(idx, pt1); vpoints2->SetPoint(idx, pt2); typename TImage::IndexType index; index[0] = (long int) loc[0] / spacing[0]; index[1] = (long int) loc[1] / spacing[1]; index[2] = (long int) loc[2] / spacing[2]; float temp; if( m_VtkSurfaceMesh ) { temp = scs->GetTuple1(idx); } else { temp = (float) image->GetPixel(index); } param->InsertNextValue(temp); (*n)->GN = idx; idx++; } ::std::cout << " done with pts " << std::endl; vtkCellArray* tris1 = vtkCellArray::New(); vtkCellArray* tris2 = vtkCellArray::New(); ::std::cout << " start with tris " << std::endl; for( ::itk::fem::Solver::ElementArray::iterator n = m_Solver.el.begin(); n != m_Solver.el.end(); ++n ) { tris1->InsertNextCell(3); tris2->InsertNextCell(3); for( int i = 0; i < (*n)->GetNumberOfNodes(); i++ ) { tris1->InsertCellPoint( (*n)->GetNode(i)->GN); tris2->InsertCellPoint( (*n)->GetNode(i)->GN); } } ::std::cout << " done with tris " << std::endl; // Assign points and cells polydata1->SetPoints(vpoints1); polydata2->SetPoints(vpoints2); polydata1->SetPolys(tris1); polydata2->SetPolys(tris2); polydata1->GetPointData()->SetScalars(param); polydata2->GetPointData()->SetScalars(param); // vtkDelaunay2D* delny1=vtkDelaunay2D::New(); // delny1->SetInput(polydata1); // m_ExtractedSurfaceMesh=delny1->GetOutput();//polydata1;// m_ExtractedSurfaceMesh = vtkGenSmoothMesh(polydata1); // vtkDelaunay2D* delny2=vtkDelaunay2D::New(); // delny2->SetInput(polydata2); // m_DiskSurfaceMesh=delny2->GetOutput(); if( !m_VtkSurfaceMesh ) { m_VtkSurfaceMesh = vtkGenSmoothMesh(polydata2); // polydata2; } return; } } // namespace itk #endif ants-2.2.0/Temporary/itkFEMConformalMap.h000066400000000000000000000144131311104306400202250ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Copyright (c) 2002 Insight 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 _FEMConformalMap_h #define _FEMConformalMap_h #include #include #include #include "vtkDataSetWriter.h" #include "vtkDataSetMapper.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkActor.h" #include "vtkRenderWindowInteractor.h" #include "vtkDataSetReader.h" #include "vtkUnstructuredGrid.h" #include "vtkDataSet.h" #include "vtkCellArray.h" #include "vtkVolume16Reader.h" #include "vtkImageReader2.h" #include "vtkPolyDataMapper.h" #include "vtkActor.h" #include "vtkOutlineFilter.h" #include "vtkCamera.h" #include "vtkProperty.h" #include "vtkPolyData.h" #include "vtkPolyVertex.h" #include "vtkPointData.h" #include "vtkExtractEdges.h" #include "vtkPolyDataNormals.h" #include "vtkMarchingCubes.h" #include "vtkImageGaussianSmooth.h" #include "vtkDecimatePro.h" #include "vtkContourFilter.h" #include "vtkPolyDataConnectivityFilter.h" // #include "vtkKitwareContourFilter.h" #include "vtkSmoothPolyDataFilter.h" #include "vtkSTLWriter.h" #include "vtkUnstructuredGridToPolyDataFilter.h" // #include "itkImageToVTKImageFilter.h" #include "vtkDelaunay2D.h" #include "vtkFloatArray.h" #include "itkObject.h" #include "itkProcessObject.h" #include "itkVectorContainer.h" #include "itkCastImageFilter.h" #include "itkFEM.h" #include "itkFEMLinearSystemWrapperItpack.h" #include "itkFEMElement3DC0LinearTriangularLaplaceBeltrami.h" #include "itkFEMElement3DC0LinearTriangularMembrane.h" namespace itk { /** \class FEMConformalMap * Angenent, Haker conformal mapping algorithm using FEM. * * \note The origin of a neighborhood is always taken to be * the first point entered into and the * last point stored in the list. */ template class FEMConformalMap : public ProcessObject { public: /** Standard class typedefs. */ typedef FEMConformalMap Self; typedef ProcessObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(FEMConformalMap, ProcessObject); /** Method for creation through the object factory. */ itkNewMacro(Self); /** Surface (mesh) types. */ typedef TImage ImageType; typedef typename TImage::Pointer ImageTypePointer; /** Surface (mesh) types. */ typedef TSurface SurfaceType; typedef typename SurfaceType::Pointer SurfaceTypePointer; typedef typename SurfaceType::PointType PointType; typedef typename SurfaceType::CellsContainerPointer InputCellsContainerPointer; typedef typename SurfaceType::CellsContainer::Iterator InputCellsContainerIterator; /** Image dimension. */ // itkStaticConstMacro(ImageDimension, unsigned int, TImage::ImageDimension); itkStaticConstMacro(ImageDimension, unsigned int, TDimension); itkStaticConstMacro(SurfaceDimension, unsigned int, TDimension); typedef double RealType; typedef vnl_vector VectorType; typedef vnl_vector_fixed FixedVectorType; typedef vnl_matrix MatrixType; /** FEM types */ typedef itk::fem::MaterialLinearElasticity MaterialType; typedef itk::fem::Node NodeType; typedef itk::fem::LoadNode LoadType; typedef itk::fem::Element3DC0LinearTriangularLaplaceBeltrami ElementType; typedef itk::fem::Element3DC0LinearTriangularMembrane ElementType1; /** Set input parameter file */ itkSetStringMacro( ParameterFileName ); /** Set input parameter file */ itkGetStringMacro( ParameterFileName ); itkGetMacro(Sigma, RealType); itkSetMacro(Sigma, RealType); itkGetMacro(SurfaceMesh, SurfaceTypePointer); itkSetMacro(SurfaceMesh, SurfaceTypePointer); itkGetMacro(Image, ImageTypePointer); itkSetMacro(Image, ImageTypePointer); itkGetMacro(SphereImage, ImageTypePointer); itkSetMacro(SphereImage, ImageTypePointer); itkSetMacro(SouthPole, int); void SetNorthPole(int p) { if( p % 2 == 0 ) { m_NorthPole = p; m_SouthPole = p + 1; } else { m_NorthPole = p - 1; m_SouthPole = p - 1; } } void SetDebug(bool b) { m_Debug = b; } void SetReadFromFile(bool b) { m_ReadFromFile = b; } void FindPoles(int dim); void FixPoles(int dim); void ConformalParameterize(); void ConformalMap(); void ComputeStereographicCoordinates(); void MapStereographicCoordinatesToImage(int dim); void MapImageToSphere(ImageType* img, float rad ); void MapCheckerboardToImage(float increment); void BuildOutputMeshes(typename TImage::Pointer image); vtkPolyData* m_ExtractedSurfaceMesh; vtkPolyData* m_VtkSurfaceMesh; protected: bool GenerateSystemFromSurfaceMesh(); bool GenerateSystemFromVtkSurfaceMesh(); void ApplyRealForces(int dim); void ApplyImaginaryForces(int dim); FEMConformalMap(); virtual ~FEMConformalMap() { }; private: RealType m_Sigma; RealType m_Pi; std::string m_ParameterFileName; int m_NorthPole; int m_SouthPole; itk::fem::Solver m_Solver; bool m_ReadFromFile; bool m_Debug; bool m_FindingRealSolution; VectorType m_RealSolution; VectorType m_ImagSolution; ImageTypePointer m_Image; ImageTypePointer m_SphereImage; SurfaceTypePointer m_SurfaceMesh; itk::fem::LinearSystemWrapperItpack itpackWrapper; unsigned long m_PoleElementsGN[7]; }; } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkFEMConformalMap.cxx" #endif #endif ants-2.2.0/Temporary/itkFEMDiscConformalMap.cxx000066400000000000000000001561721311104306400214140ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Copyright (c) 2002 Insight Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for detailm_Solver. 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 _FEMDiscConformalMap_hxx #define _FEMDiscConformalMap_hxx #include #include #include #include #include #include #include #include #include "vtkDelaunay2D.h" #include "vtkSelectPolyData.h" #include "vtkFloatArray.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkFEMDiscConformalMap.h" #include "itkFEMLoadNode.h" #include "itkSurfaceMeshCurvature.h" #include "vtkClipPolyData.h" #include "vtkContourFilter.h" #include "vtkSmartPointer.h" namespace itk { template FEMDiscConformalMap ::FEMDiscConformalMap() { m_Sigma = 2.0e-4; m_ParameterFileName = ""; m_NorthPole = 0; m_SourceNodeNumber = 1; this->m_DistanceCostWeight = 1; this->m_LabelCostWeight = 0; m_Pi = 3.14159265358979323846; m_ReadFromFile = false; m_Debug = false; m_FindingRealSolution = true; this->m_SurfaceMesh = NULL; for( int i = 0; i < 7; i++ ) { m_PoleElementsGN[i] = 0; } this->m_MapToCircle = true; this->m_MapToSquare = false; this->m_ParamWhileSearching = true; m_Smooth = 2.0; this->m_Label_to_Flatten = 0; this->m_FlatImage = NULL; manifoldIntegrator = ManifoldIntegratorType::New(); } template bool FEMDiscConformalMap ::InBorder(typename FEMDiscConformalMap::GraphSearchNodePointer g) { if( this->m_HelpFindLoop[g->GetIdentity()] > 0 ) { return true; } else { return false; } float dist = g->GetTotalCost(); if( dist > m_MaxCost && dist < m_MaxCost + 1.0 ) { return true; } return false; } template bool FEMDiscConformalMap ::InDisc(typename FEMDiscConformalMap::GraphSearchNodePointer g) { // if ( this->m_HelpFindLoop[ g->GetIdentity() ] != 0 ) return true; // else return false; // if ( g->WasVisited() ) return true; float d = g->GetTotalCost(); for( unsigned int i = 0; i < this->m_DiscBoundaryList.size(); i++ ) { if( d <= this->m_DiscBoundaryList[i]->GetTotalCost() ) { return true; } } return false; if( g->GetPredecessor() ) { return true; } else { return false; } } template void FEMDiscConformalMap ::FindSource(IndexType index) { vtkPoints* vtkpoints = this->m_SurfaceMesh->GetPoints(); int numPoints = vtkpoints->GetNumberOfPoints(); float mindist = 1.e9; for( int i = 0; i < numPoints; i++ ) { double* pt = vtkpoints->GetPoint(i); float dist = 0.0; for( int j = 0; j < ImageDimension; j++ ) { dist += (pt[j] - (float)index[j]) * (pt[j] - (float)index[j]); } dist = sqrt(dist); if( dist < mindist ) { mindist = dist; m_SourceNodeNumber = i; } } } template void FEMDiscConformalMap ::FindMeanSourceInLabel(unsigned int label ) { vtkPoints* vtkpoints = this->m_SurfaceMesh->GetPoints(); int numPoints = vtkpoints->GetNumberOfPoints(); float mindist = 1.e9; float meanx = 0., meany = 0, meanz = 0; unsigned long ct = 0; vtkDataArray* labels = this->m_SurfaceMesh->GetPointData()->GetArray("Label"); vtkDataArray* features = NULL; if( this->m_SurfaceFeatureMesh ) { if( this->m_SurfaceFeatureMesh->GetPointData()->GetArray("Feature") ) { features = this->m_SurfaceFeatureMesh->GetPointData()->GetArray("Feature"); } else { features = labels; } } if( !labels ) { std::cout << " cant get Labels --- need an array named Label in the mesh ... " << std::endl; std::exception(); } else { std::cout << " got Labels " << std::endl; } for( int i = 0; i < numPoints; i++ ) { double* pt = vtkpoints->GetPoint(i); manifoldIntegrator->GetGraphNode(i)->SetValue(labels->GetTuple1(i), 3); // std::cout << " label " << labels->GetTuple1(i) << " & " << label <GetTuple1(i) - label ) < 0.5 ) { // std::cout << " choose " << labels->GetTuple1(i) << " & " << label << std::endl; meanx += pt[0]; meany += pt[1]; meanz += pt[2]; ct++; // i=numPoints+1; } else { // ct+=0; manifoldIntegrator->GetGraphNode(i)->SetUnVisitable(); } } meanx /= (float)ct; meany /= (float)ct; meanz /= (float)ct; for( int i = 0; i < numPoints; i++ ) { double* pt = vtkpoints->GetPoint(i); float dist = 0.0; dist += (pt[0] - meanx) * (pt[0] - meanx); dist += (pt[1] - meany) * (pt[1] - meany); dist += (pt[2] - meanz) * (pt[2] - meanz); dist = sqrt(dist); if( dist < mindist && fabs( label - labels->GetTuple1(i) ) < 0.5 ) { mindist = dist; m_SourceNodeNumber = i; // std::cout << " label " << label << " chose " << labels->GetTuple1(i) << std::endl; } } if( this->FindLoopAroundNode( this->m_SourceNodeNumber ) == 2 ) { std::cout << " found loop " << std::endl; } if( ct > 0 ) { std::cout << meanx << " " << meany << " " << meanz << std::endl; } else { std::cout << " no label " << label << " exiting " << std::endl; std::exception(); } } template float FEMDiscConformalMap ::AssessNodeDistanceCost( unsigned int nodeid ) { return manifoldIntegrator->GetGraphNode(nodeid)->GetTotalCost(); /* diff=G1->GetLocation()-G3->GetLocation(); tangentlength=0; for (unsigned int d=0; dGetLocation()-G3->GetLocation(); tangentlength=0; for (unsigned int d=0; d float FEMDiscConformalMap ::GetBoundaryParameterForSquare( unsigned int nodeid, unsigned int whichParam ) { // compute loop length and check validity float tangentlength = 0, totallength = 0, nodeparam = 0, x = 0, y = 0; typename GraphSearchNodeType::NodeLocationType diff; for( unsigned int i = 0; i < this->m_DiscBoundaryList.size(); i++ ) { unsigned int nxt = ( ( i + 1 ) % this->m_DiscBoundaryList.size() ); diff = this->m_DiscBoundaryList[i]->GetLocation() - this->m_DiscBoundaryList[nxt]->GetLocation(); if( this->m_DiscBoundaryList[i]->GetIdentity() == nodeid ) { nodeparam = totallength; } tangentlength = 0; for( unsigned int d = 0; d < diff.Size(); d++ ) { tangentlength += diff[d] * diff[d]; } totallength += tangentlength; } float arclength = nodeparam / totallength; if( arclength <= 0.25 ) { x = 0; y = arclength / 0.25; /* 0 => 1 */ } else if( arclength <= 0.5 ) { x = (0.5 - arclength) / 0.25; /* 0 => 1 */ y = 1; } else if( arclength <= 0.75 ) { x = 1; y = (0.75 - arclength) / 0.25; /* 1 => 0 */ } else if( arclength <= 1 ) { x = (1 - arclength) / 0.25; /* 1 => 0 */ y = 0; } // std::cout <<" x " << x << " y " << y << " al " << arclength << std::endl; if( whichParam == 0 ) { return x; } else if( whichParam == 1 ) { return y; } else { return arclength; } } template float FEMDiscConformalMap ::GetBoundaryParameterForCircle( unsigned int nodeid, unsigned int whichParam ) { // compute loop length and check validity float tangentlength = 0, totallength = 0, nodeparam = 0; typename GraphSearchNodeType::NodeLocationType diff; for( unsigned int i = 0; i < this->m_DiscBoundaryList.size(); i++ ) { unsigned int nxt = ( ( i + 1 ) % this->m_DiscBoundaryList.size() ); diff = this->m_DiscBoundaryList[i]->GetLocation() - this->m_DiscBoundaryList[nxt]->GetLocation(); if( this->m_DiscBoundaryList[i]->GetIdentity() == nodeid ) { nodeparam = totallength; } tangentlength = 0; for( unsigned int d = 0; d < diff.Size(); d++ ) { tangentlength += diff[d] * diff[d]; } totallength += tangentlength; } float arclength = nodeparam / totallength * M_PI * 2; if( whichParam == 0 ) { return cos(arclength); } else if( whichParam == 1 ) { return sin(arclength); } else { return arclength; } } template unsigned int FEMDiscConformalMap ::AddVertexToLoop() { // compute loop length and check validity float tangentlength = 0, totallength = 0; bool isvalid = true; typename GraphSearchNodeType::NodeLocationType diff; for( unsigned int i = 0; i < this->m_DiscBoundaryList.size(); i++ ) { unsigned int nxt = ( ( i + 1 ) % this->m_DiscBoundaryList.size() ); // std::cout << " this->m_DiscBoundaryList[i]->GetLocation() " << // this->m_DiscBoundaryList[i]->GetLocation() << // std::endl; diff = this->m_DiscBoundaryList[i]->GetLocation() - this->m_DiscBoundaryList[nxt]->GetLocation(); tangentlength = 0; for( unsigned int d = 0; d < diff.Size(); d++ ) { tangentlength += diff[d] * diff[d]; } totallength += tangentlength; // check if the next node is in the current node's neighborlist isvalid = false; for( unsigned int n = 0; n < this->m_DiscBoundaryList[i]->m_NumberOfNeighbors; n++ ) { if( this->m_DiscBoundaryList[i]->m_Neighbors[n] == this->m_DiscBoundaryList[nxt] ) { isvalid = true; } } } std::cout << " length " << totallength << " valid? " << isvalid << " entries " << this->m_DiscBoundaryList.size() << " gsz " << this->m_HelpFindLoop.size() << std::endl; /** now find a node with HelpFindLoop value == 0 that minimally changes the length ... and that has a root neighbor and next neighbor consistent with the original edge */ unsigned int newnodeid = 0; unsigned int nodeparam = 0; // the position in the curve parameter float newedgelength = 1.e9; // minimize this GraphSearchNodePointer G1, G2, G3, BestG = NULL; for( unsigned int i = 0; i < this->m_DiscBoundaryList.size(); i++ ) { unsigned int nxt = ( ( i + 1 ) % this->m_DiscBoundaryList.size() ); G1 = this->m_DiscBoundaryList[i]; G2 = this->m_DiscBoundaryList[nxt]; G3 = NULL; /** 3 conditions : (1) HelpFindLoop == 0 (2) am neighbor of curnode (3) am neighbor of next node (4) minlength */ for( unsigned int n = 0; n < G1->m_NumberOfNeighbors; n++ ) { for( unsigned int m = 0; m < G2->m_NumberOfNeighbors; m++ ) { long hfl = abs(this->m_HelpFindLoop[G1->m_Neighbors[n]->GetIdentity()]); hfl += abs(this->m_HelpFindLoop[G2->m_Neighbors[m]->GetIdentity()]); if( G1->m_Neighbors[n] == G2->m_Neighbors[m] && hfl == 0 ) { G3 = G1->m_Neighbors[n]; n = G1->m_NumberOfNeighbors; m = G2->m_NumberOfNeighbors; float elementlength = this->AssessNodeDistanceCost( G3->GetIdentity() ); if( elementlength < newedgelength ) { newedgelength = elementlength; BestG = G3; newnodeid = G3->GetIdentity(); nodeparam = i; } } } } } if( !BestG ) { std::cout << " does not exist " << std::endl; return false; } if( this->m_HelpFindLoop[BestG->GetIdentity()] != 0 ) { std::cout << " already done " << std::endl; return false; } std::vector neighborlist; long paramct = 0; totallength = 0; for( unsigned int i = 0; i < this->m_DiscBoundaryList.size(); i++ ) { paramct++; neighborlist.push_back( this->m_DiscBoundaryList[i] ); this->m_HelpFindLoop[this->m_DiscBoundaryList[i]->GetIdentity()] = paramct; if( i == nodeparam ) { paramct++; neighborlist.push_back( BestG ); this->m_HelpFindLoop[BestG->GetIdentity()] = paramct; } } // std::cout << " len1 " << this->m_DiscBoundaryList.size() << " len2 " << neighborlist.size() << // std::endl; this->SetDiscBoundaryList( neighborlist ); // this->m_DiscBoundaryList.assign(neighborlist.begin(),neighborlist.end()); /* for (unsigned int i=0; im_DiscBoundaryList.size(); i++) { unsigned int nxt=( ( i+1 ) % this->m_DiscBoundaryList.size() ); diff=this->m_DiscBoundaryList[i]->GetLocation()-this->m_DiscBoundaryList[nxt]->GetLocation(); tangentlength=0; for (unsigned int d=0; dm_DiscBoundaryList.size(); i++ ) { paramct++; neighborlist.push_back( this->m_DiscBoundaryList[i] ); this->m_HelpFindLoop[this->m_DiscBoundaryList[i]->GetIdentity()] = paramct; if( this->m_DiscBoundaryList[i]->GetIdentity() == newnodeid ) { // find the parameter of any of this fellows neighors ... // if it's greater than i+1 then skip ahead long bestparam = this->m_HelpFindLoop[this->m_DiscBoundaryList[i]->GetIdentity()] + 1; for( unsigned int n = 0; n < this->m_DiscBoundaryList[i]->m_NumberOfNeighbors; n++ ) { if( this->m_HelpFindLoop[this->m_DiscBoundaryList[i]->m_Neighbors[n]->GetIdentity()] > bestparam ) { bestparam = this->m_HelpFindLoop[this->m_DiscBoundaryList[i]->m_Neighbors[n]->GetIdentity()]; BestG = this->m_DiscBoundaryList[i]->m_Neighbors[n]; } } // neighborlist.push_back( BestG ); for( unsigned int j = i + 1; j < (unsigned int) bestparam - 1; j++ ) { this->m_HelpFindLoop[this->m_DiscBoundaryList[j]->GetIdentity()] = -1; } i = (unsigned int) (bestparam - 2); } } this->SetDiscBoundaryList( neighborlist ); // this->m_DiscBoundaryList.assign(neighborlist.begin(),neighborlist.end()); float newtotallength = 0; for( unsigned int i = 0; i < this->m_DiscBoundaryList.size(); i++ ) { unsigned int nxt = ( ( i + 1 ) % this->m_DiscBoundaryList.size() ); diff = this->m_DiscBoundaryList[i]->GetLocation() - this->m_DiscBoundaryList[nxt]->GetLocation(); tangentlength = 0; for( unsigned int d = 0; d < diff.Size(); d++ ) { tangentlength += diff[d] * diff[d]; } newtotallength += tangentlength; } std::cout << " total1 " << totallength << " total2 " << newtotallength << std::endl; // check for self-intersection for( unsigned int i = 0; i < this->m_DiscBoundaryList.size(); i++ ) { for( unsigned int j = 0; j < this->m_DiscBoundaryList.size(); j++ ) { if( i != j && this->m_DiscBoundaryList[i]->GetIdentity() == this->m_DiscBoundaryList[j]->GetIdentity() ) { isvalid = false; } } } for( unsigned long g = 0; g < this->m_HelpFindLoop.size(); g++ ) { if( this->m_HelpFindLoop[g] != 0 ) { this->m_HelpFindLoop[g] = -1; } } for( unsigned int j = 0; j < this->m_DiscBoundaryList.size(); j++ ) { m_HelpFindLoop[this->m_DiscBoundaryList[j]->GetIdentity()] = j + 1; } unsigned long MAXLIST = 200; if( this->m_DiscBoundaryList.size() > MAXLIST ) { std::cout << this->m_RootNode->GetLocation() << std::endl; std::cout << "long list " << MAXLIST << std::endl; return 2; } return isvalid; } template unsigned int FEMDiscConformalMap ::FindLoopAroundNode( unsigned int j_in ) { vtkDataArray* labels = this->m_SurfaceMesh->GetPointData()->GetArray("Label"); vtkDataArray* features = NULL; if( this->m_SurfaceFeatureMesh ) { if( this->m_SurfaceFeatureMesh->GetPointData()->GetArray("Feature") ) { features = this->m_SurfaceFeatureMesh->GetPointData()->GetArray("Feature"); } else { features = labels; } } this->m_DiscBoundaryList.clear(); unsigned int gsz = manifoldIntegrator->GetGraphSize(); // get distance from this node to all others // now measure the length distortion of the given solution this->m_RootNode = manifoldIntegrator->GetGraphNode(j_in); for( int i = 0; i < manifoldIntegrator->GetGraphSize(); i++ ) { manifoldIntegrator->GetGraphNode(i)->SetTotalCost(vnl_huge_val(manifoldIntegrator->GetMaxCost() ) ); manifoldIntegrator->GetGraphNode(i)->SetUnVisited(); manifoldIntegrator->GetGraphNode(i)->SetValue(0.0, 1); manifoldIntegrator->GetGraphNode(i)->SetValue(0.0, 2); float labval = labels->GetTuple1(i); manifoldIntegrator->GetGraphNode(i)->SetValue(labval, 3); manifoldIntegrator->GetGraphNode(i)->SetPredecessor(NULL); } manifoldIntegrator->EmptyQ(); manifoldIntegrator->SetSearchFinished( false ); manifoldIntegrator->SetSource(this->m_RootNode); manifoldIntegrator->InitializeQueue(); manifoldIntegrator->SetParamWhileSearching( this->m_ParamWhileSearching ); manifoldIntegrator->SetWeights(this->m_MaxCost, this->m_DistanceCostWeight, this->m_LabelCostWeight); manifoldIntegrator->PrintWeights(); manifoldIntegrator->FindPath(); /** at this point, we should have extracted the disc now we want to find a boundary point and an instant parameterization via FEM */ this->SetDiscBoundaryList( manifoldIntegrator->m_BoundaryList ); // this->m_DiscBoundaryList.assign(manifoldIntegrator->m_BoundaryList.begin(), // manifoldIntegrator->m_BoundaryList.end()); this->m_HelpFindLoop.clear(); this->m_HelpFindLoop.resize(gsz, 0); this->m_HelpFindLoop[j_in] = -1; unsigned int furthestnode = 0; float maxdist = 0; GraphSearchNodePointer farnode1 = NULL; for( unsigned int j = 0; j < this->m_DiscBoundaryList.size(); j++ ) { if( this->m_DiscBoundaryList[j]->GetTotalCost() > maxdist ) { maxdist = this->m_DiscBoundaryList[j]->GetTotalCost(); furthestnode = this->m_DiscBoundaryList[j]->GetIdentity(); farnode1 = this->m_DiscBoundaryList[j]; } m_HelpFindLoop[this->m_DiscBoundaryList[j]->GetIdentity()] = j + 1; } if( this->m_ParamWhileSearching ) { return 2; } // butt ManifoldIntegratorTypePointer discParameterizer = ManifoldIntegratorType::New(); discParameterizer->SetSurfaceMesh(this->m_SurfaceMesh); discParameterizer->InitializeGraph3(); discParameterizer->SetMaxCost(1.e9); std::cout << " dcz " << discParameterizer->GetGraphSize() << std::endl; for( int i = 0; i < discParameterizer->GetGraphSize(); i++ ) { discParameterizer->GetGraphNode(i)->SetTotalCost(vnl_huge_val(discParameterizer->GetMaxCost() ) ); discParameterizer->GetGraphNode(i)->SetUnVisitable(); if( this->m_HelpFindLoop[i] > 0 ) { discParameterizer->GetGraphNode(i)->SetUnVisited(); } discParameterizer->GetGraphNode(i)->SetValue(0.0, 1); discParameterizer->GetGraphNode(i)->SetValue(0.0, 2); discParameterizer->GetGraphNode(i)->SetPredecessor(NULL); } discParameterizer->EmptyQ(); discParameterizer->SetSearchFinished( false ); discParameterizer->SetSource(discParameterizer->GetGraphNode(furthestnode) ); discParameterizer->InitializeQueue(); discParameterizer->SetWeights(1.e9, 1, 0); discParameterizer->FindPath(); float temp = 0; GraphSearchNodePointer farnode2 = NULL; unsigned int vct = 0; for( int i = 0; i < discParameterizer->GetGraphSize(); i++ ) { if( discParameterizer->GetGraphNode(i)->WasVisited() ) { float t = discParameterizer->GetGraphNode(i)->GetTotalCost(); if( t > temp ) { temp = t; farnode2 = discParameterizer->GetGraphNode(i); } vct++; // std::cout << " dist " << t << " vct " << vct << std::endl; } } discParameterizer->BackTrack(farnode2); // butt2 ManifoldIntegratorTypePointer lastlooper = ManifoldIntegratorType::New(); lastlooper->SetSurfaceMesh(this->m_SurfaceMesh); lastlooper->InitializeGraph3(); lastlooper->SetMaxCost(1.e9); // std::cout << " dcz "<< lastlooper->GetGraphSize() << std::endl; for( int i = 0; i < lastlooper->GetGraphSize(); i++ ) { lastlooper->GetGraphNode(i)->SetTotalCost(vnl_huge_val(lastlooper->GetMaxCost() ) ); lastlooper->GetGraphNode(i)->SetUnVisitable(); if( this->m_HelpFindLoop[i] > 0 ) { lastlooper->GetGraphNode(i)->SetUnVisited(); } lastlooper->GetGraphNode(i)->SetValue(0.0, 1); lastlooper->GetGraphNode(i)->SetValue(0.0, 2); lastlooper->GetGraphNode(i)->SetPredecessor(NULL); } lastlooper->EmptyQ(); lastlooper->SetSearchFinished( false ); for( unsigned int pp = 0; pp < discParameterizer->GetPathSize(); pp++ ) { unsigned int id = discParameterizer->GetPathAtIndex(pp)->GetIdentity(); lastlooper->SetSource( lastlooper->GetGraphNode(id) ); } lastlooper->InitializeQueue(); lastlooper->SetWeights(1.e9, 1, 0); lastlooper->FindPath(); GraphSearchNodePointer farnode3 = NULL; vct = 0; temp = 0; for( int i = 0; i < lastlooper->GetGraphSize(); i++ ) { if( lastlooper->GetGraphNode(i)->WasVisited() ) { float t = lastlooper->GetGraphNode(i)->GetTotalCost(); if( t > temp ) { temp = t; farnode3 = lastlooper->GetGraphNode(i); } vct++; // std::cout << " dist " << t << " vct " << vct << std::endl; } } // std::exception(); // finally reuse lastlooper with farnode3 as root ... for( int i = 0; i < lastlooper->GetGraphSize(); i++ ) { lastlooper->GetGraphNode(i)->SetTotalCost(vnl_huge_val(lastlooper->GetMaxCost() ) ); lastlooper->GetGraphNode(i)->SetUnVisitable(); if( this->m_HelpFindLoop[i] > 0 ) { lastlooper->GetGraphNode(i)->SetUnVisited(); } lastlooper->GetGraphNode(i)->SetValue(0.0, 1); lastlooper->GetGraphNode(i)->SetValue(0.0, 2); lastlooper->GetGraphNode(i)->SetPredecessor(NULL); } lastlooper->EmptyQ(); lastlooper->SetSearchFinished( false ); lastlooper->SetSource( lastlooper->GetGraphNode(farnode3->GetIdentity() ) ); lastlooper->InitializeQueue(); lastlooper->SetWeights(1.e9, 1, 0); lastlooper->FindPath(); // now assemble the parameterization... // this->m_DiscBoundaryList.clear(); // add both the above to the list // std::cout << " part 1 " << std::endl; for( unsigned int i = 0; i < discParameterizer->GetPathSize(); i++ ) { unsigned int id = discParameterizer->GetPathAtIndex(i)->GetIdentity(); // std::cout << manifoldIntegrator->GetGraphNode(id)->GetLocation() << std::endl; this->m_DiscBoundaryList.push_back( manifoldIntegrator->GetGraphNode(id) ); } // std::cout << " part 2 " << std::endl; lastlooper->BackTrack( lastlooper->GetGraphNode( farnode1->GetIdentity() ) ); for( unsigned int i = 0; i < lastlooper->GetPathSize(); i++ ) { unsigned int id = lastlooper->GetPathAtIndex(i)->GetIdentity(); // std::cout << manifoldIntegrator->GetGraphNode(id)->GetLocation() << std::endl; this->m_DiscBoundaryList.push_back( manifoldIntegrator->GetGraphNode(id) ); } // std::cout << farnode1->GetLocation() << std::endl; // std::cout << farnode2->GetLocation() << std::endl; // std::cout << farnode3->GetLocation() << std::endl; // std::cout << " another idea --- get two points far apart then solve a minimization problem across the // graph // that gives the average value in 0 => 1 ... " << std::endl; // std::cout << " path 1 sz " << discParameterizer->GetPathSize() << std::endl; // finally do but add in reverse order // std::cout << " part 3 " <GetIdentity() << " and " << farnode1->GetIdentity() << std::endl; lastlooper->EmptyPath(); lastlooper->BackTrack( lastlooper->GetGraphNode( farnode2->GetIdentity() ) ); for( unsigned int i = lastlooper->GetPathSize() - 1; i > 0; i-- ) { unsigned int id = lastlooper->GetPathAtIndex(i)->GetIdentity(); // std::cout << manifoldIntegrator->GetGraphNode(id)->GetLocation() << std::endl; this->m_DiscBoundaryList.push_back( manifoldIntegrator->GetGraphNode(id) ); } std::cout << " Almost ... " << std::endl; this->m_HelpFindLoop.clear(); this->m_HelpFindLoop.resize(gsz, 0); for( unsigned int j = 0; j < this->m_DiscBoundaryList.size(); j++ ) { m_HelpFindLoop[this->m_DiscBoundaryList[j]->GetIdentity()] = j + 1; } std::cout << " Achievement!! " << std::endl; return 2; } template void FEMDiscConformalMap ::LocateAndParameterizeDiscBoundary( unsigned int label, bool CheckCost ) { float effectivemaxcost = 0; this->m_DiscBoundaryList.clear(); unsigned int gsz = manifoldIntegrator->GetGraphSize(); std::vector alreadyfound(gsz, false); this->m_DiscBoundarySorter.clear(); this->m_DiscBoundarySorter.resize(gsz, 0); for( unsigned int j = 0; j < gsz; j++ ) { if( manifoldIntegrator->GetGraphNode(j) ) { float cost = manifoldIntegrator->GetGraphNode(j)->GetTotalCost(); if( !CheckCost ) { cost = 0; } if( fabs( manifoldIntegrator->GetGraphNode(j)->GetValue(3) - label ) < 0.5 && cost <= this->m_MaxCost ) { float inb = 0; for( unsigned int i = 0; i < manifoldIntegrator->GetGraphNode(j)->m_NumberOfNeighbors; i++ ) { if( fabs( manifoldIntegrator->GetGraphNode(j)->m_Neighbors[i]->GetValue(3) - label ) > 0.5 ) { // CurrentNode is in the boundary inb = 1; } } // neighborhood if( inb > 0 ) // std::cout << " Node is in boundary " << std::endl; { inb = 0; for( unsigned int i = 0; i < manifoldIntegrator->GetGraphNode(j)->m_NumberOfNeighbors; i++ ) { if( fabs( manifoldIntegrator->GetGraphNode(j)->m_Neighbors[i]->GetValue(3) - label ) < 0.5 && cost <= this->m_MaxCost ) { // CurrentNode is in the boundary inb += 1; } } // neighborhood if( inb >= 2 && alreadyfound[j] == false ) // need at least two neighbors with same label { alreadyfound[j] = true; this->m_DiscBoundaryList.push_back(manifoldIntegrator->GetGraphNode(j) ); if( cost > effectivemaxcost ) { effectivemaxcost = cost; } } } } // less than max cost } // if node exists } // gsz std::cout << " Boundary has " << this->m_DiscBoundaryList.size() << " elements with eff. max cost " << effectivemaxcost << std::endl; if( CheckCost ) { // very inefficient way to parameterize boundary .... unsigned int bsz = this->m_DiscBoundaryList.size(); std::vector alreadyfound(bsz, false); this->m_DiscBoundarySorter.resize(bsz, 0); unsigned int boundcount = 0; unsigned int rootind = 0, lastroot = 0; this->m_DiscBoundarySorter[rootind] = boundcount; alreadyfound[rootind] = true; bool paramdone = false; while( !paramdone ) { std::cout << " start param " << bsz << std::endl; if( bsz == 0 ) { std::exception(); } for( unsigned int myi = 0; myi < bsz; myi++ ) { if( this->m_DiscBoundaryList[myi] != this->m_DiscBoundaryList[rootind] ) { std::cout << " myi " << myi << " root " << rootind << " bc " << boundcount << std::endl; for( unsigned int n = 0; n < this->m_DiscBoundaryList[rootind]->m_NumberOfNeighbors; n++ ) { if( this->m_DiscBoundaryList[myi] == this->m_DiscBoundaryList[rootind]->m_Neighbors[n] && !alreadyfound[myi] ) // // its // in // the // bndry { // check that it's not an isolated bastard bool oknode = true; if( oknode ) { boundcount++; alreadyfound[myi] = true; this->m_DiscBoundarySorter[myi] = boundcount; n = this->m_DiscBoundaryList[rootind]->m_NumberOfNeighbors + 1; std::cout << " cur " << this->m_DiscBoundaryList[rootind]->GetLocation() << " next " << this->m_DiscBoundaryList[myi]->GetLocation() << " boundcount " << boundcount << " of " << bsz << " curroot " << rootind << std::endl; lastroot = rootind; rootind = myi; myi = bsz; } } // is in boundary } // neighborhood loop } // not currootnode if( boundcount >= bsz - 1 ) { paramdone = true; } else if( myi == (bsz - 1) ) { boundcount--; rootind = lastroot; this->m_DiscBoundarySorter[rootind] = -1; std::cout << " failure " << std::endl; std::exception(); } } // all boundary nodes } // while // std::exception(); } // param boundary if } template void FEMDiscConformalMap ::ExtractSurfaceDisc(unsigned int label) { std::cout << " set surface mesh " << std::endl; manifoldIntegrator->SetSurfaceMesh(this->m_SurfaceMesh); std::cout << " begin initializing graph " << std::endl; manifoldIntegrator->InitializeGraph3(); this->m_SurfaceMesh = manifoldIntegrator->GetSurfaceMesh(); // float frac=0; // IndexType index; if( this->m_Label_to_Flatten == 0 ) { std::cout << " enter LabelToExtract "; std::cin >> m_Label_to_Flatten; float mc = 0; std::cout << " Enter max cost "; std::cin >> mc; m_MaxCost = mc; } manifoldIntegrator->SetMaxCost(this->m_MaxCost); this->FindMeanSourceInLabel( this->m_Label_to_Flatten ); // this->LocateAndParameterizeDiscBoundary( m_Label_to_Flatten , false ); // this->LocateAndParameterizeDiscBoundary( m_Label_to_Flatten , true ); // std::cout << " findpath in extractsurfacedisk done "; // assign scalars to the original surface mesh // typedef itk::SurfaceMeshCurvature surfktype; // typename surfktype::Pointer surfk=surfktype::New(); vtkPoints* vtkpoints = this->m_SurfaceMesh->GetPoints(); int numPoints = vtkpoints->GetNumberOfPoints(); vtkFloatArray* param = vtkFloatArray::New(); param->SetName("angle"); for( int i = 0; i < numPoints; i++ ) { float temp = fabs(manifoldIntegrator->GetGraphNode(i)->GetTotalCost() ); if( temp > m_MaxCost ) { temp = m_MaxCost; } param->InsertNextValue(temp * 255. / m_MaxCost); } std::cout << " extractsurfacedisk done "; // m_SurfaceMesh->GetPointData()->SetScalars(param); } template bool FEMDiscConformalMap ::GenerateSystemFromSurfaceMesh() { if( !this->m_SurfaceMesh ) { std::cout << " NO MESH"; return false; } std::cout << " Generate system from surface mesh " << std::endl; m_Smooth = m_Sigma; // create a material // Choose the material properties typename MaterialType::Pointer m; m = MaterialType::New(); m->GN = 0; // Global number of the material /// m->E = 4.0; // Young modulus -- used in the membrane /// m->A = 0.0; // Crossection area /// m->h = 0.0; // Crossection area /// m->I = 0.0; // Moment of inertia /// m->nu = 0.; // .0; // poissons -- DONT CHOOSE 1.0!!/// m->RhoC = 0.0; // Create the element type ElementType::Pointer e1 = ElementType::New(); e1->m_mat = dynamic_cast( m ); vtkPoints* vtkpoints = this->m_SurfaceMesh->GetPoints(); int numPoints = vtkpoints->GetNumberOfPoints(); int foundnum = 0; int boundsz = 0; for( int i = 0; i < numPoints; i++ ) { double* pt = vtkpoints->GetPoint(i); typename NodeType::Pointer n; n = new NodeType(pt[0], pt[1], pt[2]); if( this->InDisc(manifoldIntegrator->GetGraphNode(i) ) ) { n->GN = i; m_Solver.node.push_back(itk::fem::FEMP(n) ); foundnum++; } if( this->InBorder(manifoldIntegrator->GetGraphNode(i) ) ) { boundsz++; } } typename NodeType::Pointer * narr = new NodeType::Pointer[numPoints]; for( int i = 0; i < numPoints; i++ ) { if( this->InDisc(manifoldIntegrator->GetGraphNode(i) ) || this->InBorder(manifoldIntegrator->GetGraphNode(i) ) ) { narr[i] = m_Solver.node.Find( i ); } } std::cout << " Found " << foundnum << " nodes " << std::endl; std::cout << " bound " << boundsz << std::endl; vtkCellArray* vtkcells = this->m_SurfaceMesh->GetPolys(); vtkIdType npts; vtkIdType* pts; unsigned long i = 0; // unsigned long toti = vtkcells->GetNumberOfCells(); // unsigned long rate = toti/50; // std::cout << " progress "; for( vtkcells->InitTraversal(); vtkcells->GetNextCell(npts, pts); ) { // if ( i % rate == 0 && i > rate ) std::cout << " " << (float) i / (float) toti << " "; // turn the cell into an element // std::cout << " points ids a " << pts[0] << " b " << pts[1] << " c " << pts[2] << std::endl; bool eltok = true; ElementType::Pointer e; e = dynamic_cast(e1->Clone() ); if( this->InDisc(manifoldIntegrator->GetGraphNode(pts[0]) ) ) { e->SetNode(2, narr[pts[0]]); } // e->SetNode(2,m_Solver.node.Find( pts[0] )); else { eltok = false; } if( this->InDisc(manifoldIntegrator->GetGraphNode(pts[1]) ) ) { e->SetNode(1, narr[pts[1]]); } // e->SetNode(1,m_Solver.node.Find( pts[1] )); else { eltok = false; } if( this->InDisc(manifoldIntegrator->GetGraphNode(pts[2]) ) ) { e->SetNode(0, narr[pts[2]]); } // e->SetNode(0,m_Solver.node.Find( pts[2] )); else { eltok = false; } if( eltok ) { e->GN = i; m_Solver.el.push_back(itk::fem::FEMP(e) ); i++; } // else std::cout <<" cannot find elt " << std::endl; } std::cout << " DONE: NUMBER OF CELLS " << i << std::endl; return true; } template void FEMDiscConformalMap ::FixBoundaryPoints(unsigned int option) { itk::fem::Element::ArrayType::iterator elt = m_Solver.el.begin(); unsigned int dofs = (*elt)->GetNumberOfDegreesOfFreedomPerNode(); int fixct = 0; int eltct = 0; while( elt != m_Solver.el.end() ) { for( unsigned int i = 0; i < dofs; i++ ) { int nodeid = (*elt)->GetNode(i)->GN; // if( manifoldIntegrator->GetGraphNode(nodeid)->GetTotalCost() > 222 ) if( this->InBorder(manifoldIntegrator->GetGraphNode(nodeid) ) ) { itk::fem::LoadBC::Pointer l1; l1 = itk::fem::LoadBC::New(); l1->m_element = (*elt); l1->m_dof = i; l1->m_value = vnl_vector(1, 0.0); // for exp float fixvalue = 0.0; if( this->m_MapToCircle ) { fixvalue = this->GetBoundaryParameterForCircle(nodeid, option); } else { fixvalue = this->GetBoundaryParameterForSquare(nodeid, option); } l1->m_value = vnl_vector(1, fixvalue); // for direct rad m_Solver.load.push_back( itk::fem::FEMP(&*l1) ); /* itk::fem::LoadNode::Pointer ln1=itk::fem::LoadNode::New(); ln1->m_pt=0; ln1->F.set_size(1); ln1->F.fill(fixvalue); ln1->m_element=(*elt); m_Solver.load.push_back( itk::fem::FEMP(&*ln1) ); itk::fem::LoadNode::Pointer ln2=itk::fem::LoadNode::New(); ln2->m_pt=1; ln2->F.set_size(1); ln2->F.fill(fixvalue); ln2->m_element=(*elt); m_Solver.load.push_back( itk::fem::FEMP(&*ln2) ); itk::fem::LoadNode::Pointer ln3=itk::fem::LoadNode::New(); ln3->m_pt=2; ln3->F.set_size(1); ln3->F.fill(fixvalue); ln3->m_element=(*elt); m_Solver.load.push_back( itk::fem::FEMP(&*ln3) ); */ if( i == 0 ) { fixct++; } } } ++el; eltct++; } std::cout << " Fixed elt number " << fixct << " of " << eltct << std::endl; } template void FEMDiscConformalMap ::ApplyRealForces() { /* itk::fem::Element::Pointer e = m_Solver.el[m_PoleElementsGN[0]]; // load node 0 { itk::fem::LoadNode::Pointer ln1=itk::fem::LoadNode::New(); ln1->m_pt=0; ln1->F.set_size(1); ln1->F.fill(-4.0*m_Pi); ln1->m_element=(e); m_Solver.load.push_back( itk::fem::FEMP(&*ln1) ); itk::fem::LoadNode::Pointer ln2=itk::fem::LoadNode::New(); ln2->m_pt=1; ln2->F.set_size(1); ln2->F.fill(-4.0*m_Pi); ln2->m_element=(e); m_Solver.load.push_back( itk::fem::FEMP(&*ln2) ); itk::fem::LoadNode::Pointer ln3=itk::fem::LoadNode::New(); ln3->m_pt=2; ln3->F.set_size(1); ln3->F.fill(-4.0*m_Pi); ln3->m_element=(e); m_Solver.load.push_back( itk::fem::FEMP(&*ln3) ); } */ } template void FEMDiscConformalMap ::ApplyImaginaryForces() { itk::fem::Element::Pointer e = m_Solver.el[m_PoleElementsGN[0]]; itk::fem::LoadBC::Pointer l1; l1 = itk::fem::LoadBC::New(); l1->m_element = (e); l1->m_dof = 0; l1->m_value = vnl_vector(1, -1. / 3.); // for exp m_Solver.load.push_back( itk::fem::FEMP(&*l1) ); itk::fem::LoadBC::Pointer l2; l2 = itk::fem::LoadBC::New(); l2->m_element = (e); l2->m_dof = 1; l2->m_value = vnl_vector(1, -1. / 3.); // for exp m_Solver.load.push_back( itk::fem::FEMP(&*l2) ); itk::fem::LoadBC::Pointer l3; l3 = itk::fem::LoadBC::New(); l3->m_element = (e); l3->m_dof = 2; l3->m_value = vnl_vector(1, 2. / 3.); // for exp m_Solver.load.push_back( itk::fem::FEMP(&*l3) ); } template void FEMDiscConformalMap::MakeFlatImage() { // first declare the flat image typename FlatImageType::RegionType region; typename FlatImageType::SizeType size; int sz = 256; size[0] = sz; size[1] = sz; region.SetSize( size ); m_FlatImage = FlatImageType::New(); m_FlatImage->SetRegions( region ); m_FlatImage->Allocate(); typename FlatImageType::IndexType index; std::cout << " Making flat image " << std::endl; int maxits = 100; for( int its = 0; its <= maxits; its++ ) { for( ::itk::fem::Solver::NodeArray::iterator n = m_Solver.node.begin(); n != m_Solver.node.end(); ++n ) { float temp = 255.0 - manifoldIntegrator->GetGraphNode( (*n)->GN)->GetValue(3); // curvature // float temp=255.0*manifoldIntegrator->GetGraphNode((*n)->GN)->GetValue(2); // extrinsic dist index[0] = (long int)(0.5 + ( 1.0 + manifoldIntegrator->GetGraphNode( (*n)->GN)->GetValue(0) ) * (float)(sz - 1) / 2.); index[1] = (long int)(0.5 + ( 1.0 + manifoldIntegrator->GetGraphNode( (*n)->GN)->GetValue(1) ) * (float)(sz - 1) / 2.); // std::cout << " ind " << index << std::endl; m_FlatImage->SetPixel(index, temp); } typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(1.0); filter->SetUseImageSpacingOff(); filter->SetMaximumError(.01f); filter->SetInput(m_FlatImage); filter->Update(); m_FlatImage = filter->GetOutput(); if( its < maxits ) { int center = (int)sz / 2; itk::ImageRegionIteratorWithIndex it( m_FlatImage, region ); it.GoToBegin(); typename FlatImageType::IndexType index; while( !it.IsAtEnd() ) { index = it.GetIndex(); float x = (float)index[0] - (float)sz / 2.0; float y = (float)index[1] - (float)sz / 2.0; float dist = sqrt(x * x + y * y); // std::cout << "center " << center << " index " << index << "dist " << dist ; if( dist > center ) { it.Set( 0.0 ); } ++it; } } } } template void FEMDiscConformalMap::BuildOutputMeshes(float tval) { std::cout << " build output mesh " << std::endl; typedef GraphSearchNodeType::NodeLocationType loctype; // Get the number of points in the mesh int numPoints = m_Solver.node.size(); vtkDataArray* labels = this->m_SurfaceMesh->GetPointData()->GetArray("Label"); vtkDataArray* features = NULL; if( this->m_SurfaceFeatureMesh ) { if( this->m_SurfaceFeatureMesh->GetPointData()->GetArray("Feature") ) { features = this->m_SurfaceFeatureMesh->GetPointData()->GetArray("Feature"); } else { features = labels; } } else { features = labels; } // Create vtk polydata vtkPolyData* polydata1 = vtkPolyData::New(); vtkPolyData* polydata2 = vtkPolyData::New(); // Create the vtkPoints object and set the number of points vtkPoints* vpoints1 = vtkPoints::New(); vtkPoints* vpoints2 = vtkPoints::New(); vpoints1->SetNumberOfPoints(numPoints); vpoints2->SetNumberOfPoints(numPoints); std::cout << " start pts "; int idx = 0; vtkFloatArray* param = vtkFloatArray::New(); param->SetName("feature"); vtkFloatArray* paramAngle = vtkFloatArray::New(); paramAngle->SetName("angle"); vtkFloatArray* paramDistance = vtkFloatArray::New(); paramDistance->SetName("distance"); vtkIdTypeArray* paramPoints = vtkIdTypeArray::New(); paramPoints->SetName("points"); for( ::itk::fem::Solver::NodeArray::iterator n = m_Solver.node.begin(); n != m_Solver.node.end(); ++n ) { loctype loc = manifoldIntegrator->GetGraphNode( (*n)->GN)->GetLocation(); float pt1[3]; pt1[0] = loc[0]; pt1[1] = loc[1]; pt1[2] = loc[2]; float pt2[3]; pt2[0] = manifoldIntegrator->GetGraphNode( (*n)->GN)->GetValue(0) * 100. * (1.0 - tval) + tval * loc[0]; pt2[1] = manifoldIntegrator->GetGraphNode( (*n)->GN)->GetValue(1) * 100. * (1.0 - tval) + tval * loc[1]; pt2[2] = 1. * (1.0 - tval) + tval * loc[2]; vpoints1->SetPoint(idx, pt1); vpoints2->SetPoint(idx, pt2); float temp = features->GetTuple1( (*n)->GN); float temp2 = manifoldIntegrator->GetGraphNode( (*n)->GN)->GetValue(0) * 255; // for length param->InsertNextValue(temp); paramDistance->InsertNextValue(temp2); paramPoints->InsertNextValue( (*n)->GN); paramAngle->InsertNextValue(temp); // curvature // param->InsertNextValue(temp*255./m_MaxCost); (*n)->GN = idx; idx++; } std::cout << " done with pts " << std::endl; vtkCellArray* tris1 = vtkCellArray::New(); vtkCellArray* tris2 = vtkCellArray::New(); std::cout << " start with tris " << std::endl; for( ::itk::fem::Solver::ElementArray::iterator n = m_Solver.el.begin(); n != m_Solver.el.end(); ++n ) { tris1->InsertNextCell(3); tris2->InsertNextCell(3); for( unsigned int i = 0; i < (*n)->GetNumberOfNodes(); i++ ) { tris1->InsertCellPoint( (*n)->GetNode(i)->GN); tris2->InsertCellPoint( (*n)->GetNode(i)->GN); } } std::cout << " done with tris " << std::endl; // Assign points and cells polydata1->SetPoints(vpoints1); polydata2->SetPoints(vpoints2); polydata1->SetPolys(tris1); polydata2->SetPolys(tris2); polydata1->GetPointData()->SetScalars(param); polydata2->GetPointData()->SetScalars(param); polydata1->GetPointData()->AddArray(paramAngle); polydata2->GetPointData()->AddArray(paramAngle); polydata1->GetPointData()->AddArray(paramDistance); polydata2->GetPointData()->AddArray(paramDistance); polydata1->GetPointData()->AddArray(paramPoints); polydata2->GetPointData()->AddArray(paramPoints); m_ExtractedSurfaceMesh = polydata1; // vtkDelaunay2D* delny2 = vtkDelaunay2D::New(); delny2->SetInput(polydata2); m_DiskSurfaceMesh = delny2->GetOutput(); // m_DiskSurfaceMesh=polydata2; return; } template void FEMDiscConformalMap ::ConformalMap() { m_Solver.load.clear(); m_Solver.node.clear(); m_Solver.el.clear(); /** * Open the file and assign it to stream object f */ if( m_ReadFromFile ) { const char* filename = m_ParameterFileName.c_str(); std::cout << "Reading FEM problem from file: " << std::string(filename) << "\n"; std::ifstream f; f.open(filename); if( !f ) { std::cout << "File " << filename << " not found!\n"; return; } try { m_Solver.Read(f); } catch( ::itk::fem::FEMException & e ) { std::cout << "Error reading FEM problem: " << filename << "!\n"; e.Print(std::cout); return; } f.close(); } else if( this->GenerateSystemFromSurfaceMesh() ) { ; } else { return; } /** * Assign a unique id (global freedom number - GFN) * to every degree of freedom (DOF) in a system. */ m_Solver.GenerateGFN(); m_ImagSolution.set_size(m_Solver.GetNumberOfDegreesOfFreedom() ); m_RealSolution.set_size(m_Solver.GetNumberOfDegreesOfFreedom() ); m_Radius.set_size(m_Solver.GetNumberOfDegreesOfFreedom() ); m_RealSolution.fill(0); m_ImagSolution.fill(0); m_Radius.fill(0); m_Debug = false; if( m_Debug ) { for( ::itk::fem::Solver::NodeArray::iterator n = m_Solver.node.begin(); n != m_Solver.node.end(); ++n ) { std::cout << "Node#: " << (*n)->GN << ": "; std::cout << " coord " << (*n)->GetCoordinates() << " coord2 " << manifoldIntegrator->GetGraphNode( (*n)->GN)->GetLocation() << std::endl; } for( ::itk::fem::Solver::ElementArray::iterator n = m_Solver.el.begin(); n != m_Solver.el.end(); ++n ) { std::cout << "Elt#: " << (*n)->GN << ": has " << (*n)->GetNumberOfNodes() << " nodes "; for( unsigned int i = 0; i < (*n)->GetNumberOfNodes(); i++ ) { std::cout << " coord " << (*n)->GetNode(i)->GetCoordinates() << std::endl; } } } unsigned int maxits = m_Solver.GetNumberOfDegreesOfFreedom(); // should be > twice ndofs // if (m_Debug) std::cout << " ndof " << maxits << std::endl; itpackWrapper.SetMaximumNumberIterations(maxits * 5); itpackWrapper.SetTolerance(1.e-4); itpackWrapper.SuccessiveOverrelaxation(); // itpackWrapper.JacobianConjugateGradient(); itpackWrapper.SetMaximumNonZeroValuesInMatrix(maxits * 50); m_Solver.SetLinearSystemWrapper(&itpackWrapper); this->FixBoundaryPoints(0); m_Solver.AssembleK(); m_Solver.DecomposeK(); // this->ApplyRealForces(); std::cout << " appl force "; m_Solver.AssembleF(); // for (int i=0; iGetDegreeOfFreedom(d) ) != ::itk::fem::Element::InvalidDegreeOfFreedomID; d++ ) { m_RealSolution[dof] = m_Solver.GetSolution(dof); // if ( ct % 10 == 0) std::cout << " mrdof " << m_RealSolution[dof] << " dof " << dof << // std::endl; } ct++; } this->ConformalMap2(); // this->ConformalMap3(); this->ConjugateHarmonic(); } template void FEMDiscConformalMap ::ConformalMap2() { m_Solver.load.clear(); this->FixBoundaryPoints(1); m_Solver.AssembleK(); // need to reassemble b/c LoadBC's affect K m_Solver.AssembleF(); m_Solver.Solve(); m_Solver.UpdateDisplacements(); // copies solution to nodes unsigned long ct = 0; for( ::itk::fem::Solver::NodeArray::iterator n = m_Solver.node.begin(); n != m_Solver.node.end(); ++n ) { for( unsigned int d = 0, dof; (dof = (*n)->GetDegreeOfFreedom(d) ) != ::itk::fem::Element::InvalidDegreeOfFreedomID; d++ ) { m_ImagSolution[dof] = m_Solver.GetSolution(dof); // if (ct % 10 == 0) std::cout << " midof " << m_ImagSolution[dof] << " dof " << dof << // std::endl; } ct++; } } template void FEMDiscConformalMap ::MeasureLengthDistortion() { // now measure the length distortion of the given solution manifoldIntegrator->EmptyQ(); manifoldIntegrator->SetSearchFinished( false ); for( int i = 0; i < manifoldIntegrator->GetGraphSize(); i++ ) { manifoldIntegrator->GetGraphNode(i)->SetTotalCost(vnl_huge_val(manifoldIntegrator->GetMaxCost() ) ); manifoldIntegrator->GetGraphNode(i)->SetUnVisited(); manifoldIntegrator->GetGraphNode(i)->SetValue(0.0, 1); manifoldIntegrator->GetGraphNode(i)->SetValue(0.0, 2); manifoldIntegrator->GetGraphNode(i)->SetPredecessor(NULL); } manifoldIntegrator->SetSource(manifoldIntegrator->GetGraphNode(this->m_SourceNodeNumber) ); manifoldIntegrator->InitializeQueue(); float mchere = 1.2; manifoldIntegrator->SetMaxCost(mchere); manifoldIntegrator->SetWeights(this->m_MaxCost, this->m_DistanceCostWeight, this->m_LabelCostWeight); manifoldIntegrator->FindPath(); // backtrack everywhere to set up forweard tracking float maxmanifolddist = 0; float distDistortion = 0; unsigned int ct = 0; for( int i = 0; i < manifoldIntegrator->GetGraphSize(); i++ ) { if( manifoldIntegrator->GetGraphNode(i) ) { if( manifoldIntegrator->GetGraphNode(i)->GetTotalCost() <= manifoldIntegrator->GetMaxCost() ) { float ttt = manifoldIntegrator->GetGraphNode(i)->GetValue(2); if( ttt > maxmanifolddist ) { maxmanifolddist = ttt; } ct++; } } } ct = 0; for( int i = 0; i < manifoldIntegrator->GetGraphSize(); i++ ) { if( manifoldIntegrator->GetGraphNode(i) ) { if( manifoldIntegrator->GetGraphNode(i)->GetTotalCost() < manifoldIntegrator->GetMaxCost() ) { const float rad = manifoldIntegrator->GetGraphNode(i)->GetValue(0); const float manifolddist = manifoldIntegrator->GetGraphNode(i)->GetValue(2) / maxmanifolddist; manifoldIntegrator->GetGraphNode(i)->SetValue(manifolddist, 2); distDistortion += fabs(rad - manifolddist); ct++; } } } std::cout << " distDistortion/ct " << distDistortion / (float)ct << " maxmfd " << maxmanifolddist << std::endl; return; /* m_ExtractedSurfaceMesh=polydata1;// // m_DiskSurfaceMesh=delny2->GetOutput(); float total=0.0; for (int i=0; im_SurfaceMesh->GetPoints()->GetNumberOfPoints(); i++) { float length1=0; float length2=0; float pt1[3]; for (int ne=0; neGetGraphNode(i)->GetNumberOfNeighbors(); ne++) { } } */ } template void FEMDiscConformalMap ::ConjugateHarmonic() { std::cout << " Conformal coordinates " << std::endl; unsigned long ct = 0; for( ::itk::fem::Solver::NodeArray::iterator n = m_Solver.node.begin(); n != m_Solver.node.end(); ++n ) { ct++; unsigned long dof = (*n)->GetDegreeOfFreedom(0); if( dof < m_RealSolution.size() ) { const float U = m_RealSolution[dof]; const float V = m_ImagSolution[dof]; manifoldIntegrator->GetGraphNode( (*n)->GN )->SetValue(U, 0); manifoldIntegrator->GetGraphNode( (*n)->GN )->SetValue(V, 1); } } // this->MakeFlatImage(); this->BuildOutputMeshes(); return; } } // namespace itk /* vtkSelectPolyData *loop = vtkSelectPolyData::New(); loop->SetInput(this->m_SurfaceMesh); // put points inside ... vtkPoints* points = vtkPoints::New(); points->SetNumberOfPoints( this->m_DiscBoundaryList.size()); unsigned int idx=0; for ( unsigned int j = 0 ; j < this->m_DiscBoundaryList.size(); j ++ ) { float pt1[3]; typename GraphSearchNodeType::NodeLocationType loc=this->m_DiscBoundaryList[j]->GetLocation(); pt1[0]=loc[0]; pt1[1]=loc[1]; pt1[2]=loc[2]; // unsigned int idx=this->m_DiscBoundaryList[j]->GetIdentity(); points->SetPoint(idx,pt); idx++; } loop->GenerateSelectionScalarsOff(); loop->SetSelectionModeToClosestPointRegion(); //negative scalars inside loop->SetSelectionModeToSmallestRegion(); //negative scalars inside loop->SetLoop(points); loop->Modified(); vtkClipPolyData *clip = vtkClipPolyData::New(); //clips out positive region clip->SetInput(loop->GetOutput()); vtkPolyDataMapper *clipMapper = vtkPolyDataMapper::New(); clipMapper->SetInput(clip->GetOutput()); vtkActor *clipActor = vtkActor::New(); clipActor->SetMapper(clipMapper); clipActor->AddPosition(1, 0, 0); // clipActor->GetProperty()->SetColor(0, 0, 1); //Set colour blue vtkRenderer *ren1 = vtkRenderer::New(); vtkRenderWindow* renWin = vtkRenderWindow::New(); renWin->AddRenderer(ren1); vtkRenderWindowInteractor* inter = vtkRenderWindowInteractor::New(); inter->SetRenderWindow(renWin); ren1->SetViewport(0.0, 0.0, 1.0, 1.0); ren1->AddActor(clipActor); renWin->Render(); inter->Start(); ren1->Delete(); renWin->Delete(); points-> Delete(); loop->Delete(); clip->Delete(); clipMapper->Delete(); clipActor->Delete(); */ #endif ants-2.2.0/Temporary/itkFEMDiscConformalMap.h000066400000000000000000000214371311104306400210340ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Copyright (c) 2002 Insight 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 _FEMDiscConformalMap_h #define _FEMDiscConformalMap_h #include #include #include #include "vtkDataSetWriter.h" #include "vtkDataSetMapper.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkActor.h" #include "vtkRenderWindowInteractor.h" #include "vtkDataSetReader.h" #include "vtkUnstructuredGrid.h" #include "vtkDataSet.h" #include "vtkCellArray.h" #include "vtkVolume16Reader.h" #include "vtkImageReader2.h" #include "vtkPolyDataMapper.h" #include "vtkActor.h" #include "vtkOutlineFilter.h" #include "vtkCamera.h" #include "vtkProperty.h" #include "vtkPolyData.h" #include "vtkPolyVertex.h" #include "vtkPointData.h" #include "vtkExtractEdges.h" #include "vtkPolyDataNormals.h" #include "vtkMarchingCubes.h" #include "vtkImageGaussianSmooth.h" #include "vtkDecimatePro.h" #include "vtkContourFilter.h" #include "vtkPolyDataConnectivityFilter.h" // #include "vtkKitwareContourFilter.h" #include "vtkSmoothPolyDataFilter.h" #include "vtkSTLWriter.h" #include "vtkUnstructuredGridToPolyDataFilter.h" // #include "itkImageToVTKImageFilter.h" #include "itkDijkstrasAlgorithm.h" #include "itkManifoldIntegrationAlgorithm.h" // #include "itkTriangulatedDijkstrasAlgorithm.h" #include "itkObject.h" #include "itkProcessObject.h" #include "itkVectorContainer.h" #include "itkCastImageFilter.h" // #include "itkFEM.h" #include "itkFEMLinearSystemWrapperItpack.h" #include "itkFEMLoadNode.h" #include "itkFEMSolver.h" #include "itkMesh.h" namespace itk { /** \class FEMDiscConformalMap * Avants Epstein conformal mapping algorithm using FEM. * * \note The origin of a neighborhood is always taken to be * the first point entered into and the * last point stored in the list. */ template class FEMDiscConformalMap : public ProcessObject { public: /** Standard class typedefs. */ typedef FEMDiscConformalMap Self; typedef ProcessObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(FEMDiscConformalMap, ProcessObject); /** Method for creation through the object factory. */ itkNewMacro(Self); /** Surface (mesh) types. */ typedef TImage ImageType; typedef typename TImage::Pointer ImageTypePointer; typedef typename TImage::IndexType IndexType; /** Surface (mesh) types. */ typedef TSurface SurfaceType; typedef SurfaceType * SurfaceTypePointer; // typedef typename SurfaceType::PointType PointType; // typedef typename SurfaceType::CellsContainerPointer InputCellsContainerPointer; // typedef typename SurfaceType::CellsContainer::Iterator InputCellsContainerIterator; /** Image dimension. */ // itkStaticConstMacro(ImageDimension, unsigned int, TImage::ImageDimension); itkStaticConstMacro(ImageDimension, unsigned int, TDimension); itkStaticConstMacro(SurfaceDimension, unsigned int, TDimension); typedef float RealType; typedef vnl_vector VectorType; typedef vnl_vector_fixed FixedVectorType; typedef vnl_matrix MatrixType; typedef Image FlatImageType; typedef typename FlatImageType::Pointer FlatImageTypePointer; typedef GraphSearchNode GraphSearchNodeType; typedef typename GraphSearchNodeType::Pointer GraphSearchNodePointer; typedef typename GraphSearchNodeType::NodeLocationType NodeLocationType; typedef ManifoldIntegrationAlgorithm ManifoldIntegratorType; // typedef TriangulatedDijkstrasAlgorithm ManifoldIntegratorType; typedef typename ManifoldIntegratorType::Pointer ManifoldIntegratorTypePointer; /** FEM types */ typedef itk::fem::MaterialLinearElasticity MaterialType; typedef itk::fem::Element::Node NodeType; typedef itk::fem::LoadNode LoadType; typedef itk::fem::Element3DC0LinearTriangularLaplaceBeltrami ElementType; typedef itk::fem::Element3DC0LinearTriangularMembrane ElementType1; /** Set input parameter file */ itkSetStringMacro( ParameterFileName ); /** Set input parameter file */ itkGetStringMacro( ParameterFileName ); itkGetMacro(Sigma, RealType); itkSetMacro(Sigma, RealType); itkGetMacro(SurfaceMesh, SurfaceTypePointer); itkSetMacro(SurfaceMesh, SurfaceTypePointer); itkGetMacro(SurfaceFeatureMesh, SurfaceTypePointer); itkSetMacro(SurfaceFeatureMesh, SurfaceTypePointer); void SetDebug(bool b) { m_Debug = b; } void SetReadFromFile(bool b) { m_ReadFromFile = b; } float AssessNodeDistanceCost( unsigned int ); float GetBoundaryParameterForSquare(unsigned int, unsigned int); float GetBoundaryParameterForCircle(unsigned int, unsigned int); unsigned int FindLoopAroundNode( unsigned int j ); unsigned int AddVertexToLoop(); void LocateAndParameterizeDiscBoundary(unsigned int, bool); void FixBoundaryPoints( unsigned int option ); void ConformalMap(); void ConformalMap2(); void ConjugateHarmonic(); bool InBorder(GraphSearchNodePointer); bool InDisc(GraphSearchNodePointer); void ExtractSurfaceDisc( unsigned int label = 0 ); void BuildOutputMeshes(float tval = 0.0); SurfaceTypePointer m_ExtractedSurfaceMesh; SurfaceTypePointer m_DiskSurfaceMesh; void SetSmooth(float i) { m_Smooth = i; } void MeasureLengthDistortion(); void SetParamWhileSearching( bool b ) { this->m_ParamWhileSearching = b; } void FindSource(IndexType); void FindMeanSourceInLabel(unsigned int); void MakeFlatImage(); FlatImageTypePointer m_FlatImage; inline void SetLabelToFlatten( unsigned int b ) { this->m_Label_to_Flatten = b; } inline void SetMaxCost( float f ) { this->m_MaxCost = f; } inline void SetDistanceCostWeight(float d) { this->m_DistanceCostWeight = d; } inline void SetLabelCostWeight(float d) { this->m_LabelCostWeight = d; } inline void SetMapToSquare() { this->m_MapToSquare = true; this->m_MapToCircle = false; } inline void SetMapToCircle() { this->m_MapToSquare = false; this->m_MapToCircle = true; } inline void SetDiscBoundaryList( std::vector b ) { this->m_DiscBoundaryList.assign(b.begin(), b.end() ); }; protected: bool GenerateSystemFromSurfaceMesh(); void ApplyRealForces(); void ApplyImaginaryForces(); FEMDiscConformalMap(); virtual ~FEMDiscConformalMap() { }; private: std::vector m_DiscBoundaryList; // contains ids of nodes at boundary std::vector m_HelpFindLoop; // 0 = not found, 2 = already done , 1 = in loop std::vector m_DiscBoundarySorter; // contains ids of nodes at boundary std::vector m_DiscBoundaryParameter; // contains ids of nodes at boundary RealType m_Sigma; RealType m_Pi; std::string m_ParameterFileName; int m_NorthPole; int m_SourceNodeNumber; float m_DistanceCostWeight; float m_LabelCostWeight; itk::fem::Solver<3> m_Solver; bool m_ReadFromFile; bool m_Debug; bool m_FindingRealSolution; bool m_MapToCircle; bool m_MapToSquare; bool m_ParamWhileSearching; VectorType m_RealSolution; VectorType m_ImagSolution; VectorType m_Radius; SurfaceTypePointer m_SurfaceMesh; SurfaceTypePointer m_SurfaceFeatureMesh; float m_MaxCost; itk::fem::LinearSystemWrapperItpack itpackWrapper; unsigned long m_PoleElementsGN[7]; ManifoldIntegratorTypePointer manifoldIntegrator; float m_Smooth; unsigned int m_Label_to_Flatten; GraphSearchNodePointer m_RootNode; }; } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkFEMDiscConformalMap.cxx" #endif #endif ants-2.2.0/Temporary/itkFEMElement3DMembrane1DOF.h000066400000000000000000000070531311104306400215120ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Copyright (c) 2002 Insight 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 __itkFEMElement3DMembrane1DOF_h #define __itkFEMElement3DMembrane1DOF_h #include "itkFEMElementBase.h" #include "itkFEMMaterialLinearElasticity.h" namespace itk { namespace fem { /** * \class Element3DMembrane1DOF * \brief Class that is used to define a membrane energy problem in 3D space. * * This class only defines the physics of the problem. Use his class together * with element classes that specify the geometry to fully define the element. * * You can specify one template parameter: * * TBaseClass - Class from which Element3DMembrane1DOF is derived. TBaseClass must * be derived from the Element base class. This enables you * to use this class at any level of element definition. * If not specified, it defaults to the Element base class. */ template class Element3DMembrane1DOF : public TBaseClass { FEM_ABSTRACT_CLASS(Element3DMembrane1DOF, TBaseClass) public: // Repeat the required typedefs and enums from parent class typedef typename Superclass::Float Float; typedef typename Superclass::MatrixType MatrixType; typedef typename Superclass::VectorType VectorType; /** * Read data for this class from input stream */ virtual void Read( std::istream &, void* info ); /** * Write this class to output stream */ virtual void Write( std::ostream& f ) const; /** * Default constructor only clears the internal storage */ Element3DMembrane1DOF(); // //////////////////////////////////////////////////////////////////////// /* * Methods related to the physics of the problem. */ /** * Compute the B matrix. */ virtual void GetStrainDisplacementMatrix(MatrixType& B, const MatrixType& shapeDgl) const; /** * Compute the D matrix. */ virtual void GetMaterialMatrix(MatrixType& D) const; /** * Compute the mass matrix specific for 3D membrane problems. */ void GetMassMatrix(MatrixType& Me) const; /** * 3D membrane elements have 3 DOFs per node. */ virtual unsigned int GetNumberOfDegreesOfFreedomPerNode( void ) const { return 3; } virtual void GetStiffnessMatrix( MatrixType& Ke ) const; public: /** * Pointer to material properties of the element */ MaterialLinearElasticity::ConstPointer m_mat; virtual Material::ConstPointer GetMaterial(void) const { return m_mat; } virtual void SetMaterial(Material::ConstPointer mat_ ) { m_mat = dynamic_cast(&*mat_); } }; // class Element3DMembrane1DOF #ifdef _MSC_VER // Declare a static dummy function to prevent a MSVC 6.0 SP5 from crashing. // I have no idea why things don't work when this is not declared, but it // looks like this declaration makes compiler forget about some of the // troubles it has with templates. static void Dummy(); #endif // #ifdef _MSC_VER } } // end namespace itk::fem #ifndef ITK_MANUAL_INSTANTIATION #include "itkFEMElement3DMembrane1DOF.hxx" #endif #endif // #ifndef __itkFEMElement3DMembrane1DOF_h ants-2.2.0/Temporary/itkFEMElement3DMembrane1DOF.hxx000066400000000000000000000077311311104306400220750ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Copyright (c) 2002 Insight 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 __itkFEMElement3DMembrane1DOF_hxx #define __itkFEMElement3DMembrane1DOF_hxx #include "itkFEMElement3DMembrane1DOF.h" namespace itk { namespace fem { template Element3DMembrane1DOF ::Element3DMembrane1DOF() : Superclass(), m_mat(0) { } // //////////////////////////////////////////////////////////////////////// /* * Methods related to the physics of the problem. */ template void Element3DMembrane1DOF ::GetStrainDisplacementMatrix(MatrixType& B, const MatrixType& shapeDgl) const { } template void Element3DMembrane1DOF ::GetMassMatrix(MatrixType& Me) const { // Call the parent's get matrix function Superclass::GetMassMatrix(Me); // Since parent class doesn't have the material properties, // we need to adjust Me matrix here for the density of the element. Me = Me * m_mat->RhoC; } template void Element3DMembrane1DOF ::GetMaterialMatrix(MatrixType& D) const { unsigned int d = 3; D.set_size(d, d); D.fill(0.0); // This is the main difference from the linear elasticity problem. /* Material properties matrix. Simpler than linear elasticity. */ Float disot = m_mat->E; for( unsigned int i = 0; i < d; i++ ) { D[i][i] = disot; } } template void Element3DMembrane1DOF::GetStiffnessMatrix(MatrixType& Ke) const { Superclass::GetStiffnessMatrix(Ke); } template void Element3DMembrane1DOF ::Read( std::istream& f, void* info ) { int n; /* * Convert the info pointer to a usable objects */ ReadInfoType::MaterialArrayPointer mats = static_cast(info)->m_mat; /* first call the parent's read function */ Superclass::Read(f, info); try { /* * Read and set the material pointer */ FEMLightObject::SkipWhiteSpace(f); f >> n; if( !f ) { goto out; } m_mat = dynamic_cast( &*mats->Find(n) ); } catch( FEMExceptionObjectNotFound & e ) { throw FEMExceptionObjectNotFound(__FILE__, __LINE__, "Element3DMembrane1DOF::Read()", e.m_baseClassName, e.m_GN); } // Check if the material object was of correct class if( !m_mat ) { throw FEMExceptionWrongClass(__FILE__, __LINE__, "Element3DMembrane1DOF::Read()"); } out: if( !f ) { throw FEMExceptionIO(__FILE__, __LINE__, "Element3DMembrane1DOF::Read()", "Error reading FEM element!"); } } /* * Write the element to the output stream. */ template void Element3DMembrane1DOF ::Write( std::ostream& f ) const { // First call the parent's write function Superclass::Write(f); /* * then write the actual data (material number) * We also add some comments in the output file */ f << "\t" << m_mat->GN << "\t% MaterialLinearElasticity ID\n"; // check for errors if( !f ) { throw FEMExceptionIO(__FILE__, __LINE__, "Element3DMembrane1DOF::Write()", "Error writing FEM element!"); } } #ifdef _MSC_VER // Declare a static dummy function to prevent a MSVC 6.0 SP5 from crashing. // I have no idea why things don't work when this is not declared, but it // looks like this declaration makes compiler forget about some of the // troubles it has with templates. static void Dummy(); #endif // #ifdef _MSC_VER } } // end namespace itk::fem #endif // #ifndef __itkFEMElement3DMembrane1DOF_hxx ants-2.2.0/Temporary/itkManifoldIntegrationAlgorithm.cxx000066400000000000000000000524241311104306400235020ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit (ITK) =========================================================================*/ #ifndef _itkManifoldIntegrationAlgorithm_cxx_ #define _itkManifoldIntegrationAlgorithm_cxx_ #include "itkSurfaceMeshCurvature.h" #include "vtkFeatureEdges.h" #include "vtkPointLocator.h" #include "vtkCellLocator.h" #include "vtkTriangleFilter.h" #include "vtkCleanPolyData.h" #include "vtkPolyDataConnectivityFilter.h" namespace itk { template ManifoldIntegrationAlgorithm::ManifoldIntegrationAlgorithm() { m_SurfaceMesh = NULL; m_QS = DijkstrasAlgorithmQueue::New(); m_MaxCost = vnl_huge_val(m_MaxCost); m_PureDist = false; // m_LabelCost=0; m_ParamWhileSearching = false; this->m_DistanceCostWeight = 1; this->m_LabelCostWeight = 0; } template float ManifoldIntegrationAlgorithm::dstarUestimate( typename TGraphSearchNode::Pointer G) { typedef itk::SurfaceMeshCurvature surfktype; typename surfktype::Pointer surfk = surfktype::New(); surfk->SetSurfacePatch(G); surfk->FindNeighborhood(); float dsu = (float) surfk->dstarUestimate(); return dsu; } template void ManifoldIntegrationAlgorithm::InitializeGraph3() { if( !m_SurfaceMesh ) { return; } // Construct simple triangles vtkTriangleFilter* fltTriangle = vtkTriangleFilter::New(); fltTriangle->SetInput(m_SurfaceMesh); cout << " converting mesh to triangles " << endl; fltTriangle->Update(); /* Clean the data vtkCleanPolyData* fltCleaner = vtkCleanPolyData::New(); fltCleaner->SetInput(fltTriangle->GetOutput()); fltCleaner->SetTolerance(0); fltCleaner->ConvertPolysToLinesOn(); cout << " cleaning up triangle mesh " << endl; fltCleaner->Update(); // Go through and delete the cells that are of the wrong type //m_SurfaceMesh vtkPolyData* clean= fltCleaner->GetOutput(); for(vtkIdType i = clean->GetNumberOfCells();i > 0;i--) { if(clean->GetCellType(i-1) != VTK_TRIANGLE) clean->DeleteCell(i-1); } clean->BuildCells(); m_SurfaceMesh=clean;*/ m_SurfaceMesh = fltTriangle->GetOutput(); typedef float labelType; typedef std::vector LabelSetType; LabelSetType myLabelSet; vtkPoints* vtkpoints = m_SurfaceMesh->GetPoints(); vtkPointData *pd = m_SurfaceMesh->GetPointData(); int numPoints = vtkpoints->GetNumberOfPoints(); vtkDataArray* scs = pd->GetScalars(); m_GraphX.resize(numPoints); for( int i = 0; i < numPoints; i++ ) { NodeLocationType loc; double* pt = vtkpoints->GetPoint(i); typename GraphSearchNode::Pointer G = GraphSearchNode::New(); G->SetUnVisited(); G->SetTotalCost(m_MaxCost); G->SetValue(scs->GetTuple1(i), 3); /** here we put the label value */ labelType label = scs->GetTuple1(i); if( find( myLabelSet.begin(), myLabelSet.end(), label ) == myLabelSet.end() ) { myLabelSet.push_back( label ); } // std::cout << " label " << scs->GetTuple1(i) << std::endl; // std::cout << " set3 " << scs->GetTuple3(i) << std::endl; // std::cout << " set4 " << scs->GetTuple4(i) << std::endl; for( int j = 0; j < GraphDimension; j++ ) { loc[j] = pt[j]; } G->SetLocation(loc); G->SetPredecessor(NULL); G->m_NumberOfNeighbors = 0; G->SetIdentity(i); m_GraphX[i] = G; } std::cout << " you have " << myLabelSet.size() << " labels " << std::endl; for( unsigned int i = 0; i < myLabelSet.size(); i++ ) { std::cout << " label " << myLabelSet[i] << std::endl; } std::cout << " allocation of graph done "; // now loop through the cells to get triangles and also edges vtkCellArray* vtkcells = m_SurfaceMesh->GetPolys(); vtkIdType npts; vtkIdType* pts; /* count possible neighbors ... */ for( vtkcells->InitTraversal(); vtkcells->GetNextCell(npts, pts); ) { m_GraphX[pts[0]]->m_NumberOfNeighbors += 2; m_GraphX[pts[1]]->m_NumberOfNeighbors += 2; m_GraphX[pts[2]]->m_NumberOfNeighbors += 2; } for( int i = 0; i < numPoints; i++ ) { m_GraphX[i]->m_Neighbors.resize(m_GraphX[i]->m_NumberOfNeighbors); // std::cout <<" Num Neigh " << i << " is " << m_GraphX[i]->m_NumberOfNeighbors << std::endl; m_GraphX[i]->m_NumberOfNeighbors = 0; } for( vtkcells->InitTraversal(); vtkcells->GetNextCell(npts, pts); ) { m_GraphX[pts[0]]->m_Neighbors[m_GraphX[pts[0]]->m_NumberOfNeighbors] = m_GraphX[pts[1]]; m_GraphX[pts[0]]->m_NumberOfNeighbors++; m_GraphX[pts[0]]->m_Neighbors[m_GraphX[pts[0]]->m_NumberOfNeighbors] = m_GraphX[pts[2]]; m_GraphX[pts[0]]->m_NumberOfNeighbors++; m_GraphX[pts[1]]->m_Neighbors[m_GraphX[pts[1]]->m_NumberOfNeighbors] = m_GraphX[pts[0]]; m_GraphX[pts[1]]->m_NumberOfNeighbors++; m_GraphX[pts[1]]->m_Neighbors[m_GraphX[pts[1]]->m_NumberOfNeighbors] = m_GraphX[pts[2]]; m_GraphX[pts[1]]->m_NumberOfNeighbors++; m_GraphX[pts[2]]->m_Neighbors[m_GraphX[pts[2]]->m_NumberOfNeighbors] = m_GraphX[pts[0]]; m_GraphX[pts[2]]->m_NumberOfNeighbors++; m_GraphX[pts[2]]->m_Neighbors[m_GraphX[pts[2]]->m_NumberOfNeighbors] = m_GraphX[pts[1]]; m_GraphX[pts[2]]->m_NumberOfNeighbors++; } // now go through each node and make its list of neighbors unique // had to do this b/c it's easier than fixing the junk above ... too tired! for( int i = 0; i < numPoints; i++ ) { std::vector neighlist; for( unsigned int n = 0; n < m_GraphX[i]->m_NumberOfNeighbors; n++ ) { neighlist.push_back(m_GraphX[i]->m_Neighbors[n]->GetIdentity() ); } std::sort( neighlist.begin(), neighlist.end() ); std::vector::iterator new_end_pos; new_end_pos = std::unique( neighlist.begin(), neighlist.end() ); neighlist.erase( new_end_pos, neighlist.end() ); // std::cout << " new leng " << neighlist.size() << " old " << len1 << std::endl; } } template void ManifoldIntegrationAlgorithm::InitializeGraph2() { if( !m_SurfaceMesh ) { return; } /* // Construct simple triangles vtkTriangleFilter* fltTriangle = vtkTriangleFilter::New(); fltTriangle->SetInput(m_SurfaceMesh); cout << " converting mesh to triangles " << endl; fltTriangle->Update(); cout << " this mesh has " << fltTriangle->GetOutput()->GetNumberOfPoints() << " points" << endl; cout << " this mesh has " << fltTriangle->GetOutput()->GetNumberOfCells() << " cells" << endl; // Clean the data vtkCleanPolyData* fltCleaner = vtkCleanPolyData::New(); fltCleaner->SetInput(fltTriangle->GetOutput()); fltCleaner->SetTolerance(0); fltCleaner->ConvertPolysToLinesOn(); cout << " cleaning up triangle mesh " << endl; fltCleaner->Update(); // Go through and delete the cells that are of the wrong type //m_SurfaceMesh vtkPolyData* clean= fltCleaner->GetOutput(); for(vtkIdType i = clean->GetNumberOfCells();i > 0;i--) { if(clean->GetCellType(i-1) != VTK_TRIANGLE) clean->DeleteCell(i-1); } clean->BuildCells(); */ vtkFeatureEdges* fltEdge = vtkFeatureEdges::New(); fltEdge->BoundaryEdgesOff(); fltEdge->FeatureEdgesOff(); fltEdge->NonManifoldEdgesOff(); fltEdge->ManifoldEdgesOn(); fltEdge->ColoringOff(); fltEdge->SetInput(m_SurfaceMesh); cout << " extracting edges from the mesh" << endl; fltEdge->Update(); // Got the new poly data vtkPolyData* m_EdgePolys = fltEdge->GetOutput(); m_EdgePolys->BuildCells(); m_EdgePolys->BuildLinks(); unsigned int nEdges = m_EdgePolys->GetNumberOfLines(); cout << " number of edges (lines) : " << nEdges << endl; cout << " number of cells : " << m_EdgePolys->GetNumberOfCells() << endl; cout << " number if points : " << m_EdgePolys->GetNumberOfPoints() << endl; vtkPoints* vtkpoints = m_EdgePolys->GetPoints(); int numPoints = vtkpoints->GetNumberOfPoints(); m_GraphX.resize(numPoints); for( int i = 0; i < numPoints; i++ ) { NodeLocationType loc; double* pt = vtkpoints->GetPoint(i); typename GraphSearchNode::Pointer G = GraphSearchNode::New(); G->SetUnVisited(); G->SetTotalCost(m_MaxCost); for( int j = 0; j < GraphDimension; j++ ) { loc[j] = pt[j]; } G->SetLocation(loc); G->SetPredecessor(NULL); G->m_NumberOfNeighbors = 0; m_GraphX[i] = G; } std::cout << " allocation of graph done "; vtkIdType nPoints = 0; vtkIdType *xPoints = NULL; for( unsigned int i = 0; i < nEdges; i++ ) { // Get the next edge m_EdgePolys->GetCellPoints(i, nPoints, xPoints); // Place the edge into the Edge structure assert(nPoints == 2); // Place the edge into the Edge structure // std::cout << " nPoints " << nPoints << std::endl; // std::cout << " pt " << xPoints[0] << " connects " << xPoints[1] << std::endl; assert(nPoints == 2); m_GraphX[xPoints[0]]->m_NumberOfNeighbors++; } std::cout << " counting nhood done "; // second, resize the vector for each G for( int i = 0; i < numPoints; i++ ) { m_GraphX[i]->m_Neighbors.resize(m_GraphX[i]->m_NumberOfNeighbors); m_GraphX[i]->m_NumberOfNeighbors = 0; } for( unsigned int i = 0; i < nEdges; i++ ) { // Get the next edge m_EdgePolys->GetCellPoints(i, nPoints, xPoints); // Place the edge into the Edge structure assert(nPoints == 2); m_GraphX[xPoints[0]]->m_Neighbors[m_GraphX[xPoints[0]]->m_NumberOfNeighbors] = m_GraphX[xPoints[1]]; m_GraphX[xPoints[0]]->m_NumberOfNeighbors++; } // vtkPolyDataConnectivityFilter* con = vtkPolyDataConnectivityFilter::New(); // con->SetExtractionModeToLargestRegion(); // con->SetInput(m_EdgePolys); // m_SurfaceMesh=con->GetOutput(); m_SurfaceMesh = m_EdgePolys; } template void ManifoldIntegrationAlgorithm::InitializeGraph() { if( !m_SurfaceMesh ) { return; } std::cout << " Generate graph from surface mesh " << std::endl; // get size of the surface mesh vtkExtractEdges* edgeex = vtkExtractEdges::New(); edgeex->SetInput(m_SurfaceMesh); edgeex->Update(); vtkPolyData* edg1 = edgeex->GetOutput(); vtkIdType nedg = edg1->GetNumberOfCells(); vtkIdType vers = m_SurfaceMesh->GetNumberOfPoints(); int nfac = m_SurfaceMesh->GetNumberOfPolys(); float g = 0.5 * (2.0 - vers + nedg - nfac); std::cout << " Genus " << g << std::endl; edg1->BuildCells(); // now cruise through all edges and add to each node's neighbor list // first, count the num of edges for each node // m_SurfaceMesh=edg1; vtkPoints* vtkpoints = edg1->GetPoints(); int numPoints = vtkpoints->GetNumberOfPoints(); m_GraphX.resize(numPoints); for( int i = 0; i < numPoints; i++ ) { NodeLocationType loc; double* pt = vtkpoints->GetPoint(i); typename GraphSearchNode::Pointer G = GraphSearchNode::New(); G->SetUnVisited(); G->SetTotalCost(m_MaxCost); for( int j = 0; j < GraphDimension; j++ ) { loc[j] = pt[j]; } G->SetLocation(loc); G->SetPredecessor(NULL); G->m_NumberOfNeighbors = 0; m_GraphX[i] = G; } std::cout << " allocation of graph done "; std::cout << " begin edg iter "; vtkIdType nPoints = 0; vtkIdType *xPoints = NULL; for( unsigned int i = 0; i < nedg; i++ ) { // Get the next edge edg1->GetCellPoints(i, nPoints, xPoints); // Place the edge into the Edge structure // std::cout << " nPoints " << nPoints << std::endl; // std::cout << " pt " << xPoints[0] << " connects " << xPoints[1] << std::endl; assert(nPoints == 2); m_GraphX[xPoints[0]]->m_NumberOfNeighbors++; } std::cout << " counting nhood done "; // second, resize the vector for each G for( int i = 0; i < numPoints; i++ ) { m_GraphX[i]->m_Neighbors.resize(m_GraphX[i]->m_NumberOfNeighbors); m_GraphX[i]->m_NumberOfNeighbors = 0; } for( unsigned int i = 0; i < nedg; i++ ) { // Get the next edge edg1->GetCellPoints(i, nPoints, xPoints); // Place the edge into the Edge structure assert(nPoints == 2); m_GraphX[xPoints[0]]->m_Neighbors[m_GraphX[xPoints[0]]->m_NumberOfNeighbors] = m_GraphX[xPoints[1]]; m_GraphX[xPoints[0]]->m_NumberOfNeighbors++; } m_SurfaceMesh = edg1; return; } template void ManifoldIntegrationAlgorithm ::ConvertGraphBackToMesh() { // this is a sanity check } template void ManifoldIntegrationAlgorithm::InitializeQueue() { int n = m_QS->m_SourceNodes.size(); // GraphIteratorType GraphIterator( m_Graph, m_GraphRegion ); // GraphIterator.GoToBegin(); // m_GraphIndex = GraphIterator.GetIndex(); NodeLocationType loc; // make sure the graph contains the right pointers for( int i = 0; i < n; i++ ) { typename GraphSearchNode::Pointer G = m_QS->m_SourceNodes[i]; G->SetPredecessor(G); m_QS->m_Q.push(G); loc = G->GetLocation(); // for (int d=0;dSetPixel(m_GraphIndex,G); } for( unsigned int i = 0; i < m_QS->m_SinkNodes.size(); i++ ) { typename GraphSearchNode::Pointer G = m_QS->m_SinkNodes[i]; G->SetPredecessor(NULL); loc = G->GetLocation(); // for (int d=0;dSetPixel(m_GraphIndex,G); } m_SearchFinished = false; } /** * parameterize the boundary --- an estimate */ template bool ManifoldIntegrationAlgorithm ::ParameterizeBoundary( ManifoldIntegrationAlgorithm::SearchNodePointer rootNode ) { std::vector neighborlist; bool I_Am_A_Neighbor = false; SearchNodePointer neighbor = NULL; SearchNodePointer curNode = rootNode; // unsigned int rootnn=rootNode>m_NumberOfNeighbors; unsigned int ct = 0; bool canparam = false; unsigned int qsz = this->m_QS->m_Q.size(); while( !I_Am_A_Neighbor && ct <= qsz * 3 ) { unsigned int limit = curNode->m_NumberOfNeighbors; for( unsigned int i = 0; i < limit; i++ ) { neighbor = curNode->m_Neighbors[i]; bool inb = false; for( unsigned int q = 0; q < neighborlist.size(); q++ ) { if( neighbor == neighborlist[q] ) { inb = true; } } if( neighbor == rootNode && !inb && ct > 2 ) { I_Am_A_Neighbor = true; canparam = true; } if( neighbor->IsInQueue() && !inb ) // add to border list { neighborlist.push_back(neighbor); curNode = neighbor; i = limit; } // add to border } // neighborhood ct++; } // while if( neighborlist.size() >= this->m_BoundaryList.size() && canparam ) { neighborlist.push_back(rootNode); this->m_BoundaryList.clear(); this->m_BoundaryList.assign(neighborlist.begin(), neighborlist.end() ); } // if ( ct > 0 && canparam) std::cout <<" qfrac " << this->m_BoundaryList.size() << " canp "<< canparam << // " qsz " // << qsz << " cost " << m_CurrentCost << std::endl; return canparam; } /** * Compute the local cost using Manhattan distance. */ template typename ManifoldIntegrationAlgorithm:: PixelType ManifoldIntegrationAlgorithm::MyLocalCost() { NodeLocationType dif = m_CurrentNode->GetLocation() - m_NeighborNode->GetLocation(); float mag = 0.0; for( int jj = 0; jj < GraphDimension; jj++ ) { mag += dif[jj] * dif[jj]; } mag = sqrt(mag); if( m_PureDist ) { return mag; } else { float dL = fabs(m_CurrentNode->GetValue(3) - m_NeighborNode->GetValue(3) ); if( dL > 0.5 ) { dL = this->m_MaxCost * this->m_LabelCostWeight; } else { dL = 0; } return mag * this->m_DistanceCostWeight + dL; } } template bool ManifoldIntegrationAlgorithm::TerminationCondition() { if( !m_QS->m_SinkNodes.empty() ) { if( m_NeighborNode == m_QS->m_SinkNodes[0] && !m_SearchFinished ) { // std::cout << " FOUND SINK "; m_SearchFinished = true; m_NeighborNode->SetTotalCost( m_CurrentCost + MyLocalCost() ); m_NeighborNode->SetPredecessor(m_CurrentNode); } } if( m_CurrentCost >= m_MaxCost ) { m_SearchFinished = true; } return m_SearchFinished; } template void ManifoldIntegrationAlgorithm::SearchEdgeSet() { int i = 0; // ,j=0; for( i = 0; i < m_CurrentNode->m_NumberOfNeighbors; i++ ) { m_NeighborNode = m_CurrentNode->m_Neighbors[i]; // std::cout << " i " << i << " position " << m_NeighborNode->GetLocation() << endl; // std::cout << " i " << i << " position " << m_NeighborNode->GetLocation() << " label " << // m_CurrentNode->GetValue() << endl; TerminationCondition(); if( !m_SearchFinished && m_CurrentNode != m_NeighborNode && !m_NeighborNode->GetDelivered() ) { m_NewCost = m_CurrentCost + MyLocalCost(); CheckNodeStatus(); } } } template void ManifoldIntegrationAlgorithm::GetSearchBoundary() { unsigned int gsz = this->GetGraphSize(); for( unsigned int j = 0; j < gsz; j++ ) { this->m_CurrentNode = this->m_GraphX[j]; if( this->m_CurrentNode ) { const float cost = m_CurrentNode->GetTotalCost(); if( cost <= this->m_MaxCost && (cost >= this->m_MaxCost - 4) ) { this->m_BoundaryList.push_back(this->m_CurrentNode); } } } } template void ManifoldIntegrationAlgorithm::CheckNodeStatus() // checks a graph neighbor's status { NodeLocationType dif = m_CurrentNode->GetLocation() - m_NeighborNode->GetLocation(); // std::cout << " visited? " << m_NeighborNode->GetVisited() << // " old cost " << m_NeighborNode->GetTotalCost() << " new cost " <GetVisited() && !m_NeighborNode->GetUnVisitable() ) { // set the cost and put into the queue m_NeighborNode->SetTotalCost(m_NewCost); float delt = fabs(m_CurrentNode->GetValue() - m_NeighborNode->GetValue() ); // *dif.magnitude(); m_NeighborNode->SetValue(m_CurrentNode->GetValue() + delt); m_NeighborNode->SetPredecessor(m_CurrentNode); m_NeighborNode->SetVisited(); float mag = 0.0; for( int jj = 0; jj < GraphDimension; jj++ ) { mag += dif[jj] * dif[jj]; } mag = sqrt(mag); m_NeighborNode->SetValue(m_CurrentNode->GetValue(2) + mag, 2); // the actual manifold distance travelled // if ( m_QS->m_Q.push(m_NeighborNode); // } // else { // m_NeighborNode->SetUnVisitable(); // } // std::cout << " Pushing new node on " << m_NewCost << std::endl; } else if( m_NewCost < m_NeighborNode->GetTotalCost() && !m_NeighborNode->GetUnVisitable() ) { // std::cout << " Updating " << std::endl; float delt = fabs(m_CurrentNode->GetValue() - m_NeighborNode->GetValue() ); // *dif.magnitude(); m_NeighborNode->SetValue(m_CurrentNode->GetValue() + delt); m_NeighborNode->SetTotalCost(m_NewCost); m_NeighborNode->SetPredecessor(m_CurrentNode); float mag = 0.0; for( int jj = 0; jj < GraphDimension; jj++ ) { mag += dif[jj] * dif[jj]; } mag = sqrt(mag); m_NeighborNode->SetValue(m_CurrentNode->GetValue(2) + mag, 2); // the actual manifold distance travelled m_QS->m_Q.push(m_NeighborNode); } } template void ManifoldIntegrationAlgorithm::FindPath() { if( m_QS->m_SourceNodes.empty() ) { std::cout << "ERROR !! DID NOT SET SOURCE!!\n"; return; } // std::cout << "MI start find path " << " Q size " << m_QS->m_Q.size() << " \n"; while( !m_SearchFinished && !m_QS->m_Q.empty() ) { m_CurrentNode = m_QS->m_Q.top(); m_CurrentCost = m_CurrentNode->GetTotalCost(); if( this->m_ParamWhileSearching ) { this->ParameterizeBoundary( this->m_CurrentNode ); } m_QS->m_Q.pop(); if( !m_CurrentNode->GetDelivered() ) { m_QS->IncrementTimer(); // /std::cout << " searching " << m_CurrentNode->GetLocation() << " \n"; this->SearchEdgeSet(); // if ( (m_CurrentNode->GetTimer() % 1.e5 ) == 0) // std::cout << " searched " << m_CurrentNode->GetTimer() << " \n"; } m_CurrentNode->SetDelivered(); } // end of while m_NumberSearched = (unsigned long) m_QS->GetTimer(); // std::cout << "Done with find path " << " Q size " << m_QS->m_Q.size() << // " num searched " << m_NumberSearched << " \n"; // std::cout << " Max Distance " << m_CurrentCost << std::endl; if( !this->m_ParamWhileSearching ) { this->GetSearchBoundary(); } return; } } #endif ants-2.2.0/Temporary/itkManifoldIntegrationAlgorithm.h000066400000000000000000000262571311104306400231340ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit (ITK) Copyright (c) 2001 Insight Consortium All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of the Insight Consortium, nor the names of any consortium members, nor of any contributors, may be used to endorse or promote products derived from this software without specific prior written permission. * Modified source versions must be plainly marked as such, and must not be misrepresented as being the original software. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================================*/ #ifndef _itkManifoldIntegrationAlgorithm_h_ #define _itkManifoldIntegrationAlgorithm_h_ namespace itk { /** * \class ManifoldIntegrationAlgorithm * \brief General shortest path / greedy dynamic programming solver. */ template class ManifoldIntegrationAlgorithm : public itk::LightObject { public: typedef ManifoldIntegrationAlgorithm Self; typedef LightObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; itkTypeMacro(ManifoldIntegrationAlgorithm, LightObject); itkNewMacro(Self); // Computation Data typedef TGraphSearchNode SearchNode; /** dimension of the graph */ typedef typename SearchNode::Pointer SearchNodePointer; enum { GraphDimension = SearchNode::GraphDimension }; /** dimension of the graph */ typedef typename SearchNode::PixelType PixelType; /** pixel type for the cost */ typedef typename SearchNode::CoordRep CoordRep; /** coordinate type */ typedef typename DijkstrasAlgorithmQueue::Pointer QType; typedef typename DijkstrasAlgorithmQueue::NodeListType NodeListType; typedef typename TGraphSearchNode::NodeLocationType NodeLocationType; typedef vtkPolyData TriangulationType; typedef vtkPolyData * TriangulationTypePointer; typedef vector GraphType; // FUNCTIONS // estimate the metric tensor of the surface and also the (conjugate harmonic) function dstarU void GetSearchBoundary(); float dstarUestimate(SearchNodePointer G); void InitializeGraph3(); void InitializeGraph(); /** initializes all graph values appropriately */ void InitializeGraph2(); /** initializes all graph values appropriately */ void InitializeQueue(); /** initializes all queue values appropriately call AFTER source and sink are set*/ inline void EmptyQ() { m_QS->EmptyQ(); m_QS->EmptyPath(); } /* adds a source to the source set */ void SetSource(typename TGraphSearchNode::Pointer G) { G->SetTotalCost(0.0); m_QS->m_SourceNodes.push_back(G); }; // adds a sink to the sink set void SetSink(typename TGraphSearchNode::Pointer G) { m_QS->m_SinkNodes.push_back(G); } // Backtracks from the given node to its source node; float BackTrack(typename TGraphSearchNode::Pointer SinkNode) { m_QS->m_Path.clear(); typename TGraphSearchNode::Pointer G = SinkNode; typename TGraphSearchNode::Pointer P = SinkNode->GetPredecessor(); if( !P ) { std::cout << " ERROR NO PRED TO SINK " << std::endl; return 1.; } m_QS->m_Path.push_back(G); // if (P->GetAncestor() && G) // { // if (P->GetAncestor()->GetTotalCost() < G->GetTotalCost() ) P->SetAncestor(G); // } // else if (G) P->SetAncestor(G); // if (P->GetValue(1) < G->GetValue(1) ) P->SetValue(G->GetValue(1),1); while( P && G != P ) { // std::cout << " Backtrack " << G->GetValue(0) << std::endl; G = P; P = G->GetPredecessor(); // if (P->GetValue(1) < G->GetValue(1) ) P->SetValue(G->GetValue(1),1); P->SetAncestor(G); if( P ) { m_QS->m_Path.push_back(P); } } // m_QS->m_Path.push_back(P); // std::cout << " final cost " << P->GetTotalCost() << " high " << highcost << std::endl; if( !P ) { cout << " null pred "; // else cout << " pred == self \n"; } return P->GetValue(2); } // Backtracks from the given node to its source node performing integration void IntegrateBackward(typename TGraphSearchNode::Pointer SinkNode) { typename TGraphSearchNode::Pointer G = SinkNode; typename TGraphSearchNode::Pointer P = SinkNode->GetPredecessor(); float intval = 0.0; G->SetTotalCost(intval); while( P && G != P ) { // NodeLocationType dif=P->GetLocation()-G->GetLocation(); float dU = (P->GetValue() - G->GetValue() ); intval += dU; P->SetTotalCost(intval); G = P; P = G->GetPredecessor(); } // std::cout << " intval " << intval << " at " << G->GetLocation() << std::endl; if( !P ) { cout << " null pred "; // else cout << " pred == self \n"; } return; } // Backtracks from the given node to its source node performing integration void IntegrateForward(typename TGraphSearchNode::Pointer SinkNode) { typename TGraphSearchNode::Pointer G = SinkNode; typename TGraphSearchNode::Pointer P = SinkNode->GetAncestor(); float intval = 0.0; G->SetTotalCost(intval); while( P && G != P ) { // NodeLocationType dif=P->GetLocation()-G->GetLocation(); float dU = (P->GetValue() - G->GetValue() ); intval += dU; P->SetTotalCost(intval); G = P; P = G->GetAncestor(); } // std::cout << " intval " << intval << " at " << G->GetLocation() << std::endl; if( !P ) { cout << " null pred "; // else cout << " pred == self \n"; } return; } // Inverse of backtrack - from the given node to its sink node; float ForwardTrack(typename TGraphSearchNode::Pointer SinkNode) { typename TGraphSearchNode::Pointer G = SinkNode; typename TGraphSearchNode::Pointer P = SinkNode->GetAncestor(); if( !P ) { return G->GetValue(2); } // float highcost=G->GetTotalCost(); while( P && G != P && G ) { G = P; P = G->GetAncestor(); if( !P ) { return G->GetValue(2); } // if (P->GetTotalCost() > highcost) highcost=P->GetTotalCost(); } // SinkNode->SetValue(highcost); // if (!P) cout << " null pred "; //else cout << " pred == self \n"; return P->GetValue(2); } bool ParameterizeBoundary( SearchNodePointer); bool TerminationCondition(); /** decides when the algorithm stops */ virtual void SearchEdgeSet(); /** loops over the neighbors in the graph */ void CheckNodeStatus(); /** checks if the node has been explored already, its cost, etc. */ virtual PixelType MyLocalCost(); /* computes the local cost */ /* alternatively, we could pass the function as a template parameter or set a function pointer. the latter method is used in dijkstrasegment. */ virtual void FindPath(); /* runs the algorithm */ inline unsigned int GetPathSize() { return m_QS->m_Path.size(); } inline void EmptyPath() { m_QS->m_Path.clear(); } inline typename TGraphSearchNode::Pointer GetPathAtIndex(unsigned int i) { return m_QS->m_Path[i]; } inline typename TGraphSearchNode::Pointer GetNeighborNode() { return m_NeighborNode; } inline typename TGraphSearchNode::Pointer GetCurrentNode() { return m_CurrentNode; } PixelType GetMaxCost() { return this->m_MaxCost; } void SetMaxCost(PixelType m) { this->m_MaxCost = m; } void ResetMaxCost() { this->m_MaxCost = vnl_huge_val(this->m_MaxCost); } inline void SetDistanceCostWeight(float d) { this->m_DistanceCostWeight = d; } inline void SetLabelCostWeight(float d) { this->m_LabelCostWeight = d; } inline void SetWeights(float a, float b, float c) { this->m_MaxCost = a; this->m_DistanceCostWeight = b; this->m_LabelCostWeight = c; } inline void PrintWeights() { std::cout << this->m_MaxCost << " " << this->m_DistanceCostWeight << " " << this->m_LabelCostWeight << std::endl; } void SetSearchFinished(bool m) { m_SearchFinished = m; } /** sets the boolean that indicates if the algorithm is done */ void SetSurfaceMesh( TriangulationTypePointer mesh) { m_SurfaceMesh = mesh; } TriangulationTypePointer GetSurfaceMesh() { return m_SurfaceMesh; } SearchNodePointer GetGraphNode(int i) { return m_GraphX[i]; } int GetGraphSize() { return m_GraphX.size(); } // sanity check to see if mesh to graph conversion is ok // see if genus is the same void ConvertGraphBackToMesh(); void SetParamWhileSearching( bool b ) { this->m_ParamWhileSearching = b; } std::vector m_BoundaryList; protected: QType m_QS; vector m_EdgeTemplate; /** defines neighborhood connectivity */ typename TGraphSearchNode::Pointer m_PredecessorNode; /** holds the predecessor node */ typename TGraphSearchNode::Pointer m_CurrentNode; /** holds the current node */ typename TGraphSearchNode::Pointer m_NeighborNode; /** holds the current neighbor node */ bool m_PureDist; bool m_SearchFinished; PixelType m_NewCost; PixelType m_CurrentCost; float m_MaxCost; // This creates an insurmountable barrier unless all costs are max float m_DistanceCostWeight; float m_LabelCostWeight; GraphType m_GraphX; unsigned long m_NumberSearched; TriangulationTypePointer m_SurfaceMesh; bool m_ParamWhileSearching; ManifoldIntegrationAlgorithm(); ~ManifoldIntegrationAlgorithm() { }; private: ManifoldIntegrationAlgorithm(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkManifoldIntegrationAlgorithm.cxx" #endif #endif ants-2.2.0/Temporary/itkMeanSquaresPointSetToPointSetIntensityMetricv4.h000066400000000000000000000205671311104306400265560ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkMeanSquaresPointSetToPointSetIntensityMetricv4_h #define itkMeanSquaresPointSetToPointSetIntensityMetricv4_h #include "itkPointSetToPointSetMetricv4.h" namespace itk { /** \class MeanSquaresPointSetToPointSetIntensityMetricv4 * \brief Computes a mean-squares intensity metric between two point sets. * * We only have to handle the individual point case as the parent * class handles the aggregation. * * The PointSet::PixelType contains both the intensity and gradient * information at each point and the surrounding neighborhood. The ordering * is based on the itk::Neighborhood data structure: * * http://www.itk.org/Doxygen/html/classitk_1_1Neighborhood.html * * pixelData = [intensity_0, gradientX_0, gradientY_0, gradientZ_0, // GetOffset( 0 ) * intensity_1, gradientX_1, gradientY_1, gradientZ_1, // GetOffset( 1 ) * ... , ... , ... , ... , ... * intensity_N, gradientX_N, gradientY_N, gradientZ_N] // GetOffset( N ) * * * \ingroup ITKMetricsv4 */ template class MeanSquaresPointSetToPointSetIntensityMetricv4: public PointSetToPointSetMetricv4 { public: /** Standard class typedefs. */ typedef MeanSquaresPointSetToPointSetIntensityMetricv4 Self; typedef PointSetToPointSetMetricv4 Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( MeanSquaresPointSetToPointSetIntensityMetricv4, PointSetToPointSetMetricv4 ); /** Type of the fixed point set. */ typedef TFixedPointSet FixedPointSetType; typedef typename TFixedPointSet::PointType FixedPointType; typedef typename TFixedPointSet::PixelType FixedPixelType; typedef typename TFixedPointSet::PointsContainer FixedPointsContainer; typedef typename TFixedPointSet::PointDataContainer FixedPointDataContainer; /** Dimension type */ typedef typename Superclass::DimensionType DimensionType; itkStaticConstMacro( FixedPointDimension, DimensionType, Superclass::FixedDimension ); /** Type of the moving point set. */ typedef TMovingPointSet MovingPointSetType; typedef typename TMovingPointSet::PointType MovingPointType; typedef typename TMovingPointSet::PixelType MovingPixelType; typedef typename TMovingPointSet::PointsContainer MovingPointsContainer; typedef typename TMovingPointSet::PointDataContainer MovingPointDataContainer; itkStaticConstMacro( MovingPointDimension, DimensionType, Superclass::MovingDimension ); /** Transform types from Superclass*/ typedef typename Superclass::FixedTransformType FixedTransformType; typedef typename Superclass::MovingTransformType MovingTransformType; itkStaticConstMacro( PointDimension, DimensionType, Superclass::PointDimension ); /** Types transferred from the base class */ typedef typename Superclass::MeasureType MeasureType; typedef typename Superclass::DerivativeType DerivativeType; typedef typename Superclass::LocalDerivativeType LocalDerivativeType; typedef typename Superclass::PointType PointType; typedef typename Superclass::PixelType PixelType; typedef typename Superclass::PointIdentifier PointIdentifier; typedef typename Superclass::PointsConstIterator PointsConstIterator; typedef CovariantVector CovariantVectorType; /** * Set/get estimate intensity distance sigma automatically based on reasonable * heuristics. */ itkSetMacro( EstimateIntensityDistanceSigmaAutomatically, bool ); itkGetConstMacro( EstimateIntensityDistanceSigmaAutomatically, bool ); itkBooleanMacro( EstimateIntensityDistanceSigmaAutomatically ); /** * Set/get estimate Euclidean distance sigma automatically based on reasonable * heuristics. */ itkSetMacro( EstimateEuclideanDistanceSigmaAutomatically, bool ); itkGetConstMacro( EstimateEuclideanDistanceSigmaAutomatically, bool ); itkBooleanMacro( EstimateEuclideanDistanceSigmaAutomatically ); /** * Set/get intensity sigma -- modulate the intensity distance contribution in the * metric formulation. Default = sqrt( 5.0 ). */ itkSetMacro( IntensityDistanceSigma, TInternalComputationValueType ); itkGetConstMacro( IntensityDistanceSigma, TInternalComputationValueType ); /** * Set/get distance sigma -- modulate the Euclidean distance contribution in the * metric formulation. Default = sqrt( 5.0 ). */ itkSetMacro( EuclideanDistanceSigma, TInternalComputationValueType ); itkGetConstMacro( EuclideanDistanceSigma, TInternalComputationValueType ); /** * Initialize the metric by estimating the intensity and distance sigmas */ virtual void Initialize( void ) throw ( ExceptionObject ) ITK_OVERRIDE; /** * Prepare point sets for use. */ virtual void InitializePointSets() const ITK_OVERRIDE; /** * Calculates the local metric value for a single point. */ virtual MeasureType GetLocalNeighborhoodValue( const PointType &, const PixelType & ) const ITK_OVERRIDE; /** Helper method allows for code reuse while skipping the metric value * calculation when appropriate */ void CalculateValueAndDerivative( MeasureType & value, DerivativeType & derivative, bool calculateValue ) const; /** * Calculates the local value and derivative for a single point. */ virtual void GetLocalNeighborhoodValueAndDerivative( const PointType &, MeasureType &, LocalDerivativeType &, const PixelType & ) const ITK_OVERRIDE; /** Clone method will clone the existing instance of this type, * including its internal member variables. */ virtual typename LightObject::Pointer InternalClone() const ITK_OVERRIDE; protected: MeanSquaresPointSetToPointSetIntensityMetricv4(); virtual ~MeanSquaresPointSetToPointSetIntensityMetricv4(); /** * Estimate the intensity distance sigma based on simple heuristic */ void EstimateIntensityDistanceSigma(); /** * Estimate the Euclidean distance sigma based on simple heuristic */ void EstimateEuclideanDistanceSigma(); /** * Warp the fixed point set gradients based on the fixed transform. */ void TransformFixedPointSetGradients() const; /** * Warp the moving point set gradients based on the moving transform. */ void TransformMovingPointSetGradients() const; /** PrintSelf function */ void PrintSelf( std::ostream & os, Indent indent ) const ITK_OVERRIDE; private: MeanSquaresPointSetToPointSetIntensityMetricv4(const Self &); //purposely not implemented void operator=(const Self &); //purposely not implemented bool m_EstimateIntensityDistanceSigmaAutomatically; bool m_EstimateEuclideanDistanceSigmaAutomatically; TInternalComputationValueType m_IntensityDistanceSigma; TInternalComputationValueType m_EuclideanDistanceSigma; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkMeanSquaresPointSetToPointSetIntensityMetricv4.hxx" #endif #endif ants-2.2.0/Temporary/itkMeanSquaresPointSetToPointSetIntensityMetricv4.hxx000066400000000000000000000417021311104306400271300ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkMeanSquaresPointSetToPointSetIntensityMetricv4_hxx #define itkMeanSquaresPointSetToPointSetIntensityMetricv4_hxx #include "itkMeanSquaresPointSetToPointSetIntensityMetricv4.h" namespace itk { /** Constructor */ template MeanSquaresPointSetToPointSetIntensityMetricv4 ::MeanSquaresPointSetToPointSetIntensityMetricv4() { this->m_EuclideanDistanceSigma = std::sqrt( 5.0 ); this->m_IntensityDistanceSigma = std::sqrt( 5.0 ); this->m_EstimateIntensityDistanceSigmaAutomatically = true; this->m_EstimateEuclideanDistanceSigmaAutomatically = true; this->m_UsePointSetData = true; } /** Destructor */ template MeanSquaresPointSetToPointSetIntensityMetricv4 ::~MeanSquaresPointSetToPointSetIntensityMetricv4() { } /** Initialize the metric */ template void MeanSquaresPointSetToPointSetIntensityMetricv4 ::Initialize( void ) throw ( ExceptionObject ) { Superclass::Initialize(); if( this->m_EstimateIntensityDistanceSigmaAutomatically ) { this->EstimateIntensityDistanceSigma(); } if( this->m_EstimateEuclideanDistanceSigmaAutomatically ) { this->EstimateEuclideanDistanceSigma(); } } template void MeanSquaresPointSetToPointSetIntensityMetricv4 ::InitializePointSets() const { Superclass::InitializePointSets(); if( this->m_CalculateValueAndDerivativeInTangentSpace == true ) { this->TransformMovingPointSetGradients(); this->TransformFixedPointSetGradients(); } } template void MeanSquaresPointSetToPointSetIntensityMetricv4 ::TransformFixedPointSetGradients() const { typename FixedTransformType::InverseTransformBasePointer inverseTransform = this->m_FixedTransform->GetInverseTransform(); typename FixedPointsContainer::ConstIterator It = this->m_FixedPointSet->GetPoints()->Begin(); while( It != this->m_FixedPointSet->GetPoints()->End() ) { PixelType pixel; NumericTraits::SetLength( pixel, 1 ); bool doesPointDataExist = this->m_FixedPointSet->GetPointData( It.Index(), &pixel ); if( ! doesPointDataExist ) { itkExceptionMacro( "The corresponding data for point " << It.Value() << " (pointId = " << It.Index() << ") does not exist." ); } SizeValueType numberOfVoxelsInNeighborhood = pixel.size() / ( 1 + PointDimension ); // Here we assume that transforming the vector at the neighborhood voxel // is close to performing the transformation at the center voxel. for( SizeValueType n = 0; n < numberOfVoxelsInNeighborhood; n++ ) { CovariantVectorType covariantVector; for( unsigned int d = 0; d < PointDimension; d++ ) { covariantVector[d] = pixel[n * ( PointDimension + 1 ) + d + 1]; } // First, transform from fixed to virtual domain. Then go from virtual domain // to moving. CovariantVectorType transformedCovariantVector = inverseTransform->TransformCovariantVector( covariantVector, It.Value() ); for( unsigned int d = 0; d < PointDimension; d++ ) { pixel[n * ( PointDimension + 1 ) + d + 1] = transformedCovariantVector[d]; } } this->m_FixedTransformedPointSet->SetPointData( It.Index(), pixel ); ++It; } } template void MeanSquaresPointSetToPointSetIntensityMetricv4 ::TransformMovingPointSetGradients() const { typename MovingTransformType::InverseTransformBasePointer inverseTransform = this->m_MovingTransform->GetInverseTransform(); typename MovingPointsContainer::ConstIterator It = this->m_MovingPointSet->GetPoints()->Begin(); while( It != this->m_MovingPointSet->GetPoints()->End() ) { PixelType pixel; NumericTraits::SetLength( pixel, 1 ); bool doesPointDataExist = this->m_MovingPointSet->GetPointData( It.Index(), &pixel ); if( ! doesPointDataExist ) { itkExceptionMacro( "The corresponding data for point " << It.Value() << " (pointId = " << It.Index() << ") does not exist." ); } SizeValueType numberOfVoxelsInNeighborhood = pixel.size() / ( 1 + PointDimension ); // Here we assume that transforming the vector at the neighborhood voxel // is close to performing the transformation at the center voxel. for( SizeValueType n = 0; n < numberOfVoxelsInNeighborhood; n++ ) { CovariantVectorType covariantVector; for( unsigned int d = 0; d < PointDimension; d++ ) { covariantVector[d] = pixel[n * ( PointDimension + 1 ) + d + 1]; } // First, transform from fixed to virtual domain. Then go from virtual domain // to moving. CovariantVectorType transformedCovariantVector = inverseTransform->TransformCovariantVector( covariantVector, It.Value() ); for( unsigned int d = 0; d < PointDimension; d++ ) { pixel[n * ( PointDimension + 1 ) + d + 1] = transformedCovariantVector[d]; } } this->m_MovingTransformedPointSet->SetPointData( It.Index(), pixel ); ++It; } } template void MeanSquaresPointSetToPointSetIntensityMetricv4 ::EstimateEuclideanDistanceSigma() { TInternalComputationValueType runningDistanceMean = 0.0; TInternalComputationValueType runningDistanceSigma = 0.0; if( this->m_FixedTransformedPointSet->GetNumberOfPoints() <= 1 ) { itkExceptionMacro( "Need more than 1 point to estimate the distance sigma." ); } unsigned int count = 0; PointsConstIterator ItF = this->m_FixedTransformedPointSet->GetPoints()->Begin(); while( ItF != this->m_FixedTransformedPointSet->GetPoints()->End() ) { PointIdentifier pointId = this->m_MovingTransformedPointsLocator->FindClosestPoint( ItF.Value() ); PointType closestPoint; closestPoint.Fill( 0.0 ); closestPoint = this->m_MovingTransformedPointSet->GetPoint( pointId ); TInternalComputationValueType distance = closestPoint.EuclideanDistanceTo( ItF.Value() ); if( count == 0 ) { runningDistanceMean = distance; runningDistanceSigma = 0.0; } else { TInternalComputationValueType runningDistanceMeanPreviousIteration = runningDistanceMean; runningDistanceMean = runningDistanceMeanPreviousIteration + ( distance - runningDistanceMeanPreviousIteration ) / static_cast( count + 1 ); runningDistanceSigma += ( distance - runningDistanceMeanPreviousIteration ) * ( distance - runningDistanceMean ); } ++count; ++ItF; } this->m_EuclideanDistanceSigma = std::sqrt( runningDistanceSigma / static_cast( count ) ); } template void MeanSquaresPointSetToPointSetIntensityMetricv4 ::EstimateIntensityDistanceSigma() { // Get the min/max intensities from the fixed point set PointsConstIterator ItF = this->m_FixedPointSet->GetPoints()->Begin(); TInternalComputationValueType maxFixedIntensity = NumericTraits::NonpositiveMin(); TInternalComputationValueType minFixedIntensity = NumericTraits::max(); while( ItF != this->m_FixedPointSet->GetPoints()->End() ) { PixelType pixel; NumericTraits::SetLength( pixel, 1 ); if( this->m_UsePointSetData ) { bool doesPointDataExist = this->m_FixedPointSet->GetPointData( ItF.Index(), &pixel ); if( ! doesPointDataExist ) { itkExceptionMacro( "The corresponding data for point " << ItF.Value() << " (pointId = " << ItF.Index() << ") does not exist." ); } } SizeValueType numberOfVoxelsInNeighborhood = pixel.size() / ( 1 + PointDimension ); SizeValueType centerIntensityIndex = static_cast( 0.5 * numberOfVoxelsInNeighborhood ) * ( PointDimension + 1 ); if( pixel[centerIntensityIndex] > maxFixedIntensity ) { maxFixedIntensity = pixel[centerIntensityIndex]; } else if( pixel[centerIntensityIndex] > minFixedIntensity ) { minFixedIntensity = pixel[centerIntensityIndex]; } ++ItF; } // Get the min/max intensities from the moving point set PointsConstIterator ItM = this->m_MovingPointSet->GetPoints()->Begin(); TInternalComputationValueType maxMovingIntensity = NumericTraits::NonpositiveMin(); TInternalComputationValueType minMovingIntensity = NumericTraits::max(); while( ItM != this->m_MovingPointSet->GetPoints()->End() ) { PixelType pixel; NumericTraits::SetLength( pixel, 1 ); if( this->m_UsePointSetData ) { bool doesPointDataExist = this->m_MovingPointSet->GetPointData( ItM.Index(), &pixel ); if( ! doesPointDataExist ) { itkExceptionMacro( "The corresponding data for point " << ItM.Value() << " (pointId = " << ItM.Index() << ") does not exist." ); } } SizeValueType numberOfVoxelsInNeighborhood = pixel.size() / ( 1 + PointDimension ); SizeValueType centerIntensityIndex = static_cast( 0.5 * numberOfVoxelsInNeighborhood ) * ( PointDimension + 1 ); if( pixel[centerIntensityIndex] > maxMovingIntensity ) { maxMovingIntensity = pixel[centerIntensityIndex]; } else if( pixel[centerIntensityIndex] > minMovingIntensity ) { minMovingIntensity = pixel[centerIntensityIndex]; } ++ItM; } // Now determine the sigma using a reasonable heuristic. this->m_IntensityDistanceSigma = vnl_math_max( maxMovingIntensity, maxFixedIntensity ) - vnl_math_min( minMovingIntensity, maxMovingIntensity ); if( this->m_IntensityDistanceSigma == 0 ) { this->m_IntensityDistanceSigma = vnl_math_max( maxMovingIntensity, maxFixedIntensity ); } } template typename MeanSquaresPointSetToPointSetIntensityMetricv4 ::MeasureType MeanSquaresPointSetToPointSetIntensityMetricv4 ::GetLocalNeighborhoodValue( const PointType & point, const PixelType & pixel ) const { PointIdentifier pointId = this->m_MovingTransformedPointsLocator->FindClosestPoint( point ); PixelType closestPixel; NumericTraits::SetLength( closestPixel, 1 ); if( this->m_UsePointSetData ) { bool doesPointDataExist = false; if( this->m_CalculateValueAndDerivativeInTangentSpace == true ) { doesPointDataExist = this->m_MovingTransformedPointSet->GetPointData( pointId, &closestPixel ); } else { doesPointDataExist = this->m_MovingPointSet->GetPointData( pointId, &closestPixel ); } if( ! doesPointDataExist ) { itkExceptionMacro( "The corresponding data for point " << point << " (pointId = " << pointId << ") does not exist." ); } } PointType closestPoint; closestPoint.Fill( 0.0 ); closestPoint = this->m_MovingTransformedPointSet->GetPoint( pointId ); // the probabilistic icp term const MeasureType euclideanDistance = point.EuclideanDistanceTo( closestPoint ); MeasureType distanceProbability = std::exp( -0.5 * vnl_math_sqr( euclideanDistance / this->m_EuclideanDistanceSigma ) ); SizeValueType numberOfVoxelsInNeighborhood = pixel.size() / ( 1 + PointDimension ); SizeValueType centerIntensityIndex = static_cast( 0.5 * numberOfVoxelsInNeighborhood ) * ( PointDimension + 1 ); // the probabilistic intensity term MeasureType intensityDistance = pixel[centerIntensityIndex] - closestPixel[centerIntensityIndex]; MeasureType intensityProbability = std::exp( -0.5 * vnl_math_sqr( intensityDistance / this->m_IntensityDistanceSigma ) ); const MeasureType measure = ( -1.0 ) * intensityProbability * distanceProbability; return measure; } template void MeanSquaresPointSetToPointSetIntensityMetricv4 ::GetLocalNeighborhoodValueAndDerivative( const PointType & point, MeasureType &measure, LocalDerivativeType & localDerivative, const PixelType & pixel ) const { PointIdentifier pointId = this->m_MovingTransformedPointsLocator->FindClosestPoint( point ); PixelType closestPixel; NumericTraits::SetLength( closestPixel, 1 ); if( this->m_UsePointSetData ) { bool doesPointDataExist = false; if( this->m_CalculateValueAndDerivativeInTangentSpace == true ) { doesPointDataExist = this->m_MovingTransformedPointSet->GetPointData( pointId, &closestPixel ); } else { doesPointDataExist = this->m_MovingPointSet->GetPointData( pointId, &closestPixel ); } if( ! doesPointDataExist ) { itkExceptionMacro( "The corresponding data for point " << point << " (pointId = " << pointId << ") does not exist." ); } } PointType closestPoint; closestPoint.Fill( 0.0 ); closestPoint = this->m_MovingTransformedPointSet->GetPoint( pointId ); // the probabilistic icp term const MeasureType euclideanDistance = point.EuclideanDistanceTo( closestPoint ); MeasureType distanceProbability = std::exp( -0.5 * vnl_math_sqr( euclideanDistance / this->m_EuclideanDistanceSigma ) ); SizeValueType numberOfVoxelsInNeighborhood = pixel.size() / ( 1 + PointDimension ); SizeValueType centerIntensityIndex = static_cast( 0.5 * numberOfVoxelsInNeighborhood ) * ( PointDimension + 1 ); // the probabilistic intensity term MeasureType intensityDistance = pixel[centerIntensityIndex] - closestPixel[centerIntensityIndex]; MeasureType intensityProbability = std::exp( -0.5 * vnl_math_sqr( intensityDistance / this->m_IntensityDistanceSigma ) ); measure = ( -1.0 ) * intensityProbability * distanceProbability; // total derivative is // d/dx( intProb * distProb ) = // intProb * d/dx( distProb ) + distProb * d/dx( intProb ) = // intProb * distProb * dist + distProb * intProb * intdiff = localDerivative = ( closestPoint - point ) * intensityProbability * distanceProbability; for( SizeValueType d = 0; d < PointDimension; d++ ) { localDerivative[d] += intensityProbability * distanceProbability * intensityDistance * closestPixel[centerIntensityIndex + 1 + d]; } } template typename LightObject::Pointer MeanSquaresPointSetToPointSetIntensityMetricv4 ::InternalClone( void ) const { typename Self::Pointer rval = Self::New(); rval->SetMovingPointSet( this->m_MovingPointSet ); rval->SetFixedPointSet( this->m_FixedPointSet ); return rval.GetPointer(); } /** PrintSelf method */ template void MeanSquaresPointSetToPointSetIntensityMetricv4 ::PrintSelf( std::ostream & os, Indent indent ) const { Superclass::PrintSelf( os, indent ); os << "Euclidean distance sigma = " << this->m_EuclideanDistanceSigma << std::endl; os << "intensity distance sigma = " << this->m_IntensityDistanceSigma << std::endl; } } // end namespace itk #endif ants-2.2.0/Temporary/itkMultiplyByConstantImageFilter.h000066400000000000000000000040731311104306400232550ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkMultiplyByConstantImageFilter_h #define __itkMultiplyByConstantImageFilter_h #include "itkMultiplyImageFilter.h" namespace itk { /** \class MultiplyByConstantImageFilter * * \brief Multiply all input pixels by a constant. * * This filter is templated over the input image type * and the output image type. * * \author Tom Vercauteren, INRIA & Mauna Kea Technologies * * Based on filters from the Insight Journal paper: * http://hdl.handle.net/1926/510 * * \ingroup ITKDeprecated * \sa MultiplyImageFilter */ template class MultiplyByConstantImageFilter : public MultiplyImageFilter, TOutputImage> { public: typedef MultiplyByConstantImageFilter Self; typedef MultiplyImageFilter, TOutputImage> Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** method for creation through object factory */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(MultiplyByConstantImageFilter, MultiplyImageFilter); protected: MultiplyByConstantImageFilter() { } virtual ~MultiplyByConstantImageFilter() { } }; } #endif ants-2.2.0/Temporary/topological_numbers.h000066400000000000000000000377131311104306400206660ustar00rootroot00000000000000#ifndef TOPO_INCLUDED #define TOPO_INCLUDED /* This file provides some code to compute the topological numbers introduced by Giles Bertrand and Gregoire Malandin. This code is the result of my understanding of their paper, and is not optimized. In fact, it is possible to generate look-up tables to greatly speed-up computations... Given a binary 3*3*3 neighborhood ( the structure TOPOLOGICAL_NEIGHBORHOOD or NBH ) and a pair of consistent connectivities ( 1=(6+,18), 2=(18,6+), 3=(6,26), 4=(26,6) ), the function checkTn computes the topological number associated with the structure NBH and a specific connectivity. The fucntion checkSimple checks if the point is simple or not (see definitions below). The topological neighborhood NBH has to be initialized as a binary object 0-1, where 1 represents the Foreground Object and 0 the Background Object. The first connectivity in the pair of digital connectivities ( 1=(6+,18), 2=(18,6+), 3=(6,26), 4=(26,6) ) refers to the Foreground Object, the second one to the Background Object */ /* ////////////////////////////////////////////////////////////////////// // COMPATIBLE CONNECTIVITIES / TOPOLOGICAL NUMBERS // // TOPOLOGICAL CONVENTION // 0: No topological constraint // 1: (6+,18) // 2: (18,6+) // 3: (6,26) // 4: (26,6) // default: (6+,18) //////////////////////////////////////////////////////////////////// */ typedef unsigned char TOPOLOGICAL_NEIGHBORHOOD[3][3][3]; #define NBH TOPOLOGICAL_NEIGHBORHOOD #define TMP 100 #define MAXIMUM_NUMBER_OF_COMPONENTS 10 // maximum number of components in a NBH #define MAX_COMP MAXIMUM_NUMBER_OF_COMPONENTS /*The connectivity number is equal to the maximum allowed distance (||.||1 norm) used to generate the topological neighborhood*/ int connectivityNumber(int connectivity) { int con; switch( connectivity ) { case 1: /*for the connectivity 1 [=(6+,18)], the topological neighborhood of a point x is defined by {x / ||a-x||1<=1 } -> the 6 neighborhood! */ con = 1; break; case 2: /*the 18-neighborhood*/ con = 2; break; case 3: /*the 6-heighborhood*/ con = 1; break; case 4: /*the 26-neighborhood*/ con = 3; break; default: con = 1; break; } return con; } // Digital topology requires a pair of compatible connectivities int associatedConnectivity(int connectivity) { switch( connectivity ) { case 1: return 2; break; case 2: return 1; break; case 3: return 4; break; case 4: return 3; break; default: return 2; break; } return 0; } NBH * reverseNBH(NBH* nbh_src, NBH *nbh_dst) { NBH *nbh; int i, j, k; if( nbh_dst ) { nbh = nbh_dst; } else { nbh = (NBH *)malloc(sizeof(NBH) ); } for( k = 0; k < 3; k++ ) { for( j = 0; j < 3; j++ ) { for( i = 0; i < 3; i++ ) { if( (*nbh_src)[i][j][k] ) { (*nbh)[i][j][k] = 0; } else { (*nbh)[i][j][k] = 1; } } } } return nbh; } NBH * N_6_1(NBH* nbh_src, NBH* nbh_dst) { int i, j, k; NBH* nbh; if( !nbh_dst ) { nbh = (NBH *)calloc(1, sizeof(NBH) ); if( (*nbh_src)[0][1][1] ) { (*nbh)[0][1][1] = 1; } if( (*nbh_src)[2][1][1] ) { (*nbh)[2][1][1] = 1; } if( (*nbh_src)[1][0][1] ) { (*nbh)[1][0][1] = 1; } if( (*nbh_src)[1][2][1] ) { (*nbh)[1][2][1] = 1; } if( (*nbh_src)[1][1][0] ) { (*nbh)[1][1][0] = 1; } if( (*nbh_src)[1][1][2] ) { (*nbh)[1][1][2] = 1; } return nbh; } else { nbh = nbh_dst; } if( (*nbh_src)[0][1][1] ) { (*nbh)[0][1][1] = TMP; } if( (*nbh_src)[2][1][1] ) { (*nbh)[2][1][1] = TMP; } if( (*nbh_src)[1][0][1] ) { (*nbh)[1][0][1] = TMP; } if( (*nbh_src)[1][2][1] ) { (*nbh)[1][2][1] = TMP; } if( (*nbh_src)[1][1][0] ) { (*nbh)[1][1][0] = TMP; } if( (*nbh_src)[1][1][2] ) { (*nbh)[1][1][2] = TMP; } for( k = 0; k < 3; k++ ) { for( j = 0; j < 3; j++ ) { for( i = 0; i < 3; i++ ) { if( (*nbh)[i][j][k] == TMP ) { (*nbh)[i][j][k] = 1; } else { (*nbh)[i][j][k] = 0; } } } } return nbh; } NBH * N_6_2(NBH* nbh_src, NBH* nbh_dst) { int i, j, k; NBH* nbh; nbh = N_6_1(nbh_src, NULL); for( i = 0; i < 3; i = i + 2 ) { if( (*nbh)[i][1][1] ) { if( (*nbh_src)[i][0][1] ) { (*nbh)[i][0][1] = 1; } if( (*nbh_src)[i][2][1] ) { (*nbh)[i][2][1] = 1; } if( (*nbh_src)[i][1][0] ) { (*nbh)[i][1][0] = 1; } if( (*nbh_src)[i][1][2] ) { (*nbh)[i][1][2] = 1; } } } for( j = 0; j < 3; j = j + 2 ) { if( (*nbh)[1][j][1] ) { if( (*nbh_src)[0][j][1] ) { (*nbh)[0][j][1] = 1; } if( (*nbh_src)[2][j][1] ) { (*nbh)[2][j][1] = 1; } if( (*nbh_src)[1][j][0] ) { (*nbh)[1][j][0] = 1; } if( (*nbh_src)[1][j][2] ) { (*nbh)[1][j][2] = 1; } } } for( k = 0; k < 3; k = k + 2 ) { if( (*nbh)[1][1][k] ) { if( (*nbh_src)[0][1][k] ) { (*nbh)[0][1][k] = 1; } if( (*nbh_src)[2][1][k] ) { (*nbh)[2][1][k] = 1; } if( (*nbh_src)[1][0][k] ) { (*nbh)[1][0][k] = 1; } if( (*nbh_src)[1][2][k] ) { (*nbh)[1][2][k] = 1; } } } if( nbh_dst ) { for( k = 0; k < 3; k++ ) { for( j = 0; j < 3; j++ ) { for( i = 0; i < 3; i++ ) { (*nbh_dst)[i][j][k] = (*nbh)[i][j][k]; } } } free(nbh); nbh = nbh_dst; } return nbh; } NBH * N_6_3(NBH* nbh_src, NBH* nbh_dst) { int i, j, k; NBH* nbh; nbh = N_6_2(nbh_src, NULL); i = 0; j = 0; k = 0; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] ) { (*nbh)[i][j][k] = 1; } } i = 0; j = 0; k = 2; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] ) { (*nbh)[i][j][k] = 1; } } i = 0; j = 2; k = 0; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] ) { (*nbh)[i][j][k] = 1; } } i = 0; j = 2; k = 2; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] ) { (*nbh)[i][j][k] = 1; } } i = 2; j = 0; k = 0; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] ) { (*nbh)[i][j][k] = 1; } } i = 2; j = 0; k = 2; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] ) { (*nbh)[i][j][k] = 1; } } i = 2; j = 2; k = 0; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] ) { (*nbh)[i][j][k] = 1; } } i = 2; j = 2; k = 2; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] ) { (*nbh)[i][j][k] = 1; } } if( nbh_dst ) { for( k = 0; k < 3; k++ ) { for( j = 0; j < 3; j++ ) { for( i = 0; i < 3; i++ ) { (*nbh_dst)[i][j][k] = (*nbh)[i][j][k]; } } } free(nbh); nbh = nbh_dst; } return nbh; } NBH * N_18_1(NBH* nbh_src, NBH* nbh_dst) { int i, j, k; NBH* nbh; if( !nbh_dst ) { nbh = (NBH *)calloc(1, sizeof(NBH) ); } else { nbh = nbh_dst; } (*nbh)[0][0][0] = TMP; (*nbh)[2][0][0] = TMP; (*nbh)[0][0][2] = TMP; (*nbh)[2][0][2] = TMP; (*nbh)[0][2][0] = TMP; (*nbh)[2][2][0] = TMP; (*nbh)[0][2][2] = TMP; (*nbh)[2][2][2] = TMP; (*nbh)[1][1][1] = TMP; for( k = 0; k < 3; k++ ) { for( j = 0; j < 3; j++ ) { for( i = 0; i < 3; i++ ) { if( (*nbh)[i][j][k] != TMP ) { if( (*nbh_src)[i][j][k] ) { (*nbh)[i][j][k] = 1; } else { (*nbh)[i][j][k] = 0; } } else { (*nbh)[i][j][k] = 0; } } } } return nbh; } NBH * N_18_2(NBH* nbh_src, NBH* nbh_dst) { int i, j, k; NBH* nbh; nbh = N_18_1(nbh_src, NULL); i = 0; j = 0; k = 0; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] || (*nbh)[1][1][k] || (*nbh)[1][j][1] || (*nbh)[i][1][1] ) { (*nbh)[i][j][k] = 1; } } i = 0; j = 0; k = 2; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] || (*nbh)[1][1][k] || (*nbh)[1][j][1] || (*nbh)[i][1][1] ) { (*nbh)[i][j][k] = 1; } } i = 0; j = 2; k = 0; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] || (*nbh)[1][1][k] || (*nbh)[1][j][1] || (*nbh)[i][1][1] ) { (*nbh)[i][j][k] = 1; } } i = 0; j = 2; k = 2; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] || (*nbh)[1][1][k] || (*nbh)[1][j][1] || (*nbh)[i][1][1] ) { (*nbh)[i][j][k] = 1; } } i = 2; j = 0; k = 0; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] || (*nbh)[1][1][k] || (*nbh)[1][j][1] || (*nbh)[i][1][1] ) { (*nbh)[i][j][k] = 1; } } i = 2; j = 0; k = 2; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] || (*nbh)[1][1][k] || (*nbh)[1][j][1] || (*nbh)[i][1][1] ) { (*nbh)[i][j][k] = 1; } } i = 2; j = 2; k = 0; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] || (*nbh)[1][1][k] || (*nbh)[1][j][1] || (*nbh)[i][1][1] ) { (*nbh)[i][j][k] = 1; } } i = 2; j = 2; k = 2; if( (*nbh_src)[i][j][k] ) { if( (*nbh)[1][j][k] || (*nbh)[i][1][k] || (*nbh)[i][j][1] || (*nbh)[1][1][k] || (*nbh)[1][j][1] || (*nbh)[i][1][1] ) { (*nbh)[i][j][k] = 1; } } if( nbh_dst ) { for( k = 0; k < 3; k++ ) { for( j = 0; j < 3; j++ ) { for( i = 0; i < 3; i++ ) { (*nbh_dst)[i][j][k] = (*nbh)[i][j][k]; } } } free(nbh); nbh = nbh_dst; } return nbh; } NBH * N_26_1(NBH* nbh_src, NBH* nbh_dst) { int i, j, k; NBH* nbh; if( !nbh_dst ) { nbh = (NBH *)calloc(1, sizeof(NBH) ); } else { nbh = nbh_dst; } for( k = 0; k < 3; k++ ) { for( j = 0; j < 3; j++ ) { for( i = 0; i < 3; i++ ) { if( (*nbh_src)[i][j][k] ) { (*nbh)[i][j][k] = 1; } else { (*nbh)[i][j][k] = 0; } } } } (*nbh)[1][1][1] = 0; return nbh; } NBH * Nnk(NBH* nbh_src, NBH *nbh_dst, int connectivity) { NBH* nbh; if( !nbh_dst ) { nbh = (NBH *)calloc(1, sizeof(NBH) ); } else { nbh = nbh_dst; } switch( connectivity ) { case 1: // T6+(x,X)=#C6(N_6_3(x,X)) N_6_3(nbh_src, nbh); break; case 2: // T6+(x,X)=#C18(N_18_2) N_18_2(nbh_src, nbh); break; case 3: // T6(x,X)=#C6(N_6_2) N_6_2(nbh_src, nbh); break; case 4: // T26(x,X)=#C6(N_26_1) N_26_1(nbh_src, nbh); break; default: // T6+(x,X)=#C6(N_6_3(x,X)) N_6_3(nbh_src, nbh); break; } return nbh; } /*This function computes the topological number associated with NBH and a certain connectivity. This function is a "hack": a more elegant/efficient implementation should be done... NBH is a binary object (0-1) connectivity should be equal to 1,2,3, or 4 according to the convention above*/ int checkTn(NBH *nbh_src, NBH *nbh_dst, int connectivity) { int i, j, k, a, b, c, ik, jk, kk, ct; int con, nvox, label, sum; int comp_table[MAX_COMP]; int min_val; int x, y, z; NBH* nbh; if( !nbh_dst ) { nbh = (NBH *)calloc(1, sizeof(NBH) ); } else { nbh = nbh_dst; } con = connectivityNumber(connectivity); Nnk(nbh_src, nbh, connectivity); memset(comp_table, 0, MAX_COMP * sizeof(int) ); for( i = 0; i < 3; i++ ) { for( j = 0; j < 3; j++ ) { for( k = 0; k < 3; k++ ) { if( (*nbh)[i][j][k] ) { for( nvox = 0, ik = -1; ik <= 1; ik++ ) { a = i + ik; if( a < 0 || a >= 3 ) { continue; } for( jk = -1; jk <= 1; jk++ ) { b = j + jk; if( b < 0 || b >= 3 ) { continue; } for( kk = -1; kk <= 1; kk++ ) { sum = abs(ik) + abs(jk) + abs(kk); if( sum > con || (!sum) ) { continue; } c = k + kk; if( c < 0 || c >= 3 ) { continue; } label = (*nbh)[a][b][c]; if( label > 1 ) { comp_table[label - 1] = 2; nvox++; } } } } if( !nvox ) // find new basin! { for( ct = 1; comp_table[ct] && ct < MAX_COMP; ct++ ) { ; } (*nbh)[i][j][k] = ct + 1; // label the new basin comp_table[ct] = 1; // note that this number is taken } else { min_val = MAX_COMP + 1; // merging into the smallest value for( ct = 1; ct < MAX_COMP; ct++ ) { if( comp_table[ct] == 2 ) { min_val = ct; break; } } (*nbh)[i][j][k] = min_val + 1; comp_table[min_val] = 1; // merging of the other neighboring values into the smallest one for( ct = min_val + 1; ct < MAX_COMP; ct++ ) { if( comp_table[ct] == 2 ) { for( x = 0; x < 3; x++ ) { for( y = 0; y < 3; y++ ) { for( z = 0; z < 3; z++ ) { if( (*nbh)[x][y][z] == ct + 1 ) { (*nbh)[x][y][z] = min_val + 1; } } } } // specify that this basin nbr // doesn't exist anymore comp_table[ct] = 0; } } } } } } } for( nvox = 0, ct = 1; ct < MAX_COMP; ct++ ) { if( comp_table[ct] ) { nvox++; } } if( !nbh_dst ) { free(nbh); } return nvox; } /*This function checks if a point is simple or not NBH is a binary object (0-1) connectivity should be equal to 1,2,3, or 4 according to the convention above*/ int checkSimple(NBH *nbh, int connectivity) { NBH bnbh, dnbh; reverseNBH(nbh, &bnbh); if( checkTn(nbh, &dnbh, connectivity) == 1 && checkTn(&bnbh, &dnbh, associatedConnectivity(connectivity) ) == 1 ) { return 1; } else { return 0; } } #endif ants-2.2.0/Tensor/000077500000000000000000000000001311104306400137235ustar00rootroot00000000000000ants-2.2.0/Tensor/TensorFunctions.h000066400000000000000000000524231311104306400172450ustar00rootroot00000000000000#ifndef _TensorFunctions_cxx_ #define _TensorFunctions_cxx_ // #include "itkSmallStrainDiffusionTensorReorientationImageFilter.h" // #include "itkVectorIndexSelectionCastImageFilter.h" #include "antsUtilities.h" #include "itkSymmetricSecondRankTensor.h" #include "itkVector.h" #include "itkVersor.h" #include "itkVariableSizeMatrix.h" #include "itkDecomposeTensorFunction2.h" #include "itkRotationMatrixFromVectors.h" #include "vnl/algo/vnl_matrix_inverse.h" #include "vnl/algo/vnl_symmetric_eigensystem.h" #include "itkMatrix.h" #include "itkVariableSizeMatrix.h" namespace matHelper { template unsigned int Rows(const vnl_matrix_fixed &) { return dim; } template unsigned int Columns(const vnl_matrix_fixed &) { return dim; } template unsigned int Rows(const itk::Matrix &) { return rows; } template unsigned int Columns(const itk::Matrix &) { return columns; } template unsigned int Rows(const itk::VariableSizeMatrix & mat) { return mat.Rows(); } template unsigned int Columns(const itk::VariableSizeMatrix & mat) { return mat.Cols(); } } template void Vector2Matrix( TensorType & dtv, MatrixType & dtm ) { // dtm(0, 0) = dtv[0]; // dtm(0, 1) = dtm(1, 0) = dtv[1]; // dtm(0, 2) = dtm(2, 0) = dtv[2]; // dtm(1, 1) = dtv[3]; // dtm(1, 2) = dtm(2, 1) = dtv[4]; // dtm(2, 2) = dtv[5]; unsigned int tensorIndex = 0; for( unsigned i = 0; i < matHelper::Rows(dtm); ++i ) { for( unsigned j = i; j < matHelper::Columns(dtm); ++j, ++tensorIndex ) { dtm(i, j) = dtm(j, i) = dtv[tensorIndex]; } } } template MatrixType Vector2Matrix( TensorType dtv ) { MatrixType dtm(3, 3); Vector2Matrix(dtv, dtm); return dtm; } template TensorType Matrix2Vector( MatrixType dtm ) { TensorType dtv; // dtv[0] = dtm(0, 0); // dtv[1] = dtm(0, 1); // dtv[2] = dtm(0, 2); // dtv[3] = dtm(1, 1); // dtv[4] = dtm(1, 2); // dtv[5] = dtm(2, 2); unsigned int tensorIndex = 0; for( unsigned i = 0; i < dtm.rows(); ++i ) { for( unsigned j = i; j < dtm.cols(); ++j, ++tensorIndex ) { dtv[tensorIndex] = dtm(i, j); } } return dtv; } template void EigenAnalysis(TensorType dtv, MatrixType & evals, MatrixType & evecs) { MatrixType dtm = Vector2Matrix(dtv); itk::DecomposeTensorFunction2 decomposer; decomposer.EvaluateSymmetricEigenDecomposition( dtm, evals, evecs ); } template float DiffusionCoefficient( TensorType dtv, VectorType direction, bool normalized = false) { vnl_matrix tensor(3, 3); tensor[0][0] = dtv[0]; tensor[1][0] = tensor[0][1] = dtv[1]; tensor[2][0] = tensor[0][2] = dtv[2]; tensor[1][1] = dtv[3]; tensor[1][2] = tensor[2][1] = dtv[4]; tensor[2][2] = dtv[5]; vnl_matrix tangentmat(3, 1); tangentmat(0, 0) = direction[0]; tangentmat(1, 0) = direction[1]; tangentmat(2, 0) = direction[2]; vnl_matrix fddmat = tangentmat.transpose() * tensor * tangentmat; float fdd = (float) fddmat(0, 0); if( normalized > 0 ) { fdd = fdd / (tensor[0][0] + tensor[1][1] + tensor[2][2]); } return fdd; } namespace tensorHelper { template unsigned int size(const itk::SymmetricSecondRankTensor &) { return NDim; } template unsigned int size(const itk::Vector &) { return NDim; } } template TensorType TensorLogAndExp( TensorType dtv, bool takelog, bool & success) { float eps = 1.e-12, mag = 0; for( unsigned int jj = 0; jj < tensorHelper::size(dtv); jj++ ) { float ff = dtv[jj]; mag += ff * ff; if( vnl_math_isnan( ff ) || vnl_math_isinf(ff) ) { dtv.Fill(0); // dtv[0]=eps; dtv[3]=eps; dtv[5]=eps; success = false; return dtv; } } mag = sqrt(mag); // if( dtv[1] == 0 && dtv[2] == 0 && dtv[4] == 0 ) // { // success = false; // return dtv; // } // rewrite above to avoid going beyond tensor array bounds. { unsigned int indices[3] = { 1, 2, 4 }; unsigned int jj; for( jj = 0; indices[jj] < tensorHelper::size(dtv); ++jj ) { if( dtv[indices[jj]] != 0.0 ) { break; } } if( jj == 3 ) { success = false; return dtv; } } if( mag < eps ) { success = false; return dtv; } // typedef vnl_matrix MatrixType; typedef itk::VariableSizeMatrix MatrixType; // MatrixType DT = Vector2Matrix(dtv); MatrixType D; MatrixType V; EigenAnalysis(dtv, D, V); double e1 = D(0, 0); double e2 = D(1, 1); double e3 = D(2, 2); // float peigeps=1.e-12; if( fabs(e3) < eps ) { // success = false; // std::cout << "-4" << std::flush; // return dtv; } MatrixType eigmat(3, 3); eigmat.Fill(0); if( takelog ) { if( e1 < 0 ) { e1 = e2; } if( e3 < 0 ) { e3 = e2; } eigmat(0, 0) = log(fabs(e1) ); eigmat(1, 1) = log(fabs(e2) ); eigmat(2, 2) = log(fabs(e3) ); } else // take exp { eigmat(0, 0) = exp(e1); eigmat(1, 1) = exp(e2); eigmat(2, 2) = exp(e3); } if( vnl_math_isnan(eigmat(0, 0) ) || vnl_math_isnan(eigmat(1, 1) ) || vnl_math_isnan(eigmat(2, 2) ) ) { dtv.Fill(0); success = false; return dtv; } typedef typename MatrixType::InternalMatrixType VnlMatrixType; VnlMatrixType DTrec = V.GetVnlMatrix() * eigmat.GetVnlMatrix() * V.GetTranspose(); TensorType dtv2 = Matrix2Vector(DTrec); return dtv2; } template TensorType TensorLog( TensorType dtv, bool success = true ) { return TensorLogAndExp( dtv, true, success ); } template TensorType TensorExp( TensorType dtv, bool /* takelog */, bool success = true) { return TensorLogAndExp( dtv, false, success ); } template bool IsRealTensor( TensorType dtv ) { bool isreal = true; for( unsigned int i = 0; i < 6; i++ ) { if( !vnl_math_isfinite( dtv[i] ) ) { isreal = false; } } return isreal; } template float GetTensorFA( TensorType dtv ) { // Check for zero diffusion (probably background) and return zero FA // if that's the case if (dtv[0] + dtv[3] + dtv[5] == 0.0f) { return 0.0f; } typedef vnl_matrix MatrixType; MatrixType DT(3, 3); DT.fill(0); DT(0, 0) = dtv[0]; DT(1, 1) = dtv[3]; DT(2, 2) = dtv[5]; DT(1, 0) = DT(0, 1) = dtv[1]; DT(2, 0) = DT(0, 2) = dtv[2]; DT(2, 1) = DT(1, 2) = dtv[4]; vnl_symmetric_eigensystem eig(DT); double e1 = (eig.D(0, 0) ); double e2 = (eig.D(1, 1) ); double e3 = (eig.D(2, 2) ); if( e1 < 0 ) { e1 = e2; } if( e3 < 0 ) { e3 = e2; } // compute variance of e's double emean = (e1 + e2 + e3) / 3.0; double numer = sqrt( (e1 - emean) * (e1 - emean) + (e2 - emean) * (e2 - emean) + (e3 - emean) * (e3 - emean) ); double denom = sqrt(e1 * e1 + e2 * e2 + e3 * e3); double fa = sqrt(3.0 / 2.0) * numer / denom; return fa; } template float GetTensorFANumerator( TensorType dtv ) { typedef vnl_matrix MatrixType; MatrixType DT(3, 3); DT.fill(0); DT(0, 0) = dtv[0]; DT(1, 1) = dtv[3]; DT(2, 2) = dtv[5]; DT(1, 0) = DT(0, 1) = dtv[1]; DT(2, 0) = DT(0, 2) = dtv[2]; DT(2, 1) = DT(1, 2) = dtv[4]; vnl_symmetric_eigensystem eig(DT); double e1 = (eig.D(0, 0) ); double e2 = (eig.D(1, 1) ); double e3 = (eig.D(2, 2) ); if( e1 < 0 ) { e1 = e2; } if( e3 < 0 ) { e3 = e2; } // compute variance of e's double emean = (e1 + e2 + e3) / 3.0; float numer = sqrt( (e1 - emean) * (e1 - emean) + (e2 - emean) * (e2 - emean) + (e3 - emean) * (e3 - emean) ); return numer; } template float GetTensorFADenominator( TensorType dtv ) { typedef vnl_matrix MatrixType; MatrixType DT(3, 3); DT.fill(0); DT(0, 0) = dtv[0]; DT(1, 1) = dtv[3]; DT(2, 2) = dtv[5]; DT(1, 0) = DT(0, 1) = dtv[1]; DT(2, 0) = DT(0, 2) = dtv[2]; DT(2, 1) = DT(1, 2) = dtv[4]; vnl_symmetric_eigensystem eig(DT); double e1 = (eig.D(0, 0) ); double e2 = (eig.D(1, 1) ); double e3 = (eig.D(2, 2) ); if( e1 < 0 ) { e1 = e2; } if( e3 < 0 ) { e3 = e2; } // compute variance of e's float denom = sqrt(e1 * e1 + e2 * e2 + e3 * e3); return denom; } template float GetMetricTensorCost( TVectorType dpath, TTensorType dtv, unsigned int matrixpower) { typedef vnl_matrix MatrixType; MatrixType DT(3, 3); DT.fill(0); DT(0, 0) = dtv[0]; DT(1, 1) = dtv[3]; DT(2, 2) = dtv[5]; DT(1, 0) = DT(0, 1) = dtv[1]; DT(2, 0) = DT(0, 2) = dtv[2]; DT(2, 1) = DT(1, 2) = dtv[4]; vnl_symmetric_eigensystem eig(DT); double e1 = (eig.D(0, 0) ); double e2 = (eig.D(1, 1) ); double e3 = (eig.D(2, 2) ); double etot = e1 + e2 + e3; if( etot == 0 ) { etot = 1; } MatrixType vec(3, 1); vec(0, 0) = dpath[0]; vec(1, 0) = dpath[1]; vec(2, 0) = dpath[2]; MatrixType inv = vnl_matrix_inverse(DT); for( unsigned int lo = 1; lo < matrixpower; lo++ ) { inv = inv * inv; } MatrixType sol = vec.transpose() * inv * vec; float cost = sol(0, 0); // /etot; return sqrt(cost); } template TVectorType ChangeTensorByVector( TVectorType dpath, TTensorType dtv, float epsilon) { typedef vnl_matrix MatrixType; MatrixType DT(3, 3); DT.fill(0); DT(0, 0) = dtv[0]; DT(1, 1) = dtv[3]; DT(2, 2) = dtv[5]; DT(1, 0) = DT(0, 1) = dtv[1]; DT(2, 0) = DT(0, 2) = dtv[2]; DT(2, 1) = DT(1, 2) = dtv[4]; vnl_symmetric_eigensystem eig(DT); double e3 = (eig.D(0, 0) ); double e2 = (eig.D(1, 1) ); double e1 = (eig.D(2, 2) ); double etot = e1 + e2 + e3; if( etot == 0 ) { etot = 1; } MatrixType vec(3, 1); vec(0, 0) = dpath[0]; vec(1, 0) = dpath[1]; vec(2, 0) = dpath[2]; MatrixType evec1(3, 1); // biggest evec1(0, 0) = eig.V(0, 2); evec1(1, 0) = eig.V(1, 2); evec1(2, 0) = eig.V(2, 2); MatrixType evec2(3, 1); // middle evec2(0, 0) = eig.V(0, 1); evec2(1, 0) = eig.V(1, 1); evec2(2, 0) = eig.V(2, 1); MatrixType evec3(3, 1); // smallest evec3(0, 0) = eig.V(0, 0); evec3(1, 0) = eig.V(1, 0); evec3(2, 0) = eig.V(2, 0); float temp; temp = (vec.transpose() * evec1)(0, 0); temp = sqrt(temp * temp); e1 *= ( 1.0 - epsilon * temp); if( e1 < 1.e-11 ) { e1 = 1.e-11; } temp = (vec.transpose() * evec2)(0, 0); temp = sqrt(temp * temp); e2 *= ( 1.0 - epsilon * temp); if( e2 < 1.e-11 ) { e2 = 1.e-11; } temp = (vec.transpose() * evec3)(0, 0); temp = sqrt(temp * temp); e3 *= ( 1.0 - epsilon * temp); if( e3 < 1.e-11 ) { e3 = 1.e-11; } DT = (evec3 * evec3.transpose() ) * e3 + (evec2 * evec2.transpose() ) * e2 + (evec1 * evec1.transpose() ) * e1; itk::Vector newtens; newtens[0] = DT(0, 0); newtens[3] = DT(1, 1); newtens[5] = DT(2, 2); newtens[1] = DT(0, 1); newtens[2] = DT(0, 2); newtens[4] = DT(2, 1); return newtens; } template float GetTensorADC( TTensorType dtv, unsigned int opt = 0) { if( opt <= 1 ) { return ( dtv[0] + dtv[3] + dtv[5] ) / 3.0; } float eps = 1.e-9, mag = 0; for( unsigned int jj = 0; jj < 6; jj++ ) { float ff = dtv[jj]; mag += ff * ff; if( vnl_math_isnan( ff ) || vnl_math_isinf(ff) ) { return 0; } } mag = sqrt(mag); if( dtv[1] == 0 && dtv[2] == 0 && dtv[4] == 0 ) { return 0; } if( mag < eps ) { return 0; } itk::Vector dtv2; typedef vnl_matrix MatrixType; MatrixType DT(3, 3); DT.fill(0); DT(0, 0) = dtv[0]; DT(1, 1) = dtv[3]; DT(2, 2) = dtv[5]; DT(1, 0) = DT(0, 1) = dtv[1]; DT(2, 0) = DT(0, 2) = dtv[2]; DT(2, 1) = DT(1, 2) = dtv[4]; // if (takelog )std::cout << " TAKING LOG " << std::endl; elsestd::cout << "TAKING EXP " << std::endl; // std::cout << " dtv " << dtv << std::endl; vnl_symmetric_eigensystem eig(DT); double e1 = (eig.D(0, 0) ); double e2 = (eig.D(1, 1) ); double e3 = (eig.D(2, 2) ); /* opt return 1 ADC 2 radial diffusion 3 e1 4 e2 5 e3 */ if( opt <= 1 ) { return (e1 + e2 + e3) / 3.0; } else if( opt == 2 ) { return (e2 + e1) / 2.0; // radial diffusion } else if( opt == 3 ) // e1 { return e1; } else if( opt == 4 ) // e2 { return e2; } else if( opt == 5 ) // e3 { return e3; } else { return (e1 + e2 + e3) / 3.0; } } template itk::RGBPixel GetTensorRGB( TTensorType dtv ) { typedef TTensorType TensorType; itk::RGBPixel zero; zero.Fill(0); float eps = 1.e-9, mag = 0; for( unsigned int jj = 0; jj < 6; jj++ ) { float ff = dtv[jj]; mag += ff * ff; if( vnl_math_isnan( ff ) || vnl_math_isinf(ff) ) { return zero; } } mag = sqrt(mag); itk::RGBPixel rgb; if( dtv[1] == 0 && dtv[2] == 0 && dtv[4] == 0 ) { return zero; } if( mag < eps ) { return zero; } typedef itk::VariableSizeMatrix EigenMatrixType; EigenMatrixType evals(3, 3); EigenMatrixType evecs(3, 3); EigenAnalysis(dtv, evals, evecs); float fa = GetTensorFA(dtv); rgb[0] = (unsigned char)(std::fabs(evecs(0, 2) ) * fa * 255); rgb[1] = (unsigned char)(std::fabs(evecs(1, 2) ) * fa * 255); rgb[2] = (unsigned char)(std::fabs(evecs(2, 2) ) * fa * 255); return rgb; } template itk::RGBPixel GetTensorPrincipalEigenvector( TTensorType dtv ) { itk::RGBPixel zero; zero.Fill(0); float eps = 1.e-9, mag = 0; for( unsigned int jj = 0; jj < 6; jj++ ) { float ff = dtv[jj]; mag += ff * ff; if( vnl_math_isnan( ff ) || vnl_math_isinf(ff) ) { return zero; } } mag = sqrt(mag); if( dtv[1] == 0 && dtv[2] == 0 && dtv[4] == 0 ) { return zero; } if( mag < eps ) { return zero; } itk::Vector dtv2; typedef vnl_matrix MatrixType; MatrixType DT(3, 3); DT.fill(0); DT(0, 0) = dtv[0]; DT(1, 1) = dtv[3]; DT(2, 2) = dtv[5]; DT(1, 0) = DT(0, 1) = dtv[1]; DT(2, 0) = DT(0, 2) = dtv[2]; DT(2, 1) = DT(1, 2) = dtv[4]; // if (takelog )std::cout << " TAKING LOG " << std::endl; elsestd::cout << "TAKING EXP " << std::endl; // std::cout << " dtv " << dtv << std::endl; vnl_symmetric_eigensystem eig(DT); itk::RGBPixel rgb; float xx = dtv[0]; float xy = dtv[1]; float xz = dtv[2]; float yy = dtv[3]; float yz = dtv[4]; float zz = dtv[5]; float isp = ( xx * xx + yy * yy + zz * zz + 2.0 * (xy * xy + xz * xz + yz * yz) ); float fa = 0.0; if( isp > 0.0 ) { float trace = dtv[0]; trace += dtv[3]; trace += dtv[5]; float anisotropy = 3.0 * isp - trace * trace; fa = ( std::sqrt(anisotropy / ( 2.0 * isp ) ) ); } // rgb[0]=eig.V(2,0)*fa*255;//+eig.V(1,0)*e2; // rgb[1]=eig.V(2,1)*fa*255;//+eig.V(1,1)*e2; // rgb[2]=eig.V(2,2)*fa*255;//+eig.V(1,2)*e2; // biggest evec rgb[0] = eig.V(0, 2); // +eig.V(1,0)*e2; rgb[1] = eig.V(1, 2); // +eig.V(1,1)*e2; rgb[2] = eig.V(2, 2); // +eig.V(1,2)*e2; return rgb; mag = rgb[0] * rgb[0] + rgb[1] * rgb[1] + rgb[2] * rgb[2]; mag = sqrt(mag); rgb[0] = rgb[0] / mag; rgb[1] = rgb[1] / mag; rgb[2] = rgb[2] / mag; return rgb; } template itk::Vector GetTensorPrincipalEigenvector( TTensorType dtv, unsigned int whichvec) { itk::Vector zero; zero.Fill(0); float eps = 1.e-9, mag = 0; for( unsigned int jj = 0; jj < 6; jj++ ) { float ff = dtv[jj]; mag += ff * ff; if( vnl_math_isnan( ff ) || vnl_math_isinf(ff) ) { return zero; } } mag = sqrt(mag); if( dtv[1] == 0 && dtv[2] == 0 && dtv[4] == 0 ) { return zero; } if( mag < eps ) { return zero; } itk::Vector dtv2; typedef vnl_matrix MatrixType; MatrixType DT(3, 3); DT.fill(0); DT(0, 0) = dtv[0]; DT(1, 1) = dtv[3]; DT(2, 2) = dtv[5]; DT(1, 0) = DT(0, 1) = dtv[1]; DT(2, 0) = DT(0, 2) = dtv[2]; DT(2, 1) = DT(1, 2) = dtv[4]; // if (takelog )std::cout << " TAKING LOG " << std::endl; else std::cout << "TAKING EXP " << std::endl; // std::cout << " dtv " << dtv << std::endl; vnl_symmetric_eigensystem eig(DT); itk::Vector rgb; float xx = dtv[0]; float xy = dtv[1]; float xz = dtv[2]; float yy = dtv[3]; float yz = dtv[4]; float zz = dtv[5]; float isp = ( xx * xx + yy * yy + zz * zz + 2.0 * (xy * xy + xz * xz + yz * yz) ); if( isp > 0.0 ) { float trace = dtv[0]; trace += dtv[3]; trace += dtv[5]; } rgb[0] = eig.V(0, whichvec); // +eig.V(1,0)*e2; rgb[1] = eig.V(1, whichvec); // +eig.V(1,1)*e2; rgb[2] = eig.V(2, whichvec); // +eig.V(1,2)*e2; return rgb; } template static float GetMetricTensorCost( itk::Vector dpath, TTensorType dtv ) { typedef vnl_matrix MatrixType; MatrixType DT(3, 3); DT.fill(0); DT(0, 0) = dtv[0]; DT(1, 1) = dtv[3]; DT(2, 2) = dtv[5]; DT(1, 0) = DT(0, 1) = dtv[1]; DT(2, 0) = DT(0, 2) = dtv[2]; DT(2, 1) = DT(1, 2) = dtv[4]; vnl_symmetric_eigensystem eig(DT); double e1 = (eig.D(0, 0) ); double e2 = (eig.D(1, 1) ); double e3 = (eig.D(2, 2) ); double etot = e1 + e2 + e3; if( etot == 0 ) { etot = 1; } MatrixType vec(3, 1); vec(0, 0) = dpath[0]; vec(1, 0) = dpath[1]; vec(2, 0) = dpath[2]; MatrixType inv = vnl_matrix_inverse(DT); MatrixType sol = vec.transpose() * inv * vec; float cost = sol(0, 0) / etot; return cost; } template VersorTensorType VersorTensor( TensorType dtv ) { typedef itk::VariableSizeMatrix EigenMatrixType; typedef itk::Vector VectorType; EigenMatrixType D; EigenMatrixType V; EigenAnalysis(dtv, D, V); VectorType e3; e3[0] = V(0, 2); e3[1] = V(1, 2); e3[2] = V(2, 2); VectorType e2; e2[0] = V(0, 1); e2[1] = V(1, 1); e2[2] = V(2, 1); VectorType xAxis; xAxis[0] = 1; xAxis[1] = 0; xAxis[2] = 0; VectorType yAxis; yAxis[0] = 0; yAxis[1] = 1; yAxis[2] = 0; MatrixType R1 = RotationMatrixFromVectors(e3, xAxis); e2 = R1 * e2; MatrixType R2 = RotationMatrixFromVectors(e2, yAxis); MatrixType R = R2 * R1; itk::Versor versor; versor.SetMatrix( R ); VersorTensorType dtq; dtq[0] = D(0, 0); dtq[1] = D(1, 1); dtq[2] = D(2, 2); dtq[3] = versor[0]; dtq[4] = versor[1]; dtq[5] = versor[2]; return dtq; } template VersorTensorType VersorTensor( TensorType dtv, MatrixType frame ) { typedef itk::VariableSizeMatrix EigenMatrixType; typedef itk::Vector VectorType; EigenMatrixType D; EigenMatrixType V; EigenAnalysis(dtv, D, V); VectorType e3; e3[0] = V(0, 2); e3[1] = V(1, 2); e3[2] = V(2, 2); VectorType e2; e2[0] = V(0, 1); e2[1] = V(1, 1); e2[2] = V(2, 1); VectorType xAxis; xAxis[0] = frame(0, 0); xAxis[1] = frame(1, 0); xAxis[2] = frame(2, 0); VectorType yAxis; yAxis[0] = frame(0, 1); yAxis[1] = frame(1, 1); yAxis[2] = frame(2, 1); MatrixType R1 = RotationMatrixFromVectors(e3, xAxis); e2 = R1 * e2; MatrixType R2 = RotationMatrixFromVectors(e2, yAxis); MatrixType R = R2 * R1; itk::Versor versor; versor.SetMatrix( R ); VersorTensorType dtq; dtq[0] = D(0, 0); dtq[1] = D(1, 1); dtq[2] = D(2, 2); dtq[3] = versor[0]; dtq[4] = versor[1]; dtq[5] = versor[2]; return dtq; } #endif ants-2.2.0/Tensor/itkDecomposeTensorFunction2.h000066400000000000000000000052221311104306400215060ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkDecomposeTensorFunction2_h #define __itkDecomposeTensorFunction2_h #include "itkVariableSizeMatrix.h" namespace itk { /** \class DecomposeTensorFunction2 * */ template > class DecomposeTensorFunction2 { public: /** Standard class typedefs. */ typedef DecomposeTensorFunction2 Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Extract some information from the image types. Dimensionality * of the two images is assumed to be the same. */ typedef TInput InputMatrixType; typedef TOutput OutputMatrixType; /** Define the data type and the vector of data type used in calculations. */ typedef TRealType RealType; // Wrappers for vnl routines void EvaluateEigenDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateSymmetricEigenDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateQRDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateSVDDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateSVDEconomyDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateLeftPolarDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateRightPolarDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateCholeskyDecomposition( InputMatrixType &, OutputMatrixType & ); RealType EvaluateDeterminant( InputMatrixType & ); DecomposeTensorFunction2(); virtual ~DecomposeTensorFunction2() { } protected: void PrintSelf( std::ostream& os, Indent indent ) const; private: DecomposeTensorFunction2(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkDecomposeTensorFunction2.hxx" #endif #endif ants-2.2.0/Tensor/itkDecomposeTensorFunction2.hxx000066400000000000000000000236241311104306400220740ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkDecomposeTensorFunction2_hxx #define _itkDecomposeTensorFunction2_hxx #include "itkDecomposeTensorFunction2.h" #include "vnl/algo/vnl_cholesky.h" #include "vnl/algo/vnl_qr.h" #include "vnl/algo/vnl_real_eigensystem.h" #include "vnl/algo/vnl_svd.h" #include "vnl/algo/vnl_svd_economy.h" #include "vnl/algo/vnl_symmetric_eigensystem.h" #include "vnl/vnl_matrix.h" #include "vnl/vnl_matrix_fixed.h" #include "vnl/vnl_det.h" namespace itk { template DecomposeTensorFunction2 ::DecomposeTensorFunction2() { } template void DecomposeTensorFunction2 ::EvaluateEigenDecomposition( InputMatrixType & M, OutputMatrixType & D, OutputMatrixType & V ) { unsigned int RowDimensions = M.Rows(); unsigned int ColumnDimensions = M.Cols(); D.SetSize( RowDimensions, RowDimensions ); V.SetSize( RowDimensions, RowDimensions ); D.Fill( 0.0 ); vnl_matrix v( RowDimensions, ColumnDimensions ); for( unsigned int j = 0; j < ColumnDimensions; j++ ) { for( unsigned int i = 0; i < RowDimensions; i++ ) { v.put( i, j, M[i][j] ); } } vnl_real_eigensystem eig( v ); for( unsigned int j = 0; j < ColumnDimensions; j++ ) { for( unsigned int i = 0; i < RowDimensions; i++ ) { V[i][j] = static_cast( (eig.Vreal).get( i, j ) ); if( i == j ) { D[i][j] = static_cast( std::real( eig.D(j) ) ); } } } } template void DecomposeTensorFunction2 ::EvaluateSymmetricEigenDecomposition( InputMatrixType & M, OutputMatrixType & D, OutputMatrixType & V ) { // The resulting eigenvectors and values are sorted in increasing order // so V.column(0) is the eigenvector corresponding to the // smallest eigenvalue. unsigned int RowDimensions = M.Rows(); unsigned int ColumnDimensions = M.Cols(); D.SetSize( RowDimensions, RowDimensions ); V.SetSize( RowDimensions, RowDimensions ); D.Fill( 0.0 ); vnl_symmetric_eigensystem eig( M.GetVnlMatrix() ); for( unsigned int j = 0; j < ColumnDimensions; j++ ) { for( unsigned int i = 0; i < RowDimensions; i++ ) { V[i][j] = (eig.V).get( i, j ); if( i == j ) { D[i][j] = eig.D(j); } } } } template void DecomposeTensorFunction2 ::EvaluateRightPolarDecomposition( InputMatrixType & M, OutputMatrixType & R, OutputMatrixType & S ) { OutputMatrixType U; OutputMatrixType W; OutputMatrixType V; this->EvaluateSVDDecomposition( M, U, W, V ); R = U * V.GetTranspose(); S = V * W * V.GetTranspose(); } template void DecomposeTensorFunction2 ::EvaluateLeftPolarDecomposition( InputMatrixType & M, OutputMatrixType & S, OutputMatrixType & R ) { OutputMatrixType U; OutputMatrixType W; OutputMatrixType V; this->EvaluateSVDDecomposition( M, U, W, V ); R = U * V.GetTranspose(); S = U * W * U.GetTranspose(); } template void DecomposeTensorFunction2 ::EvaluateQRDecomposition( InputMatrixType & M, OutputMatrixType & Q, OutputMatrixType & R ) { unsigned int RowDimensions = M.Rows(); unsigned int ColumnDimensions = M.Cols(); Q.SetSize( RowDimensions, ColumnDimensions ); R.SetSize( ColumnDimensions, ColumnDimensions ); vnl_qr qr( M.GetVnlMatrix() ); for( unsigned int i = 0; i < RowDimensions; i++ ) { for( unsigned int j = 0; j < ColumnDimensions; j++ ) { Q[i][j] = qr.Q() (i, j); } } for( unsigned int i = 0; i < ColumnDimensions; i++ ) { for( unsigned int j = 0; j < ColumnDimensions; j++ ) { R[i][j] = qr.R() (i, j); } } } template void DecomposeTensorFunction2 ::EvaluateSVDDecomposition( InputMatrixType & M, OutputMatrixType & U, OutputMatrixType & W, OutputMatrixType & V ) { unsigned int RowDimensions = M.Rows(); unsigned int ColumnDimensions = M.Cols(); U.SetSize( RowDimensions, RowDimensions ); V.SetSize( ColumnDimensions, ColumnDimensions ); W.SetSize( RowDimensions, ColumnDimensions ); bool isidentity = true; float tol = 1.e-12; for( unsigned int i = 0; i < RowDimensions; i++ ) { for( unsigned int j = 0; j < RowDimensions; j++ ) { if( i == j ) { if( fabs(M[i][j] - 1.0) > tol ) { isidentity = false; } } else { if( fabs(M[i][j]) > tol ) { isidentity = false; } } } } if( isidentity ) { for( unsigned int i = 0; i < RowDimensions; i++ ) { for( unsigned int j = 0; j < RowDimensions; j++ ) { if( i == j ) { U[i][j] = 1.0; V[i][j] = 1.0; W[i][i] = 1; } else { U[i][j] = 0.0; V[i][j] = 0.0; } } } return; } vnl_svd svd( M.GetVnlMatrix() ); for( unsigned int i = 0; i < RowDimensions; i++ ) { for( unsigned int j = 0; j < RowDimensions; j++ ) { U[i][j] = svd.U(i, j); } } for( unsigned int i = 0; i < ColumnDimensions; i++ ) { for( unsigned int j = 0; j < ColumnDimensions; j++ ) { V[i][j] = svd.V(i, j); } } W.Fill( 0.0 ); unsigned int minDimensions = ColumnDimensions; if( static_cast( RowDimensions ) < static_cast( ColumnDimensions ) ) { minDimensions = RowDimensions; } for( unsigned int i = 0; i < minDimensions; i++ ) { W[i][i] = svd.W(i, i); } } template void DecomposeTensorFunction2 ::EvaluateSVDEconomyDecomposition( InputMatrixType & M, OutputMatrixType & W, OutputMatrixType & V ) { /** * Same as SVD except the routine does not return U --- * allows for faster computation. */ unsigned int RowDimensions = M.Rows(); unsigned int ColumnDimensions = M.Cols(); V.SetSize( ColumnDimensions, ColumnDimensions ); W.SetSize( RowDimensions, ColumnDimensions ); vnl_svd_economy svd( M.GetVnlMatrix() ); for( unsigned int i = 0; i < ColumnDimensions; i++ ) { for( unsigned int j = 0; j < ColumnDimensions; j++ ) { V[i][j] = svd.V() (i, j); } } W.Fill( 0.0 ); unsigned int minDimensions = ColumnDimensions; if( static_cast( RowDimensions ) < static_cast( ColumnDimensions ) ) { minDimensions = RowDimensions; } for( unsigned int i = 0; i < minDimensions; i++ ) { W[i][i] = svd.lambdas()[i]; } } template void DecomposeTensorFunction2 ::EvaluateCholeskyDecomposition( InputMatrixType & M, OutputMatrixType & L ) { // Assumes symmetric tensor of type double vnl_matrix m( M.Rows(), M.Cols() ); for( unsigned int j = 0; j < M.Cols(); j++ ) { for( unsigned int i = 0; i < M.Rows(); i++ ) { m.put( i, j, M[i][j] ); } } vnl_cholesky cholesky( m, vnl_cholesky::quiet ); L.SetSize( M.Rows(), M.Cols() ); for( unsigned int i = 0; i < L.Rows(); i++ ) { for( unsigned int j = 0; j < L.Cols(); j++ ) { L[i][j] = cholesky.L_badly_named_method().get(i, j); } } } template typename DecomposeTensorFunction2::RealType DecomposeTensorFunction2 ::EvaluateDeterminant( InputMatrixType & M ) { if( M.Rows() == M.Cols() && M.Rows() >= 2 && M.Rows() <= 4 ) { vnl_matrix_fixed m2; vnl_matrix_fixed m3; vnl_matrix_fixed m4; for( unsigned int i = 0; i < M.Rows(); i++ ) { for( unsigned int j = 0; j < M.Cols(); j++ ) { switch( M.Rows() ) { case 2: { m2.put( i, j, M[i][j] ); } break; case 3: { m3.put( i, j, M[i][j] ); } break; case 4: { m4.put( i, j, M[i][j] ); } break; } } } switch( M.Rows() ) { case 2: { return static_cast( vnl_det( m2 ) ); } break; case 3: { return static_cast( vnl_det( m3 ) ); } break; case 4: { return static_cast( vnl_det( m4 ) ); } break; } } else { vnl_qr qr( M.GetVnlMatrix() ); return static_cast( qr.determinant() ); } } template void DecomposeTensorFunction2 ::PrintSelf( std::ostream & /* os */, Indent /* indent */ ) const { } } // end namespace itk #endif ants-2.2.0/Tensor/itkExpTensorImageFilter.h000066400000000000000000000107111311104306400206440ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkExpTensorImageFilter_h #define __itkExpTensorImageFilter_h #include "itkImageToImageFilter.h" #include "itkImage.h" #include "itkNumericTraits.h" #include "itkDiffusionTensor3D.h" namespace itk { /** \class ExpTensorImageFilter * \brief Applies an averaging filter to an image * * Computes an image where a given pixel is the mean value of the * the pixels in a neighborhood about the corresponding input pixel. * * A mean filter is one of the family of linear filters. * * \sa Image * \sa Neighborhood * \sa NeighborhoodOperator * \sa NeighborhoodIterator * * \ingroup IntensityImageFilters */ template class ExpTensorImageFilter : public ImageToImageFilter { public: /** Extract dimension from input and output image. */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); /** Convenient typedefs for simplifying declarations. */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; /** Standard class typedefs. */ typedef ExpTensorImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(ExpTensorImageFilter, ImageToImageFilter); /** Image typedef support. */ typedef typename InputImageType::ConstPointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename InputImageType::PixelType InputPixelType; typedef typename OutputImageType::PixelType OutputPixelType; typedef typename InputPixelType::ValueType InputRealType; typedef typename OutputPixelType::ValueType OutputRealType; // typedef typename DiffusionTensor3D TensorType; // typedef typename TensorType::EigenValuesArrayType EigenValuesArrayType; // typedef typename TensorType::EigenVectorsMatrixType EigenVectorsMatrixType; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::RegionType OutputImageRegionType; typedef typename InputImageType::SizeType InputSizeType; typedef typename OutputImageType::SizeType OutputSizeType; typedef typename InputImageType::IndexType InputIndexType; typedef typename OutputImageType::IndexType OutputIndexType; /** Set the radius of the neighborhood used to compute the mean. */ // itkSetMacro(Radius, InputSizeType); /** Get the radius of the neighborhood used to compute the mean */ // itkGetConstReferenceMacro(Radius, InputSizeType); protected: ExpTensorImageFilter(); virtual ~ExpTensorImageFilter() { } void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; /** ExpTensorImageFilter can be implemented as a multithreaded filter. * Therefore, this implementation provides a ThreadedGenerateData() * routine which is called for each processing thread. The output * image data is allocated automatically by the superclass prior to * calling ThreadedGenerateData(). ThreadedGenerateData can only * write to the portion of the output image specified by the * parameter "outputRegionForThread" * * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ void GenerateData() ITK_OVERRIDE; private: ExpTensorImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkExpTensorImageFilter.hxx" #endif #endif ants-2.2.0/Tensor/itkExpTensorImageFilter.hxx000066400000000000000000000043241311104306400212270ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkExpTensorImageFilter_hxx #define _itkExpTensorImageFilter_hxx #include "itkConstNeighborhoodIterator.h" #include "itkNeighborhoodInnerProduct.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIterator.h" #include "itkOffset.h" #include "itkProgressReporter.h" #include "itkObjectFactory.h" #include "vnl/vnl_matrix.h" #include "vnl/algo/vnl_symmetric_eigensystem.h" #include "itkExpTensorImageFilter.h" #include "TensorFunctions.h" namespace itk { template ExpTensorImageFilter ::ExpTensorImageFilter() { } template void ExpTensorImageFilter ::GenerateData() { InputImagePointer input = this->GetInput(); OutputImagePointer output = this->GetOutput(); ImageRegionConstIterator inputIt( input, input->GetLargestPossibleRegion() ); output->SetRegions( input->GetLargestPossibleRegion() ); output->Allocate(); ImageRegionIterator outputIt( output, output->GetLargestPossibleRegion() ); for( inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !outputIt.IsAtEnd(); ++inputIt, ++outputIt ) { bool success; // TODO -- actually check the result? InputPixelType result = TensorLogAndExp(inputIt.Value(), false, success); outputIt.Set( result ); } } /** * Standard "PrintSelf" method */ template void ExpTensorImageFilter ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); } } // end namespace itk #endif ants-2.2.0/Tensor/itkLogTensorImageFilter.h000066400000000000000000000107041311104306400206330ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkLogTensorImageFilter_h #define __itkLogTensorImageFilter_h #include "itkImageToImageFilter.h" #include "itkImage.h" #include "itkNumericTraits.h" #include "TensorFunctions.h" namespace itk { /** \class LogTensorImageFilter * \brief Applies an averaging filter to an image * * Computes an image where a given pixel is the mean value of the * the pixels in a neighborhood about the corresponding input pixel. * * A mean filter is one of the family of linear filters. * * \sa Image * \sa Neighborhood * \sa NeighborhoodOperator * \sa NeighborhoodIterator * * \ingroup IntensityImageFilters */ template class LogTensorImageFilter : public ImageToImageFilter { public: /** Extract dimension from input and output image. */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); /** Convenient typedefs for simplifying declarations. */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; /** Standard class typedefs. */ typedef LogTensorImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(LogTensorImageFilter, ImageToImageFilter); /** Image typedef support. */ typedef typename InputImageType::ConstPointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename InputImageType::PixelType InputPixelType; typedef typename OutputImageType::PixelType OutputPixelType; typedef typename InputPixelType::ValueType InputRealType; typedef typename OutputPixelType::ValueType OutputRealType; // typedef typename DiffusionTensor3D TensorType; // typedef typename TensorType::EigenValuesArrayType EigenValuesArrayType; // typedef typename TensorType::EigenVectorsMatrixType EigenVectorsMatrixType; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::RegionType OutputImageRegionType; typedef typename InputImageType::SizeType InputSizeType; typedef typename OutputImageType::SizeType OutputSizeType; typedef typename InputImageType::IndexType InputIndexType; typedef typename OutputImageType::IndexType OutputIndexType; /** Set the radius of the neighborhood used to compute the mean. */ // itkSetMacro(Radius, InputSizeType); /** Get the radius of the neighborhood used to compute the mean */ // itkGetConstReferenceMacro(Radius, InputSizeType); protected: LogTensorImageFilter(); virtual ~LogTensorImageFilter() { } void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; /** LogTensorImageFilter can be implemented as a multithreaded filter. * Therefore, this implementation provides a ThreadedGenerateData() * routine which is called for each processing thread. The output * image data is allocated automatically by the superclass prior to * calling ThreadedGenerateData(). ThreadedGenerateData can only * write to the portion of the output image specified by the * parameter "outputRegionForThread" * * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ void GenerateData() ITK_OVERRIDE; private: LogTensorImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkLogTensorImageFilter.hxx" #endif #endif ants-2.2.0/Tensor/itkLogTensorImageFilter.hxx000066400000000000000000000042261311104306400212150ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkLogTensorImageFilter_hxx #define _itkLogTensorImageFilter_hxx #include "itkConstNeighborhoodIterator.h" #include "itkNeighborhoodInnerProduct.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkImageRegionConstIterator.h" #include "itkOffset.h" #include "itkProgressReporter.h" #include "itkObjectFactory.h" #include "vnl/vnl_matrix.h" #include "vnl/algo/vnl_symmetric_eigensystem.h" #include "itkLogTensorImageFilter.h" #include "TensorFunctions.h" namespace itk { template LogTensorImageFilter ::LogTensorImageFilter() { } template void LogTensorImageFilter ::GenerateData() { InputImagePointer input = this->GetInput(); OutputImagePointer output = this->GetOutput(); ImageRegionConstIterator inputIt( input, input->GetLargestPossibleRegion() ); output->SetRegions( input->GetLargestPossibleRegion() ); output->Allocate(); ImageRegionIteratorWithIndex outputIt( output, output->GetLargestPossibleRegion() ); for( inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !outputIt.IsAtEnd(); ++inputIt, ++outputIt ) { InputPixelType result = TensorLog(inputIt.Value() ); outputIt.Set( result ); } } /** * Standard "PrintSelf" method */ template void LogTensorImageFilter ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); } } // end namespace itk #endif ants-2.2.0/Tensor/itkPreservationOfPrincipalDirectionTensorReorientationImageFilter.cxx000066400000000000000000000320711311104306400317020ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkPreservationOfPrincipalDirectionTensorReorientationImageFilter_cxx #define _itkPreservationOfPrincipalDirectionTensorReorientationImageFilter_cxx #include "antsAllocImage.h" #include "itkConstNeighborhoodIterator.h" #include "itkNeighborhoodInnerProduct.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkImageRegionConstIterator.h" #include "itkNeighborhoodAlgorithm.h" #include "itkOffset.h" #include "itkProgressReporter.h" #include "itkObjectFactory.h" #include "itkVector.h" #include "itkPreservationOfPrincipalDirectionTensorReorientationImageFilter.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkNumericTraitsFixedArrayPixel.h" #include "itkCentralDifferenceImageFunction.h" #include "itkVariableSizeMatrix.h" #include "itkDecomposeTensorFunction.h" #include "itkSymmetricSecondRankTensor.h" #include #include #include "vnl/algo/vnl_qr.h" #include "vnl/algo/vnl_svd.h" // #include namespace itk { template PreservationOfPrincipalDirectionTensorReorientationImageFilter ::PreservationOfPrincipalDirectionTensorReorientationImageFilter() { m_DisplacementField = ITK_NULLPTR; m_DirectionTransform = ITK_NULLPTR; m_AffineTransform = ITK_NULLPTR; m_InverseAffineTransform = ITK_NULLPTR; m_UseAffine = false; m_UseImageDirection = true; } template void PreservationOfPrincipalDirectionTensorReorientationImageFilter ::DirectionCorrectTransform( AffineTransformPointer transform, AffineTransformPointer direction ) { AffineTransformPointer directionTranspose = AffineTransformType::New(); directionTranspose->SetIdentity(); typename AffineTransformType::MatrixType dirTransposeMatrix( direction->GetMatrix().GetTranspose() ); directionTranspose->SetMatrix( dirTransposeMatrix ); transform->Compose( direction, true ); transform->Compose( directionTranspose, false ); } template typename PreservationOfPrincipalDirectionTensorReorientationImageFilter::TensorType PreservationOfPrincipalDirectionTensorReorientationImageFilter ::ApplyReorientation(InverseTransformPointer deformation, TensorType tensor ) { VnlMatrixType DT(3, 3); DT.fill(0); DT(0, 0) = tensor[0]; DT(1, 1) = tensor[3]; DT(2, 2) = tensor[5]; DT(1, 0) = DT(0, 1) = tensor[1]; DT(2, 0) = DT(0, 2) = tensor[2]; DT(2, 1) = DT(1, 2) = tensor[4]; vnl_symmetric_eigensystem eig(DT); TensorType outTensor; TransformInputVectorType ev1; TransformInputVectorType ev2; TransformInputVectorType ev3; for( unsigned int i = 0; i < 3; i++ ) { ev1[i] = eig.get_eigenvector(2)[i]; ev2[i] = eig.get_eigenvector(1)[i]; ev3[i] = eig.get_eigenvector(0)[i]; } TransformOutputVectorType ev1r = deformation->TransformVector( ev1 ); ev1r.Normalize(); // Get aspect of rotated e2 that is perpendicular to rotated e1 TransformOutputVectorType ev2a = deformation->TransformVector( ev2 ); if( (ev2a * ev1r) < 0 ) { ev2a = ev2a * (-1.0); } TransformOutputVectorType ev2r = ev2a - (ev2a * ev1r) * ev1r; ev2r.Normalize(); TransformOutputVectorType ev3r = CrossProduct( ev1r, ev2r ); ev3r.Normalize(); VnlVectorType e1(3); VnlVectorType e2(3); VnlVectorType e3(3); for( unsigned int i = 0; i < 3; i++ ) { e1[i] = ev1r[i]; e2[i] = ev2r[i]; e3[i] = ev3r[i]; } VnlMatrixType DTrotated = eig.get_eigenvalue(2) * outer_product(e1, e1) + eig.get_eigenvalue(1) * outer_product(e2, e2) + eig.get_eigenvalue(0) * outer_product(e3, e3); outTensor[0] = DTrotated(0, 0); outTensor[1] = DTrotated(0, 1); outTensor[2] = DTrotated(0, 2); outTensor[3] = DTrotated(1, 1); outTensor[4] = DTrotated(1, 2); outTensor[5] = DTrotated(2, 2); return outTensor; } template typename PreservationOfPrincipalDirectionTensorReorientationImageFilter::AffineTransformPointer PreservationOfPrincipalDirectionTensorReorientationImageFilter ::GetLocalDeformation(DisplacementFieldPointer field, typename DisplacementFieldType::IndexType index) { AffineTransformPointer affineTransform = AffineTransformType::New(); affineTransform->SetIdentity(); typename AffineTransformType::MatrixType jMatrix; jMatrix.Fill(0.0); typename DisplacementFieldType::SizeType size = field->GetLargestPossibleRegion().GetSize(); typename DisplacementFieldType::SpacingType spacing = field->GetSpacing(); typename DisplacementFieldType::IndexType ddrindex; typename DisplacementFieldType::IndexType ddlindex; typename DisplacementFieldType::IndexType difIndex[ImageDimension][2]; unsigned int posoff = 1; RealType space = 1.0; RealType mindist = 1.0; RealType dist = 100.0; bool oktosample = true; for( unsigned int row = 0; row < ImageDimension; row++ ) { dist = fabs( (RealType)index[row]); if( dist < mindist ) { oktosample = false; } dist = fabs( (RealType)size[row] - (RealType)index[row]); if( dist < mindist ) { oktosample = false; } } if( oktosample ) { typename DisplacementFieldType::PixelType cpix = m_DisplacementField->GetPixel(index); cpix = this->TransformVectorByDirection(cpix); // itkCentralDifferenceImageFunction does not support vector images so do this manually here for( unsigned int row = 0; row < ImageDimension; row++ ) { difIndex[row][0] = index; difIndex[row][1] = index; ddrindex = index; ddlindex = index; if( (int) index[row] < (int)(size[row] - 2) ) { difIndex[row][0][row] = index[row] + posoff; ddrindex[row] = index[row] + posoff * 2; } if( index[row] > 1 ) { difIndex[row][1][row] = index[row] - 1; ddlindex[row] = index[row] - 2; } RealType h = 1; space = 1.0; // should use image spacing here? typename DisplacementFieldType::PixelType rpix = m_DisplacementField->GetPixel( difIndex[row][1] ); typename DisplacementFieldType::PixelType lpix = m_DisplacementField->GetPixel( difIndex[row][0] ); typename DisplacementFieldType::PixelType rrpix = m_DisplacementField->GetPixel( ddrindex ); typename DisplacementFieldType::PixelType llpix = m_DisplacementField->GetPixel( ddlindex ); if( this->m_UseImageDirection ) { rpix = this->TransformVectorByDirection(rpix); lpix = this->TransformVectorByDirection(lpix); rrpix = this->TransformVectorByDirection(rrpix); llpix = this->TransformVectorByDirection(llpix); } rpix = rpix * h + cpix * (1. - h); lpix = lpix * h + cpix * (1. - h); rrpix = rrpix * h + rpix * (1. - h); llpix = llpix * h + lpix * (1. - h); typename DisplacementFieldType::PixelType dPix = ( lpix * 8.0 + llpix - rrpix - rpix * 8.0 ) * space / (12.0); // 4th order centered difference // typename DisplacementFieldType::PixelType dPix=( lpix - rpix )*space/(2.0*h); //4th order centered difference for( unsigned int col = 0; col < ImageDimension; col++ ) { RealType val = dPix[col] / spacing[col]; if( row == col ) { val += 1.0; } jMatrix(col, row) = val; } } } for( unsigned int jx = 0; jx < ImageDimension; jx++ ) { for( unsigned int jy = 0; jy < ImageDimension; jy++ ) { if( !vnl_math_isfinite(jMatrix(jx, jy) ) ) { oktosample = false; } } } if( !oktosample ) { jMatrix.Fill(0.0); for( unsigned int i = 0; i < ImageDimension; i++ ) { jMatrix(i, i) = 1.0; } } affineTransform->SetMatrix( jMatrix ); // this->DirectionCorrectTransform( affineTransform, this->m_DirectionTransform ); return affineTransform; } template void PreservationOfPrincipalDirectionTensorReorientationImageFilter ::GenerateData() { // get input and output images // FIXME - use buffered region, etc InputImagePointer input = this->GetInput(); OutputImagePointer output = this->GetOutput(); this->m_DirectionTransform = AffineTransformType::New(); this->m_DirectionTransform->SetIdentity(); AffineTransformPointer directionTranspose = AffineTransformType::New(); directionTranspose->SetIdentity(); if( this->m_UseAffine ) { this->m_DirectionTransform->SetMatrix( input->GetDirection() ); if( this->m_UseImageDirection ) { this->DirectionCorrectTransform( this->m_AffineTransform, this->m_DirectionTransform ); } this->m_InverseAffineTransform = this->m_AffineTransform->GetInverseTransform(); output->SetRegions( input->GetLargestPossibleRegion() ); output->SetSpacing( input->GetSpacing() ); output->SetOrigin( input->GetOrigin() ); output->SetDirection( input->GetDirection() ); output->Allocate(); } else { // Retain input image space as that should be handled in antsApplyTransforms this->m_DirectionTransform->SetMatrix( m_DisplacementField->GetDirection() ); output->SetRegions( input->GetLargestPossibleRegion() ); output->SetSpacing( input->GetSpacing() ); output->SetOrigin( input->GetOrigin() ); output->SetDirection( input->GetDirection() ); output->Allocate(); this->m_DisplacementTransform = DisplacementFieldTransformType::New(); this->m_DisplacementTransform->SetDisplacementField( m_DisplacementField ); } ImageRegionIteratorWithIndex outputIt( output, output->GetLargestPossibleRegion() ); VariableMatrixType jMatrixAvg; jMatrixAvg.SetSize(ImageDimension, ImageDimension); jMatrixAvg.Fill(0.0); std::cout << "Iterating over image" << std::endl; // for all voxels for( outputIt.GoToBegin(); !outputIt.IsAtEnd(); ++outputIt ) { InverseTransformPointer localDeformation; // FIXME - eventually this will be callable via a generic transform base class if( this->m_UseAffine ) { localDeformation = this->m_InverseAffineTransform; } else { AffineTransformPointer deformation = this->GetLocalDeformation( this->m_DisplacementField, outputIt.GetIndex() ); localDeformation = deformation->GetInverseTransform(); } TensorType inTensor = input->GetPixel(outputIt.GetIndex() ); TensorType outTensor; // valid values? bool hasNans = false; for( unsigned int jj = 0; jj < 6; jj++ ) { if( vnl_math_isnan( inTensor[jj] ) || vnl_math_isinf( inTensor[jj]) ) { hasNans = true;; } } bool isNull = false; RealType trace = inTensor[0] + inTensor[3] + inTensor[5]; if( trace <= 0.0 ) { isNull = true; } if( hasNans || isNull ) { outTensor = inTensor; } else { // outTensor = inTensor; // InverseTransformPointer localDeformation; if( this->m_UseAffine ) { outTensor = this->m_AffineTransform->TransformDiffusionTensor3D( inTensor ); // localDeformation = this->m_InverseAffineTransform; } else { typename DisplacementFieldType::PointType pt; this->m_DisplacementField->TransformIndexToPhysicalPoint( outputIt.GetIndex(), pt ); outTensor = this->m_DisplacementTransform->TransformDiffusionTensor3D( inTensor, pt ); // AffineTransformPointer deformation = this->GetLocalDeformation( this->m_DeformationField, outputIt.GetIndex() // ); // localDeformation = deformation->GetInverseTransform(); } /* std::cout << "apply"; outTensor = this->ApplyReorientation( localDeformation, inTensor ); std::cout << " ok" << std::endl; */ } // valid values? for( unsigned int jj = 0; jj < 6; jj++ ) { if( vnl_math_isnan( outTensor[jj] ) || vnl_math_isinf( outTensor[jj]) ) { outTensor[jj] = 0; } } outputIt.Set( outTensor ); } } /** * Standard "PrintSelf" method */ template void PreservationOfPrincipalDirectionTensorReorientationImageFilter ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); } } // end namespace itk #endif ants-2.2.0/Tensor/itkPreservationOfPrincipalDirectionTensorReorientationImageFilter.h000066400000000000000000000167351311104306400313400ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkPreservationOfPrincipalDirectionTensorReorientationImageFilter_h #define __itkPreservationOfPrincipalDirectionTensorReorientationImageFilter_h #include "itkImageToImageFilter.h" #include "itkVectorInterpolateImageFunction.h" #include "itkImage.h" #include "itkMatrix.h" #include "itkNumericTraits.h" #include "itkVector.h" #include "itkSymmetricSecondRankTensor.h" #include "itkDisplacementFieldTransform.h" namespace itk { /** \class PreservationOfPrincipalDirectionImageFilter * \brief Applies an averaging filter to an image * * Computes an image where a given pixel is the mean value of the * the pixels in a neighborhood about the corresponding input pixel. * * A mean filter is one of the family of linear filters. * * \sa Image * \sa Neighborhood * \sa NeighborhoodOperator * \sa NeighborhoodIterator * * \ingroup IntensityImageFilters */ template class PreservationOfPrincipalDirectionTensorReorientationImageFilter : public ImageToImageFilter { public: typedef TTensorImage InputImageType; typedef TTensorImage OutputImageType; typedef TVectorImage DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldPointer; typedef typename DisplacementFieldType::PixelType VectorType; typedef typename VectorType::RealValueType RealType; typedef itk::DisplacementFieldTransform DisplacementFieldTransformType; typedef typename DisplacementFieldTransformType::Pointer DisplacementFieldTransformPointer; typedef Matrix MatrixType; // typedef Vector VectorType; typedef VariableSizeMatrix VariableMatrixType; itkStaticConstMacro(ImageDimension, unsigned int, TTensorImage::ImageDimension); typedef itk::Image RealTypeImageType; typedef itk::MatrixOffsetTransformBase AffineTransformType; typedef typename AffineTransformType::Pointer AffineTransformPointer; typedef typename AffineTransformType::InputVectorType TransformInputVectorType; typedef typename AffineTransformType::OutputVectorType TransformOutputVectorType; typedef typename AffineTransformType::InverseTransformBaseType InverseTransformType; typedef typename InverseTransformType::Pointer InverseTransformPointer; // typedef Vector TensorType; // typedef itk::SymmetricSecondRankTensor< RealType, 3 > TensorType; // typedef Image TensorImageType; // typedef typename TensorImageType::Pointer TensorImagePointer; typedef typename InputImageType::PixelType InputImagePixelType; typedef InputImagePixelType TensorType; typedef vnl_matrix VnlMatrixType; typedef vnl_vector VnlVectorType; typedef vnl_vector vvec; /** Standard class typedefs. */ typedef PreservationOfPrincipalDirectionTensorReorientationImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); itkSetMacro(DisplacementField, DisplacementFieldPointer); itkGetMacro(DisplacementField, DisplacementFieldPointer); void SetAffineTransform( AffineTransformPointer aff ) { this->m_AffineTransform = aff; this->m_UseAffine = true; } itkGetMacro( AffineTransform, AffineTransformPointer ); itkSetMacro( UseImageDirection, bool ); itkGetMacro( UseImageDirection, bool ); /** Run-time type information (and related methods). */ itkTypeMacro(PreservationOfPrincipalDirectionTensorReorientationImageFilter, ImageToImageFilter); /** Image typedef support. */ typedef typename InputImageType::ConstPointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename InputImageType::PixelType InputPixelType; typedef typename OutputImageType::PixelType OutputPixelType; typedef typename InputPixelType::ValueType InputRealType; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::RegionType OutputImageRegionType; typedef typename InputImageType::SizeType InputSizeType; typedef typename OutputImageType::SizeType OutputSizeType; typedef typename InputImageType::IndexType InputIndexType; typedef typename OutputImageType::IndexType OutputIndexType; protected: PreservationOfPrincipalDirectionTensorReorientationImageFilter(); virtual ~PreservationOfPrincipalDirectionTensorReorientationImageFilter() { } void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; /** PreservationOfPrincipalDirectionTensorReorientationImageFilter can be implemented as a multithreaded filter. * Therefore, this implementation provides a ThreadedGenerateData() * routine which is called for each processing thread. The output * image data is allocated automatically by the superclass prior to * calling ThreadedGenerateData(). ThreadedGenerateData can only * write to the portion of the output image specified by the * parameter "outputRegionForThread" * * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ void GenerateData() ITK_OVERRIDE; typename DisplacementFieldType::PixelType TransformVectorByDirection( typename DisplacementFieldType::PixelType cpix ) { typedef itk::Vector locVectorType; if( this->m_UseImageDirection ) { locVectorType outpix; for( unsigned int d = 0; d < ImageDimension; d++ ) { outpix[d] = cpix[d]; } outpix = m_DirectionTransform->TransformVector( outpix ); for( unsigned int d = 0; d < ImageDimension; d++ ) { cpix[d] = outpix[d]; } } return cpix; } private: PreservationOfPrincipalDirectionTensorReorientationImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented AffineTransformPointer GetLocalDeformation(DisplacementFieldPointer, typename DisplacementFieldType::IndexType ); TensorType ApplyReorientation(InverseTransformPointer, TensorType ); void DirectionCorrectTransform( AffineTransformPointer, AffineTransformPointer ); DisplacementFieldPointer m_DisplacementField; DisplacementFieldTransformPointer m_DisplacementTransform; AffineTransformPointer m_DirectionTransform; AffineTransformPointer m_AffineTransform; InverseTransformPointer m_InverseAffineTransform; bool m_UseAffine; bool m_UseImageDirection; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkPreservationOfPrincipalDirectionTensorReorientationImageFilter.cxx" #endif #endif ants-2.2.0/Tensor/itkRotationMatrixFromVectors.h000066400000000000000000000135161311104306400217700ustar00rootroot00000000000000/* from * @article{MollerHughes99, author = "Tomas Mller and John F. Hughes", title = "Efficiently Building a Matrix to Rotate One Vector to Another", journal = "journal of graphics tools", volume = "4", number = "4", pages = "1-4", year = "1999", } http://jgt.akpeters.com/papers/MollerHughes99/code.html */ #include #include #include template MatrixType RotationMatrixFromVectors(VectorType from, VectorType to) { double EPSILON = 0.000001; typename VectorType::ValueType e, h, f; MatrixType mtx; VectorType v = CrossProduct(from, to); e = from * to; f = (e < 0) ? -e : e; if( f > 1.0 - EPSILON ) /* "from" and "to"-vector almost parallel */ { VectorType ut, vt; /* temporary storage vectors */ VectorType x; /* vector most nearly orthogonal to "from" */ float c1, c2, c3; /* coefficients for later use */ int i, j; x[0] = (from[0] > 0.0) ? from[0] : -from[0]; x[1] = (from[1] > 0.0) ? from[1] : -from[1]; x[2] = (from[2] > 0.0) ? from[2] : -from[2]; if( x[0] < x[1] ) { if( x[0] < x[2] ) { x[0] = 1.0; x[1] = x[2] = 0.0; } else { x[2] = 1.0; x[0] = x[1] = 0.0; } } else { if( x[1] < x[2] ) { x[1] = 1.0; x[0] = x[2] = 0.0; } else { x[2] = 1.0; x[0] = x[1] = 0.0; } } ut[0] = x[0] - from[0]; ut[1] = x[1] - from[1]; ut[2] = x[2] - from[2]; vt[0] = x[0] - to[0]; vt[1] = x[1] - to[1]; vt[2] = x[2] - to[2]; c1 = 2.0 / (ut * ut); c2 = 2.0 / (vt * vt); c3 = c1 * c2 * (ut * vt); for( i = 0; i < 3; i++ ) { for( j = 0; j < 3; j++ ) { mtx[i][j] = -c1 * ut[i] * ut[j] - c2 * vt[i] * vt[j] + c3 * vt[i] * ut[j]; } mtx[i][i] += 1.0; } } else /* the most common case, unless "from"="to", or "from"=-"to" */ { /* ...otherwise use this hand optimized version (9 mults less) */ float hvx, hvz, hvxy, hvxz, hvyz; /* h = (1.0 - e)/DOT(v, v); old code */ h = 1.0 / (1.0 + e); /* optimization by Gottfried Chen */ hvx = h * v[0]; hvz = h * v[2]; hvxy = hvx * v[1]; hvxz = hvx * v[2]; hvyz = hvz * v[1]; mtx[0][0] = e + hvx * v[0]; mtx[0][1] = hvxy - v[2]; mtx[0][2] = hvxz + v[1]; mtx[1][0] = hvxy + v[2]; mtx[1][1] = e + h * v[1] * v[1]; mtx[1][2] = hvyz - v[0]; mtx[2][0] = hvxz - v[1]; mtx[2][1] = hvyz + v[0]; mtx[2][2] = e + hvz * v[2]; } return mtx; } // /* // * A function for creating a rotation matrix that rotates a vector called // * "from" into another vector called "to". // * Input : from[3], to[3] which both must be *normalized* non-zero vectors // * Output: mtx[3][3] -- a 3x3 matrix in colum-major form // * Authors: Tomas Möller, John Hughes // * "Efficiently Building a Matrix to Rotate One Vector to Another" // * Journal of Graphics Tools, 4(4):1-4, 1999 // */ // void fromToRotation(float from[3], float to[3], float mtx[3][3]) { // float v[3]; // float e, h, f; // CROSS(v, from, to); // e = DOT(from, to); // f = (e < 0)? -e:e; // if (f > 1.0 - EPSILON) /* "from" and "to"-vector almost parallel */ // { // float u[3], v[3]; /* temporary storage vectors */ // float x[3]; /* vector most nearly orthogonal to "from" */ // float c1, c2, c3; /* coefficients for later use */ // int i, j; // x[0] = (from[0] > 0.0)? from[0] : -from[0]; // x[1] = (from[1] > 0.0)? from[1] : -from[1]; // x[2] = (from[2] > 0.0)? from[2] : -from[2]; // if (x[0] < x[1]) // { // if (x[0] < x[2]) // { // x[0] = 1.0; x[1] = x[2] = 0.0; // } // else // { // x[2] = 1.0; x[0] = x[1] = 0.0; // } // } // else // { // if (x[1] < x[2]) // { // x[1] = 1.0; x[0] = x[2] = 0.0; // } // else // { // x[2] = 1.0; x[0] = x[1] = 0.0; // } // } // u[0] = x[0] - from[0]; u[1] = x[1] - from[1]; u[2] = x[2] - from[2]; // v[0] = x[0] - to[0]; v[1] = x[1] - to[1]; v[2] = x[2] - to[2]; // c1 = 2.0 / DOT(u, u); // c2 = 2.0 / DOT(v, v); // c3 = c1 * c2 * DOT(u, v); // for (i = 0; i < 3; i++) { // for (j = 0; j < 3; j++) { // mtx[i][j] = - c1 * u[i] * u[j] // - c2 * v[i] * v[j] // + c3 * v[i] * u[j]; // } // mtx[i][i] += 1.0; // } // } // else /* the most common case, unless "from"="to", or "from"=-"to" */ // { // #if 0 // /* unoptimized version - a good compiler will optimize this. */ // /* h = (1.0 - e)/DOT(v, v); old code */ // h = 1.0/(1.0 + e); /* optimization by Gottfried Chen */ // mtx[0][0] = e + h * v[0] * v[0]; // mtx[0][1] = h * v[0] * v[1] - v[2]; // mtx[0][2] = h * v[0] * v[2] + v[1]; // mtx[1][0] = h * v[0] * v[1] + v[2]; // mtx[1][1] = e + h * v[1] * v[1]; // mtx[1][2] = h * v[1] * v[2] - v[0]; // mtx[2][0] = h * v[0] * v[2] - v[1]; // mtx[2][1] = h * v[1] * v[2] + v[0]; // mtx[2][2] = e + h * v[2] * v[2]; // #else // /* ...otherwise use this hand optimized version (9 mults less) */ // float hvx, hvz, hvxy, hvxz, hvyz; // /* h = (1.0 - e)/DOT(v, v); old code */ // h = 1.0/(1.0 + e); /* optimization by Gottfried Chen */ // hvx = h * v[0]; // hvz = h * v[2]; // hvxy = hvx * v[1]; // hvxz = hvx * v[2]; // hvyz = hvz * v[1]; // mtx[0][0] = e + hvx * v[0]; // mtx[0][1] = hvxy - v[2]; // mtx[0][2] = hvxz + v[1]; // mtx[1][0] = hvxy + v[2]; // mtx[1][1] = e + h * v[1] * v[1]; // mtx[1][2] = hvyz - v[0]; // mtx[2][0] = hvxz - v[1]; // mtx[2][1] = hvyz + v[0]; // mtx[2][2] = e + hvz * v[2]; // #endif // } // } ants-2.2.0/Tensor/itkWarpTensorImageMultiTransformFilter.h000066400000000000000000000300241311104306400237270ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 ITKWarpTensorImageMultiTRANSFORMFILTER_H_ #define ITKWarpTensorImageMultiTRANSFORMFILTER_H_ #include "itkImageToImageFilter.h" #include "itkVectorInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkPoint.h" #include "itkFixedArray.h" #include "itkRecursiveGaussianImageFilter.h" #include #include namespace itk { /** \class WarpTensorImageMultiTransformFilter * \brief Warps a tensor image using an input deformation field. * * WarpTensorImageMultiTransformFilter warps an existing tensor image with respect to * a given deformation field. * * A deformation field is represented as a image whose pixel type is some * vector type with at least N elements, where N is the dimension of * the input image. The vector type must support element access via operator * []. * * The output image is produced by inverse mapping: the output pixels * are mapped back onto the input image. This scheme avoids the creation of * any holes and overlaps in the output image. * * Each vector in the deformation field represent the distance between * a geometric point in the input space and a point in the output space such * that: * * \f[ p_{in} = p_{out} + d \f] * * Typically the mapped position does not correspond to an integer pixel * position in the input image. Interpolation via an image function * is used to compute values at non-integer positions. The default * interpolation typed used is the LinearInterpolateImageFunction. * The user can specify a particular interpolation function via * SetInterpolator(). Note that the input interpolator must derive * from base class InterpolateImageFunction. * * Position mapped to outside of the input image buffer are assigned * a edge padding value. * * The LargetPossibleRegion for the output is inherited from the * input deformation field. The output image spacing and origin may be set * via SetOutputSpacing, SetOutputOrigin. The default are respectively a * vector of 1's and a vector of 0's. * * This class is templated over the type of the input image, the * type of the output image and the type of the deformation field. * * The input image is set via SetInput. The input deformation field * is set via SetDisplacementField. * * This filter is implemented as a multithreaded filter. * * \warning This filter assumes that the input type, output type * and deformation field type all have the same number of dimensions. * * \ingroup GeometricTransforms MultiThreaded */ template < class TInputImage, class TOutputImage, class TDisplacementField, class TTransform > class WarpTensorImageMultiTransformFilter : public ImageToImageFilter { public: /** transform order type **/ // typedef enum _TransformOrderType {AffineFirst=0, AffineLast} TransformOrderType; /** transform type **/ typedef enum _SingleTransformType { EnumAffineType = 0, EnumDisplacementFieldType } SingleTransformType; /** Standard class typedefs. */ typedef WarpTensorImageMultiTransformFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods) */ itkTypeMacro( WarpTensorImageMultiTransformFilter, ImageToImageFilter ); /** Typedef to describe the output image region type. */ typedef typename TOutputImage::RegionType OutputImageRegionType; /** Inherit some types from the superclass. */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::InputImagePointer InputImagePointer; typedef typename Superclass::OutputImageType OutputImageType; typedef typename Superclass::OutputImagePointer OutputImagePointer; typedef typename Superclass::InputImageConstPointer InputImageConstPointer; typedef typename OutputImageType::IndexType IndexType; typedef typename OutputImageType::SizeType SizeType; typedef typename OutputImageType::PixelType PixelType; typedef typename OutputImageType::SpacingType SpacingType; typedef typename OutputImageType::DirectionType DirectionType; /** Determine the image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension ); itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension ); itkStaticConstMacro(DisplacementFieldDimension, unsigned int, TDisplacementField::ImageDimension ); /** Deformation field typedef support. */ typedef TDisplacementField DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldPointer; typedef typename DisplacementFieldType::PixelType DisplacementType; typedef typename DisplacementType::ValueType DisplacementScalarValueType; /** songgang: Affine transform typedef support. */ typedef TTransform TransformType; typedef typename TransformType::Pointer TransformTypePointer; /** Interpolator typedef support. */ typedef double CoordRepType; typedef VectorInterpolateImageFunction InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; typedef VectorLinearInterpolateImageFunction DefaultInterpolatorType; typedef VectorLinearInterpolateImageFunction DefaultVectorInterpolatorType; typedef typename DefaultVectorInterpolatorType::Pointer VectorInterpolatorPointer; /** Point type */ typedef Point PointType; typedef struct _DeformationTypeEx { DisplacementFieldPointer field; VectorInterpolatorPointer vinterp; } DeformationTypeEx; typedef struct _AffineTypeEx { TransformTypePointer aff; } AffineTypeEx; typedef struct _VarTransformType { AffineTypeEx aex; DeformationTypeEx dex; } VarTransformType; typedef std::pair SingleTransformItemType; typedef std::list TransformListType; /** Set the interpolator function. */ itkSetObjectMacro( Interpolator, InterpolatorType ); /** Get a pointer to the interpolator function. */ itkGetModifiableObjectMacro( Interpolator, InterpolatorType ); /** Set the output image spacing. */ itkSetMacro(OutputSpacing, SpacingType); virtual void SetOutputSpacing( const double* values); /** Get the output image spacing. */ itkGetConstReferenceMacro(OutputSpacing, SpacingType); /** Set the output image spacing. */ itkSetMacro(OutputDirection, DirectionType); /** Get the output image spacing. */ itkGetConstReferenceMacro(OutputDirection, DirectionType); /** Set the output image origin. */ itkSetMacro(OutputOrigin, PointType); virtual void SetOutputOrigin( const double* values); /** Get the output image origin. */ itkGetConstReferenceMacro(OutputOrigin, PointType); /** Set the output image size. */ itkSetMacro(OutputSize, SizeType); // virtual void SetOutputSize( const double *values); itkGetConstReferenceMacro(OutputSize, SizeType); /** Set the edge padding value */ itkSetMacro( EdgePaddingValue, PixelType ); /** Get the edge padding value */ itkGetMacro( EdgePaddingValue, PixelType ); TransformListType & GetTransformList() { return m_TransformList; } void PrintTransformList(); /** WarpTensorImageMultiTransformFilter produces an image which is a different * size than its input image. As such, it needs to provide an * implemenation for GenerateOutputInformation() which set * the output information according the OutputSpacing, OutputOrigin * and the deformation field's LargestPossibleRegion. */ virtual void GenerateOutputInformation(); /** It is difficult to compute in advance the input image region * required to compute the requested output region. Thus the safest * thing to do is to request for the whole input image. * * For the deformation field, the input requested region * set to be the same as that of the output requested region. */ virtual void GenerateInputRequestedRegion(); /** This method is used to set the state of the filter before * multi-threading. */ virtual void BeforeThreadedGenerateData(); /** This method is used to set the state of the filter after * multi-threading. */ virtual void AfterThreadedGenerateData(); /** precompute the smoothed image if necessary **/ void SetSmoothScale(double scale); double GetSmoothScale() { return m_SmoothScale; }; // void UpdateSizeByScale(); void PushBackAffineTransform(const TransformType* t); void PushBackDisplacementFieldTransform(const DisplacementFieldType* t); bool MultiInverseAffineOnlySinglePoint(const PointType & point1, PointType & point2); bool MultiTransformSinglePoint(const PointType & point1, PointType & point2); bool MultiTransformPoint(const PointType & point1, PointType & point2, bool bFisrtDeformNoInterp, const IndexType & index); void DetermineFirstDeformNoInterp(); inline bool IsOutOfNumericBoundary(const PointType & p); // set interpolator from outside // virtual void SetInterpolator(InterpolatorPointer interp); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(SameDimensionCheck1, (Concept::SameDimension ) ); itkConceptMacro(SameDimensionCheck2, (Concept::SameDimension ) ); // removed to be compatible with vector form input image // itkConceptMacro(InputHasNumericTraitsCheck, // (Concept::HasNumericTraits)); itkConceptMacro(DisplacementFieldHasNumericTraitsCheck, (Concept::HasNumericTraits ) ); /** End concept checking */ #endif bool m_bFirstDeformNoInterp; protected: WarpTensorImageMultiTransformFilter(); ~WarpTensorImageMultiTransformFilter() { }; void PrintSelf(std::ostream& os, Indent indent) const; /** WarpTensorImageMultiTransformFilter is implemented as a multi-threaded filter. * As such, it needs to provide and implementation for * ThreadedGenerateData(). */ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, int threadId ); InterpolatorPointer m_Interpolator; PixelType m_EdgePaddingValue; SpacingType m_OutputSpacing; PointType m_OutputOrigin; SizeType m_OutputSize; DirectionType m_OutputDirection; TransformListType m_TransformList; DisplacementFieldPointer m_FullWarp; double m_SmoothScale; InputImagePointer m_CachedSmoothImage; private: WarpTensorImageMultiTransformFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkWarpTensorImageMultiTransformFilter.hxx" #endif #endif /*ITKWarpTensorImageMultiTRANSFORMFILTER_H_*/ ants-2.2.0/Tensor/itkWarpTensorImageMultiTransformFilter.hxx000066400000000000000000000530221311104306400243120ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkWarpTensorImageMultiTransformFilter_hxx #define __itkWarpTensorImageMultiTransformFilter_hxx #include "itkWarpTensorImageMultiTransformFilter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkNumericTraits.h" #include "itkProgressReporter.h" #include "itkVectorLinearInterpolateImageFunction.h" #include #include "vnl/vnl_matrix.h" #include "vnl/algo/vnl_symmetric_eigensystem.h" #include "TensorFunctions.h" namespace itk { template TTensorType LogExpTensor( TTensorType inTensor, bool doLog ) { TTensorType result; if( (inTensor[0] != inTensor[0] ) || (inTensor[1] != inTensor[1] ) || (inTensor[2] != inTensor[2] ) || (inTensor[3] != inTensor[3] ) || (inTensor[4] != inTensor[4] ) || (inTensor[5] != inTensor[5] ) ) { result[0] = result[1] = result[2] = result[3] = result[4] = result[5] = 0; // std::cout << " - " << inTensor << std::endl; return result; } double sigma = 1.0e-15; if( (inTensor[0] < sigma ) || (inTensor[1] < sigma ) || (inTensor[2] < sigma ) || (inTensor[3] < sigma ) || (inTensor[4] < sigma ) || (inTensor[5] < sigma ) ) { result[0] = result[1] = result[2] = result[3] = result[4] = result[5] = 0; // std::cout << " - " << inTensor << std::endl; return result; } vnl_matrix tensor(3, 3); tensor[0][0] = inTensor[0]; tensor[1][0] = tensor[0][1] = inTensor[1]; tensor[1][1] = inTensor[2]; tensor[2][0] = tensor[0][2] = inTensor[3]; tensor[2][1] = tensor[1][2] = inTensor[4]; tensor[2][2] = inTensor[5]; // std::cout << " + " << inTensor << std::endl; if( ( (tensor[0][0] == 0) && (tensor[0][1] == 0) && (tensor[0][2] == 0) && (tensor[1][1] == 0) && (tensor[1][2] == 0) && (tensor[2][2] == 0) ) || (tensor.has_nans() ) || (!tensor.is_finite() ) ) { result[0] = result[1] = result[2] = result[3] = result[4] = result[5] = 0; } else { vnl_symmetric_eigensystem eSystem(tensor); for( unsigned int i = 0; i < 3; i++ ) { if( doLog ) { eSystem.D[i] = std::log(std::fabs(eSystem.D[i]) ); } else { eSystem.D[i] = std::exp(eSystem.D[i]); } } vnl_matrix leTensor = eSystem.recompose(); result[0] = leTensor[0][0]; result[1] = leTensor[1][0]; result[2] = leTensor[1][1]; result[3] = leTensor[2][0]; result[4] = leTensor[2][1]; result[5] = leTensor[2][2]; } return result; } /** * Default constructor. */ template WarpTensorImageMultiTransformFilter ::WarpTensorImageMultiTransformFilter() { // Setup the number of required inputs this->SetNumberOfRequiredInputs( 1 ); // Setup default values m_OutputSpacing.Fill( 1.0 ); m_OutputOrigin.Fill( 0.0 ); m_EdgePaddingValue = NumericTraits::ZeroValue(); // Setup default interpolator typename DefaultInterpolatorType::Pointer interp = DefaultInterpolatorType::New(); m_Interpolator = static_cast( interp.GetPointer() ); m_SmoothScale = -1; // m_bOutputDisplacementField = false; // m_TransformOrder = AffineFirst; } // template // void // WarpTensorImageMultiTransformFilter // ::SetInterpolator1(InterpolatorPointer interp) // { // m_Interpolator = static_cast (interp.GetPointer()); // std::cout << "set interpolator in WarpImage:" << interp << std::endl; // } template void WarpTensorImageMultiTransformFilter ::PrintTransformList() { std::cout << "transform list: " << std::endl; typename TransformListType::iterator it = (m_TransformList.begin() ); for( int ii = 0; it != m_TransformList.end(); it++, ii++ ) { switch( it->first ) { case EnumAffineType: { std::cout << '[' << ii << "]: EnumAffineType" << std::endl; std::cout << it->second.aex.aff << std::endl; } break; case EnumDisplacementFieldType: std::cout << '[' << ii << "]: EnumDisplacementFieldType: size" << it->second.dex.field->GetLargestPossibleRegion().GetSize() << std::endl; } } } /** * Standard PrintSelf method. */ template void WarpTensorImageMultiTransformFilter ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "OutputSpacing: " << m_OutputSpacing << std::endl;; os << indent << "OutputOrigin: " << m_OutputOrigin << std::endl; os << indent << "EdgePaddingValue: " << static_cast::PrintType>(m_EdgePaddingValue) << std::endl; os << indent << "Interpolator: " << m_Interpolator.GetPointer() << std::endl; os << indent << "m_bFirstDeformNoInterp = " << m_bFirstDeformNoInterp << std::endl; } /** * Set the output image spacing. * */ template void WarpTensorImageMultiTransformFilter ::SetOutputSpacing( const double* spacing) { SpacingType s(spacing); this->SetOutputSpacing( s ); } /** * Set the output image origin. * */ template void WarpTensorImageMultiTransformFilter ::SetOutputOrigin( const double* origin) { PointType p(origin); this->SetOutputOrigin(p); } /** * Setup state of filter before multi-threading. * InterpolatorType::SetInputImage is not thread-safe and hence * has to be setup before ThreadedGenerateData */ template void WarpTensorImageMultiTransformFilter ::BeforeThreadedGenerateData() { if( !m_Interpolator ) { itkExceptionMacro(<< "Interpolator not set"); } // Connect input image to interpolator // m_Interpolator->SetInputImage( this->GetInput() ); if( m_CachedSmoothImage.IsNull() && (this->GetInput() ) ) { m_CachedSmoothImage = const_cast(this->GetInput() ); } m_Interpolator->SetInputImage( m_CachedSmoothImage ); } /** * Setup state of filter after multi-threading. */ template void WarpTensorImageMultiTransformFilter ::AfterThreadedGenerateData() { // Disconnect input image from interpolator m_Interpolator->SetInputImage( NULL ); } template void WarpTensorImageMultiTransformFilter ::GenerateInputRequestedRegion() { // call the superclass's implementation Superclass::GenerateInputRequestedRegion(); // request the largest possible region for the input image InputImagePointer inputPtr = const_cast( this->GetInput() ); if( inputPtr ) { inputPtr->SetRequestedRegionToLargestPossibleRegion(); } return; } template void WarpTensorImageMultiTransformFilter ::GenerateOutputInformation() { // call the superclass's implementation of this method Superclass::GenerateOutputInformation(); OutputImagePointer outputPtr = this->GetOutput(); if( !outputPtr ) { return; } typename TOutputImage::RegionType outputLargestPossibleRegion; outputLargestPossibleRegion.SetSize( this->m_OutputSize ); // outputLargestPossibleRegion.SetIndex( 0 ); outputPtr->SetLargestPossibleRegion( outputLargestPossibleRegion ); outputPtr->SetSpacing( this->m_OutputSpacing ); outputPtr->SetOrigin( this->m_OutputOrigin ); outputPtr->SetDirection( this->m_OutputDirection ); // this->m_FullWarp->SetLargestPossibleRegion( outputLargestPossibleRegion ); // this->m_FullWarp->SetSpacing( this->m_OutputSpacing ); // this->m_FullWarp->SetOrigin( this->m_OutputOrigin ); // this->m_FullWarp->SetDirection( this->m_OutputDirection ); // this->m_FullWarp->Allocate(); // determine if the deformation field is the same dimension as the image // so that it does not need interpolation in the first step DetermineFirstDeformNoInterp(); } template void WarpTensorImageMultiTransformFilter ::SetSmoothScale(double /* scale */) { /* if (m_SmoothScale != scale){ // compute the new cached // std::cout << "change smooth scale: " << m_SmoothScale << " ---> " << scale << std::endl; m_SmoothScale = scale; typename InputImageType::SpacingType inputSpacing = this->GetInput()->GetSpacing(); typename InputImageType::RegionType::SizeType inputSize = this->GetInput()->GetLargestPossibleRegion().GetSize(); typename InputImageType::SpacingType outputSpacing; typename InputImageType::RegionType::SizeType outputSize; double minimumSpacing = inputSpacing.GetVnlVector().min_value(); double maximumSpacing = inputSpacing.GetVnlVector().max_value(); InputImagePointer image = const_cast (this->GetInput()); for ( unsigned int d = 0; d < ImageDimension; d++ ) { double scaling = vnl_math_min( 1.0 / scale * minimumSpacing / inputSpacing[d], static_cast( inputSize[d] ) / 32.0 ); outputSpacing[d] = inputSpacing[d] * scaling; outputSize[d] = static_cast( inputSpacing[d] * static_cast( inputSize[d] ) / outputSpacing[d] + 0.5 ); double sigma = 0.25 * ( outputSpacing[d] / inputSpacing[d] ); if (sigma < 0) sigma=0; typedef RecursiveGaussianImageFilter GaussianFilterType; typename GaussianFilterType::Pointer smoother = GaussianFilterType::New(); smoother->SetInputImage( image ); smoother->SetDirection( d ); smoother->SetNormalizeAcrossScale( false ); // std::cout << "scale = " << scale << " => " << "sigma of dim " << d << ": " << sigma << " out size " << outputSize << " spc1 " << outputSpacing << " in " << inputSpacing << std::endl; smoother->SetSigma( sigma ); if ( smoother->GetSigma() > 0.0 ) { smoother->Update(); image = smoother->GetOutput(); } } SetOutputSpacing( outputSpacing ); SetOutputOrigin( this->GetInput()->GetOrigin() ); SetOutputSize(outputSize); } */ InputImagePointer image = const_cast(this->GetInput() ); m_CachedSmoothImage = image; } /** * Compute the output for the region specified by outputRegionForThread. */ template void WarpTensorImageMultiTransformFilter ::ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, int threadId ) { InputImageConstPointer inputPtr = this->GetInput(); OutputImagePointer outputPtr = this->GetOutput(); // std::cout << "inputPtr->GetOrigin():" << inputPtr->GetOrigin() << std::endl; // std::cout << "outputPtr->GetOrigin():" << outputPtr->GetOrigin() << std::endl; // std::exception(); // Need to get full warp for reorientation of tensors IndexType index; index.Fill(0); this->m_EdgePaddingValue = inputPtr->GetPixel(index); // support progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels() ); // iterator for the output image ImageRegionIteratorWithIndex outputIt(outputPtr, outputRegionForThread); while( !outputIt.IsAtEnd() ) { PointType point1, point2; // get the output image index IndexType index2 = outputIt.GetIndex(); outputPtr->TransformIndexToPhysicalPoint( index2, point1 ); bool isinside = MultiTransformPoint(point1, point2, m_bFirstDeformNoInterp, index2); // std::cout << "point1:" << point1 << " point2:" << point2 << " index:" << index2 << std::endl; // std::exception(); // DisplacementFieldType::PixelType diff; // diff[0] = point2[0]-point1[0]; // diff[1] = point2[1]-point1[1]; // diff[2] = point2[2]-point1[2]; // warp the image // get the interpolated value if( isinside && (m_Interpolator->IsInsideBuffer( point2 ) ) ) { PixelType value = static_cast(m_Interpolator->Evaluate(point2) ); outputIt.Set( value ); } else { // std::cout << "OUTSIDE" << " isinside:" << isinside << " m_Interpolator->IsInsideBuffer( point2 ):" << // m_Interpolator->IsInsideBuffer( point2 ) << std::endl; outputIt.Set( m_EdgePaddingValue ); } ++outputIt; } progress.CompletedPixel(); } // template // void // WarpTensorImageMultiTransformFilter // ::UpdateSizeByScale() // { // // // DisplacementFieldPointer field = this->GetDisplacementField(); // // // // SetOutputSpacing( field->GetSpacing() / m_SmoothScale ); // // SetOutputOrigin( field->GetOrigin() ); // // // // typename InputImageType::SizeType imgsz = field->GetLargestPossibleRegion().GetSize(); // // for(int ii = 0; ii < InputImageType::ImageDimension; ii++) imgsz[ii] = (typename // InputImageType::SizeType::SizeValueType) (imgsz[ii] * m_SmoothScale + 0.5); // // // // SetOutputSize(imgsz); // // } template void WarpTensorImageMultiTransformFilter ::PushBackAffineTransform(const TransformType* t) { if( t ) { VarTransformType t1; t1.aex.aff = const_cast(t); m_TransformList.push_back(SingleTransformItemType(EnumAffineType, t1) ); } } template void WarpTensorImageMultiTransformFilter ::PushBackDisplacementFieldTransform(const DisplacementFieldType* t) { if( t ) { VarTransformType t1; t1.dex.field = const_cast(t); t1.dex.vinterp = DefaultVectorInterpolatorType::New(); t1.dex.vinterp->SetInputImage(t1.dex.field); m_TransformList.push_back(SingleTransformItemType(EnumDisplacementFieldType, t1) ); } } template bool WarpTensorImageMultiTransformFilter ::MultiTransformSinglePoint(const PointType & point1, PointType & point2) { IndexType null_index; bool isinside = MultiTransformPoint(point1, point2, false, null_index); return isinside; } template bool WarpTensorImageMultiTransformFilter ::IsOutOfNumericBoundary(const PointType & p) { const DisplacementScalarValueType kMaxDisp = itk::NumericTraits::max(); bool b = false; for( int i = 0; i < ImageDimension; i++ ) { if( p[i] >= kMaxDisp ) { b = true; break; } } return b; } template bool WarpTensorImageMultiTransformFilter ::MultiInverseAffineOnlySinglePoint(const PointType & p1, PointType & p2) { bool isinside = true; PointType point1 = p1, point2; typename TransformListType::iterator it = m_TransformList.begin(); for( ; it != m_TransformList.end(); it++ ) { SingleTransformType ttype = it->first; switch( ttype ) { case EnumAffineType: { TransformTypePointer aff = it->second.aex.aff; TransformTypePointer aff_inv = TransformTypePointer::ObjectType::New(); // std::cout << "aff before:" << aff << std::endl; aff->GetInverse(aff_inv); // aff->GetInverse(aff); // std::cout << "aff after:" << aff << std::endl; // std::cout << "aff_inv after:" << aff_inv << std::endl; point2 = aff_inv->TransformPoint(point1); point1 = point2; isinside = true; break; } case EnumDisplacementFieldType: { } break; default: itkExceptionMacro(<< "Single Transform Not Supported!"); } if( IsOutOfNumericBoundary(point2) ) { isinside = false; break; } point1 = point2; } p2 = point2; return isinside; } template bool WarpTensorImageMultiTransformFilter ::MultiTransformPoint(const PointType & p1, PointType & p2, bool bFisrtDeformNoInterp, const IndexType & index) { bool isinside = false; PointType point1 = p1, point2; typename TransformListType::iterator it = m_TransformList.begin(); for( ; it != m_TransformList.end(); it++ ) { SingleTransformType ttype = it->first; switch( ttype ) { case EnumAffineType: { TransformTypePointer aff = it->second.aex.aff; point2 = aff->TransformPoint(point1); point1 = point2; isinside = true; } break; case EnumDisplacementFieldType: { DisplacementFieldPointer fieldPtr = it->second.dex.field; if( bFisrtDeformNoInterp && it == m_TransformList.begin() ) { // use discrete coordinates DisplacementType displacement = fieldPtr->GetPixel(index); for( int j = 0; j < ImageDimension; j++ ) { point2[j] = point1[j] + displacement[j]; } isinside = true; } else { // use continous coordinates typename DefaultVectorInterpolatorType::ContinuousIndexType contind; // use ITK implementation to use orientation header fieldPtr->TransformPhysicalPointToContinuousIndex(point1, contind); isinside = fieldPtr->GetLargestPossibleRegion().IsInside( contind ); VectorInterpolatorPointer vinterp = it->second.dex.vinterp; typename DefaultVectorInterpolatorType::OutputType disp2; if( isinside ) { disp2 = vinterp->EvaluateAtContinuousIndex( contind ); } else { disp2.Fill(0); } for( int jj = 0; jj < ImageDimension; jj++ ) { point2[jj] = disp2[jj] + point1[jj]; } } } break; default: itkExceptionMacro(<< "Single Transform Not Supported!"); } if( IsOutOfNumericBoundary(point2) ) { isinside = false; break; } point1 = point2; } p2 = point2; return isinside; } template void WarpTensorImageMultiTransformFilter ::DetermineFirstDeformNoInterp() { m_bFirstDeformNoInterp = false; if( m_TransformList.size() > 0 ) { if( (m_TransformList.front().first == EnumDisplacementFieldType) ) { DisplacementFieldPointer field = m_TransformList.front().second.dex.field; m_bFirstDeformNoInterp = (this->GetOutputSize() == field->GetLargestPossibleRegion().GetSize() ) & (this->GetOutputSpacing() == field->GetSpacing() ) & (this->GetOutputOrigin() == field->GetOrigin() ); // std::cout << "in set: field size: " << field->GetLargestPossibleRegion().GetSize() // << "output spacing: " << this->GetOutputSize() << std::endl; // std::cout << field->GetSpacing() << " | " << this->GetOutputSpacing() << std::endl; // std::cout << field->GetOrigin() << " | " << this->GetOutputOrigin() << std::endl; } } } } // end namespace itk #endif // __itkWarpTensorImageMultiTransformFilter_hxx ants-2.2.0/TestData/000077500000000000000000000000001311104306400141625ustar00rootroot00000000000000ants-2.2.0/TestData/ANON0006_20_T1_dbg_splayed.nii.gz.md5000066400000000000000000000000411311104306400222240ustar00rootroot00000000000000c34916939b720607bb5e288c14cf8b65 ants-2.2.0/TestData/ANON0006_20_T1_sag_twisted.nii.gz.md5000066400000000000000000000000411311104306400222640ustar00rootroot00000000000000dda9fde8ee4fa034a6a56f4cc22f2f55 ants-2.2.0/TestData/CompositeTransformUtilTest.result.h5.md5000066400000000000000000000000401311104306400237470ustar00rootroot00000000000000132d4e4768490fba638e7fda2f02d2f9ants-2.2.0/TestData/CompositeTransformUtilTest_AffineTransform.mat.md5000066400000000000000000000000401311104306400260430ustar00rootroot000000000000003b4b24a44ef916191a3290525c0caa01ants-2.2.0/TestData/CompositeTransformUtilTest_RigidTransform.mat.md5000066400000000000000000000000401311104306400257110ustar00rootroot00000000000000d21eb34e63ed3035b2df1652a81d3803ants-2.2.0/TestData/CompositeTransformUtilTest_SyNTransform.nii.gz.md5000066400000000000000000000000401311104306400260010ustar00rootroot000000000000009b27a434beea55f2f8a78c672837a467ants-2.2.0/TestData/Data/000077500000000000000000000000001311104306400150335ustar00rootroot00000000000000ants-2.2.0/TestData/Data/B1.tiff.md5000066400000000000000000000000401311104306400166250ustar00rootroot00000000000000d50be7f1c451bf832c534206842b2d37ants-2.2.0/TestData/Data/B2.tiff.md5000066400000000000000000000000401311104306400166260ustar00rootroot0000000000000008ee0426072d1444706889ba66988cf1ants-2.2.0/TestData/Data/B3.tiff.md5000066400000000000000000000000401311104306400166270ustar00rootroot0000000000000020f1223c0010b9e4c346cfee9190acb4ants-2.2.0/TestData/Data/B4.tiff.md5000066400000000000000000000000401311104306400166300ustar00rootroot00000000000000f37cb48710987c33a4aee3ed8abaad57ants-2.2.0/TestData/Data/B5.tiff.md5000066400000000000000000000000401311104306400166310ustar00rootroot00000000000000d0c7e80f3a88639c6af4a2e749f4cda4ants-2.2.0/TestData/Data/Frown.nii.md5000066400000000000000000000000401311104306400173050ustar00rootroot0000000000000011ea9f4d17e24ab90d8bc0b4b087d235ants-2.2.0/TestData/Data/Frown.txt.md5000066400000000000000000000000401311104306400173450ustar00rootroot0000000000000084cf0e06646142e0aa4c3d3324a62701ants-2.2.0/TestData/Data/Frown.vtk.md5000066400000000000000000000000401311104306400173320ustar00rootroot00000000000000f41df9ed883e63bdd31b440fcb82be6aants-2.2.0/TestData/Data/README.md5000066400000000000000000000000401311104306400163710ustar00rootroot00000000000000c753ce2a74481486c37d101ba224b2fbants-2.2.0/TestData/Data/Smile.nii.md5000066400000000000000000000000401311104306400172630ustar00rootroot0000000000000067a066125ba4132655c1ad91c34e06a4ants-2.2.0/TestData/Data/Smile.txt.md5000066400000000000000000000000401311104306400173230ustar00rootroot00000000000000f225299c0f092d275ce5cb41f30eed46ants-2.2.0/TestData/Data/Smile.vtk.md5000066400000000000000000000000401311104306400173100ustar00rootroot000000000000000cd4a0165822312ef368c8a006a5e28dants-2.2.0/TestData/Data/beetlerot.jpg.md5000066400000000000000000000000401311104306400202000ustar00rootroot000000000000005335f839229b48e82db8861f8ea552e8ants-2.2.0/TestData/Data/c.nii.gz.md5000066400000000000000000000000401311104306400170530ustar00rootroot0000000000000011701bbc90621d482532064dac4eb068ants-2.2.0/TestData/Data/ch2brainmask.nii.gz.md5000066400000000000000000000000401311104306400211750ustar00rootroot00000000000000a396d2381eb357181ab0038f0559d9dbants-2.2.0/TestData/Data/chalf.nii.gz.md5000066400000000000000000000000401311104306400177060ustar00rootroot000000000000005eb74684e6b59c03f956368a81972e79ants-2.2.0/TestData/Data/ford.jpg.md5000066400000000000000000000000401311104306400171450ustar00rootroot00000000000000f5c08ab5a885182cae48ace65f1bd91fants-2.2.0/TestData/Data/functional.nii.md5000066400000000000000000000000401311104306400203540ustar00rootroot00000000000000941066d9b635e8c272e647abd6a00f9bants-2.2.0/TestData/Data/listPriorWolk_nirepDemo.txt.md5000066400000000000000000000000401311104306400231000ustar00rootroot0000000000000099f91c8c0b957cd5a678c7385f258e74ants-2.2.0/TestData/Data/mov2.nii.gz.md5000066400000000000000000000000401311104306400175140ustar00rootroot0000000000000016d7b1385c3fcad2c0fe45c53123e8a3ants-2.2.0/TestData/Data/myview1.csv.md5000066400000000000000000000000401311104306400176270ustar00rootroot00000000000000c9449fdf994ee8b813386fe49bcd0059ants-2.2.0/TestData/Data/myview2.csv.md5000066400000000000000000000000401311104306400176300ustar00rootroot0000000000000046fbac6191b33e297871c364c709711cants-2.2.0/TestData/Data/myview3.csv.md5000066400000000000000000000000401311104306400176310ustar00rootroot00000000000000b90fd8bff771a67013717fbc549326a0ants-2.2.0/TestData/Data/myview4.csv.md5000066400000000000000000000000401311104306400176320ustar00rootroot000000000000008c4ea9d008a044c0b145125a455fa42aants-2.2.0/TestData/Data/myview_mismatch.csv.md5000066400000000000000000000000401311104306400214330ustar00rootroot0000000000000068c7bda70524918d509c9edf6a0ec203ants-2.2.0/TestData/Data/phantomAwmgm.jpg.md5000066400000000000000000000000401311104306400206520ustar00rootroot00000000000000a1c5447d6937909e7d853e113a6073fcants-2.2.0/TestData/Data/phantomBwmgm.jpg.md5000066400000000000000000000000401311104306400206530ustar00rootroot00000000000000dc52ba5f395cd1b1612148da844d78edants-2.2.0/TestData/Data/phantomCwmgm.jpg.md5000066400000000000000000000000401311104306400206540ustar00rootroot00000000000000ae8ed778e46b39d350108a86cba5301dants-2.2.0/TestData/Data/phantomDwmgm.jpg.md5000066400000000000000000000000401311104306400206550ustar00rootroot000000000000000094881420d3f4fa2be08e5dad4e98efants-2.2.0/TestData/Data/phantomEwmgm.jpg.md5000066400000000000000000000000401311104306400206560ustar00rootroot00000000000000035e70ac7a4ffc010d319c7ea8879567ants-2.2.0/TestData/Data/phantomFwmgm.jpg.md5000066400000000000000000000000401311104306400206570ustar00rootroot00000000000000c8e0f1fc42799b1394770036cccb562cants-2.2.0/TestData/Data/phantomGwmgm.jpg.md5000066400000000000000000000000401311104306400206600ustar00rootroot000000000000006c9016956c8296b32a4dbc3eb6ff8c1aants-2.2.0/TestData/Data/phantomHwmgm.jpg.md5000066400000000000000000000000401311104306400206610ustar00rootroot00000000000000de88309aac182bfe0abaf869eb59ed81ants-2.2.0/TestData/Data/phantomtemplate.jpg.md5000066400000000000000000000000401311104306400214150ustar00rootroot00000000000000179dc1c16d96548564f35f1132efce22ants-2.2.0/TestData/Data/priorScaleListWolk_nirep0p1Demo.csv.md5000066400000000000000000000000401311104306400243450ustar00rootroot000000000000001261fc7c8ba9fae6046cf18d8f9a2f49ants-2.2.0/TestData/Data/r16mask.nii.gz.md5000066400000000000000000000000401311104306400201150ustar00rootroot00000000000000acdbe32b199591bca333add30cf7b547ants-2.2.0/TestData/Data/r16priors.nii.gz.md5000066400000000000000000000000401311104306400205000ustar00rootroot0000000000000003dd793ed073bbe0c8b19cc5f4033d2aants-2.2.0/TestData/Data/r16roth.nii.gz.md5000066400000000000000000000000401311104306400201360ustar00rootroot000000000000003cfda4cafaa89cf924411c1911e66d36ants-2.2.0/TestData/Data/r16slice.nii.gz.md5000066400000000000000000000000401311104306400202610ustar00rootroot0000000000000037aaa33029410941bf4affff0479fa18ants-2.2.0/TestData/Data/r27slice.nii.gz.md5000066400000000000000000000000401311104306400202630ustar00rootroot000000000000008228d2a1fa139a1eb8804ceaddcdcff3ants-2.2.0/TestData/Data/r30slice.nii.gz.md5000066400000000000000000000000401311104306400202550ustar00rootroot00000000000000090a1086e91222800f1e9aa4105b2c0fants-2.2.0/TestData/Data/r62slice.nii.gz.md5000066400000000000000000000000401311104306400202620ustar00rootroot00000000000000fa1c95f06e2ce7642f6967eb4fa0b80dants-2.2.0/TestData/Data/r64roth.nii.gz.md5000066400000000000000000000000401311104306400201410ustar00rootroot0000000000000014461dbe8b8cff1266186ee7bc9ba438ants-2.2.0/TestData/Data/r64slice.nii.gz.md5000066400000000000000000000000401311104306400202640ustar00rootroot000000000000008a629ee7ea32013c76af5b05f880b5c6ants-2.2.0/TestData/Data/r85slice.nii.gz.md5000066400000000000000000000000401311104306400202670ustar00rootroot00000000000000602bdf5fc198a86712157dee7fa17027ants-2.2.0/TestData/Data/ref2.nii.gz.md5000066400000000000000000000000401311104306400174670ustar00rootroot000000000000001e3d598f7af2d226512a5004c664915eants-2.2.0/TestData/Data/test_DiReCT.sh.md5000066400000000000000000000000401311104306400201560ustar00rootroot0000000000000065caa9274f60b238365d0a675f9204cfants-2.2.0/TestData/Data/test_image_ground_truth.nii.gz.md5000066400000000000000000000000401311104306400235560ustar00rootroot000000000000004d4650e16da2a1b5be7f78b1f9e32dfbants-2.2.0/TestData/Data/test_image_seg.nii.gz.md5000066400000000000000000000000401311104306400216100ustar00rootroot00000000000000b92477360d95deb585590bc65c44d80fants-2.2.0/TestData/Data/wolk_dataMCINormalDemo.mhd.md5000066400000000000000000000000401311104306400224570ustar00rootroot000000000000008e5337c9077993f2a248bcc41a58c202ants-2.2.0/TestData/Data/wolk_dataMCINormalDemo.raw.md5000066400000000000000000000000401311104306400225000ustar00rootroot0000000000000013d6e66ea47619ca43235086c343fc4aants-2.2.0/TestData/Data/wolk_mask.nii.gz.md5000066400000000000000000000000401311104306400206200ustar00rootroot000000000000008556f22c41488ae6da0e7a2de0f9d828ants-2.2.0/TestData/Initializer_0.05_antsRegistrationTest_AffineRotationMasks.mat.md5000066400000000000000000000000411311104306400305400ustar00rootroot00000000000000da00b498086cd163f8e15efa604a63e2 ants-2.2.0/TestData/Initializer_0.05_antsRegistrationTest_AffineRotationNoMasks.mat.md5000066400000000000000000000000411311104306400310350ustar00rootroot000000000000002e14eb9e9ffa361873d91a944874e1e3 ants-2.2.0/TestData/Initializer_0.05_antsRegistrationTest_AffineScaleMasks.mat.md5000066400000000000000000000000411311104306400277700ustar00rootroot000000000000005e61f913c54ebac17a33ec5a75eb42b6 ants-2.2.0/TestData/Initializer_0.05_antsRegistrationTest_AffineScaleMasks_Float.mat.md5000066400000000000000000000000401311104306400311140ustar00rootroot0000000000000047f6560d0932761a78e95d76597dc9c1ants-2.2.0/TestData/Initializer_0.05_antsRegistrationTest_RigidRotationHeadMasks.mat.md5000066400000000000000000000000411311104306400311700ustar00rootroot000000000000006a6838a0375dc17c6f2ee3dc214a68d1 ants-2.2.0/TestData/Initializer_0.05_antsRegistrationTest_RigidRotationNoMasks.mat.md5000066400000000000000000000000411311104306400307030ustar00rootroot000000000000006a6838a0375dc17c6f2ee3dc214a68d1 ants-2.2.0/TestData/Initializer_0.05_antsRegistrationTest_RigidRotationNoMasks_Float.mat.md5000066400000000000000000000000401311104306400320270ustar00rootroot00000000000000572dd896a8b1c5440fba73448946a6baants-2.2.0/TestData/Initializer_0.05_antsRegistrationTest_SimilarityRotationNoMasks.mat.md5000066400000000000000000000000411311104306400317730ustar00rootroot00000000000000875b7adb24f8958648a4b6ec495dcf00 ants-2.2.0/TestData/Initializer_0.05_antsRegistrationTest_SimilarityScaleNoMasks.mat.md5000066400000000000000000000000411311104306400312230ustar00rootroot0000000000000042cd2424c063ac3be1aa469ad4f1d2db ants-2.2.0/TestData/Initializer_antsRegistrationTest_AffineTranslationMasks.mat.md5000066400000000000000000000000401311104306400305740ustar00rootroot00000000000000847b99bd7246b865b3fd3ddd7ec9ae9eants-2.2.0/TestData/Initializer_antsRegistrationTest_RigidAnisotropicMasks.mat.md5000066400000000000000000000000401311104306400304360ustar00rootroot00000000000000bcee891964b2718ec15cb6159c5f90f1ants-2.2.0/TestData/Initializer_antsRegistrationTest_RigidRotGeomNoMasks.mat.md5000066400000000000000000000000401311104306400300150ustar00rootroot000000000000004b6d4ad88b793a8918cc53049ea4773eants-2.2.0/TestData/Initializer_antsRegistrationTest_ScaleRotationRescaleHeadMasks.mat.md5000066400000000000000000000000401311104306400320150ustar00rootroot00000000000000bcd1a22ef9786c30d14e19eacca6a505ants-2.2.0/TestData/Initializer_antsRegistrationTest_ScaleRotationRescaleHeadMasks_Float.mat.md5000066400000000000000000000000401311104306400331420ustar00rootroot00000000000000052d3892297d4a9db1b60b4a7ec80ed5ants-2.2.0/TestData/Initializer_antsRegistrationTest_TranslationRescaleHeadMasks.mat.md5000066400000000000000000000000411311104306400315450ustar00rootroot00000000000000582002fcc691ac23f9783c9db218187c ants-2.2.0/TestData/Initializer_antsRegistrationTest_TranslationRescaleHeadMasks_Float.mat.md5000066400000000000000000000000401311104306400326710ustar00rootroot00000000000000ddfa2b70dfc92b8ea394be023c31d359ants-2.2.0/TestData/antsApplyTransformsTesting.result.nii.gz.md5000066400000000000000000000000401311104306400246650ustar00rootroot000000000000002a5285e174f08ba8ba43624e29745461ants-2.2.0/TestData/antsApplyTransformsTesting_InputWarpTransform.nii.gz.md5000066400000000000000000000000401311104306400272550ustar00rootroot0000000000000097599dee3450ef9bf31959ee9aa9cd39ants-2.2.0/TestData/antsRegistrationTest_AffineRotationMasks.result.nii.gz.md5000066400000000000000000000000401311104306400274640ustar00rootroot00000000000000b578a088bc5f10660c8369db1c45008fants-2.2.0/TestData/antsRegistrationTest_AffineRotationNoMasks.result.nii.gz.md5000066400000000000000000000000401311104306400277610ustar00rootroot000000000000009cebd84c62819feed72cd31a3bbe38e0ants-2.2.0/TestData/antsRegistrationTest_AffineScaleMasks.result.nii.gz.md5000066400000000000000000000000411311104306400267150ustar00rootroot00000000000000fe05585c5264a75b0f462c5f353ad81c ants-2.2.0/TestData/antsRegistrationTest_AffineScaleNoMasks.result.nii.gz.md5000066400000000000000000000000401311104306400272110ustar00rootroot00000000000000fe05585c5264a75b0f462c5f353ad81cants-2.2.0/TestData/antsRegistrationTest_AffineTranslationMasks.result.nii.gz.md5000066400000000000000000000000401311104306400301630ustar00rootroot000000000000006530f00683b8dda8286969d8fb0dcc9eants-2.2.0/TestData/antsRegistrationTest_AffineTranslationNoMasks.result.nii.gz.md5000066400000000000000000000000401311104306400304600ustar00rootroot000000000000006530f00683b8dda8286969d8fb0dcc9eants-2.2.0/TestData/antsRegistrationTest_CCSimilarityRotationMasks.result.nii.gz.md5000066400000000000000000000000401311104306400306300ustar00rootroot000000000000009fc14ac96152833bbdf7fe12649af9e3ants-2.2.0/TestData/antsRegistrationTest_MSEAffineRotationMasks.result.nii.gz.md5000066400000000000000000000000401311104306400300310ustar00rootroot00000000000000ace4ff2a7053c8ca01621b5df52b7760ants-2.2.0/TestData/antsRegistrationTest_MSESimilarityRotationMasks.result.nii.gz.md5000066400000000000000000000000401311104306400307670ustar00rootroot000000000000005035baae7748e66f1685b429acc151ebants-2.2.0/TestData/antsRegistrationTest_MSESimilarityRotationMasks_Float.result.nii.gz.md5000066400000000000000000000000401311104306400321140ustar00rootroot0000000000000069da3dcf39d1fec7c39fd85de4791c71ants-2.2.0/TestData/antsRegistrationTest_RigidAnisotropicMasks.result.nii.gz.md5000066400000000000000000000000401311104306400300250ustar00rootroot00000000000000d628681badc2cac83560501cd58b83b4ants-2.2.0/TestData/antsRegistrationTest_RigidRotGeomNoMasks.result.nii.gz.md5000066400000000000000000000000401311104306400274040ustar00rootroot000000000000001a80cabf93bb4f1751f99c1c78b854f8ants-2.2.0/TestData/antsRegistrationTest_RigidRotaRotaRotNoMasks.result.nii.gz.md5000066400000000000000000000000401311104306400302500ustar00rootroot00000000000000117f875053b27f2c3ebf1feec0467a03ants-2.2.0/TestData/antsRegistrationTest_RigidRotaRotaRotNoMasks_Float.result.nii.gz.md5000066400000000000000000000000401311104306400313750ustar00rootroot00000000000000eafa6ede1160a069e15224c4f5900139ants-2.2.0/TestData/antsRegistrationTest_RigidRotationHeadMasks.result.nii.gz.md5000066400000000000000000000000401311104306400301140ustar00rootroot0000000000000003c17a83fce98d381b4ecac6cd8830b0ants-2.2.0/TestData/antsRegistrationTest_RigidRotationMasks.result.nii.gz.md5000066400000000000000000000000401311104306400273320ustar00rootroot0000000000000003c17a83fce98d381b4ecac6cd8830b0ants-2.2.0/TestData/antsRegistrationTest_RigidRotationNoMasks.result.nii.gz.md5000066400000000000000000000000401311104306400276270ustar00rootroot0000000000000003c17a83fce98d381b4ecac6cd8830b0ants-2.2.0/TestData/antsRegistrationTest_ScaleRotationRescaleHeadMasks.result.nii.gz.md5000066400000000000000000000000401311104306400314040ustar00rootroot0000000000000005078cb207cee8f34a9e855339277ebbants-2.2.0/TestData/antsRegistrationTest_ScaleRotationRescaleHeadMasks_Float.result.nii.gz.md5000066400000000000000000000000401311104306400325310ustar00rootroot00000000000000da964dae15746e4bb7ac23a471605a9aants-2.2.0/TestData/antsRegistrationTest_ScaleTranslationRescaleHeadMasks.result.nii.gz.md5000066400000000000000000000000401311104306400321030ustar00rootroot000000000000001ce2c5d2f1bcd1a9936f99b13944e80fants-2.2.0/TestData/antsRegistrationTest_SimilarityRotationMasks.result.nii.gz.md5000077500000000000000000000000401311104306400304250ustar00rootroot00000000000000d76e2a02d7747f28ef7171942d51db6fants-2.2.0/TestData/antsRegistrationTest_SimilarityRotationNoMasks.result.nii.gz.md5000077500000000000000000000000401311104306400307220ustar00rootroot00000000000000d8cc0ec6d7ac125e1e01c36bef82979cants-2.2.0/TestData/antsRegistrationTest_SimilarityScaleMasks.result.nii.gz.md5000066400000000000000000000000401311104306400276520ustar00rootroot00000000000000d09bf0c48be7f60a51c6c5d8a31d7561ants-2.2.0/TestData/antsRegistrationTest_SimilarityScaleNoMasks.result.nii.gz.md5000077500000000000000000000000401311104306400301520ustar00rootroot000000000000005b288520ff4ef3079eef456b2d066e01ants-2.2.0/TestData/antsRegistrationTest_SimilarityTranslationRescaleNoMasks.result.nii.gz.md5000077500000000000000000000000401311104306400327200ustar00rootroot00000000000000442e6c2a5868d23acc8ae6f530592ba9ants-2.2.0/TestData/antsRegistrationTest_SimilarityTranslationRescaleNoMasks_Float.result.nii.gz.md5000066400000000000000000000000401311104306400340420ustar00rootroot00000000000000b8b4a6be59b9f4be7a37bda3a067a363ants-2.2.0/TestData/antsRegistrationTest_SyNScaleNoMasks.result.nii.gz.md5000066400000000000000000000000401311104306400265320ustar00rootroot00000000000000755643c096aebb56d110bff63ac68371ants-2.2.0/TestData/antsRegistrationTest_SyNScaleNoMasks_Float.result.nii.gz.md5000066400000000000000000000000401311104306400276570ustar00rootroot00000000000000a97b81fba828e0a89203346dbf198284ants-2.2.0/TestData/antsRegistrationTest_initialize_transforms_per_stageComposite.result.h5.md5000077500000000000000000000000401311104306400332310ustar00rootroot00000000000000f6893d1b89063c7334bd951daa747671ants-2.2.0/TestData/rotation.geom.test.nii.gz.md5000066400000000000000000000000411311104306400215240ustar00rootroot000000000000005dca76db8ec2f8430896a8192bb18bda ants-2.2.0/TestData/rotation.rescale.test.nii.gz.md5000066400000000000000000000000411311104306400222130ustar00rootroot000000000000003705b9d451b1eb52eb7c15d4d5650dd3 ants-2.2.0/TestData/rotation.test.nii.gz.md5000066400000000000000000000000411311104306400205760ustar00rootroot00000000000000e4891faada07461571c27c477e6d736d ants-2.2.0/TestData/rotation.test_mask.nii.gz.md5000066400000000000000000000000411311104306400216110ustar00rootroot00000000000000989827131b94b75a201a7c4a946ca95b ants-2.2.0/TestData/scale.test.nii.gz.md5000066400000000000000000000000411311104306400200260ustar00rootroot000000000000001fa2661a07130796300f23b66d7b5149 ants-2.2.0/TestData/scale.test_mask.nii.gz.md5000066400000000000000000000000411311104306400210410ustar00rootroot000000000000005c57ed69a6dfbab1875ec00879aa6fe2 ants-2.2.0/TestData/test.nii.gz.md5000066400000000000000000000000411311104306400167400ustar00rootroot0000000000000070518dc3306838ebf1a6b187d61a28e9 ants-2.2.0/TestData/test_mask.nii.gz.md5000066400000000000000000000000411311104306400177530ustar00rootroot000000000000005baf8e5fbcc414f3f4e84afd3de2f18a ants-2.2.0/TestData/translation.rescale.test.nii.gz.md5000066400000000000000000000000411311104306400227120ustar00rootroot00000000000000cba09d76112bb66e183b6abcd5b7ec00 ants-2.2.0/TestData/translation.test.nii.gz.md5000066400000000000000000000000411311104306400212750ustar00rootroot000000000000000acc2a5a1f20ed373c49103e189ba549 ants-2.2.0/TestData/translation.test_mask.nii.gz.md5000066400000000000000000000000411311104306400223100ustar00rootroot00000000000000bf6514195fffb4c61cc1f1894b24f59e ants-2.2.0/Utilities/000077500000000000000000000000001311104306400144245ustar00rootroot00000000000000ants-2.2.0/Utilities/ANTSMakeMD5SigFileAndMoveData.py000066400000000000000000000046441311104306400221660ustar00rootroot00000000000000# \author Hans J. Johnson # This file will help store new data # in a publicly available web location # for the purpose of allowing larger # testing data to be separate from # the source tree. # # NOTE: This is not a global solution # for any developer. Only members # of the Iowa PINC lab have access # to generate data in this way. # # import hashlib import os import argparse import sys import shutil def md5_for_file(f, block_size=2**20): """Generate a hash key from a file""" md5 = hashlib.md5() while True: data = f.read(block_size) if not data: break md5.update(data) return md5.hexdigest() if __name__ == '__main__': defaultCMakeRepository='/iplweb/html/users/brainstestdata/ctestdata' parser = argparse.ArgumentParser(description="Program to move local test data to external repository") parser.add_argument('--src',dest='sourceFile',help='The source file to be moved.') parser.add_argument('--dest',dest='destPath',default=defaultCMakeRepository,help='The external repository location') args = parser.parse_args() fileName = args.sourceFile # Do not upload file hashes of file hashs. To prevent infinite recursion when using sloppy glob expressions! if fileName[-4:] == ".md5": print("Skipping md5 file: {0}".format(fileName)) exit(0) print("Preparing: {0}".format(fileName)) localPublicPath = args.destPath algo = 'MD5' f = open(fileName) value = md5_for_file(f) f.close() #print('MD5 value={0}'.format(value)) #print('MD5 value={0}'.format('59871b1b19b16a3c04d752f54bbf8bfd')) source = fileName destPath=localPublicPath+'/'+algo if not os.path.exists(destPath): os.mkdir(destPath) dest = destPath +'/'+value print("mv -f {0} {1}".format(source,dest)) md5FileName=fileName+'.md5' f = open(md5FileName,'w') f.write(value) f.close() finalDestination=destPath+'/'+os.path.basename(md5FileName) if os.path.exists(finalDestination): print("Destination file already exists: SKIPPING {0}".format(finalDestination)) else: shutil.copyfile(md5FileName,destPath+'/'+os.path.basename(md5FileName)) shutil.copy(source,dest) os.unlink(source) ## if prepareing data remotely, echo a helpful rsync command needed to push from remote destination to IPL via rsync if args.destPath != defaultCMakeRepository: print('rsync -av {0}/ neuron.psychiatry.uiowa.edu:{1}/'.format(args.destPath,defaultCMakeRepository)) ants-2.2.0/Utilities/BinaryImageToMeshFilter.h000066400000000000000000000403201311104306400212510ustar00rootroot00000000000000/******************************************************************************** * Create a VTK mesh from the white matter image *******************************************************************************/ #ifndef __BinaryImageToMeshFilter_h_ #define __BinaryImageToMeshFilter_h_ #include #include "itkImage.h" #include "itkResampleImageFilter.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkAntiAliasBinaryImageFilter.h" #include "itkUnaryFunctorImageFilter.h" // #include "itkBinaryWellComposed3DImageFilter.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkCommand.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; template void ConnectITKToVTK(itk::VTKImageExport *fltExport, vtkImageImport *fltImport) { fltImport->SetUpdateInformationCallback( fltExport->GetUpdateInformationCallback() ); fltImport->SetPipelineModifiedCallback( fltExport->GetPipelineModifiedCallback() ); fltImport->SetWholeExtentCallback( fltExport->GetWholeExtentCallback() ); fltImport->SetSpacingCallback( fltExport->GetSpacingCallback() ); fltImport->SetOriginCallback( fltExport->GetOriginCallback() ); fltImport->SetScalarTypeCallback( fltExport->GetScalarTypeCallback() ); fltImport->SetNumberOfComponentsCallback( fltExport->GetNumberOfComponentsCallback() ); fltImport->SetPropagateUpdateExtentCallback( fltExport->GetPropagateUpdateExtentCallback() ); fltImport->SetUpdateDataCallback( fltExport->GetUpdateDataCallback() ); fltImport->SetDataExtentCallback( fltExport->GetDataExtentCallback() ); fltImport->SetBufferPointerCallback( fltExport->GetBufferPointerCallback() ); fltImport->SetCallbackUserData( fltExport->GetCallbackUserData() ); } class UnaryFunctorBinaryToFloat { public: UnaryFunctorBinaryToFloat() { m_InsideValue = 1.0f; } void SetInvertImage(bool invert) { m_InsideValue = invert ? -1.0f : 1.0f; } inline float operator()(short in) { return in == 0 ? -m_InsideValue : m_InsideValue; } inline bool operator !=(const UnaryFunctorBinaryToFloat & otro) const { return this->m_InsideValue != otro.m_InsideValue; } private: float m_InsideValue; }; template class BinaryImageToMeshFilter : public itk::ProcessObject { public: typedef BinaryImageToMeshFilter Self; typedef itk::ProcessObject Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; typedef itk::Image FloatImageType; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TImage::ImageDimension); /** Get the result mesh */ vtkPolyData * GetMesh() { return fltTriangle->GetOutput(); } /** Get the intermediate antialiased image */ FloatImageType * GetAntiAliasImage() { return fltAlias->GetOutput(); } /** Whether to invert the binary image */ itkSetMacro(InvertInput, bool); itkGetMacro(InvertInput, bool); /** Whether to invert the binary image */ itkSetMacro(ResampleScaleFactor, float); itkGetMacro(ResampleScaleFactor, float); /** Whether to decimate the mesh and how much, 0 for none*/ itkSetMacro(DecimateFactor, double); itkGetMacro(DecimateFactor, double); /** Smoothing iterations, or 0 for none */ itkSetMacro(SmoothingIterations, int); itkGetMacro(SmoothingIterations, int); /** Set the input */ using Superclass::SetInput; virtual void SetInput(TImage *image) { this->SetNthInput(0, image); } /** Update method (why?) */ void Update() { this->GenerateData(); } /** Set the anti-aliasing quality parameter */ void SetAntiAliasMaxRMSError(double value) { fltAlias->SetMaximumRMSError(value); } /** Get the 'distance image' based on anti-aliasing the binary image */ FloatImageType * GetDistanceImage() { return fltAlias->GetOutput(); } /** Get the floating point 'resampled' image, if resampling was enabled */ FloatImageType * GetResampledImage() { if( m_ResampleScaleFactor != 1.0f ) { return fltResample->GetOutput(); } else { return fltToFloat->GetOutput(); } } void PrintMeshStatistics(vtkPolyData *mesh) { vector cellHist(100, 0); for( vtkIdType i = 0; i < mesh->GetNumberOfCells(); i++ ) { cellHist[mesh->GetCellType(i)]++; } cout << " mesh has " << mesh->GetNumberOfPoints() << " points." << endl; cout << " mesh has " << mesh->GetNumberOfCells() << " cells. " << endl; cout << " mesh has " << cellHist[VTK_VERTEX] << " vtk_vertex" << endl; cout << " mesh has " << cellHist[VTK_LINE] << " vtk_line" << endl; cout << " mesh has " << cellHist[VTK_POLY_LINE] << " vtk_poly_line" << endl; cout << " mesh has " << cellHist[VTK_TRIANGLE] << " vtk_triangle" << endl; cout << " mesh has " << cellHist[VTK_TRIANGLE_STRIP] << " vtk_triangle_strip" << endl; } protected: BinaryImageToMeshFilter() { // Set the cardinality of the filter this->SetNumberOfIndexedInputs(1); this->SetNumberOfIndexedOutputs(1); // Begin with the well-connectedness filter // fltTopology = TopologyFilter::New(); // Create the converter to float fltToFloat = ToFloatFilter::New(); // fltToFloat->SetInput(this->GetInput());//fltTopology->GetOutput()); typename FloatImageType::Pointer imgPipeEnd = fltToFloat->GetOutput(); // Initialize the interpolation function fnInterpolate = ResampleFunction::New(); // Create a resampling image filter fltResample = ResampleFilter::New(); fltResample->SetInput(imgPipeEnd); fltResample->SetTransform(itk::IdentityTransform::New() ); fltResample->SetInterpolator(fnInterpolate); imgPipeEnd = fltResample->GetOutput(); // Create an anti-aliasing image filter fltAlias = AAFilter::New(); fltAlias->SetMaximumRMSError(0.024); fltAlias->SetInput(imgPipeEnd); imgPipeEnd = fltAlias->GetOutput(); // Cast the image to VTK fltExport = ExportFilter::New(); fltImport = vtkImageImport::New(); fltExport->SetInput(imgPipeEnd); ConnectITKToVTK(fltExport.GetPointer(), fltImport); // Compute marching cubes vtkImageData *importPipeEnd = fltImport->GetOutput(); fltMarching = vtkImageMarchingCubes::New(); fltMarching->SetInputData(importPipeEnd); fltMarching->ComputeScalarsOff(); fltMarching->ComputeGradientsOff(); fltMarching->SetNumberOfContours(1); fltMarching->SetValue(0, 0.0f); vtkPolyData *meshPipeEnd = fltMarching->GetOutput(); // Keep the largest connected component fltConnect = vtkPolyDataConnectivityFilter::New(); fltConnect->SetInputData(meshPipeEnd); fltConnect->SetExtractionModeToLargestRegion(); meshPipeEnd = fltMarching->GetOutput(); fltClean = vtkCleanPolyData::New(); bool doclean = false; if ( doclean ) { // Clean up the data fltClean->SetInputData(meshPipeEnd); meshPipeEnd = fltClean->GetOutput(); // Decimate the data fltDecimate = vtkDecimatePro::New(); fltDecimate->SetInputData(meshPipeEnd); fltDecimate->PreserveTopologyOn(); meshPipeEnd = fltDecimate->GetOutput(); } // Smooth the data, keeping it on the contour vtkSmartPointer smoothFilter = vtkSmartPointer::New(); smoothFilter->SetInputData(meshPipeEnd); smoothFilter->SetNumberOfIterations(5); meshPipeEnd = smoothFilter->GetOutput(); // Compute triangle strips for faster display fltTriangle = vtkTriangleFilter::New(); fltTriangle->SetInputData(meshPipeEnd); meshPipeEnd = fltTriangle->GetOutput(); // Set up progress typedef itk::MemberCommand CommandType; typename CommandType::Pointer cmd = CommandType::New(); cmd->SetCallbackFunction(this, &Self::ProgressCommand); // Add progress to the two slow filters fltResample->AddObserver(itk::ProgressEvent(), cmd); fltAlias->AddObserver(itk::ProgressEvent(), cmd); // Invert - NO m_InvertInput = false; // Resample - NO m_ResampleScaleFactor = 1.0f; // Decimate - NO m_DecimateFactor = 0.0f; } ~BinaryImageToMeshFilter() { // CLean up fltMarching->Delete(); fltConnect->Delete(); fltImport->Delete(); fltTriangle->Delete(); fltClean->Delete(); fltDecimate->Delete(); fltSmoothMesh->Delete(); } /** Generate Data */ virtual void GenerateData( void ) { // Run the computation cout << "Computing mesh from binary image" << endl; // Get the input and output pointers typename TImage::ConstPointer inputImage = reinterpret_cast(this->GetInput(0) ); // Pass the input to the topology filter // fltTopology->SetInputData(inputImage); // Compute the max/min of the image to set fore/back typedef itk::MinimumMaximumImageCalculator CalcType; typename CalcType::Pointer calc = CalcType::New(); calc->SetImage(inputImage); calc->Compute(); // Set the background and foreground in the topology filter // fltTopology->SetBackgroundValue(calc->GetMinimum()); // fltTopology->SetForegroundValue(calc->GetMaximum()); // fltTopology->Update(); // Pass the input to the remapper fltToFloat->SetInput(inputImage); // Set the inversion if necessary m_ToFloatFunctor.SetInvertImage(m_InvertInput); fltToFloat->SetFunctor(m_ToFloatFunctor); // Convert the image to floats fltToFloat->Update(); // Peform resampling only if necessary if( m_ResampleScaleFactor != 1.0 ) { // Include the filter in the pipeline fltAlias->SetInput(fltResample->GetOutput() ); // Set the size parameter FloatImageType::SizeType szOutput = inputImage->GetBufferedRegion().GetSize(); szOutput[0] = (unsigned long) (szOutput[0] * m_ResampleScaleFactor); szOutput[1] = (unsigned long) (szOutput[1] * m_ResampleScaleFactor); szOutput[2] = (unsigned long) (szOutput[2] * m_ResampleScaleFactor); fltResample->SetSize(szOutput); // Set the scale and origin FloatImageType::SpacingType xSpacing = inputImage->GetSpacing(); xSpacing[0] /= m_ResampleScaleFactor; xSpacing[1] /= m_ResampleScaleFactor; xSpacing[2] /= m_ResampleScaleFactor; fltResample->SetOutputSpacing(xSpacing); fltResample->SetOutputOrigin(inputImage->GetOrigin() ); // Compute the resampling cout << " resampling the image " << endl; fltResample->Update(); cout << endl; } else { // Exclude the filter from the pipeline fltAlias->SetInput(fltToFloat->GetOutput() ); } // Run the filters if( fltAlias->GetMaximumRMSError() > 0.0 ) { cout << " anti-aliasing the image " << endl; fltAlias->Update(); fltExport->SetInput(fltAlias->GetOutput() ); } else { fltExport->SetInput(fltAlias->GetInput() ); } bool verbose = true; if( verbose ) { cout << endl << " converting image to VTK" << endl; } fltImport->Update(); if( verbose ) { cout << " running marching cubes algorithm" << endl; } fltMarching->Update(); if( verbose ) { cout << " mesh has " << fltMarching->GetOutput()->GetNumberOfCells() << " cells and " << fltMarching->GetOutput()->GetNumberOfPoints() << " points. " << endl; } if( verbose ) { cout << " extracting the largest component" << endl; } fltConnect->Update(); if( verbose ) { cout << " mesh has " << fltConnect->GetOutput()->GetNumberOfCells() << " cells and " << fltConnect->GetOutput()->GetNumberOfPoints() << " points. " << endl; } bool doclean = false; if ( doclean ) { if (verbose) cout << " cleaning the mesh " << endl; fltClean->Update(); if (verbose) cout << " after clean step mesh uses " << m_DecimateFactor << " decimation " << fltClean->GetOutput()->GetNumberOfCells() << " cells and " << fltClean->GetOutput()->GetNumberOfPoints() << " points. " << endl; } std::cout << " to decimation with factor " << m_DecimateFactor << std::endl; // If decimation is on, run it if( m_DecimateFactor > 0.0 ) { if( verbose ) { cout << " decimating the mesh by factor of " << m_DecimateFactor << endl; } fltDecimate->SetTargetReduction(m_DecimateFactor); fltDecimate->SetInputData(fltConnect->GetOutput()); fltDecimate->Update(); fltTriangle->SetInputData(fltDecimate->GetOutput() ); // if (verbose) cout << " mesh has " // << fltClean->GetOutput()->GetNumberOfCells() << " cells and " // << fltClean->GetOutput()->GetNumberOfPoints() << " points. " << endl; } else { fltTriangle->SetInputData(fltConnect->GetOutput()); } if( verbose ) { cout << " converting mesh to triangles" << endl; } fltTriangle->Update(); m_Result = fltTriangle->GetOutput(); if( verbose ) { cout << " mesh has " << m_Result->GetNumberOfCells() << " cells and " << m_Result->GetNumberOfPoints() << " points. " << endl; } // If smoothing is on, run it if( m_SmoothingIterations > 0 ) { if( verbose ) { cout << " smoothing the mesh " << m_SmoothingIterations << endl; } fltSmoothMesh->SetNumberOfIterations(m_SmoothingIterations); fltSmoothMesh->SetInputData(m_Result); // fltSmoothMesh->SetInputConnection(m_Result); // BA FIXME fltSmoothMesh->Update(); m_Result = fltSmoothMesh->GetOutput(); std::cout << " Done " << std::endl; } } private: typedef itk::ImageRegionIterator IteratorType; typedef itk::ImageRegionConstIterator ConstIteratorType; // Topology correction filter // typedef itk::BinaryWellComposed3DImageFilter TopologyFilter; // Functor for remapping to float UnaryFunctorBinaryToFloat m_ToFloatFunctor; // Filter to remap image to floating point typedef itk::UnaryFunctorImageFilter< TImage, FloatImageType, UnaryFunctorBinaryToFloat> ToFloatFilter; // Windowed sinc for resampling typedef itk::Function::WelchWindowFunction<4> WindowFunction; typedef itk::NearestNeighborInterpolateImageFunction< FloatImageType, double> ResampleFunction; // Filter to resample image typedef itk::ResampleImageFilter ResampleFilter; // Antialiasing filter typedef itk::AntiAliasBinaryImageFilter AAFilter; // Export to VTK filter typedef itk::VTKImageExport ExportFilter; // typename TopologyFilter::Pointer fltTopology; typename AAFilter::Pointer fltAlias; typename ExportFilter::Pointer fltExport; typename ToFloatFilter::Pointer fltToFloat; typename ResampleFilter::Pointer fltResample; typename ResampleFunction::Pointer fnInterpolate; vtkImageImport * fltImport; vtkImageMarchingCubes * fltMarching; vtkPolyDataConnectivityFilter *fltConnect; vtkCleanPolyData * fltClean; vtkTriangleFilter * fltTriangle; vtkDecimatePro * fltDecimate; vtkSmoothPolyDataFilter * fltSmoothMesh; bool m_InvertInput; float m_ResampleScaleFactor; double m_DecimateFactor; int m_SmoothingIterations; vtkPolyData *m_Result; void ProgressCommand(itk::Object *source, const itk::EventObject & /*evt*/) { // Get the elapsed progress itk::ProcessObject *po = reinterpret_cast(source); float progress = po->GetProgress(); cout << setprecision(4) << (100 * progress) << " " << flush; } }; #endif ants-2.2.0/Utilities/DevelopmentSetupScripts/000077500000000000000000000000001311104306400212775ustar00rootroot00000000000000ants-2.2.0/Utilities/DevelopmentSetupScripts/SetupHooks.sh000077500000000000000000000041351311104306400237450ustar00rootroot00000000000000#!/usr/bin/env bash #========================================================================== # # Copyright Insight Software Consortium # # 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. # #==========================================================================*/ # Run this script to set up the git hooks for committing changes to ANTs. egrep-q() { egrep "$@" >/dev/null 2>/dev/null } die() { echo 'failure during hook setup' 1>&2 echo '-------------------------' 1>&2 echo '' 1>&2 echo "$@" 1>&2 exit 1 } u=$(cd "$(echo "$0"|sed 's/[^/]*$//')"; pwd) cd "$u/../../.git/hooks" # Symlink the hooks in from a local dir. for file in ../../Utilities/Hooks/* ../../Utilities/Hooks/.*; do base=$(basename "$file") if [ x"$base" == x"." -o x"$base" == x".." ]; then continue fi rm "$base" ln -s "$file" done # Set up uncrustify hook. echo "Setting up the uncrustify hook..." git config hooks.uncrustify.conf "Utilities/Maintenance/uncrustify.cfg" # Set up KWStyle hook. echo "Setting up the KWStyle hook..." git config hooks.KWStyle.conf "Utilities/KWStyle/kws.xml.in" git config hooks.KWStyle.overwriteRulesConf "Utilities/KWStyle/Overwrite.txt" git config hooks.KWStyle false # Set up cppcheck hook. echo "Setting up the cppcheck hook..." git config hooks.cppcheck true # Set up hook chaining. echo "Setting up hook chaining: prepare-commit-msg, commit-msg, pre-commit" git config hooks.chain-prepare-commit-msg Utilities/Hooks/prepare-commit-msg git config hooks.chain-commit-msg Utilities/Hooks/commit-msg git config hooks.chain-pre-commit Utilities/Hooks/pre-commit echo "Done." ants-2.2.0/Utilities/Hooks/000077500000000000000000000000001311104306400155075ustar00rootroot00000000000000ants-2.2.0/Utilities/Hooks/commit-msg000077500000000000000000000044301311104306400175120ustar00rootroot00000000000000#!/usr/bin/env bash #========================================================================== # # Copyright Insight Software Consortium # # 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. # #==========================================================================*/ egrep-q() { egrep "$@" >/dev/null 2>/dev/null } # Prepare a copy of the message: # - strip comment lines # - stop at "diff --git" (git commit -v) commit_msg="$GIT_DIR/COMMIT_MSG" sed -n -e '/^#/d' -e '/^diff --git/q' -e 'p;d' "$1" > "$commit_msg" die() { echo 'commit-msg hook failure' 1>&2 echo '-----------------------' 1>&2 echo '' 1>&2 echo "$@" 1>&2 echo ' To continue editing, run the command git commit -e -F '"$commit_msg"' (assuming your working directory is at the top).' 1>&2 exit 1 } # Check the first line for a standard prefix. cat "$commit_msg" | while IFS='' read line; do # Look for the first non-empty line. len=$(echo -n "$line" | wc -c) if test $len -gt 0; then # Look for valid prefixes. echo "$line" | egrep-q '^(Merge|Revert|BUG:|COMP:|DOC:|ENH:|PERF:|STYLE:|WIP:) ' || die 'Start BRAINS commit messages with a standard prefix (and a space): BUG: - fix for runtime crash or incorrect result COMP: - compiler error or warning fix DOC: - documentation change ENH: - new functionality PERF: - performance improvement STYLE: - no logic impact (indentation, comments) WIP: - Work In Progress not ready for merge To reference Mantis issue XY, add " (#XY)" to the END of the FIRST line.' # Reject bug number reference that CDash rejects. echo "$line" | egrep-q '^BUG: [0-9][0-9]*\.' && die 'Do not put a "." after the bug number: '"$line" # Done. Skip rest of lines. break fi done && rm -f "$commit_msg" || exit 1 ants-2.2.0/Utilities/Hooks/pre-commit000077500000000000000000000320511311104306400175120ustar00rootroot00000000000000#!/usr/bin/env bash #========================================================================== # # Copyright Insight Software Consortium # # 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. # #==========================================================================*/ echo "Running the pre-commit hooks to keep the code base nice and clean" prohibited_list='~|\.o|\.a|\.lib|\.dyld|\.nii\.gz|\.nrrd|\.nhdr|\.img|\.hdr|\.img\.gz|\.hdr\.gz|\.png|\.tiff|\.jpeg' if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # If you want to allow non-ascii filenames set this variable to true. allownonascii=$(git config hooks.allownonascii) # Cross platform projects tend to avoid non-ascii filenames; prevent # them from being added to the repository. We exploit the fact that the # printable range starts at the space character and ends with tilde. if [ "$allownonascii" != "true" ] && # Note that the use of brackets around a tr range is ok here, (it's # even required, for portability to Solaris 10's /usr/bin/tr), since # the square bracket bytes happen to fall in the designated range. test "$(git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0')" then echo "Error: Attempt to add a non-ascii file name." echo echo "This can cause problems if you want to work" echo "with people on other platforms." echo echo "To be portable it is advisable to rename the file ..." echo echo "If you know what you are doing you can disable this" echo "check using:" echo echo " git config hooks.allownonascii true" echo exit 1 fi # If you want to allow non-standard filenames set this variable to true. allownonstandard=$(git config hooks.allownonstandard) # Cross platform projects tend to avoid non-standard filenames; prevent # them from being added to the repository. We exploit the fact that the # printable range starts at the space character and ends with tilde. egrep_prohibited_list="^.*(${prohibited_list})\$" if [ "$allownonstandard" != "true" ]; then for file in "$(git diff --cached --name-only --diff-filter=A)"; do # echo "Evaluating filename for $file" if [ $(echo $file | egrep -c $egrep_prohibited_list) -ne 0 ]; then echo "Error: Attempt to add a non-standard file name:" echo "($prohibited_list)." echo echo "This can cause problems if you want to work" echo "with people on other platforms." echo echo "To be portable it is advisable to rename the file ..." echo echo "If you know what you are doing you can disable this" echo "check using:" echo echo " git config hooks.allownonstandard true" echo exit 1 fi done fi #--- die() { echo 'pre-commit hook failure' 1>&2 echo '-----------------------' 1>&2 echo '' 1>&2 echo "$@" 1>&2 exit 1 } zero='0000000000000000000000000000000000000000' #----------------------------------------------------------------------------- # Check content that will be added by this commit. if git rev-parse --verify -q HEAD > /dev/null; then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # Merge ("git commit" after "git merge" with conflicts or --no-commit) merge_head=$(git rev-parse -q --verify MERGE_HEAD) || merge_head='' # Disallow non-ascii file names. The printable range starts at the # space character and ends with tilde. if test "$(git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0')"; then die 'Non-ascii file names may not be added: '"$(git diff --cached --name-only --diff-filter=A $against)" fi #----------------------------------------------------------------------------- # Builtin whitespace checks. bad=$(git diff-index --check --cached $against --) || die "$bad" # Approximate whitespace=tab-in-indent check with Git < 1.7.2. git --version | grep -q " \(1\.[0-6]\|1\.7\.[01]\)" && approx_tab_in_indent=true || approx_tab_in_indent=false check_tab() { lines=$(git diff-index -p --cached $against -- "$1" | grep '^+ ') && echo "$lines" | while read line; do echo "$1: tab in indent." && echo "$line" done } # Reject addition of a line without a newline at end-of-file. check_no_lf_at_eof() { lines=$(git diff-index -p --cached $against -- "$1" | tail -2) if echo "$lines" | head -1 | grep -q '^+' && echo "$lines" | tail -1 | grep -q '^\\ No newline'; then echo "$1: No newline at end of file" fi } # Custom whitespace checks. check_whitespace() { ws=$(git check-attr whitespace -- "$file" | sed 's/^[^:]*: whitespace: //') if $approx_tab_in_indent; then case ",$ws," in *,tab-in-indent,*) check_tab "$1" ;; esac fi case ",$ws," in *,no-lf-at-eof,*) check_no_lf_at_eof "$1" ;; esac } bad=$(git diff-index --name-only --cached $against -- | while read file; do check_whitespace "$file" done) test -z "$bad" || die "$bad" #----------------------------------------------------------------------------- # Check file modes and sizes. mode_looks_exe() { case "$1" in *.bat) return 0 ;; *.cmd) return 0 ;; *.exe) return 0 ;; *.com) return 0 ;; esac git cat-file blob "$2" | head -1 | grep "^#!/" > /dev/null } mode_not_exe () { echo "The file '$file' has looks executable but does not have an executable mode." } mode_bad_exe () { echo "The file '$file' has executable mode but does not look executable." } mode_non_file () { echo "The path '$file' has a non-file mode." } check_mode() { case "$dst_mode" in 100755) mode_looks_exe "$file" "$dst_obj" || mode_bad_exe ;; 100644) mode_looks_exe "$file" "$dst_obj" && mode_not_exe ;; 160000) ;; *) mode_non_file ;; esac } size_max_KiB=$(git config hooks.MaxObjectKiB) test -n "$size_max_KiB" || size_max_KiB=1024 size_too_large_once="" size_too_large_once() { test -z "$size_too_large_once" || return ; size_too_large_once=done echo 'At least one file is staged for commit with size larger than its limit. We prefer to keep large files out of the main source tree, especially binary files that do not compress well. This hook disallows large files by default but can be configured. A limit for specific files or patterns may be set in ".gitattributes" with the "hooks.MaxObjectKiB" attribute. For example, the line *.c hooks.MaxObjectKiB=2048 sets a limit of 2048 KiB for C source files. See "git help attributes" for details on the .gitattributes format. If no attribute has been set for a given file then its size is limited by the local default. Run git config hooks.MaxObjectKiB $KiB to set the local default limit (0 to disable). ' } size_too_large() { size_too_large_once echo "The path '$file' has size $file_KiB KiB, greater than allowed $max_KiB KiB." } size_validate_max_KiB() { test "$max_KiB" -ge "0" 2>/dev/null && return 0 echo "The path '$file' has invalid attribute \"hooks-MaxObjectKiB=$max_KiB\"." return 1 } check_size() { test "$dst_obj" != "$zero" || return max_KiB=$(git check-attr hooks.MaxObjectKiB -- "$file" | sed 's/^[^:]*: hooks.MaxObjectKiB: //') case "$max_KiB" in 'unset') return ;; # No maximum for this object. 'set') max_KiB="$size_max_KiB" ;; # Use local default. 'unspecified') max_KiB="$size_max_KiB" ;; # Use local default. *) size_validate_max_KiB || return ;; esac if test "$max_KiB" -gt "0"; then file_KiB=$(expr '(' $(git cat-file -s "$dst_obj") + 1023 ')' / 1024) test "$file_KiB" -le "$max_KiB" || size_too_large fi } short_commit() { git rev-parse --short "$1" 2>/dev/null || echo "$1" } lookup_config_module_update() { update=$(git config "hooks.$1.update") # Special-case "true" to accept any update. test "{$update}" = "{true}" && echo '.' && return # Format is "aaaaaa..bbbbbb" for update aaaaaa => bbbbbb. # Convert to regex "^aaaaaa[a-z0-9]* bbbbbb[a-z0-9]*$". sha1ex='[a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9]*' regex='^\('"$sha1ex"'\)\.\.\('"$sha1ex"'\)$' echo "$update" | sed -n "/$regex/ {s/$regex/"'^\1[a-z0-9]* \2[a-z0-9]*$/;p;}' | grep '.' # Return false if result is empty. } check_module() { enabled=$(git config --get --bool hooks.submodule) || enabled=true test "$enabled" = "false" && return # Allow merged submodule updates. test -n "$merge_head" && merge_obj=$(git rev-parse -q --verify "$merge_head:$file") && test "$merge_obj" = "$dst_obj" && return # Allow module-only commits without extra work. test -z "$diffs_normal" && return # Check if module update is allowed with other changes. allow=$(lookup_config_module_update "$file") || allow='none' if echo "$src_obj $dst_obj" | grep "$allow" > /dev/null; then return fi src_short=$(short_commit "$src_obj") dst_short=$(short_commit "$dst_obj") echo 'A submodule link is staged for commit (among other changes): "'"$file"'" '"$src_short => $dst_short"' This may occur by accident so we require an extra step to commit. If you intend to include this change in your commit, run git config "hooks.'"$file"'.update" '"$src_short..$dst_short"' to explicitly allow the change and try the commit again. Otherwise run git reset HEAD -- "'"$file"'" to unstage the change. Furthermore, if you did not intend to modify the submodule at all, also run git submodule update -- "'"$file"'" to checkout the current version of the submodule in your work tree. Test your changes again to see if they still work with the module. Finally, try the commit again. ' return 1 } check_module_rewind() { parent_name="$1" parent_commit="$2" base=$(GIT_DIR="$file/.git" \ git merge-base $src_obj $dst_obj 2>/dev/null) || base='' test "$base" != "$dst_obj" && return parent_short=$(short_commit "$parent_commit") src_short=$(GIT_DIR="$file/.git" short_commit "$src_obj") dst_short=$(GIT_DIR="$file/.git" short_commit "$dst_obj") echo 'This commit would rewind a submodule link: "'"$file"'" '"$src_short => $dst_short"' from the newer version in '"$parent_name"' ('"$parent_short"'). Run git reset '"$parent_name"' -- "'"$file"'" git submodule update -- "'"$file"'" to checkout the newer version of the submodule in your work tree. Then try the commit again. ' return 1 } diffs=$(git diff-index --cached $against -- | sed -n '/^:[^:]/ {s/^://;p;}') diffs_normal=$(echo "$diffs" | grep -v '^...... 160000') diffs_module=$(echo "$diffs" | grep '^...... 160000') bad=$( test -n "$diffs_normal" && echo "$diffs_normal" | while read src_mode dst_mode src_obj dst_obj status file; do # if test "$src_mode" != "$dst_mode" -a "$dst_mode" != "000000"; then # check_mode # fi if test "$dst_mode" != "160000" -a "$dst_mode" != '000000'; then check_size fi done test -n "$diffs_module" && echo "$diffs_module" | while read src_mode dst_mode src_obj dst_obj status file; do check_module_rewind HEAD "$against" && check_module || break done ) test -z "$bad" || die "$bad" ##----------------------------------------------------------------------------- ## Do not allow adding of files with .txx extension. diffs_added=$(git diff-index --diff-filter=A --cached $against -- | sed -n '/^:[^:]/ {s/^://;p;}') diffs_normal_added=$(echo "$diffs" | grep -v '^...... 160000') bad=$( test -n "$diffs_normal" && echo "$diffs_normal" | while read src_mode dst_mode src_obj dst_obj status file; do if echo "$file" | grep -q -E '\.txx$'; then echo "Attempted to add file $file." echo "Files with the .txx extension are not allowed -- use .hxx instead." fi done ) test -z "$bad" || die "$bad" #----------------------------------------------------------------------------- # Merge checks. if test -n "$merge_head"; then merge_diffs=$(git diff-index --cached $merge_head -- | sed -n '/^:[^:]/ {s/^://;p;}') else merge_diffs='' fi merge_diffs_normal=$(echo "$merge_diffs" | grep -v '^...... 160000') merge_diffs_module=$(echo "$merge_diffs" | grep '^...... 160000') bad=$( test -n "$merge_diffs_module" && echo "$merge_diffs_module" | while read src_mode dst_mode src_obj dst_obj status file; do check_module_rewind MERGE_HEAD "$merge_head" || break done ) test -z "$bad" || die "$bad" #----------------------------------------------------------------------------- # Style hooks. . "$GIT_DIR/hooks/pre-commit-style" #----------------------------------------------------------------------------- # Chain to project-specific hook. #. "$GIT_DIR/hooks/hooks-chain.bash" #hooks_chain pre-commit "$@" # # vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : exec git diff-index --check --cached $against -- ants-2.2.0/Utilities/Hooks/pre-commit-style000077500000000000000000000234261311104306400206560ustar00rootroot00000000000000#============================================================================= # Copyright 2010-2011 Kitware, Inc. # # 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 # # 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. #============================================================================= echo "Running Style" # Run uncrustify and KWStyle pre-commit hooks. # # 'git config' is used to enable the hooks and set their configuration files. # The repository .gitattributes must also enable the hooks on the targeted # files. do_KWStyle=$(git config --bool hooks.KWStyle) || do_KWStyle=false do_uncrustify=$(git config --bool hooks.uncrustify) || do_uncrustify=false #----------------------------------------------------------------------------- # Check if we want to run the style on a given file. Uses git attributes. If # the hook.style attribute is set, then all styles are executed. If the # hook.style attribute is set to a value, only the values given are executed. # Also, do not run the style check if there are unstaged changes in the file. # The first positional parameter is the file to check. # The second positional parameter is the style to check. # Returns 0 for execute, 1 for don't execute. run_style_on_file() { # Do not run on submodule changes. if git diff-index --cached HEAD -- "$1" | grep -q '^:...... 160000'; then return 1 fi if ! git diff-files --quiet -- "$1"; then # A way to always allow skipping. skip_unstaged=$(git config --bool hooks.styleSkipUnstaged) || skip_unstaged=false file_sha=$(git diff-index --cached --abbrev=7 HEAD -- "$1" | \ awk '{print substr($3,1,9) substr($4,1,7)}') if file_skip_unstaged=$(git config "hooks.$1.styleSkipUnstaged"); then if test ",$file_skip_unstaged," = ",$file_sha," -o \ ",$file_skip_unstaged," = ",true,"; then skip_unstaged=true fi fi if $skip_unstaged; then echo "The file '$1' contains unstaged stages. Skipping style \ check '$2'." else die "Style check '$2' cannot run on '$1' with unstaged stages. Allow skipping the style check for this commit with git config \"hooks.$1.styleSkipUnstaged\" $file_sha" fi return 1 fi style=$(git check-attr hooks.style -- "$1" | sed 's/^[^:]*: hooks.style: //') case "$style" in 'unset') return 1 ;; 'set') return 0 ;; 'unspecified') return 1 ;; *) echo ",$style," | grep -iq ",$2," && return 0 ;; esac return 1 } #----------------------------------------------------------------------------- # KWStyle. check_for_KWStyle() { KWStyle_path=$(git config hooks.KWStyle.path) || KWStyle_path=$(which KWStyle) if [ $? != 0 ] ; then echo "KWStyle executable was not found. No style verification will be performed with KWStyle! Please install KWStyle or set the executable location with git config hooks.KWStyle.path /path/to/KWStyle See http://public.kitware.com/KWStyle/ " >&2 return 1 fi KWStyle_conf=$(git config hooks.KWStyle.conf) if ! test -f "$KWStyle_conf"; then die "The file '$KWStyle_conf' does not exist. Please run git config hooks.KWStyle.conf path/to/KWStyle.conf.xml" fi KWStyle_overWriteRulesConf=$(git config hooks.KWStyle.overwriteRulesConf) if test $? -eq 0 && ! test -f "$KWStyle_overWriteRulesConf"; then die "The hooks.KWStyle.overwriteRulesConf file '$KWStyle_overWriteRulesConf' does not exist." fi } run_KWStyle_on_file() { if test -z "$KWStyle_overWriteRulesConf"; then "$KWStyle_path" -v -xml "$KWStyle_conf" "$1" else "$KWStyle_path" -v -xml "$KWStyle_conf" -o "$KWStyle_overWriteRulesConf" "$1" fi if test $? -ne 0; then cp -- "$1"{,.kws} die "KWStyle check failed. Line numbers in the errors shown refer to the file: ${1}.kws" fi } run_KWStyle() { git diff-index --cached --diff-filter=ACMR --name-only HEAD -- | while read f; do if run_style_on_file "$f" KWStyle; then run_KWStyle_on_file "$f" fi || return done } #----------------------------------------------------------------------------- # uncrustify. check_for_uncrustify() { uncrustify_path=$(git config hooks.uncrustify.path) || uncrustify_path=$(which uncrustify) || die "uncrustify executable was not found. Please install uncrustify or set the executable location with git config hooks.uncrustify.path /path/to/uncrustify See http://uncrustify.sourceforge.net/" uncrustify_conf=$(git config hooks.uncrustify.conf) if ! test -f "$uncrustify_conf"; then die "The file '$uncrustify_conf' does not exist. Please run git config hooks.uncrustify.conf path/to/uncrustify.conf" fi } run_uncrustify_on_file() { MERGED="$1" if run_style_on_file "$MERGED" uncrustify; then ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" BACKUP="./$MERGED.BACKUP.$ext" LOCAL="./$MERGED.STAGED.$ext" REMOTE="./$MERGED.UNCRUSTIFY.$ext" NEW_MERGED="./$MERGED.NEW.$ext" OLD_MERGED="$MERGED" mv -- "$MERGED" "$BACKUP" # We temporarily change MERGED because the file might already be open, and # the text editor may complain. MERGED="$NEW_MERGED" cp -- "$BACKUP" "$MERGED" cp -- "$BACKUP" "$LOCAL" if ! "$uncrustify_path" -c "$uncrustify_conf" -f "$LOCAL" \ -o "$REMOTE" 2> /dev/null; then mv -- "$BACKUP" "$OLD_MERGED" if test "$merge_keep_temporaries" = "false"; then rm -f -- "$LOCAL" "$REMOTE" "$BACKUP" fi die "error when running uncrustify on $OLD_MERGED" fi if test $(git hash-object -- "$LOCAL") != $(git hash-object -- "$REMOTE") && ! run_merge_tool "$merge_tool" "false" For more information, see git help mergetool" merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)" merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)" git diff-index --cached --diff-filter=ACMR --name-only HEAD -- | while read MERGED; do run_uncrustify_on_file "$MERGED" || return done # end for changed files } # Do not run during merge commits for now. if test -f "$GIT_DIR/MERGE_HEAD"; then : elif $do_uncrustify; then # We use git-mergetool settings to review the uncrustify changes. TOOL_MODE=merge . "$(git --exec-path)/git-mergetool--lib" # Redefine check_unchanged because we do not need to check if the merge was # successful. check_unchanged() { status=0 } check_for_uncrustify run_uncrustify || exit 1 # do_uncrustify will run KWStyle on the files incrementally so excessive # uncrustify merges do not have to occur. elif $do_KWStyle; then if check_for_KWStyle; then run_KWStyle || exit 1 fi fi #----------------------------------------------------------------------------- # cppcheck. check_for_cppcheck() { cppcheck_path=$(git config hooks.cppcheck.path) if [ x"$cppcheck_path" == x ]; then cppcheck_path=$(which cppcheck) if [ x"$cppcheck_path" == x ]; then echo "cppcheck executable was not found. Please install cppcheck or set the executable location with git config hooks.cppcheck.path /path/to/cppcheck See http://cppcheck.sourceforge.net/ To suppress all errors in all files, disable cppcheck, as: git config --bool hooks.cppcheck false " return 1 fi fi return 0 } run_cppcheck_on_file() { file="$1" echo "Running cppcheck on $file " if [ $(echo "$file" | grep -c "\.h\>\|\.cpp\>\|\.cxx\>\|\.c\>\|\.txx\>|\.hxx\>") != 0 ]; then echo Run: $cppcheck_path -q --enable=all --force --inline-suppr --template='{file}:{line},{severity},{id},{message}' "${file}" output=$( $cppcheck_path -q --enable=all --force --inline-suppr --template='{file}:{line},{severity},{id},{message}' "${file}" 2>&1 | grep -v missingInclude) if [ x"$output" != x ]; then die "Error(s) when running cppcheck on $file: $output To suppress an individual false-positive, insert a "suppression" in the code before the 'erroneous' line, as follows: // cppcheck-suppress ErrorType To suppress all errors in all files, disable cppcheck, as: git config --bool hooks.cppcheck false " fi fi # end if run cppcheck on file } run_cppcheck() { check_for_cppcheck if [ $? != 0 ]; then exit 1 fi git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; do run_cppcheck_on_file "$file" || return done # end for changed files } do_cppcheck=$(git config --bool hooks.cppcheck) || do_cppcheck=false if $do_cppcheck; then run_cppcheck || exit 1 fi # vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : ants-2.2.0/Utilities/Hooks/prepare-commit-msg000077500000000000000000000034061311104306400211500ustar00rootroot00000000000000#!/usr/bin/env bash #========================================================================== # # Copyright Insight Software Consortium # # 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. # #==========================================================================*/ egrep-q() { egrep "$@" >/dev/null 2>/dev/null } # First argument is file containing commit message. commit_msg="$1" # Check for our extra instructions. egrep-q "^# Start BRAINSTools commit messages" -- "$commit_msg" && return 0 # Insert our extra instructions. commit_msg_tmp="$commit_msg.$$" sed 's/^# On branch.*/'\ '# Start BRAINSTools commit messages with a standard prefix (and a space):\ # BUG: - fix for runtime crash or incorrect result\ # COMP: - compiler error or warning fix\ # DOC: - documentation change\ # ENH: - new functionality\ # PERF: - performance improvement\ # STYLE: - no logic impact (indentation, comments)\ # WIP: - Work In Progress not ready for merge\ # To reference JIRA issue XY, add " (#XY)" to the END of the FIRST line.\ #/' "$commit_msg" > "$commit_msg_tmp" && sed '/^# On branch.*$/ a\ '"$instructions"' /^# Not currently on any branch.*$/ a\ '"$instructions"' ' "$commit_msg" > "$commit_msg_tmp" && mv "$commit_msg_tmp" "$commit_msg" ants-2.2.0/Utilities/ReadWriteData.cxx000066400000000000000000000013431311104306400176310ustar00rootroot00000000000000#include "ReadWriteData.h" bool ANTSFileExists(const std::string & strFilename) { // Attempt to get the file attributes struct stat stFileInfo; const int intStat = stat(strFilename.c_str(), &stFileInfo); bool blnReturn; if( intStat == 0 ) { // We were able to get the file attributes // so the file obviously exists. blnReturn = true; } else { // We were not able to get the file attributes. // This may mean that we don't have permission to // access the folder which contains this file. If you // need to do that level of checking, lookup the // return values of stat which will give you // more details on why stat failed. blnReturn = false; } return blnReturn; } ants-2.2.0/Utilities/ReadWriteData.h000066400000000000000000000517201311104306400172620ustar00rootroot00000000000000#ifndef __ReadWriteData_h_ #define __ReadWriteData_h_ #include #include #include #include #include "itkVector.h" #include "itkImage.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkLabeledPointSetFileReader.h" #include "itkLabeledPointSetFileWriter.h" #include "itkImageIntensityAndGradientToPointSetFilter.h" #include "itkWarpImageFilter.h" // #include "itkInverseWarpImageFilter.h" #include "itkAffineTransform.h" #include "itkImageRegionIterator.h" #include "itkResampleImageFilter.h" #include "itkVectorIndexSelectionCastImageFilter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkVectorIndexSelectionCastImageFilter.h" #include "itkLogTensorImageFilter.h" #include "itkExpTensorImageFilter.h" #include "itkCastImageFilter.h" #include extern bool ANTSFileExists(const std::string & strFilename); // Nifti stores DTI values in lower tri format but itk uses upper tri // currently, nifti io does nothing to deal with this. if this changes // the function below should be modified/eliminated. #if 1 // currrently unimplemented template void NiftiDTICheck(itk::SmartPointer &, const char *, bool ) { } #else template void NiftiDTICheck(itk::SmartPointer & target, const char *file, bool makeLower) { typedef typename TImageType::PixelType PixType; return; // typedef itk::ImageFileWriter Writer; // typename Writer::Pointer writer = Writer::New(); // writer->SetInput( target ); // writer->SetFileName( "testdt.nii" ); // writer->Update(); // Check for nifti file std::string filename = file; std::string::size_type pos1 = filename.find( ".nii" ); std::string::size_type pos2 = filename.find( ".nia" ); if( (pos1 == std::string::npos) && (pos2 == std::string::npos) ) { return; } if( PixType::Length != 6 ) { return; } std::cout << "Performing lower/upper triangular format check for Nifti DTI" << std::endl; // swap elements 2 and 3 for lower<->upper conversion itk::ImageRegionIteratorWithIndex iter(target, target->GetLargestPossibleRegion() ); unsigned int looksLikeLower = 0; unsigned int looksLikeUpper = 0; unsigned int nBadVoxels = 0; unsigned int count = 0; unsigned int el2Neg = 0; unsigned int el3Neg = 0; while( !iter.IsAtEnd() ) { bool isValid = true; for( unsigned int i = 0; i < 6; i++ ) { if( iter.Get()[i] != iter.Get()[i] ) { ++nBadVoxels; isValid = false; } } double el2 = iter.Get()[2]; double el3 = iter.Get()[3]; if( el2 < 0 ) { ++el2Neg; } if( el3 < 0 ) { ++el3Neg; } if( isValid ) { if( el2 > el3 ) { ++looksLikeLower; } else { ++looksLikeUpper; } } ++count; ++iter; } // std::cout << "Invalid: " << nBadVoxels << "/" << count << std::endl; // std::cout << "Lower: " << looksLikeLower << ", Upper: " << looksLikeUpper << std::endl; // std::cout << "el2Neg: " << el2Neg << ", el3Neg: " << el3Neg << std::endl; if( ( (looksLikeUpper > looksLikeLower) && makeLower) || ( (looksLikeLower > looksLikeUpper) && !makeLower) ) { std::cout << "Performing lower/upper triangular format swap for Nifti DTI" << std::endl; iter.GoToBegin(); while( !iter.IsAtEnd() ) { PixType pix = iter.Get(); typename PixType::ValueType temp; temp = pix[2]; pix[2] = pix[3]; pix[3] = temp; iter.Set(pix); ++iter; } } } #endif template void ReadTensorImage(itk::SmartPointer & target, const char *file, bool takelog = true) { if( !ANTSFileExists(std::string(file) ) ) { std::cerr << " file " << std::string(file) << " does not exist . " << std::endl; return; } typedef TImageType ImageType; typedef itk::ImageFileReader FileSourceType; typedef itk::LogTensorImageFilter LogFilterType; typename FileSourceType::Pointer reffilter = ITK_NULLPTR; if( file[0] == '0' && file[1] == 'x' ) { void* ptr; sscanf(file, "%p", (void **)&ptr); target = *( static_cast( ptr ) ); } else { // Read the image files begin reffilter = FileSourceType::New(); reffilter->SetFileName( file ); try { reffilter->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught during reference file reading " << std::endl; std::cerr << e << " file " << file << std::endl; target = ITK_NULLPTR; return; } target = reffilter->GetOutput(); } //NiftiDTICheck(target, file, false); if( takelog ) { typename LogFilterType::Pointer logFilter = LogFilterType::New(); logFilter->SetInput( reffilter->GetOutput() ); try { logFilter->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught during log tensor filter " << std::endl; std::cerr << e << " file " << file << std::endl; target = ITK_NULLPTR; return; } target = logFilter->GetOutput(); std::cout << "Returning Log(D) for log-euclidean math ops" << std::endl; } } template // void ReadImage(typename TImageType::Pointer target, const char *file) bool ReadImage(itk::SmartPointer & target, const char *file) { enum { ImageDimension = TImageType::ImageDimension }; if( std::string(file).length() < 3 ) { target = ITK_NULLPTR; return false; } std::string comparetype1 = std::string( "0x" ); std::string comparetype2 = std::string( file ); comparetype2 = comparetype2.substr( 0, 2 ); // Read the image files begin if( comparetype1 == comparetype2 ) { typedef TImageType RImageType; void* ptr; sscanf(file, "%p", (void **)&ptr); typename RImageType::Pointer Rimage = *( static_cast( ptr ) ); /** more needs to be done here to cast the pointer to an image type --- this is a work-around */ typedef itk::CastImageFilter CastFilterType; typename CastFilterType::Pointer caster = CastFilterType::New(); caster->SetInput( Rimage ); caster->UpdateLargestPossibleRegion(); target = caster->GetOutput(); } else { if( !ANTSFileExists(std::string(file) ) ) { std::cerr << " file " << std::string(file) << " does not exist . " << std::endl; target = ITK_NULLPTR; return false; } typedef TImageType ImageType; typedef itk::ImageFileReader FileSourceType; typename FileSourceType::Pointer reffilter = FileSourceType::New(); reffilter->SetFileName( file ); try { reffilter->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught during reference file reading " << std::endl; std::cerr << e << " file " << file << std::endl; target = ITK_NULLPTR; std::exception(); return false; } // typename ImageType::DirectionType dir; // dir.SetIdentity(); // reffilter->GetOutput()->SetDirection(dir); // std::cout << " setting pointer " << std::endl; target = reffilter->GetOutput(); } return true; } template typename ImageType::Pointer ReadImage(char* fn ) { // Read the image files begin typedef itk::ImageFileReader FileSourceType; typename FileSourceType::Pointer reffilter = FileSourceType::New(); reffilter->SetFileName( fn ); try { reffilter->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught during image reference file reading " << std::endl; std::cerr << e << std::endl; return NULL; } //typename ImageType::DirectionType dir; //dir.SetIdentity(); // reffilter->GetOutput()->SetDirection(dir); typename ImageType::Pointer target = reffilter->GetOutput(); // if (reffilter->GetImageIO->GetNumberOfComponents() == 6) // NiftiDTICheck(target,fn); return target; } template typename ImageType::Pointer ReadTensorImage(char* fn, bool takelog = true ) { // Read the image files begin typedef itk::ImageFileReader FileSourceType; typedef itk::LogTensorImageFilter LogFilterType; typename FileSourceType::Pointer reffilter = FileSourceType::New(); reffilter->SetFileName( fn ); try { reffilter->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught during tensor image reference file reading " << std::endl; std::cerr << e << std::endl; return NULL; } typename ImageType::Pointer target = reffilter->GetOutput(); NiftiDTICheck(target, fn, false); if( takelog ) { typename LogFilterType::Pointer logFilter = LogFilterType::New(); logFilter->SetInput( target->GetOutput() ); logFilter->Update(); target = logFilter->GetOutput(); } return target; } template // void ReadImage(typename TPointSet::Pointer target, const char *file) bool ReadLabeledPointSet( itk::SmartPointer & target, const char *file, bool boundaryPointsOnly = false, float samplingPercentage = 1.0 ) { if( std::string( file ).length() < 3 ) { target = ITK_NULLPTR; return false; } if( !ANTSFileExists( std::string( file ) ) ) { std::cerr << " file " << std::string( file ) << " does not exist . " << std::endl; return false; } // Read the image files begin typedef itk::LabeledPointSetFileReader FileSourceType; typename FileSourceType::Pointer reffilter = FileSourceType::New(); reffilter->SetFileName( file ); reffilter->SetExtractBoundaryPoints( boundaryPointsOnly ); reffilter->SetRandomPercentage( samplingPercentage ); try { reffilter->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught during point set reference file reading " << std::endl; std::cerr << e << std::endl; return false; } target = reffilter->GetOutput(); return true; } template bool ReadImageIntensityPointSet( itk::SmartPointer & target, const char *imageFile, const char *maskFile, std::vector neighborhoodRadius, double sigma ) { if( std::string( imageFile ).length() < 3 ) { std::cerr << " bad image file name " << std::string( imageFile ) << std::endl; target = ITK_NULLPTR; return false; } if( !ANTSFileExists( std::string( imageFile ) ) ) { std::cerr << " image file " << std::string( imageFile ) << " does not exist . " << std::endl; target = ITK_NULLPTR; return false; } if( std::string( maskFile ).length() < 3 ) { std::cerr << " bad mask file name " << std::string( maskFile ) << std::endl; target = ITK_NULLPTR; return false; } if( !ANTSFileExists( std::string( maskFile ) ) ) { std::cerr << " mask file " << std::string( maskFile ) << " does not exist . " << std::endl; target = ITK_NULLPTR; return false; } if( neighborhoodRadius.size() != TImage::ImageDimension ) { std::cerr << " size of the neighborhood radius is not equal to the image dimension." << std::endl; target = ITK_NULLPTR; return false; } typename TImage::Pointer intensityImage = ReadImage( (char *)imageFile ); typename TMask::Pointer maskImage = ReadImage( (char *)maskFile ); typedef itk::ImageIntensityAndGradientToPointSetFilter FilterType; typename FilterType::NeighborhoodRadiusType radius; for( unsigned int d = 0; d < TImage::ImageDimension; d++ ) { radius[d] = neighborhoodRadius[d]; } typename FilterType::Pointer filter = FilterType::New(); filter->SetInput1( intensityImage ); filter->SetInput2( maskImage ); filter->SetSigma( sigma ); filter->SetNeighborhoodRadius( radius ); filter->Update(); target = filter->GetOutput(); return true; } template typename TPointSet::Pointer ReadLabeledPointSet( char* fn ) { if( !ANTSFileExists( std::string( fn ) ) ) { std::cerr << " file " << std::string( fn ) << " does not exist . " << std::endl; return; } // Read the image files begin typedef itk::LabeledPointSetFileReader FileSourceType; typename FileSourceType::Pointer reffilter = FileSourceType::New(); reffilter->SetFileName( fn ); try { reffilter->Update(); } catch( itk::ExceptionObject & e ) { std::cerr << "Exception caught during point set reference file reading " << std::endl; std::cerr << e << std::endl; return NULL; } typename TPointSet::Pointer target = reffilter->GetOutput(); return target; } template bool WritePointSet( itk::SmartPointer pointSet, const char *file ) { if( std::string(file).length() < 3 ) { return false; } typename itk::LabeledPointSetFileWriter::Pointer writer = itk::LabeledPointSetFileWriter::New(); writer->SetFileName( file ); if( !pointSet ) { std::cerr << " Point set is null." << std::endl; std::exception(); } writer->SetInput( pointSet ); writer->Update(); return true; } template bool WriteImage(const itk::SmartPointer image, const char *file) { if( std::string(file).length() < 3 ) { return false; } // typename TImageType::DirectionType dir; // dir.SetIdentity(); // image->SetDirection(dir); // std::cout << " now Write direction " << image->GetOrigin() << std::endl; // if (writer->GetImageIO->GetNumberOfComponents() == 6) // NiftiDTICheck(image,file); if( file[0] == '0' && file[1] == 'x' ) { void* ptr; sscanf(file, "%p", (void **)&ptr); *( static_cast( ptr ) ) = image; } else { typename itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); writer->SetFileName(file); if( !image ) { std::cerr << "Image is null." << std::endl; std::exception(); } writer->SetInput(image); writer->SetUseCompression( true ); writer->Update(); } return true; } template void WriteTensorImage(itk::SmartPointer image, const char *file, bool takeexp = true) { typedef itk::ExpTensorImageFilter ExpFilterType; typename itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); writer->SetFileName(file); typename TImageType::Pointer writeImage = image; if( takeexp ) { typename ExpFilterType::Pointer expFilter = ExpFilterType::New(); expFilter->SetInput( image ); expFilter->Update(); writeImage = expFilter->GetOutput(); std::cout << "Taking Exp(D) before writing" << std::endl; } // convert from upper tri to lower tri NiftiDTICheck(writeImage, file, true); // BA May 30 2009 -- remove b/c ITK fixed NIFTI reader if( file[0] == '0' && file[1] == 'x' ) { void* ptr; sscanf(file, "%p", (void **)&ptr); *( static_cast( ptr ) ) = writeImage; } else { writer->SetInput(writeImage); writer->SetUseCompression( true ); writer->Update(); } } template typename TField::Pointer ReadWarpFromFile( std::string warpfn, std::string ext) { typedef TField FieldType; typedef typename FieldType::PixelType VectorType; enum { ImageDimension = FieldType::ImageDimension }; typedef itk::Image RealImageType; typedef RealImageType ImageType; // typedef itk::Vector VectorType; // typedef itk::Image FieldType; // std::cout << " warp file name " << warpfn + ext << std::endl; // First - read the vector fields // NOTE : THE FIELD SHOULD WARP INPUT1 TO INPUT2, THUS SHOULD POINT // FROM INPUT2 TO INPUT1 std::string fn = warpfn + "x" + ext; typename RealImageType::Pointer xvec = ReadImage( (char *)fn.c_str() ); // std::cout << " done reading " << fn << std::endl; fn = warpfn + "y" + ext; typename RealImageType::Pointer yvec = ReadImage( (char *)fn.c_str() ); // std::cout << " done reading " << fn << std::endl; fn = warpfn + "z" + ext; typename RealImageType::Pointer zvec = NULL; // std::cout << " done reading " << fn << std::endl; if( ImageDimension == 3 ) { zvec = ReadImage( (char *)fn.c_str() ); } typename FieldType::Pointer field = AllocImage(xvec); itk::ImageRegionIteratorWithIndex it( xvec, xvec->GetLargestPossibleRegion() ); // std::cout << " spacing xv " << xvec->GetSpacing()[0] // << " field " << field->GetSpacing()[0] << std::endl; unsigned int ct = 0; for( it.GoToBegin(); !it.IsAtEnd(); ++it ) { ct++; typename ImageType::IndexType index = it.GetIndex(); VectorType disp; disp[0] = xvec->GetPixel(index); disp[1] = yvec->GetPixel(index); if( ImageDimension == 3 ) { disp[2] = zvec->GetPixel(index); } field->SetPixel(index, disp); // if (ct == 10000) std::cout << " 10000th pix " << disp << std::endl; } return field; } template typename TImage::Pointer MakeNewImage(typename TImage::Pointer image1, typename TImage::PixelType initval ) { typedef itk::ImageRegionIteratorWithIndex Iterator; typename TImage::Pointer varimage = AllocImage(image1); Iterator vfIter2( varimage, varimage->GetLargestPossibleRegion() ); for( vfIter2.GoToBegin(); !vfIter2.IsAtEnd(); ++vfIter2 ) { if( initval >= 0 ) { vfIter2.Set(initval); } else { vfIter2.Set(image1->GetPixel(vfIter2.GetIndex() ) ); } } return varimage; } template void WriteDisplacementField(TField* field, std::string filename) { typedef TField FieldType; enum { ImageDimension = FieldType::ImageDimension }; typedef itk::Image RealImageType; // Initialize the caster to the displacement field typedef itk::VectorIndexSelectionCastImageFilter IndexSelectCasterType; for( unsigned int dim = 0; dim < ImageDimension; dim++ ) { typename IndexSelectCasterType::Pointer fieldCaster = IndexSelectCasterType::New(); fieldCaster->SetInput( field ); fieldCaster->SetIndex( dim ); fieldCaster->Update(); // Set up the output filename std::string outfile = filename + static_cast('x' + dim) + std::string("vec.nii.gz"); std::cout << "Writing displacements to " << outfile << " spacing " << field->GetSpacing()[0] << std::endl; typename RealImageType::Pointer fieldcomponent = fieldCaster->GetOutput(); fieldcomponent->SetSpacing(field->GetSpacing() ); fieldcomponent->SetOrigin(field->GetOrigin() ); fieldcomponent->SetDirection(field->GetDirection() ); WriteImage(fieldcomponent, outfile.c_str() ); } std::cout << "...done" << std::endl; return; } template void WriteDisplacementField2(TField* field, std::string filename, std::string app) { typedef TField FieldType; enum { ImageDimension = FieldType::ImageDimension }; typedef itk::Image RealImageType; // Initialize the caster to the displacement field typedef itk::VectorIndexSelectionCastImageFilter IndexSelectCasterType; for( unsigned int dim = 0; dim < ImageDimension; dim++ ) { typename IndexSelectCasterType::Pointer fieldCaster = IndexSelectCasterType::New(); fieldCaster->SetInput( field ); fieldCaster->SetIndex( dim ); fieldCaster->Update(); // Set up the output filename std::string outfile = filename + static_cast('x' + dim) + std::string(app); std::cout << "Writing displacements to " << outfile << " spacing " << field->GetSpacing()[0] << std::endl; typename RealImageType::Pointer fieldcomponent = fieldCaster->GetOutput(); fieldcomponent->SetSpacing(field->GetSpacing() ); fieldcomponent->SetOrigin(field->GetOrigin() ); WriteImage(fieldcomponent, outfile.c_str() ); } std::cout << "...done" << std::endl; return; } class nullBuf : public std::streambuf { public: virtual std::streamsize xsputn( const char * itkNotUsed( s ), std::streamsize n ) { return n; } virtual int overflow( int itkNotUsed( c ) ) { return 1; } }; class nullStream : public std::ostream { public: nullStream() : std::ostream( &buf ) {} private: nullBuf buf; }; #endif ants-2.2.0/Utilities/SetupForDevelopment.sh000077500000000000000000000001031311104306400207270ustar00rootroot00000000000000#!/bin/bash $(dirname "$0")/DevelopmentSetupScripts/SetupHooks.sh ants-2.2.0/Utilities/antsAllocImage.h000066400000000000000000000112011311104306400174530ustar00rootroot00000000000000#ifndef ANTSAllocImage_h #define ANTSAllocImage_h #include "itkImageBase.h" #include "itkImage.h" /** Allocate an image based only on region */ template typename ImageType::Pointer AllocImage(const typename ImageType::RegionType & region) { typename ImageType::Pointer rval = ImageType::New(); rval->SetRegions(region); rval->Allocate(); return rval; } /** Allocate an image based only on size */ template typename ImageType::Pointer AllocImage(const typename ImageType::SizeType & size) { typename ImageType::RegionType region; region.SetSize(size); return AllocImage(region); } template typename ImageType::Pointer AllocImage(const typename ImageType::RegionType & region, const typename ImageType::PixelType & init) { typename ImageType::Pointer rval = AllocImage(region); rval->FillBuffer(init); return rval; } /** Allocate image based on region, spaciing, origin & directions */ template typename ImageType::Pointer AllocImage(const typename ImageType::RegionType & region, const typename ImageType::SpacingType & spacing, const typename ImageType::PointType & origin, const typename ImageType::DirectionType & directions) { typename ImageType::Pointer rval = AllocImage(region); rval->SetSpacing(spacing); rval->SetOrigin(origin); rval->SetDirection(directions); return rval; } /** Allocate image based on region, spacing, origin & directions * then initialize with initValue */ template typename ImageType::Pointer AllocImage(const typename ImageType::RegionType & region, const typename ImageType::SpacingType & spacing, const typename ImageType::PointType & origin, const typename ImageType::DirectionType & directions, const typename ImageType::PixelType initValue) { typename ImageType::Pointer rval = AllocImage(region, spacing, origin, directions); rval->FillBuffer(initValue); return rval; } /** Allocate an image based on size, spacing, origin, directions */ template typename ImageType::Pointer AllocImage(const typename ImageType::SizeType & size, const typename ImageType::SpacingType & spacing, const typename ImageType::PointType & origin, const typename ImageType::DirectionType & directions) { typename ImageType::RegionType region; region.SetSize(size); return AllocImage(region, spacing, origin, directions); } /** Allocate image based on size,spacing,origin&directions * then initialize with initial Pixel. */ template typename ImageType::Pointer AllocImage(const typename ImageType::SizeType & size, const typename ImageType::SpacingType & spacing, const typename ImageType::PointType & origin, const typename ImageType::DirectionType & directions, const typename ImageType::PixelType initValue) { typename ImageType::RegionType region; region.SetSize(size); return AllocImage(region, spacing, origin, directions, initValue); } /** Allocate an image based on an exemplar image. */ template typename ImageType::Pointer AllocImage(const typename itk::ImageBase *exemplar) { typename ImageType::Pointer rval = ImageType::New(); // it may be the case that the output image might have a different // number of PixelComponents than the exemplar, so only copy this // information. // rval->CopyInformation(exemplar); rval->SetLargestPossibleRegion( exemplar->GetLargestPossibleRegion() ); rval->SetBufferedRegion( exemplar->GetBufferedRegion() ); rval->SetRequestedRegion( exemplar->GetRequestedRegion() ); rval->SetSpacing( exemplar->GetSpacing() ); rval->SetOrigin( exemplar->GetOrigin() ); rval->SetDirection( exemplar->GetDirection() ); rval->Allocate(); return rval; } /** Allocate an image based on an exemplar image, and then init all * voxels to the suppled fillValue */ template typename ImageType::Pointer AllocImage(const typename itk::ImageBase *exemplar, const typename ImageType::PixelType & fillValue) { typename ImageType::Pointer rval = AllocImage(exemplar); rval->FillBuffer(fillValue); return rval; } #endif // ANTSAllocImage_h ants-2.2.0/Utilities/antsCommandLineOption.cxx000066400000000000000000000047051311104306400214230ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "antsCommandLineOption.h" namespace itk { namespace ants { CommandLineOption ::CommandLineOption() : m_ShortName( '\0' ), m_LongName( "" ), m_Description( "" ) { this->m_OptionFunctions.clear(); this->m_UsageOptions.clear(); } void CommandLineOption ::AddFunction( std::string functionString, char leftDelimiter, char rightDelimiter, unsigned int order ) { OptionFunctionType::Pointer optionFunction = OptionFunctionType::New(); optionFunction->SetArgOrder( order ); std::string::size_type leftDelimiterPos = functionString.find( leftDelimiter ); std::string::size_type rightDelimiterPos = functionString.find( rightDelimiter ); if( leftDelimiterPos == std::string::npos || rightDelimiterPos == std::string::npos ) { optionFunction->SetName( functionString ); this->m_OptionFunctions.push_front( optionFunction ); } else { OptionFunctionType::ParameterStackType parameters; optionFunction->SetName( functionString.substr( 0, leftDelimiterPos ) ); std::string::size_type leftPos = leftDelimiterPos; std::string::size_type rightPos = functionString.find( ',', leftPos + 1 ); while( rightPos != std::string::npos ) { parameters.push_back( functionString.substr( leftPos + 1, rightPos - leftPos - 1 ) ); leftPos = rightPos; rightPos = functionString.find( ',', leftPos + 1 ); } rightPos = rightDelimiterPos; parameters.push_back( functionString.substr( leftPos + 1, rightPos - leftPos - 1 ) ); optionFunction->SetParameters( parameters ); this->m_OptionFunctions.push_front( optionFunction ); } this->Modified(); } void CommandLineOption ::SetUsageOption( unsigned int i, std::string usage ) { if( i >= this->m_UsageOptions.size() ) { this->m_UsageOptions.resize( i + 1 ); } this->m_UsageOptions[i] = usage; this->Modified(); } } // end namespace ants } // end namespace itk ants-2.2.0/Utilities/antsCommandLineOption.h000066400000000000000000000116351311104306400210500ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsCommandLineOption_h #define __antsCommandLineOption_h #include "itkDataObject.h" #include "itkObjectFactory.h" #include "itkMacro.h" #include "itkNumericTraits.h" #include #include #include namespace itk { namespace ants { /** \class CommandLineOption \brief Simple data structure for holding command line options. An option can have multiple values with each value holding 0 or more parameters. E.g. suppose we were creating an image registration program which has several transformation model options such as 'rigid', 'affine', and 'deformable'. A instance of the command line option could have a long name of "transformation", short name 't', and description "Transformation model---rigid, affine, or deformable". The values for this option would be "rigid", "affine", and "deformable". Each value would then hold parameters that relate to that value. For example, a possible subsection of the command line would be " --transformation rigid[parameter1,parameter2,etc.] -m mutualinformation[parameter1] --optimization gradientdescent" */ class OptionFunction : public DataObject { public: OptionFunction() : m_Name( "" ), m_ArgOrder( 0 ), m_StageID( 0 ) { }; ~OptionFunction() { }; typedef OptionFunction Self; typedef DataObject Superclass; typedef SmartPointer Pointer; itkNewMacro( Self ); itkTypeMacro( Option, DataObject ); typedef std::deque ParameterStackType; itkSetStringMacro( Name ); itkGetStringMacro( Name ); itkGetMacro( Name, std::string ); itkSetMacro( ArgOrder, unsigned int ); itkGetConstMacro( ArgOrder, unsigned int ); itkSetMacro( StageID, unsigned int ); itkGetConstMacro( StageID, unsigned int ); ParameterStackType GetParameters() { return this->m_Parameters; } void SetParameters( ParameterStackType parameters ) { this->m_Parameters = parameters; this->Modified(); } std::string GetParameter( unsigned int i = 0 ) { if( i < this->m_Parameters.size() ) { return this->m_Parameters[i]; } else { std::string empty( "" ); return empty; } } unsigned int GetNumberOfParameters() { return this->m_Parameters.size(); } private: std::string m_Name; unsigned int m_ArgOrder; unsigned int m_StageID; ParameterStackType m_Parameters; }; class CommandLineOption : public DataObject { public: typedef CommandLineOption Self; typedef DataObject Superclass; typedef SmartPointer Pointer; itkNewMacro( Self ); itkTypeMacro( Option, DataObject ); typedef OptionFunction OptionFunctionType; typedef std::deque FunctionStackType; typedef std::deque UsageOptionStackType; FunctionStackType GetFunctions() { return this->m_OptionFunctions; } unsigned int GetNumberOfFunctions() { return this->m_OptionFunctions.size(); } OptionFunction::Pointer GetFunction( unsigned int i = 0 ) { if( i < this->m_OptionFunctions.size() ) { return this->m_OptionFunctions[i]; } else { return ITK_NULLPTR; } } UsageOptionStackType GetUsageOptions() { return this->m_UsageOptions; } unsigned int GetNumberOfUsageOptions() { return this->m_UsageOptions.size(); } std::string GetUsageOption( unsigned int i = 0 ) { if( i < this->m_UsageOptions.size() ) { return this->m_UsageOptions[i]; } else { return std::string( "" ); } } itkSetMacro( ShortName, char ); itkGetConstMacro( ShortName, char ); itkSetStringMacro( LongName ); itkGetConstMacro( LongName, std::string ); itkSetStringMacro( Description ); itkGetMacro( Description, std::string ); void AddFunction( std::string, char, char, unsigned int order = 0 ); void AddFunction( std::string s ) { this->AddFunction( s, '[', ']' ); } void SetUsageOption( unsigned int, std::string ); protected: CommandLineOption(); virtual ~CommandLineOption() { }; private: char m_ShortName; std::string m_LongName; std::string m_Description; UsageOptionStackType m_UsageOptions; FunctionStackType m_OptionFunctions; }; } // end namespace ants } // end namespace itk #endif ants-2.2.0/Utilities/antsCommandLineParser.cxx000066400000000000000000000456271311104306400214170ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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. =========================================================================*/ #include "antsCommandLineParser.h" #include namespace itk { namespace ants { std::string ConvertToHumanReadable(const std::string & input) { typedef std::map TypeMapType; TypeMapType cnvtMap; cnvtMap[typeid(signed char).name()] = "signed int"; cnvtMap[typeid(unsigned char).name()] = "unsigned int"; cnvtMap[typeid(signed short).name()] = "signed int"; cnvtMap[typeid(unsigned short).name()] = "unsigned int"; cnvtMap[typeid(signed int).name()] = "signed int"; cnvtMap[typeid(unsigned int).name()] = "unsigned int"; cnvtMap[typeid(signed long).name()] = "signed int"; cnvtMap[typeid(unsigned long).name()] = "unsigned int"; cnvtMap[typeid(float).name()] = "float"; cnvtMap[typeid(double).name()] = "double"; cnvtMap[typeid(std::string).name()] = "std::string"; cnvtMap[typeid(char *).name()] = "char *"; TypeMapType::iterator mi=cnvtMap.find(input); if ( mi == cnvtMap.end() ) { return std::string("Unmapped Type"); } return mi->second; } CommandLineParser ::CommandLineParser(): m_LeftDelimiter ( '[' ), m_RightDelimiter ( ']' ) { this->m_Options.clear(); this->m_Command.clear(); this->m_CommandDescription.clear(); this->m_UnknownOptions.clear(); } void CommandLineParser ::AddOption( OptionType::Pointer option ) { if( ( option->GetShortName() != '\0' || !this->GetOption( option->GetShortName() ) ) || ( !option->GetLongName().empty() || !this->GetOption( option->GetLongName() ) ) ) { this->m_Options.push_back( option ); } else { if( option->GetShortName() != '\0' && this->GetOption( option->GetShortName() ) ) { itkWarningMacro( "Duplicate short option '-" << option->GetShortName() << "'" ); } if( !( option->GetLongName().empty() ) && this->GetOption( option->GetLongName() ) ) { itkWarningMacro( "Duplicate long option '--" << option->GetLongName() << "'" ); } } } bool CommandLineParser ::starts_with(const std::string & s1, const std::string & s2) { return s2.length() <= s1.length() && s1.compare(0, s2.length(), s2) == 0; } int CommandLineParser ::Parse( unsigned int argc, char * *argv ) { std::vector arguments = this->RegroupCommandLineArguments( argc, argv ); unsigned int n = 0; unsigned int order = 0; bool allFlagsAreValid = true; if ( arguments.size() > 1 ) { this->m_Command = arguments[n++]; } while( n < arguments.size() ) { std::string argument = arguments[n++]; std::string name; name.clear(); if( starts_with( argument, "--" ) ) { name = argument.substr( 2, argument.length() - 1 ); } else if( starts_with( argument, "-" ) && !starts_with( argument, "--" ) ) { name = argument.substr( 1, 2 ); } if( atof( name.c_str() ) ) { continue; } if ( name.size() > 0 ) { allFlagsAreValid &= this->ValidateFlag( name ); } if( ( !( name.empty() ) ) && ( name.size() > 0 ) ) { OptionType::Pointer option = this->GetOption( name ); if( !option ) { OptionType::Pointer unknownOption = OptionType::New(); if( name.length() > 1 ) { unknownOption->SetLongName( name ); } else { unknownOption->SetShortName( name.at( 0 ) ); } if( n == arguments.size() ) { unknownOption->AddFunction( "1", this->m_LeftDelimiter, this->m_RightDelimiter, order++ ); } else { for( unsigned int m = n; m < arguments.size(); m++ ) { std::string function = arguments[m]; if( !starts_with( function, "-" ) ) { unknownOption->AddFunction( function, this->m_LeftDelimiter, this->m_RightDelimiter, order++ ); } else { if( m == n ) { unknownOption->AddFunction( "1", this->m_LeftDelimiter, this->m_RightDelimiter, order++ ); } n = m; break; } } } this->m_UnknownOptions.push_back( unknownOption ); } else // the option exists { if( n == arguments.size() ) { option->AddFunction( "1", this->m_LeftDelimiter, this->m_RightDelimiter, order++ ); } else { for( unsigned int m = n; m < arguments.size(); m++ ) { std::string function = arguments[m]; if( !starts_with( function, "-" ) || atof( function.c_str() ) ) { option->AddFunction( function, this->m_LeftDelimiter, this->m_RightDelimiter, order++ ); } else { if( m == n ) { option->AddFunction( "1", this->m_LeftDelimiter, this->m_RightDelimiter, order++ ); } n = m; break; } } } } } } if( ! allFlagsAreValid ) { std::cerr << "ERROR: Invalid command line flags found! Aborting execution." << std::endl; return EXIT_FAILURE; } this->AssignStages(); return EXIT_SUCCESS; } std::vector CommandLineParser ::RegroupCommandLineArguments( unsigned int argc, char * *argv ) { /** * Inclusion of this function allows the user to use spaces inside * the left and right delimiters. Also replace other left and right * delimiters. */ std::vector arguments; std::string currentArg( "" ); bool isArgOpen = false; for( unsigned int n = 0; n < argc; n++ ) { std::string a( argv[n] ); if( n == 0 ) { arguments.push_back( a ); } else { // replace left delimiters std::replace( a.begin(), a.begin()+1, '{', '[' ); std::replace( a.begin(), a.begin()+1, '(', '[' ); std::replace( a.begin(), a.begin()+1, '<', '[' ); // replace right delimiters std::replace( a.end()-1, a.end(), '}', ']' ); std::replace( a.end()-1, a.end(), ')', ']' ); std::replace( a.end()-1, a.end(), '>', ']' ); if( isArgOpen ) { std::size_t leftDelimiterPosition = a.find( this->m_LeftDelimiter ); if( leftDelimiterPosition != std::string::npos ) { itkExceptionMacro( "Incorrect command line specification. Missing leftDelimiterPosition? " << a ); } std::size_t rightDelimiterPosition = a.find( this->m_RightDelimiter ); if( rightDelimiterPosition != std::string::npos ) { if( rightDelimiterPosition < a.length() - 1 ) { itkExceptionMacro( "Incorrect command line specification. Missing rightDelimiterPosition? " << a ); } else { currentArg += a; arguments.push_back( currentArg ); currentArg.clear(); isArgOpen = false; } } else { currentArg += a; } } else { std::size_t leftDelimiterPosition = a.find( this->m_LeftDelimiter ); std::size_t rightDelimiterPosition = a.find( this->m_RightDelimiter ); if( leftDelimiterPosition == std::string::npos ) { if( rightDelimiterPosition == std::string::npos ) { currentArg += a; arguments.push_back( currentArg ); currentArg.clear(); } else { itkExceptionMacro( "Incorrect command line specification. " << a); } } else if( leftDelimiterPosition != std::string::npos && rightDelimiterPosition != std::string::npos && leftDelimiterPosition < rightDelimiterPosition ) { if( rightDelimiterPosition < a.length() - 1 ) { itkExceptionMacro( "Incorrect command line specification. " << a ); } currentArg += a; arguments.push_back( currentArg ); currentArg.clear(); isArgOpen = false; } else if( rightDelimiterPosition == std::string::npos && leftDelimiterPosition != std::string::npos ) { currentArg += a; isArgOpen = true; } } } } return arguments; } CommandLineParser::OptionType::Pointer CommandLineParser ::GetOption( std::string name ) { if( name.length() == 1 ) { return this->GetOption( name.at( 0 ) ); } OptionListType::iterator it; for( it = this->m_Options.begin(); it != this->m_Options.end(); ++it ) { if( name.compare( (*it)->GetLongName() ) == 0 ) { return *it; } } return ITK_NULLPTR; } CommandLineParser::OptionType::Pointer CommandLineParser ::GetOption( char name ) { OptionListType::iterator it; for( it = this->m_Options.begin(); it != this->m_Options.end(); ++it ) { if( name == (*it)->GetShortName() ) { return *it; } } return ITK_NULLPTR; } bool CommandLineParser:: ValidateFlag(const std::string & currentFlag) { bool validFlagFound = false; for( OptionListType::const_iterator it = this->m_Options.begin(); it != this->m_Options.end(); ++it ) { const char shortName = (*it)->GetShortName(); const std::string longName = (*it)->GetLongName(); if( ( ( currentFlag.size() == 1 ) && ( shortName == currentFlag[0] ) ) || ( longName == currentFlag ) ) { validFlagFound = true; } } if ( ( ! validFlagFound ) && ( currentFlag.size() > 0 ) && ( !atof( currentFlag.c_str() ) ) ) { std::cout << "ERROR: Invalid flag provided " << currentFlag << std::endl; } return validFlagFound; } void CommandLineParser ::PrintMenu( std::ostream& os, Indent indent, bool printShortVersion ) const { os << std::endl; os << "COMMAND: " << std::endl; os << indent << this->m_Command << std::endl; if( !this->m_CommandDescription.empty() && !printShortVersion ) { std::stringstream ss1; ss1 << indent << indent; std::stringstream ss2; ss2 << this->m_CommandDescription; std::string description = this->BreakUpStringIntoNewLines( ss2.str(), ss1.str(), 80 ); os << indent << indent << description << std::endl; } os << std::endl; os << "OPTIONS: " << std::endl; OptionListType::const_iterator it; for( it = this->m_Options.begin(); it != this->m_Options.end(); ++it ) { os << indent; std::stringstream ss; ss << indent; if( (*it)->GetShortName() != '\0' ) { os << "-" << (*it)->GetShortName(); ss << Indent( 2 ); if( !( (*it)->GetLongName() ).empty() ) { os << ", " << "--" << (*it)->GetLongName() << " " << std::flush; ss << Indent( 5 + ( (*it)->GetLongName() ).length() ); } else { os << " " << std::flush; ss << Indent( 1 ); } } else { os << "--" << (*it)->GetLongName() << " " << std::flush; ss << Indent( 3 + ( (*it)->GetLongName() ).length() ); } if( (*it)->GetNumberOfUsageOptions() > 0 ) { os << (*it)->GetUsageOption( 0 ) << std::endl; for( unsigned int i = 1; i < (*it)->GetNumberOfUsageOptions(); i++ ) { os << ss.str() << (*it)->GetUsageOption( i ) << std::endl; } } else { os << std::endl; } if( !( (*it)->GetDescription().empty() ) && !printShortVersion ) { std::stringstream ss1; ss1 << indent << indent; std::stringstream ss2; ss2 << (*it)->GetDescription(); std::string description = this->BreakUpStringIntoNewLines( ss2.str(), ss1.str(), 80 ); os << indent << indent << description << std::endl; } if( !printShortVersion ) { if( (*it)->GetFunctions().size() == 1 ) { os << indent << indent << ": " << (*it)->GetFunction( 0 )->GetName(); if( (*it)->GetFunction( 0 )->GetParameters().size() > 0 ) { os << "["; if( (*it)->GetFunction( 0 )->GetParameters().size() == 1 ) { os << (*it)->GetFunction( 0 )->GetParameter( 0 ); } else { for( unsigned int i = 0; i < (*it)->GetFunction( 0 )->GetParameters().size() - 1; i++ ) { os << (*it)->GetFunction( 0 )->GetParameter( i ) << ","; } os << (*it)->GetFunction( 0 )->GetParameter( (*it)->GetFunction( 0 )->GetParameters().size() - 1 ); } os << "]"; } os << std::endl; } else if( (*it)->GetFunctions().size() > 1 ) { os << indent << indent << ": "; for( unsigned int n = 0; n < (*it)->GetFunctions().size() - 1; n++ ) { os << (*it)->GetFunction( n )->GetName(); if( (*it)->GetFunction( n )->GetParameters().size() > 0 ) { os << "["; if( (*it)->GetFunction( n )->GetParameters().size() == 1 ) { os << (*it)->GetFunction( n )->GetParameter( 0 ) << "], "; } else { for( unsigned int i = 0; i < (*it)->GetFunction( n )->GetParameters().size() - 1; i++ ) { os << (*it)->GetFunction( n )->GetParameter( i ) << ","; } os << (*it)->GetFunction( n )->GetParameter( (*it)->GetFunction( n )->GetParameters().size() - 1 ) << "], "; } } else { os << ", "; } } unsigned int nn = (*it)->GetFunctions().size() - 1; os << (*it)->GetFunction( nn )->GetName(); if( (*it)->GetFunction( nn )->GetParameters().size() > 0 ) { os << "["; if( (*it)->GetFunction( nn )->GetParameters().size() == 1 ) { os << (*it)->GetFunction( nn )->GetParameter( 0 ) << "]"; } else { for( unsigned int i = 0; i < (*it)->GetFunction( nn )->GetParameters().size() - 1; i++ ) { os << (*it)->GetFunction( nn )->GetParameter( i ) << ","; } os << (*it)->GetFunction( nn )->GetParameter( (*it)->GetFunction( nn )->GetParameters().size() - 1 ) << "]"; } } } os << std::endl; } } } std::string CommandLineParser ::BreakUpStringIntoNewLines( std::string longString, std::string indentString, unsigned int numberOfCharactersPerLine ) const { std::vector tokens; this->TokenizeString( longString, tokens, " " ); std::string newString( "" ); unsigned int currentTokenId = 0; unsigned int currentLineLength = 0; while( currentTokenId < tokens.size() ) { if( tokens[currentTokenId].length() >= numberOfCharactersPerLine ) { newString += ( std::string( "\n" ) + tokens[currentTokenId] + std::string( "\n" ) ); currentTokenId++; currentLineLength = 0; } else if( currentTokenId < tokens.size() && currentLineLength + tokens[currentTokenId].length() > numberOfCharactersPerLine ) { newString += ( std::string( "\n" ) + indentString ); currentLineLength = 0; } else { newString += ( tokens[currentTokenId] + std::string( " " ) ); currentLineLength += ( tokens[currentTokenId].length() + 1 ); currentTokenId++; } } return newString; } void CommandLineParser ::TokenizeString( std::string str, std::vector & tokens, std::string delimiters ) const { // Skip delimiters at beginning. std::string::size_type lastPos = str.find_first_not_of( delimiters, 0 ); // Find first "non-delimiter". std::string::size_type pos = str.find_first_of( delimiters, lastPos ); while( std::string::npos != pos || std::string::npos != lastPos ) { // Found a token, add it to the vector. tokens.push_back( str.substr( lastPos, pos - lastPos ) ); // Skip delimiters. Note the "not_of" lastPos = str.find_first_not_of( delimiters, pos ); // Find next "non-delimiter" pos = str.find_first_of( delimiters, lastPos ); } } void CommandLineParser ::AssignStages() { OptionListType::const_iterator it; for( it = this->m_Options.begin(); it != this->m_Options.end(); ++it ) { typedef OptionType::FunctionStackType OptionFunctionStackType; OptionFunctionStackType functions = (*it)->GetFunctions(); OptionFunctionStackType::const_iterator it2; unsigned int previousOrder = 0; unsigned int currentOrder = 0; for( it2 = functions.begin(); it2 != functions.end(); ++it2 ) { if( it2 == functions.begin() ) { previousOrder = (*it2)->GetArgOrder(); (*it2)->SetStageID( 0 ); } else { currentOrder = (*it2)->GetArgOrder(); if( previousOrder == currentOrder + 1 ) { (*it2)->SetStageID( functions[it2 - functions.begin() - 1]->GetStageID() ); } else { (*it2)->SetStageID( functions[it2 - functions.begin() - 1]->GetStageID() + 1 ); } previousOrder = currentOrder; } } } } /** * Standard "PrintSelf" method */ void CommandLineParser ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << "Command: " << this->m_Command << std::endl; os << indent << "Options: " << std::endl; OptionListType::const_iterator it; for( it = this->m_Options.begin(); it != this->m_Options.end(); ++it ) { (*it)->Print( os, indent ); } if( this->m_UnknownOptions.size() ) { os << indent << "Unknown Options: " << std::endl; OptionListType::const_iterator its; for( its = this->m_UnknownOptions.begin(); its != this->m_UnknownOptions.end(); ++its ) { (*its)->Print( os, indent ); } } } } // end namespace ants } // end namespace itk ants-2.2.0/Utilities/antsCommandLineParser.h000066400000000000000000000124641311104306400210350ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 __antsCommandLineParser_h #define __antsCommandLineParser_h #include "antsCommandLineOption.h" #include "itkDataObject.h" #include "itkObjectFactory.h" #include "itkMacro.h" #include "itkNumericTraits.h" #include #include #include #include #include #include namespace itk { namespace ants { /** * A untilty function to convert internal typeid(???).name() to * the human readable equivalent format. */ extern std::string ConvertToHumanReadable(const std::string & input); /** \class CommandLineParser \brief Simple command line parser. \par Parses the standard ( argc, argv ) variables which are stored as options in the helper class antsCommandLineOption. Also contains routines for converting types including std::vectors using 'x' as a delimiter. For example, I can specify the 3-element std::vector {10, 20, 30} as "10x20x30". */ class CommandLineParser : public DataObject { public: /** Standard class typedefs. */ typedef CommandLineParser Self; typedef DataObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( CommandLineParser, DataObject ); typedef CommandLineOption OptionType; typedef std::list OptionListType; typedef std::list StringListType; /** * Interface routines */ OptionType::Pointer GetOption( char ); OptionType::Pointer GetOption( std::string ); bool starts_with( const std::string &, const std::string & ); int Parse( unsigned int, char * * ); bool ValidateFlag(const std::string & currentFlag); void AddOption( OptionType::Pointer ); void PrintMenu( std::ostream& os, Indent indent, bool printShortVersion = false ) const; itkSetStringMacro( Command ); itkGetStringMacro( Command ); itkSetStringMacro( CommandDescription ); itkGetStringMacro( CommandDescription ); OptionListType GetOptions() const { return this->m_Options; } OptionListType GetUnknownOptions() const { return this->m_UnknownOptions; } /** * This feature is designed for a more advanced command line usage * where multiple option values are used per stage (e.g. * antsRegistration). Multiple option value are considered to be of * the same stage if they are situated adjacently on the command line. */ void AssignStages(); template TValue Convert( std::string optionString ) const { //Strip whitespace at end optionString.erase(optionString.find_last_not_of(" \n\r\t")+1); TValue value; std::istringstream iss( optionString ); if (!(iss >> value) //Conversion did not fail || !( iss.peek() == EOF ) // All content parsed ) { std::string internalTypeName( typeid(value).name() ); itkExceptionMacro( "ERROR: Parse error occured during command line argument processing\n" << "ERROR: Unable to convert '" << optionString << "' to type '" << internalTypeName << "' as " << ConvertToHumanReadable(internalTypeName) << std::endl); } return value; } template std::vector ConvertVector( std::string optionString ) const { //Strip whitespace at end optionString.erase(optionString.find_last_not_of(" \n\r\t")+1); std::vector optionElementString; std::istringstream f(optionString); std::string s; while( std::getline(f, s, 'x')) { optionElementString.push_back(s); } std::vector< TValue > values; for ( std::vector< std::string >::const_iterator oESit = optionElementString.begin(); oESit != optionElementString.end(); ++oESit) { const TValue & value = this->Convert( *oESit ); values.push_back ( value ); } return values; } protected: CommandLineParser(); virtual ~CommandLineParser() { } void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; private: CommandLineParser( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented std::vector RegroupCommandLineArguments( unsigned int, char * * ); std::string BreakUpStringIntoNewLines( std::string, const std::string, unsigned int ) const; void TokenizeString( std::string, std::vector &, std::string ) const; OptionListType m_Options; std::string m_Command; std::string m_CommandDescription; OptionListType m_UnknownOptions; char m_LeftDelimiter; char m_RightDelimiter; }; } // end namespace ants } // end namespace itk #endif ants-2.2.0/Utilities/antsMatrixUtilities.h000066400000000000000000000223771311104306400206360ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsMatrixUtilities_h #define __antsMatrixUtilities_h #include #include #include "itkImageToImageFilter.h" namespace itk { namespace ants { template class antsMatrixUtilities : public ImageToImageFilter { public: /** Standard class typdedefs. */ typedef antsMatrixUtilities Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( antsMatrixUtilities, ImageToImageFilter ); /** Dimension of the images. */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); itkStaticConstMacro( MatrixDimension, unsigned int, 2 ); /** Typedef support of input types. */ typedef TInputImage ImageType; typedef typename ImageType::Pointer ImagePointer; typedef typename ImageType::PixelType PixelType; typedef typename ImageType::IndexType IndexType; /** Some convenient typedefs. */ typedef TRealType RealType; typedef Image RealImageType; /** note, eigen for pseudo-eigenvals */ typedef vnl_matrix MatrixType; typedef vnl_vector VectorType; typedef MatrixType VariateType; typedef vnl_diag_matrix DiagonalMatrixType; void NormalizeWeightsByCovariance(); void SetPseudoInversePercentVariance( RealType p ) { this->m_PercentVarianceForPseudoInverse = p; } MatrixType PseudoInverse( MatrixType p_in, bool take_sqrt = false ) { return this->VNLPseudoInverse( p_in, take_sqrt ); } MatrixType VNLPseudoInverse( MatrixType, bool take_sqrt = false ); VectorType Orthogonalize(VectorType Mvec, VectorType V, MatrixType* projecterM = NULL, MatrixType* projecterV = NULL ) { if( !projecterM && !projecterV ) { double ratio = inner_product(Mvec, V) / inner_product(V, V); VectorType ortho = Mvec - V * ratio; return ortho; } else if( !projecterM && projecterV ) { double ratio = inner_product(Mvec, *projecterV * V) / inner_product(*projecterV * V, *projecterV * V); VectorType ortho = Mvec - V * ratio; return ortho; } else if( !projecterV && projecterM ) { double ratio = inner_product(*projecterM * Mvec, V) / inner_product(V, V); VectorType ortho = (*projecterM * Mvec) - V * ratio; return ortho; } else { double ratio = inner_product(*projecterM * Mvec, *projecterV * V) / inner_product(*projecterV * V, *projecterV * V); VectorType ortho = Mvec - V * ratio; return ortho; } } MatrixType OrthogonalizeMatrix(MatrixType M, VectorType V ) { for( unsigned int j = 0; j < M.cols(); j++ ) { VectorType Mvec = M.get_column(j); double ratio = inner_product(Mvec, V) / inner_product(V, V); VectorType ortho = Mvec - V * ratio; M.set_column(j, ortho); } return M; } void SetMaskImageP( ImagePointer mask ) { this->m_MaskImageP = mask; } void SetMatrixP( MatrixType matrix ) { this->m_OriginalMatrixP.set_size(matrix.rows(), matrix.cols() ); this->m_MatrixP.set_size( matrix.rows(), matrix.cols() ); this->m_OriginalMatrixP.update(matrix); this->m_MatrixP.update(matrix); } itkSetMacro( FractionNonZeroQ, RealType ); itkSetMacro( KeepPositiveQ, bool ); void SetMaskImageQ( ImagePointer mask ) { this->m_MaskImageQ = mask; } void SetMatrixQ( MatrixType matrix ) { this->m_OriginalMatrixQ.set_size(matrix.rows(), matrix.cols() ); this->m_MatrixQ.set_size( matrix.rows(), matrix.cols() ); this->m_OriginalMatrixQ.update(matrix); this->m_MatrixQ.update(matrix); } itkSetMacro( FractionNonZeroR, RealType ); itkSetMacro( KeepPositiveR, bool ); void SetMaskImageR( ImagePointer mask ) { this->m_MaskImageR = mask; } void SetMatrixR( MatrixType matrix ) { this->m_OriginalMatrixR.set_size(matrix.rows(), matrix.cols() ); this->m_MatrixR.set_size( matrix.rows(), matrix.cols() ); this->m_OriginalMatrixR.update(matrix); this->m_MatrixR.update(matrix); } MatrixType GetMatrixP() { return this->m_MatrixP; } MatrixType GetMatrixQ() { return this->m_MatrixQ; } MatrixType GetMatrixR() { return this->m_MatrixR; } MatrixType GetOriginalMatrixP() { return this->m_OriginalMatrixP; } MatrixType GetOriginalMatrixQ() { return this->m_OriginalMatrixQ; } MatrixType GetOriginalMatrixR() { return this->m_OriginalMatrixR; } VectorType InitializeV( MatrixType p ); MatrixType NormalizeMatrix(MatrixType p); MatrixType CovarianceMatrix(MatrixType p, RealType regularization = 1.e-2 ) { if( p.rows() < p.columns() ) { MatrixType invcov = p * p.transpose(); invcov.set_identity(); invcov = invcov * regularization + p * p.transpose(); return invcov; } else { MatrixType invcov = p.transpose() * p; invcov.set_identity(); invcov = invcov * regularization + p.transpose() * p; return invcov; } } VectorType GetCovMatEigenvector( MatrixType p, unsigned int evec ); MatrixType GetCovMatEigenvectors( MatrixType p ); VectorType AverageColumns( MatrixType p ) { unsigned int ncol = p.columns(); VectorType v = p.get_column(0); v.fill(0); for( unsigned int i = 0; i < ncol; i++ ) { v = v + p.get_column(i); } return v / (RealType)ncol; } MatrixType WhitenMatrix(MatrixType p, RealType regularization = 1.e-2 ) { MatrixType invcov = this->CovarianceMatrix(p, regularization); invcov = this->PseudoInverse( invcov, true ); if( p.rows() < p.columns() ) { return invcov * p; } else { return p * invcov; } } MatrixType WhitenMatrixByAnotherMatrix(MatrixType p, MatrixType op, RealType regularization = 1.e-2) { MatrixType invcov = this->CovarianceMatrix(op, regularization); invcov = this->PseudoInverse( invcov, true ); if( p.rows() < p.columns() ) { return invcov * p; } else { return p * invcov; } } MatrixType ProjectionMatrix(MatrixType b) { b = this->NormalizeMatrix(b); b = this->WhitenMatrix(b); return b * b.transpose(); } MatrixType DeleteCol( MatrixType p_in, unsigned int col) { unsigned int ncols = p_in.cols() - 1; if( col >= ncols ) { ncols = p_in.cols(); } MatrixType p(p_in.rows(), ncols); unsigned int colct = 0; for( long i = 0; i < p.cols(); ++i ) // loop over cols { if( i != col ) { p.set_column(colct, p_in.get_column(i) ); colct++; } } return p; } RealType PearsonCorr(VectorType v1, VectorType v2 ) { double xysum = 0; for( unsigned int i = 0; i < v1.size(); i++ ) { xysum += v1(i) * v2(i); } double frac = 1.0 / (double)v1.size(); double xsum = v1.sum(), ysum = v2.sum(); double xsqr = v1.squared_magnitude(); double ysqr = v2.squared_magnitude(); double numer = xysum - frac * xsum * ysum; double denom = sqrt( ( xsqr - frac * xsum * xsum) * ( ysqr - frac * ysum * ysum) ); if( denom <= 0 ) { return 0; } return numer / denom; } antsMatrixUtilities(); ~antsMatrixUtilities() { } void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE { os << indent; } private: bool m_Debug; MatrixType m_OriginalMatrixP; MatrixType m_OriginalMatrixQ; MatrixType m_OriginalMatrixR; antsMatrixUtilities(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented RealType m_PinvTolerance; RealType m_PercentVarianceForPseudoInverse; MatrixType m_MatrixP; ImagePointer m_MaskImageP; RealType m_FractionNonZeroP; bool m_KeepPositiveP; MatrixType m_MatrixQ; ImagePointer m_MaskImageQ; RealType m_FractionNonZeroQ; bool m_KeepPositiveQ; MatrixType m_MatrixR; ImagePointer m_MaskImageR; RealType m_FractionNonZeroR; bool m_KeepPositiveR; }; } // namespace ants } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsMatrixUtilities.hxx" #endif #endif ants-2.2.0/Utilities/antsMatrixUtilities.hxx000066400000000000000000000116451311104306400212120ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include #include #include #include #include "antsMatrixUtilities.h" namespace itk { namespace ants { template antsMatrixUtilities::antsMatrixUtilities() { this->m_Debug = false; this->m_PinvTolerance = 1.e-2; this->m_PercentVarianceForPseudoInverse = 0.9; this->m_MaskImageP = ITK_NULLPTR; this->m_MaskImageQ = ITK_NULLPTR; this->m_MaskImageR = ITK_NULLPTR; } template typename antsMatrixUtilities::MatrixType antsMatrixUtilities ::NormalizeMatrix( typename antsMatrixUtilities::MatrixType p ) { vnl_random randgen(time(ITK_NULLPTR) ); MatrixType np( p.rows(), p.columns() ); for( unsigned long i = 0; i < p.columns(); i++ ) { VectorType wpcol = p.get_column(i); VectorType wpcol2 = wpcol - wpcol.mean(); double sd = wpcol2.squared_magnitude(); sd = sqrt( sd / (p.rows() - 1) ); if( sd <= 0 ) { if( this->m_Debug ) { std::cout << " bad-row " << i << wpcol << std::endl; } for( unsigned long j = 0; j < wpcol.size(); j++ ) { wpcol2(j) = randgen.drand32(); } wpcol2 = wpcol2 - wpcol2.mean(); sd = wpcol2.squared_magnitude(); sd = sqrt( sd / (p.rows() - 1) ); } wpcol = wpcol2 / sd; np.set_column(i, wpcol); } return np; } template typename antsMatrixUtilities::MatrixType antsMatrixUtilities ::VNLPseudoInverse( typename antsMatrixUtilities::MatrixType rin, bool take_sqrt ) { double pinvTolerance = this->m_PinvTolerance; MatrixType dd = rin; unsigned int ss = dd.rows(); if( dd.rows() > dd.columns() ) { ss = dd.columns(); } vnl_svd eig(dd, pinvTolerance); for( unsigned int j = 0; j < ss; j++ ) { RealType eval = eig.W(j, j); if( eval > pinvTolerance ) // FIXME -- check tolerances against matlab pinv { eig.W(j, j) = 1 / (eval); // need eval for inv cov if( take_sqrt ) { eig.W(j, j) = 1 / sqrt(eval); } } else { eig.W(j, j) = 0; } } return ( eig.recompose() ).transpose(); } template typename antsMatrixUtilities::VectorType antsMatrixUtilities ::GetCovMatEigenvector( typename antsMatrixUtilities::MatrixType rin, unsigned int which ) { double pinvTolerance = this->m_PinvTolerance; MatrixType dd = this->NormalizeMatrix(rin); MatrixType cov = dd * dd.transpose(); cov.set_identity(); TRealType regularization = 1.e-3; cov = cov * regularization + rin * rin.transpose(); vnl_svd eig(cov, pinvTolerance); VectorType vec1 = eig.U().get_column(which); VectorType vec2 = eig.V().get_column(which); // std::cout <<" W 1 " << eig.W(0,0) << " W 2 " << eig.W(1,1) << std::endl; if( vec2.size() == rin.rows() ) { return vec2; } else { return vec1; } } template typename antsMatrixUtilities::MatrixType antsMatrixUtilities ::GetCovMatEigenvectors( typename antsMatrixUtilities::MatrixType rin ) { double pinvTolerance = this->m_PinvTolerance; MatrixType dd = this->NormalizeMatrix(rin); MatrixType cov = dd * dd.transpose(); cov.set_identity(); TRealType regularization = 1.e-3; cov = cov * regularization + rin * rin.transpose(); vnl_svd eig(cov, pinvTolerance); VectorType vec1 = eig.U().get_column(0); VectorType vec2 = eig.V().get_column(0); double trace = vnl_trace(cov); double evalsum = 0; for( unsigned int i = 0; i < cov.rows(); i++ ) { evalsum += eig.W(i, i); std::cout << " variance-explained-eval " << i << " = " << evalsum / trace * 100 << std::endl; } // std::cout <<" W 1 " << eig.W(0,0) << " W 2 " << eig.W(1,1) << std::endl; if( vec2.size() == rin.rows() ) { return eig.V(); } else { return eig.U(); } } } // namespace ants } // namespace itk ants-2.2.0/Utilities/antsSCCANObject.h000066400000000000000000001145441311104306400174520ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __antsSCCANObject_h #define __antsSCCANObject_h // #define EIGEN_DEFAULT_TO_ROW_MAJOR // #define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET // #include // #include // #include // #include "armadillo" #include #include #include "itkImageToImageFilter.h" /** Custom SCCA implemented with vnl and ITK: Flexible positivity constraints, image ops, permutation testing, etc. */ namespace itk { namespace ants { template class antsSCCANObject : public ImageToImageFilter { public: /** Standard class typdedefs. */ typedef antsSCCANObject Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( antsSCCANObject, ImageToImageFilter ); /** Dimension of the images. */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); itkStaticConstMacro( MatrixDimension, unsigned int, 2 ); /** Typedef support of input types. */ typedef TInputImage ImageType; typedef typename ImageType::Pointer ImagePointer; typedef typename ImageType::PixelType PixelType; typedef typename ImageType::IndexType IndexType; /** Some convenient typedefs. */ typedef TRealType RealType; typedef Image RealImageType; typedef Image RealImageTypeDminus1; /** Define eigen types */ // typedef Eigen::Matrix eMatrix; // typedef Eigen::Matrix eVector; // typedef Eigen::DynamicSparseMatrix sMatrix; // typedef Eigen::FullPivHouseholderQR svdobj2; // typedef Eigen::JacobiSVD svdobj; /** note, eigen for pseudo-eigenvals */ typedef vnl_matrix MatrixType; typedef vnl_vector VectorType; typedef MatrixType VariateType; typedef vnl_diag_matrix DiagonalMatrixType; enum SCCANFormulationType { PQ, PminusRQ, PQminusR, PminusRQminusR, PQR }; /** ivars Set/Get functionality */ itkSetMacro( MaximumNumberOfIterations, unsigned int ); itkGetConstMacro( MaximumNumberOfIterations, unsigned int ); itkSetMacro( MinClusterSizeP, unsigned int ); itkGetConstMacro( MinClusterSizeP, unsigned int ); itkSetMacro( MinClusterSizeQ, unsigned int ); itkGetConstMacro( MinClusterSizeQ, unsigned int ); itkSetMacro( KeptClusterSize, unsigned int ); itkGetConstMacro( KeptClusterSize, unsigned int ); itkSetMacro( AlreadyWhitened, bool ); itkGetConstMacro( AlreadyWhitened, bool ); itkSetMacro( PriorWeight, RealType ); itkGetConstMacro( PriorWeight, RealType ); itkSetMacro( ConvergenceThreshold, RealType ); itkGetConstMacro( ConvergenceThreshold, RealType ); itkGetConstMacro( CurrentConvergenceMeasurement, RealType ); itkGetConstMacro( ElapsedIterations, unsigned int ); itkSetMacro( SCCANFormulation, SCCANFormulationType ); itkGetConstMacro( SCCANFormulation, SCCANFormulationType ); itkSetMacro( Silent, bool ); itkGetMacro( Silent, bool ); itkSetMacro( RowSparseness, RealType ); itkGetMacro( RowSparseness, RealType ); itkSetMacro( UseLongitudinalFormulation, RealType ); itkGetMacro( UseLongitudinalFormulation, RealType ); itkSetMacro( Smoother, RealType ); itkGetMacro( Smoother, RealType ); void NormalizeWeights(const unsigned int k ); void NormalizeWeightsByCovariance(const unsigned int k, const TRealType taup = 0, const TRealType tauq = 0); void WhitenDataSetForRunSCCANMultiple(unsigned int nvecs = 0); void SetPseudoInversePercentVariance( RealType p ) { this->m_PercentVarianceForPseudoInverse = p; } MatrixType PseudoInverse( MatrixType p_in, bool take_sqrt = false ) { return this->VNLPseudoInverse( p_in, take_sqrt ); } MatrixType VNLPseudoInverse( MatrixType, bool take_sqrt = false ); void ZeroProduct( VectorType& v1, VectorType& v2 ) { for( unsigned int i = 0; i < v1.size(); i++ ) { if( fabs( v2( i ) ) > 1.e-9 ) { v1( i ) = 0; } } } void DeleteRow( MatrixType &, unsigned int ); void PosNegVector( VectorType& v1, bool pos ) { for( unsigned int i = 0; i < v1.size(); i++ ) { if( v1( i ) < 0 && pos ) { v1( i ) = 0; } else if( v1( i ) > 0 && !pos ) { v1( i ) = 0; } } } RealType ReconstructionError( MatrixType, MatrixType ); VectorType Orthogonalize(VectorType Mvec, VectorType V, MatrixType* projecterM = NULL, MatrixType* projecterV = NULL ) { if( ( !projecterM ) && ( !projecterV ) ) { double ipv = inner_product(V, V); if( ipv == 0 ) { return Mvec; } double ratio = inner_product(Mvec, V) / ipv; VectorType ortho = Mvec - V * ratio; return ortho; } else if( ( !projecterM ) && ( projecterV ) ) { double ratio = inner_product(Mvec, *projecterV * V) / inner_product(*projecterV * V, *projecterV * V); VectorType ortho = Mvec - V * ratio; return ortho; } else if( ( !projecterV ) && ( projecterM ) ) { double ratio = inner_product(*projecterM * Mvec, V) / inner_product(V, V); VectorType ortho = (*projecterM * Mvec) - V * ratio; return ortho; } else { double ratio = inner_product(*projecterM * Mvec, *projecterV * V) / inner_product(*projecterV * V, *projecterV * V); VectorType ortho = Mvec - V * ratio; for( unsigned int i = 0; i < Mvec.size(); i++ ) { if( Mvec(i) == 0 ) { ortho(i) = 0; } } return ortho; } } MatrixType OrthogonalizeMatrix(MatrixType M, VectorType V ) { for( unsigned int j = 0; j < M.cols(); j++ ) { VectorType Mvec = M.get_column(j); double vnorm = inner_product(V, V); if ( vnorm < this->m_Epsilon ) vnorm = 1; double ratio = inner_product(Mvec, V) / vnorm; VectorType ortho = Mvec - V * ratio; M.set_column(j, ortho); } return M; } MatrixType RankifyMatrixColumns(MatrixType M ) { RealType rows = (RealType)M.rows(); for( unsigned long j = 0; j < M.cols(); j++ ) { VectorType Mvec = M.get_column(j); VectorType rank = M.get_column(j); for( unsigned int i = 0; i < rows; i++ ) { double rankval = 0; RealType xi = Mvec(i); for( unsigned int k = 0; k < rows; k++ ) { RealType yi = Mvec(k); RealType diff = fabs(xi - yi); if( diff > 0 ) { RealType val = (xi - yi) / diff; rankval += val; } } rank(i) = rankval / rows; } M.set_column(j, rank); } return M; } itkSetMacro( FractionNonZeroP, RealType ); itkSetMacro( KeepPositiveP, bool ); itkGetMacro( KeepPositiveP, bool ); void SetMaskImageP( ImagePointer mask ) { this->m_MaskImageP = mask; } void SetMatrixP( MatrixType matrix ) { this->m_OriginalMatrixP.set_size(matrix.rows(), matrix.cols() ); this->m_MatrixP.set_size( matrix.rows(), matrix.cols() ); this->m_OriginalMatrixP.update(matrix); this->m_MatrixP.update(matrix); } itkSetMacro( FractionNonZeroQ, RealType ); itkSetMacro( KeepPositiveQ, bool ); itkGetMacro( KeepPositiveQ, bool ); void SetMaskImageQ( ImagePointer mask ) { this->m_MaskImageQ = mask; } void SetMatrixQ( MatrixType matrix ) { this->m_OriginalMatrixQ.set_size(matrix.rows(), matrix.cols() ); this->m_MatrixQ.set_size( matrix.rows(), matrix.cols() ); this->m_OriginalMatrixQ.update(matrix); this->m_MatrixQ.update(matrix); } itkSetMacro( Covering, unsigned int ); itkSetMacro( GetSmall, bool ); itkSetMacro( UseL1, bool ); itkSetMacro( GradStep, RealType ); itkSetMacro( FractionNonZeroR, RealType ); itkSetMacro( KeepPositiveR, bool ); void SetMaskImageR( ImagePointer mask ) { this->m_MaskImageR = mask; } void SetMatrixR( MatrixType matrix ) { this->m_OriginalMatrixR.set_size(matrix.rows(), matrix.cols() ); this->m_MatrixR.set_size( matrix.rows(), matrix.cols() ); this->m_OriginalMatrixR.update(matrix); this->m_MatrixR.update(matrix); } MatrixType GetMatrixP() { return this->m_MatrixP; } MatrixType GetMatrixQ() { return this->m_MatrixQ; } MatrixType GetMatrixR() { return this->m_MatrixR; } MatrixType GetMatrixU() { return this->m_MatrixU; } MatrixType GetOriginalMatrixP() { return this->m_OriginalMatrixP; } RealType GetLambda() { return this->m_lambda; } RealType SetLambda(RealType lambda) { return this->m_lambda = lambda; } MatrixType GetOriginalMatrixQ() { return this->m_OriginalMatrixQ; } MatrixType GetOriginalMatrixR() { return this->m_OriginalMatrixR; } // Prior Constrained PCA MatrixType GetMatrixPriorROI() { return this->m_MatrixPriorROI; } // Prior Constrained PCA MatrixType GetMatrixPriorROI2() { return this->m_MatrixPriorROI2; } void SetFlagForSort() { this->flagForSort = true; } // Prior Constrained PCA MatrixType GetOriginalMatrixPriorROI() { return this->m_OriginalMatrixPriorROI; } RealType RunSCCAN2multiple( unsigned int n_vecs ); RealType RunSCCAN2(); RealType RunSCCAN3(); RealType LineSearch( MatrixType & A, VectorType & x_k, VectorType & p_k, VectorType & b, RealType minalph, RealType maxalph, RealType ); RealType EvaluateEnergy( MatrixType & A, VectorType & x_k, VectorType & p_k, VectorType & b, RealType minalph, RealType ); RealType SparseConjGrad( VectorType &, VectorType, RealType, unsigned int ); RealType ConjGrad( MatrixType& A, VectorType& x_k, VectorType b_in, RealType convcrit, unsigned int ); RealType SparseConjGradRidgeRegression( MatrixType& A, VectorType& x_k, VectorType b_in, RealType convcrit, unsigned int, bool ); RealType IHTRegression( MatrixType& A, VectorType& x_k, VectorType b_in, RealType convcrit, unsigned int, RealType mu, bool isp = true, bool verbose = false ); RealType MatchingPursuit( MatrixType& A, VectorType& x_k, RealType convcrit, unsigned int ); RealType SparseNLConjGrad( MatrixType & A, VectorType & x_k, VectorType b, RealType, unsigned int, bool makeprojsparse = false, unsigned int loorth = 0, unsigned int hiorth = 0 ); RealType SparseNLPreConjGrad( MatrixType & A, VectorType & x_k, VectorType b, RealType, unsigned int ); RealType RidgeRegression( MatrixType & A, VectorType & x_k, VectorType b, RealType lambda, unsigned int, bool makesparse = false ); /** Return Rayleigh quotient */ RealType PowerIteration( MatrixType & A, VectorType & x_k, unsigned int, bool); RealType IHTPowerIteration( MatrixType & A, VectorType & x_k, unsigned int, unsigned int ); RealType IHTPowerIterationU( MatrixType & A, VectorType & x_k, unsigned int, unsigned int ); RealType IHTPowerIterationPrior( MatrixType & A, VectorType & x_k, VectorType & x_k_1, unsigned int, unsigned int, double ); void SoftClustThreshold( VectorType & v_in, RealType fractional_goal, bool allow_negative_weights, unsigned int, ImagePointer ); void ReSoftThreshold( VectorType& v_in, RealType fractional_goal, bool allow_negative_weights ); void ConstantProbabilityThreshold( VectorType& v_in, RealType probability_goal, bool allow_negative_weights ); VectorType InitializeV( MatrixType p, unsigned long seed = 0 ); TRealType InitializeSCCA_simple( unsigned int n_vecs ); TRealType InitializeSCCA( unsigned int n_vecs, unsigned int seeder ); TRealType InitializeSPCA( unsigned int n_vecs, unsigned int seeder, MatrixType &, VectorType & ); VectorType ComputeVectorLaplacian( VectorType, ImagePointer ); VectorType ComputeVectorGradMag( VectorType, ImagePointer ); VectorType SpatiallySmoothVector( VectorType, ImagePointer, bool surface = true ); void SetSortFinalLocArray(VectorType locArray) { this->loc_Array = locArray; } VectorType GetSortFinalLocArray() { return this->loc_Array; } MatrixType NormalizeMatrix(MatrixType p, bool makepositive = true ); /** needed for partial scca */ MatrixType CovarianceMatrix(MatrixType p, RealType regularization = 1.e-2 ) { if( p.rows() < p.columns() ) { MatrixType invcov = p * p.transpose(); invcov.set_identity(); invcov = invcov * regularization + p * p.transpose(); return invcov; } else { MatrixType invcov = p.transpose() * p; invcov.set_identity(); invcov = invcov * regularization + p.transpose() * p; return invcov; } } MatrixType WhitenMatrix(MatrixType p, RealType regularization = 1.e-2 ) { double reg = 1.e-9; if( p.rows() < p.cols() ) { reg = regularization; } MatrixType cov = this->CovarianceMatrix(p, reg); MatrixType invcov = this->PseudoInverse( cov, true ); bool debug = false; if( ( ! this->m_Silent ) && ( debug ) ) { std::cout << " cov " << std::endl; std::cout << cov << std::endl; std::cout << " invcov " << std::endl; std::cout << invcov << std::endl; std::cout << " id? " << std::endl; std::cout << cov * invcov << std::endl; } if( p.rows() < p.columns() ) { return invcov * p; } else { return p * invcov; } } MatrixType WhitenMatrixByAnotherMatrix(MatrixType p, MatrixType op, RealType regularization = 1.e-2) { MatrixType invcov = this->CovarianceMatrix(op, regularization); invcov = this->PseudoInverse( invcov, true ); if( p.rows() < p.columns() ) { return invcov * p; } else { return p * invcov; } } MatrixType ProjectionMatrix(MatrixType b, double regularization = 0.001) { bool armadillo = false; b = this->NormalizeMatrix( b ); MatrixType mat = b * b.transpose(); if( !armadillo ) { MatrixType cov( mat.rows(), mat.cols(), 0); cov.set_identity(); mat = cov * regularization + mat; return vnl_svd( mat ).inverse(); // return vnl_svd( mat ).pinverse( mc ); } else { /* arma::mat amat( b.rows(), b.rows()); for ( unsigned int i = 0 ; i < b.rows(); i++ ) for ( unsigned int j = 0 ; j < b.rows(); j++ ) { amat( i , j ) = mat( i , j ); if ( i == j ) amat( i , j ) += regularization; } arma::mat invamat = arma::inv( amat , 1.e-2 ); for ( unsigned int i = 0 ; i < b.rows(); i++ ) for ( unsigned int j = 0 ; j < b.rows(); j++ ) mat( i , j ) = invamat( i , j ); */ return mat; } } VectorType TrueCCAPowerUpdate(RealType penaltyP, MatrixType p, VectorType w_q, MatrixType q, bool keep_pos, bool factorOutR); MatrixType PartialOutZ( MatrixType /*X*/, MatrixType /*Y*/, MatrixType /*Z*/ ) { if ( ! this->m_Silent ) std::cout << "ERROR: This function not yet implemented." << std::endl; /** compute the effect of Z and store it for later use */ } // Prior Constrained PCA void SetMatrixPriorROI( MatrixType matrix ) { this->m_OriginalMatrixPriorROI.set_size(matrix.rows(), matrix.cols() ); this->m_MatrixPriorROI.set_size( matrix.rows(), matrix.cols() ); this->m_OriginalMatrixPriorROI.update(matrix); this->m_MatrixPriorROI.update( matrix); } void SetMatrixPriorROI2( MatrixType matrix ) { this->m_MatrixPriorROI2.set_size( matrix.rows(), matrix.cols() ); this->m_MatrixPriorROI2.update( matrix); } // itkSetMacro( priorScale, RealType ); // itkGetMacro( priorScale, RealType ); RealType GetPriorScaleMat() { return this->m_priorScaleMat; } void SetPriorScaleMat(MatrixType priorScaleMat) { this->m_priorScaleMat.set_size(priorScaleMat.rows(), priorScaleMat.cols() ); this->m_priorScaleMat.update( priorScaleMat); } VectorType GetPWeights() { return this->m_WeightsP; } VectorType GetQWeights() { return this->m_WeightsQ; } VectorType GetRWeights() { return this->m_WeightsR; } RealType GetCorrelationForSignificanceTest() { return this->CorrelationForSignificanceTest; } VectorType GetCanonicalCorrelations() { return this->m_CanonicalCorrelations; } VectorType GetVariateP( unsigned int i = 0 ) { return this->m_VariatesP.get_column(i); } VectorType GetVariateQ( unsigned int i = 0 ) { return this->m_VariatesQ.get_column(i); } MatrixType GetVariatesP() { return this->m_VariatesP; } MatrixType GetVariatesQ() { return this->m_VariatesQ; } VectorType FastOuterProductVectorMultiplication( VectorType& p, VectorType& v ) { // computes outer_product( p , p ) * v // std::cout << p.size() <m_Silent ) std::cout << "FastOuterProductVectorMultiplication Usage Error " << std::endl; if ( ! this->m_Silent ) std::cout << "Size 1: " << p.size() << "Size 2: " << v.size() << std::endl; return v; } RealType ip = inner_product( p, v ); VectorType vout( p ); return vout * ip; } RealType ComputeEnergySlope( std::vector vexlist, unsigned int n ) { unsigned int N = vexlist.size(); unsigned int loline = N - n; if( N < n * 2 ) { return 1; } double s0 = (n + 1); double s1 = 0; double s2 = 0; double t0 = 0; double t1 = 0; for( unsigned int i = loline; i < N; ++i ) { const double t = (i - loline); s1 += t; s2 += (t * t); const double e = vexlist[i] - vexlist[loline]; t0 += e; t1 += (e * e); } double M = 1; double denom = (s0 * s2 - s1 * s1); if( denom > 0 ) { M = ( s1 * t0 - s0 * t1 ) / denom; } return M; /* std::vector sublist; for (int i=listsize-4; i 0 && gamma < beta ) { return beta - gamma; } else if( beta > 0 && gamma > beta ) { return 0; } else if( beta < 0 && gamma < ( beta * (-1) ) ) { return beta + gamma; } return 0; } inline RealType ComputeIntercept( MatrixType A, VectorType x, VectorType b ) { RealType intercept = b.mean(); for( unsigned int Acol = 0; Acol < A.cols(); Acol++ ) { intercept -= A.get_column( Acol ).mean() * x( Acol ); } return intercept; } inline RealType SimpleRegression( VectorType y, VectorType ypred ) { RealType corr = this->PearsonCorr( y, ypred ); double sdy = sqrt( ( y - y.mean() ).squared_magnitude() / ( y.size() - 1) ); double sdyp = sqrt( ( ypred - ypred.mean() ).squared_magnitude() / ( y.size() - 1) ); if( sdyp == 0 ) { return 0; } return corr * sdy / sdyp; } MatrixType GetCovMatEigenvectors( MatrixType p ); void MRFFilterVariateMatrix(); ImagePointer ConvertVariateToSpatialImage( VectorType variate, ImagePointer mask, bool threshold_at_zero = false ); protected: void SortResults(unsigned int n_vecs); // for pscca void UpdatePandQbyR(); void PositivePart( VectorType& x_k1, bool takemin = false ) { if( takemin ) { RealType minval = x_k1.min_value(); x_k1 = x_k1 - minval; return; } for( unsigned int i = 0; i < x_k1.size(); i++ ) { if( x_k1[i] < 0 ) { x_k1[i] = vnl_math_abs( x_k1[i] ); x_k1[i] = 0; } } } void SparsifyOther( VectorType& x_k1 , bool doclassic = false ) { RealType fnp = vnl_math_abs( this->m_RowSparseness ); if( fnp < 1.e-11 ) { return; } if ( doclassic ) { if ( this->m_RowSparseness < 0 ) { if ( fnp > x_k1.max_value() ) fnp = x_k1.max_value() * 0.9; for ( unsigned int i = 0; i < x_k1.size(); i++ ) { RealType delta = vnl_math_abs( x_k1( i ) ) - fnp; if ( delta < 0 ) delta = 0; else if ( x_k1( i ) < 0 ) delta *= ( -1 ); x_k1( i ) = delta; } return; } if ( this->m_RowSparseness > 0 ) { if ( fnp > x_k1.max_value() ) fnp = x_k1.max_value() * 0.9; for ( unsigned int i = 0; i < x_k1.size(); i++ ) { RealType delta = vnl_math_abs( x_k1( i ) ) - fnp; if ( delta < 0 ) delta = 0; x_k1( i ) = delta; } return; } }//doclassic bool usel1 = this->m_UseL1; this->m_UseL1 = true; bool keeppos = false; if( this->m_RowSparseness > 1.e-11 ) { keeppos = true; } this->Sparsify( x_k1, fnp, keeppos, 0, ITK_NULLPTR ); this->m_UseL1 = usel1; } void SparsifyP( VectorType& x_k1 ) { RealType fnp = vnl_math_abs( this->m_FractionNonZeroP ); this->Sparsify( x_k1, fnp, this->m_KeepPositiveP, this->m_MinClusterSizeP, this->m_MaskImageP); } void SparsifyQ( VectorType& x_k1 ) { RealType fnp = vnl_math_abs( this->m_FractionNonZeroQ ); this->Sparsify( x_k1, fnp, this->m_KeepPositiveQ, this->m_MinClusterSizeQ, this->m_MaskImageQ); } void Sparsify( VectorType& x_k1, RealType fnp, bool keeppos, unsigned int clust, ImagePointer mask ) { if( x_k1.size() <= 1 ) { return; } if( fnp >= 1 && keeppos ) { this->PositivePart( x_k1 ); return; } if( fnp >= 1 ) { return; } bool negate = false; if( x_k1.mean() <= 0 ) { negate = true; } if( negate ) { x_k1 = x_k1 * ( -1 ); } RealType initmax = x_k1.max_value(); x_k1 = x_k1 / initmax; std::vector x_k1sort( x_k1.size() , 0 ); for ( unsigned long j = 0; j < x_k1.size(); ++j ) x_k1sort[j] = ( x_k1(j) ); sort(x_k1sort.begin(), x_k1sort.end() , std::less() ); // std::cout << "Sorted " << x_k1sort[1] << " " << x_k1sort[x_k1.size()-1] << std::endl; RealType low = 0; RealType maxj = static_cast( x_k1.size() ); unsigned long maxjind = static_cast( (1-fnp*0.5) * maxj + 0.5 ); unsigned long minjind = static_cast( ( fnp*0.5) * maxj + 0.5 ); if ( maxjind > ( maxj - 1 ) ) maxjind = ( maxj - 1 ); RealType maxval = x_k1sort[ maxjind ]; RealType minval = x_k1sort[ minjind ]; RealType high = maxval; if ( vnl_math_abs( minval ) > high ) high = vnl_math_abs( minval ); // if ( high * 0.2 > 0 ) low = high * 0.2; // hack to speed convergence RealType eng = fnp; RealType mid = low + 0.5 * ( high - low ); unsigned int its = 0; RealType fnm = 0; RealType lastfnm = 1; while( ( ( eng > (fnp*0.1) ) && ( vnl_math_abs( high - low ) > this->m_Epsilon ) && ( its < 20 ) ) || its < 3 ) { mid = low + 0.5 * ( high - low ); VectorType searcherm( x_k1 ); // if ( its > 10 & fnm > 0.99 ) std::cout << " A " << searcherm << std::endl; this->SoftClustThreshold( searcherm, mid, keeppos, clust, mask ); // if ( its > 10 & fnm > 0.99 ) std::cout << " B " << searcherm << std::endl; searcherm = this->SpatiallySmoothVector( searcherm, mask ); // if ( its > 10 & fnm > 0.99 ) std::cout << " C " << searcherm << std::endl; // if ( its > 10 & fnm > 0.99 ) exit(1); lastfnm = fnm; fnm = this->CountNonZero( searcherm ); if( fnm > fnp ) { low = mid; // 0.5 * ( low + mid ); // relax this b/c it may not be a strictly quadratic space } if( fnm < fnp ) { high = mid; // 0.5 * ( high + mid ); } eng = vnl_math_abs( fnp - fnm ); its++; } this->SoftClustThreshold( x_k1, mid, keeppos, clust, mask ); x_k1 = this->SpatiallySmoothVector( x_k1, mask ); if( negate ) { x_k1 = x_k1 * ( -1 ); } return; } void SparsifyOld( VectorType& x_k1, RealType fnp, bool keeppos, unsigned int clust, ImagePointer mask ) { if( x_k1.size() <= 1 ) { return; } if( fnp >= 1 && keeppos ) { this->PositivePart( x_k1 ); return; } if( fnp >= 1 ) { return; } bool negate = false; if( x_k1.mean() <= 0 ) { negate = true; } if( negate ) { x_k1 = x_k1 * ( -1 ); } RealType initmax = x_k1.max_value(); x_k1 = x_k1 / initmax; RealType low = 0; RealType high = 1; RealType eng = fnp; RealType mid = low + 0.5 * ( high - low ); unsigned int its = 0; RealType fnm = 0; RealType lastfnm = 1; while( ( ( eng > 5.e-3 ) && ( vnl_math_abs( high - low ) > this->m_Epsilon ) && ( its < 50 ) ) || its < 5 ) { mid = low + 0.5 * ( high - low ); VectorType searcherm( x_k1 ); this->SoftClustThreshold( searcherm, mid, keeppos, clust, mask ); searcherm = this->SpatiallySmoothVector( searcherm, mask ); lastfnm = fnm; fnm = this->CountNonZero( searcherm ); // if ( mask ) std::cout <<" its " << its << " spar " << fnm << " low " << low << " mid " << mid << " high " << high << " eng " << eng << std::endl; if( fnm > fnp ) { low = mid; } if( fnm < fnp ) { high = mid; } eng = vnl_math_abs( fnp - fnm ); its++; } this->SoftClustThreshold( x_k1, mid, keeppos, clust, mask ); x_k1 = this->SpatiallySmoothVector( x_k1, mask ); if( negate ) { x_k1 = x_k1 * ( -1 ); } return; } void SparsifyP( VectorType& x_k1, VectorType& refvec ) { if( x_k1.size() != refvec.size() ) { if ( ! this->m_Silent ) { std::cout << " sizes dont match " << std::endl; } std::exception(); } for( unsigned int i = 0; i < x_k1.size(); i++ ) { if( refvec(i) == 0 ) { x_k1(i) = 0; } } } MatrixType DeleteCol( MatrixType p_in, unsigned int col) { unsigned int ncols = p_in.cols() - 1; if( col >= ncols ) { ncols = p_in.cols(); } MatrixType p(p_in.rows(), ncols); unsigned int colct = 0; for( long i = 0; i < p.cols(); ++i ) // loop over cols { if( i != col ) { p.set_column(colct, p_in.get_column(i) ); colct++; } } return p; } RealType CountNonZero( VectorType v ) { unsigned long ct = 0; for( unsigned int i = 0; i < v.size(); i++ ) { if( vnl_math_abs( v[i] ) > this->m_Epsilon ) { ct++; } } return (RealType)ct / (RealType)v.size(); } bool Close2Zero( RealType x ) { RealType eps = this->m_Epsilon * 5.0; // eps = 0.0001; if ( vnl_math_abs( x - itk::NumericTraits::ZeroValue() ) < eps ) return true; return false; } RealType PearsonCorr(VectorType v1, VectorType v2 ) { double xysum = 0; for( unsigned int i = 0; i < v1.size(); i++ ) { xysum += v1(i) * v2(i); } double frac = 1.0 / (double)v1.size(); double xsum = v1.sum(), ysum = v2.sum(); double xsqr = v1.squared_magnitude(); double ysqr = v2.squared_magnitude(); double numer = xysum - frac * xsum * ysum; double denom = sqrt( ( xsqr - frac * xsum * xsum) * ( ysqr - frac * ysum * ysum) ); if( denom <= 0 ) { return 0; } return vnl_math_abs( numer / denom ); } RealType RPearsonCorr(VectorType v1, VectorType v2 ) { std::vector zeromatch( v1.size(), 0); unsigned int zct = 0; for ( unsigned int zm = 0; zm < v1.size(); zm++ ) { if ( ( this->Close2Zero( v1(zm) ) || this->Close2Zero( v2(zm) ) ) ) { zct++; zeromatch[ zm ] = 1; v1(zm) = 0; v2(zm) = 0; } } double frac = 1.0 / (double)v1.size(); double xysum = 0; double xsum = 0; double ysum = 0; double xsqr = 0; double ysqr = 0; for( unsigned int i = 0; i < v1.size(); i++ ) { if ( zeromatch[i] == 0 ) { xysum += v1(i) * v2(i); xsum += v1(i); xsqr += v1(i) * v1(i); ysum += v2(i); ysqr += v2(i) * v2(i); } } double numer = xysum - frac * xsum * ysum; double denom = sqrt( ( xsqr - frac * xsum * xsum) * ( ysqr - frac * ysum * ysum) ); if( denom <= 0 ) { return 0; } return vnl_math_abs( numer / denom ); } RealType GoldenSection( MatrixType& A, VectorType& x_k, VectorType& p_k, VectorType& bsol, RealType a, RealType b, RealType c, RealType tau, RealType lambda); // VectorType vEtoV( eVector v ) { // VectorType v_out( v.data() , v.size() ); // return v_out; // } // eVector vVtoE( VectorType v ) { // eVector v_out( v.size() ); // for (unsigned int i=0; i < v.size() ; i++) v_out(i)=v(i); // return v_out; // } /* MatrixType mEtoV( eMatrix m , unsigned int ncols = 0) { MatrixType m_out( m.data() , m.rows() , m.cols() ); if ( m(0,1) != m_out(0,1) ) { std::cout << " WARNING!! in eigen to vnl coversion for matrices " << std::endl; std::cout <<" eigen " << m(0,1) << " vnl " << m_out(0,1) << std::endl; } // std::cout <<" eigen at (0,1) " << m(0,1) << " vnl at (0,1) " << m_out(0,1) << " vnl at (1,0) " << m_out(1,0) << std::endl; if ( ncols == 0 ) return m_out; else return (m_out).get_n_columns(0,ncols); // use this if you dont set #define EIGEN_DEFAULT_TO_ROW_MAJOR (we do this) if ( ncols == 0 ) return m_out.transpose(); else return (m_out.transpose()).get_n_columns(0,ncols); }*/ /* eMatrix mVtoE( MatrixType m ) { // NOTE: Eigen matrices are the transpose of vnl matrices unless you set #define EIGEN_DEFAULT_TO_ROW_MAJOR which we do eMatrix m_out(m.rows(),m.cols()); for ( long i=0; im_Indicator.diagonal(); unsigned int nzct = ( unsigned int ) diag.sum(); MatrixType submat( A.rows(), nzct, 0 ); nzct = 0; for( unsigned int i = 0; i < diag.size(); i++ ) { if( diag( i ) > 0 ) { submat.set_column( nzct, A.get_column( i ) ); nzct++; } } submatout = submat; } antsSCCANObject(); ~antsSCCANObject() { } void PrintSelf( std::ostream &, /* os */ Indent /* indent */) const ITK_OVERRIDE { if( this->m_MaskImageP && this->m_MaskImageQ && this->m_MaskImageR ) { if ( ! this->m_Silent ) std::cout << " 3 matrices " << std::endl; } else if( this->m_MaskImageP && this->m_MaskImageQ ) { if ( ! this->m_Silent ) std::cout << " 2 matrices " << std::endl; } else { if ( ! this->m_Silent ) std::cout << " fewer than 2 matrices " << std::endl; } } void RunDiagnostics(unsigned int); void AddColumnsToMatrix( MatrixType& mat_to_add_to, MatrixType& mat_to_take_from, unsigned int col0, unsigned int coln ) { MatrixType outmat( mat_to_add_to.rows(), mat_to_add_to.cols() + ( coln - col0 ) + 1, 0 ); for( unsigned int i = 0; i < mat_to_add_to.cols(); i++ ) { outmat.set_column( i, mat_to_add_to.get_column( i ) ); } unsigned int ct = mat_to_add_to.cols(); for( unsigned int i = col0; i <= coln; i++ ) { outmat.set_column( ct, mat_to_take_from.get_column( i ) ); ct++; } mat_to_add_to = outmat; } RealType CurvatureSparseness( VectorType & x, RealType sparsenessgoal, unsigned int maxit, ImagePointer ); private: ImagePointer ConvertVariateToSpatialImage4D( VectorType variate, ImagePointer mask, bool threshold_at_zero = false ); MatrixType m_OriginalMatrixPriorROI; VectorType ConvertImageToVariate( ImagePointer image, ImagePointer mask ); VectorType ConvertImageToVariate4D( ImagePointer image, ImagePointer mask ); VectorType ClusterThresholdVariate( VectorType &, ImagePointer mask, unsigned int); VectorType ClusterThresholdVariate4D( VectorType &, ImagePointer mask, unsigned int); bool m_Debug; bool m_Silent; MatrixType m_OriginalMatrixP; MatrixType m_OriginalMatrixQ; MatrixType m_OriginalMatrixR; RealType m_RowSparseness; antsSCCANObject(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented unsigned int m_ElapsedIterations; unsigned int m_MaximumNumberOfIterations; RealType m_CurrentConvergenceMeasurement; RealType m_ConvergenceThreshold; SCCANFormulationType m_SCCANFormulation; RealType m_PinvTolerance; RealType m_PercentVarianceForPseudoInverse; RealType m_Epsilon; /** used to prevent div by zero */ // Prior constrained PCA --Refer to notation in the paper MatrixType m_MatrixPriorROI; MatrixType m_MatrixPriorROI2; MatrixType m_SortedIndicesAll; VectorType sortedIndicesLoop; MatrixType m_Ip; MatrixType m_Ik; MatrixType m_priorScaleMat; // VectorType m_WeightsP; VectorType loc_Array; bool flagForSort; VectorType m_WeightsP; MatrixType m_MatrixP; ImagePointer m_MaskImageP; RealType m_FractionNonZeroP; bool m_KeepPositiveP; RealType m_UseLongitudinalFormulation; RealType m_Smoother; VectorType m_WeightsQ; MatrixType m_MatrixQ; ImagePointer m_MaskImageQ; RealType m_FractionNonZeroQ; bool m_KeepPositiveQ; MatrixType m_Eigenvectors; VectorType m_Eigenvalues; VectorType m_CanonicalCorrelations; VariateType m_SparseVariatesP; VariateType m_VariatesP; VariateType m_VariatesQ; /** solution to X - U V */ MatrixType m_MatrixU; VectorType m_WeightsR; MatrixType m_MatrixR; ImagePointer m_MaskImageR; RealType m_FractionNonZeroR; bool m_KeepPositiveR; /** a special variable for pscca, holds R^T R */ MatrixType m_MatrixRRt; MatrixType m_MatrixRp; MatrixType m_MatrixRq; /** softer = true will compute the update : if ( beta > thresh ) beta <- beta - thresh * rather than the default update : if ( beta > thresh ) beta <- beta */ unsigned int m_Covering; unsigned int m_VecToMaskSize; bool m_GetSmall; bool m_UseL1; bool m_AlreadyWhitened; bool m_SpecializationForHBM2011; RealType m_CorrelationForSignificanceTest; RealType m_lambda; // this->ComputeIntercept( A, x, b ); RealType m_Intercept; unsigned int m_NTimeDimensions; unsigned int m_MinClusterSizeP; unsigned int m_MinClusterSizeQ; unsigned int m_KeptClusterSize; unsigned int m_GoldenSectionCounter; VectorType m_ClusterSizes; VectorType m_OriginalB; VectorType m_SparsenessP; VectorType m_SparsenessQ; vnl_diag_matrix m_Indicator; vnl_diag_matrix m_PreC; // preconditioning RealType m_GSBestSol; RealType m_GradStep; RealType m_GradStepP; RealType m_GradStepQ; RealType m_PriorWeight; }; } // namespace ants } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "antsSCCANObject.hxx" #endif #endif /* RealType SparseRayleighQuotientIteration( MatrixType& A, VectorType& x) { if ( x.two_norm() == 0 ) return; x = x / x.two_norm(); for ( unsigned int i = 0 ; i < 5; i++) { VectorType Ax = A * x; RealType lambda = inner_product( Ax , Ax ); vnl_diag_matrix diag( A , lambda ); this->RidgeRegression( A, x, 1.e2, 10, diag ); } } */ ants-2.2.0/Utilities/antsSCCANObject.hxx000066400000000000000000007664221311104306400200420ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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. =========================================================================*/ #include "itkMinimumMaximumImageFilter.h" #include "itkConnectedComponentImageFilter.h" #include "itkRelabelComponentImageFilter.h" #include "itkExtractImageFilter.h" #include #include #include #include #include #include #include "antsSCCANObject.h" #include #include "itkCSVNumericObjectFileWriter.h" #include "itkCSVArray2DDataObject.h" #include "itkCSVArray2DFileReader.h" #include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" #include "itkLaplacianRecursiveGaussianImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkSurfaceImageCurvature.h" #include "itkImageFileWriter.h" #include "itkGradientAnisotropicDiffusionImageFilter.h" #include "ReadWriteData.h" namespace itk { namespace ants { template antsSCCANObject::antsSCCANObject() { this->m_UseL1 = true; this->m_VecToMaskSize = 1; this->m_MinClusterSizeP = 1; this->m_MinClusterSizeQ = 1; this->m_KeptClusterSize = 0; this->m_Debug = false; this->m_Silent = true; this->m_CorrelationForSignificanceTest = 0; this->m_SpecializationForHBM2011 = false; this->m_AlreadyWhitened = false; this->m_PinvTolerance = 1.e-6; this->m_PercentVarianceForPseudoInverse = 0.9; this->m_MaximumNumberOfIterations = 20; this->m_MaskImageP = ITK_NULLPTR; this->m_MaskImageQ = ITK_NULLPTR; this->m_MaskImageR = ITK_NULLPTR; this->m_KeepPositiveP = true; this->m_KeepPositiveQ = true; this->m_KeepPositiveR = true; this->m_FractionNonZeroP = 0.5; this->m_FractionNonZeroQ = 0.5; this->m_FractionNonZeroR = 0.5; this->m_ConvergenceThreshold = 1.e-6; this->m_Epsilon = 1.e-12; this->m_GetSmall = true; this->m_GradStep = 0.1; this->m_UseLongitudinalFormulation = 0; this->m_RowSparseness = 0; this->m_Smoother = 0; this->m_Covering = 0; this->m_PriorWeight = 0; } template typename TInputImage::Pointer antsSCCANObject ::ConvertVariateToSpatialImage( typename antsSCCANObject::VectorType w_p, typename TInputImage::Pointer mask, bool threshold_at_zero ) { if ( ImageDimension == 4 ) return this->ConvertVariateToSpatialImage4D( w_p, mask, threshold_at_zero ); typename TInputImage::Pointer weights = TInputImage::New(); weights->SetOrigin( mask->GetOrigin() ); weights->SetSpacing( mask->GetSpacing() ); weights->SetRegions( mask->GetLargestPossibleRegion() ); weights->SetDirection( mask->GetDirection() ); weights->Allocate(); weights->FillBuffer( itk::NumericTraits::ZeroValue() ); // overwrite weights with vector values; unsigned long vecind = 0; typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator mIter(mask, mask->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { vecind++; } } this->m_VecToMaskSize = static_cast ( static_cast( w_p.size() ) / static_cast( vecind ) + 0.5 ); RealType avgwt = 1.0 / static_cast( this->m_VecToMaskSize ); vecind = 0; for ( unsigned int k = 0; k < this->m_VecToMaskSize; k++ ) // loop begin { unsigned long maskct = 0; for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { TRealType val = 0; if( vecind < w_p.size() ) { val = w_p( vecind ) * avgwt + weights->GetPixel(mIter.GetIndex()); } else { if ( ! this->m_Silent ) std::cout << "vecind too large " << vecind << " vs " << w_p.size() << std::endl; if ( ! this->m_Silent ) std::cout << " this is likely a mask problem --- exiting! " << std::endl; std::exception(); } if ( threshold_at_zero && ( fabs(val) > this->m_Epsilon ) ) { weights->SetPixel( mIter.GetIndex(), 1 ); } else { weights->SetPixel( mIter.GetIndex(), val ); } vecind++; // if ( maskct % 289 == 0 ) if ( ! this->m_Silent ) std::cout << "k "<< k << " mval " << val << " w " << w_p( vecind ) << std::endl; maskct++; } else { mIter.Set(0); } } }// loop end return weights; } template typename TInputImage::Pointer antsSCCANObject ::ConvertVariateToSpatialImage4D( typename antsSCCANObject::VectorType w_p, typename TInputImage::Pointer mask, bool threshold_at_zero ) { typename TInputImage::Pointer weights = TInputImage::New(); weights->SetOrigin( mask->GetOrigin() ); weights->SetSpacing( mask->GetSpacing() ); weights->SetRegions( mask->GetLargestPossibleRegion() ); weights->SetDirection( mask->GetDirection() ); weights->Allocate(); weights->FillBuffer( itk::NumericTraits::ZeroValue() ); typename RealImageTypeDminus1::Pointer maskdm1; typedef itk::ExtractImageFilter ExtractFilterType; typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( mask ); extractFilter->SetDirectionCollapseToIdentity(); extractFilter->SetDirectionCollapseToSubmatrix(); typename ImageType::RegionType extractRegion = mask->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); extractRegion.SetIndex(ImageDimension - 1, 0 ); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); maskdm1 = extractFilter->GetOutput(); // WriteImage( maskdm1, "maskdm1.nii.gz" ); // overwrite weights with vector values; unsigned long vecind = 0; typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator mIter(maskdm1, maskdm1->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { vecind++; } } this->m_VecToMaskSize = static_cast ( static_cast( w_p.size() ) / static_cast( vecind ) + 0.5 ); // if ( ! this->m_Silent ) std::cout << " this->m_VecToMaskSize " << this->m_VecToMaskSize << " mask size " << vecind << std::endl; vecind = 0; for ( unsigned int k = 0; k < this->m_VecToMaskSize; k++ ) { // loop begin for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { typename RealImageTypeDminus1::IndexType mind = mIter.GetIndex(); typename ImageType::IndexType ind4d; for ( unsigned int dim = 0; dim < ImageDimension-1; dim++ ) ind4d[ dim ] = mind[ dim ]; ind4d[ ImageDimension - 1 ] = k; TRealType val = 0; if( vecind < w_p.size() ) { val = w_p( vecind ); } else { if ( ! this->m_Silent ) std::cout << "vecind too large " << vecind << " vs " << w_p.size() << std::endl; if ( ! this->m_Silent ) std::cout << " this is likely a mask problem --- exiting! " << std::endl; std::exception(); } if ( threshold_at_zero && ( fabs(val) > this->m_Epsilon ) ) { weights->SetPixel( ind4d, 1 ); } else { weights->SetPixel( ind4d, val ); } vecind++; } } } // loop end return weights; } template TRealType antsSCCANObject ::CurvatureSparseness( typename antsSCCANObject::VectorType& x, TRealType sparsenessgoal, unsigned int maxit , typename TInputImage::Pointer mask ) // , typename antsSCCANObject::MatrixType& A, // typename antsSCCANObject::VectorType& b, { if( mask.IsNull() ) { return 0; } /** penalize by curvature */ VectorType signvec( x ); RealType kappa = 20; RealType lastkappa = 20; unsigned int kkk = 0; unsigned int zct = 0; RealType dkappa = 1; RealType sp = 0; bool notdone = true; unsigned int nzct = 0; while( notdone ) { kappa = 0; VectorType gradvec = this->ComputeVectorLaplacian( x, mask ); nzct = 0; for( unsigned int kk = 0; kk < x.size(); kk++ ) { RealType grad = ( gradvec[kk] ) * 0.5; if( ( ( x[kk] > 0 ) && ( signvec[kk] < 0 ) ) || ( ( x[kk] < 0 ) && ( signvec[kk] > 0 ) ) ) { x[kk] = 0; zct++; } else if( vnl_math_abs( x[kk] ) > 1.e-6 ) { kappa += vnl_math_abs( grad ); x[kk] = x[kk] + grad; nzct++; } else { x[kk] = 0; zct++; } } if( nzct > 0 ) { kappa /= ( RealType ) nzct; } sp = ( RealType ) ( x.size() - nzct ) / x.size() * 100.0; // VectorType pp = A.transpose() * ( A * x ); // RealType err = ( pp - b ).one_norm() / x.size(); // kappa = err + this->m_FractionNonZeroP * ( 100 - sp); dkappa = lastkappa - kappa; lastkappa = kappa; if( kkk > maxit || sp > sparsenessgoal ) { notdone = false; } if( kkk < 2 ) { notdone = true; } kkk++; // if ( ! notdone ) // if ( ! this->m_Silent ) std::cout << " Kappa " << kappa << " " << kkk << " sparseness " << sp << " dkap " << dkappa << " nzct " // << nzct << std::endl; } VectorType gradvec = this->ComputeVectorGradMag( x, mask ); // if ( ! this->m_Silent ) std::cout << " Kappa " << kappa << " " << kkk << " sparseness " << sp << " dkap " << dkappa << " nzct " // << nzct << " GradNorm " << gradvec.two_norm() << std::endl; return gradvec.two_norm(); } template typename antsSCCANObject::VectorType antsSCCANObject ::ClusterThresholdVariate( typename antsSCCANObject::VectorType& w_p, typename TInputImage::Pointer mask, unsigned int minclust ) { if( minclust <= 1 || mask.IsNull() ) { return w_p; } if ( ImageDimension == 4 ) return this->ClusterThresholdVariate4D( w_p, mask, minclust ); typedef unsigned long ULPixelType; typedef itk::Image labelimagetype; typedef itk::ImageRegionIteratorWithIndex fIterator; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::ConnectedComponentImageFilter FilterType; typedef itk::RelabelComponentImageFilter RelabelType; // we assume w_p has been thresholded by another function bool threshold_at_zero = true; typename TInputImage::Pointer image = this->ConvertVariateToSpatialImage( w_p, mask, threshold_at_zero ); typename FilterType::Pointer filter = FilterType::New(); typename RelabelType::Pointer relabel = RelabelType::New(); filter->SetInput( image ); filter->SetFullyConnected( 0 ); relabel->SetInput( filter->GetOutput() ); relabel->SetMinimumObjectSize( 1 ); try { relabel->Update(); } catch( itk::ExceptionObject & excep ) { if ( ! this->m_Silent ) std::cout << "Relabel: exception caught !" << std::endl; if ( ! this->m_Silent ) std::cout << excep << std::endl; } Iterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); float maximum = relabel->GetNumberOfObjects(); std::vector histogram( (int)maximum + 1); for( int i = 0; i <= maximum; i++ ) { histogram[i] = 0; } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { float vox = vfIter.Get(); if( vox > 0 ) { if( vox > 0 ) { histogram[(unsigned long)vox] = histogram[(unsigned long)vox] + 1; } } } // get the largest component's size unsigned long largest_component_size = 0; for( int i = 0; i <= maximum; i++ ) { if( largest_component_size < histogram[i] ) { largest_component_size = histogram[i]; } } if( largest_component_size < minclust ) { minclust = largest_component_size - 1; } // now create the output vector // iterate through the image and set the voxels where countinlabel[(unsigned // long)(labelimage->GetPixel(vfIter.GetIndex()) - min)] // is < MinClusterSize unsigned long vecind = 0, keepct = 0; for ( unsigned int k = 0; k < this->m_VecToMaskSize; k++ ) { fIterator mIter( mask, mask->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() > 0 ) { float vox = mask->GetPixel(vfIter.GetIndex() ); unsigned long clustersize = 0; if( vox >= 0 ) { clustersize = histogram[(unsigned long)(relabel->GetOutput()->GetPixel(mIter.GetIndex() ) )]; if( clustersize > minclust ) { keepct += 1; } // get clusters > minclust // if ( clustersize == largest_component_size ) { keepct++; } // get largest cluster else { w_p(vecind) = 0; } vecind++; } } } } this->m_KeptClusterSize = histogram[1]; // only records the size of the largest cluster in the variate // for (unsigned int i=0; i minclust ) this->m_KeptClusterSize+=histogram[i]; // if ( ! this->m_Silent ) std::cout << " Cluster Threshold Kept % of sparseness " << ( (float)keepct/(float)w_p.size() ) / // this->m_FractionNonZeroP << " kept clust size " << keepct << std::endl; return w_p; } template typename antsSCCANObject::VectorType antsSCCANObject ::ClusterThresholdVariate4D( typename antsSCCANObject::VectorType& w_p, typename TInputImage::Pointer mask, unsigned int minclust ) { if( minclust <= 1 || mask.IsNull() ) { return w_p; } typedef unsigned long ULPixelType; typedef itk::Image labelimagetype; typedef itk::ImageRegionIteratorWithIndex Iterator; typedef itk::ConnectedComponentImageFilter FilterType; typedef itk::RelabelComponentImageFilter RelabelType; typedef itk::ImageRegionIteratorWithIndex fIterator; typename RealImageTypeDminus1::Pointer maskdm1; typedef itk::ExtractImageFilter ExtractFilterType; typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( mask ); extractFilter->SetDirectionCollapseToIdentity(); extractFilter->SetDirectionCollapseToSubmatrix(); typename ImageType::RegionType extractRegion = mask->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); extractRegion.SetIndex(ImageDimension - 1, 0 ); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); maskdm1 = extractFilter->GetOutput(); // we assume w_p has been thresholded by another function bool threshold_at_zero = true; typename TInputImage::Pointer image = this->ConvertVariateToSpatialImage( w_p, mask, threshold_at_zero ); typename FilterType::Pointer filter = FilterType::New(); typename RelabelType::Pointer relabel = RelabelType::New(); filter->SetInput( image ); filter->SetFullyConnected( 0 ); relabel->SetInput( filter->GetOutput() ); relabel->SetMinimumObjectSize( 1 ); try { relabel->Update(); } catch( itk::ExceptionObject & excep ) { if ( ! this->m_Silent ) std::cout << "Relabel: exception caught !" << std::endl; if ( ! this->m_Silent ) std::cout << excep << std::endl; } Iterator vfIter( relabel->GetOutput(), relabel->GetOutput()->GetLargestPossibleRegion() ); float maximum = relabel->GetNumberOfObjects(); std::vector histogram( (int)maximum + 1); for( int i = 0; i <= maximum; i++ ) { histogram[i] = 0; } for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { float vox = vfIter.Get(); if( vox > 0 ) { if( vox > 0 ) { histogram[(unsigned long)vox] = histogram[(unsigned long)vox] + 1; } } } // get the largest component's size unsigned long largest_component_size = 0; for( int i = 0; i <= maximum; i++ ) { if( largest_component_size < histogram[i] ) { largest_component_size = histogram[i]; } } if( largest_component_size < minclust ) { minclust = largest_component_size - 1; } // now create the output vector // iterate through the image and set the voxels where countinlabel[(unsigned // long)(labelimage->GetPixel(vfIter.GetIndex()) - min)] // is < MinClusterSize unsigned long vecind = 0; for ( unsigned int k = 0; k < this->m_VecToMaskSize; k++ ) { fIterator mIter( maskdm1, maskdm1->GetLargestPossibleRegion() ); for( mIter.GoToBegin(); !mIter.IsAtEnd(); ++mIter ) { if( mIter.Get() >= 0.5 ) { typename RealImageTypeDminus1::IndexType mind = mIter.GetIndex(); typename ImageType::IndexType ind4d; for ( unsigned int dim = 0; dim < ImageDimension-1; dim++ ) ind4d[ dim ] = mind[ dim ]; ind4d[ ImageDimension - 1 ] = k; unsigned long clustersize = 0; clustersize = histogram[(unsigned long)(relabel->GetOutput()->GetPixel( ind4d ) )]; if( clustersize < minclust ) { w_p( vecind ) = 0; } vecind++; } } }// loop this->m_KeptClusterSize = histogram[1]; // only records the size of the largest cluster in the variate // if ( ! this->m_Silent ) std::cout << " w_p out " << w_p.two_norm() << " hist " << histogram[1] << " nz " << this->CountNonZero( w_p ) << std::endl; return w_p; } template typename antsSCCANObject::VectorType antsSCCANObject ::ConvertImageToVariate( typename TInputImage::Pointer image, typename TInputImage::Pointer mask ) { typedef unsigned long ULPixelType; typedef itk::ImageRegionIteratorWithIndex Iterator; if ( ImageDimension == 4 ) return this->ConvertImageToVariate4D( image, mask ); ULPixelType maskct = 0; Iterator vfIter( mask, mask->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { RealType maskval = vfIter.Get(); if( maskval >= 0.5 ) { maskct++; } } VectorType vec( maskct * this->m_VecToMaskSize ); vec.fill( 0 ); ULPixelType maskct2 = 0; for ( unsigned int k = 0; k < this->m_VecToMaskSize; k++ ) // loop begin { ULPixelType maskctbase = 0; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { RealType maskval = vfIter.Get(); RealType imageval = image->GetPixel( vfIter.GetIndex() ); if( maskval >= 0.5 ) { vec[maskct2] = imageval; maskct2++; maskctbase++; } } } return vec; } template typename antsSCCANObject::VectorType antsSCCANObject ::ConvertImageToVariate4D( typename TInputImage::Pointer image, typename TInputImage::Pointer mask ) { typedef unsigned long ULPixelType; typename RealImageTypeDminus1::Pointer maskdm1; typedef itk::ExtractImageFilter ExtractFilterType; typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); extractFilter->SetInput( mask ); extractFilter->SetDirectionCollapseToIdentity(); extractFilter->SetDirectionCollapseToSubmatrix(); typename ImageType::RegionType extractRegion = mask->GetLargestPossibleRegion(); extractRegion.SetSize(ImageDimension - 1, 0); extractRegion.SetIndex(ImageDimension - 1, 0 ); extractFilter->SetExtractionRegion( extractRegion ); extractFilter->Update(); maskdm1 = extractFilter->GetOutput(); ULPixelType maskct = 0; typedef itk::ImageRegionIteratorWithIndex Iterator; Iterator vfIter( maskdm1, maskdm1->GetLargestPossibleRegion() ); for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { RealType maskval = vfIter.Get(); if( maskval >= 0.5 ) { maskct++; } } VectorType vec( maskct * this->m_VecToMaskSize ); if ( this->m_Debug ) if ( ! this->m_Silent ) std::cout << "I2V maskct " << maskct << " VecToMaskSize " << this->m_VecToMaskSize << std::endl; vec.fill( 0 ); ULPixelType maskct2 = 0; for ( unsigned int k = 0; k < this->m_VecToMaskSize; k++ ) // loop begin { ULPixelType maskctbase = 0; for( vfIter.GoToBegin(); !vfIter.IsAtEnd(); ++vfIter ) { RealType maskval = vfIter.Get(); if( maskval >= 0.5 ) { typename RealImageTypeDminus1::IndexType mind = vfIter.GetIndex(); typename ImageType::IndexType ind4d; for ( unsigned int dim = 0; dim < ImageDimension-1; dim++ ) ind4d[ dim ] = mind[ dim ]; ind4d[ ImageDimension - 1 ] = k; vec[maskct2] = image->GetPixel( ind4d ); maskct2++; maskctbase++; } } } return vec; } template typename antsSCCANObject::VectorType antsSCCANObject ::InitializeV( typename antsSCCANObject::MatrixType p, unsigned long seed ) { VectorType w_p( p.columns() ); w_p.fill(0); for( unsigned int its = 0; its < 1; its++ ) { vnl_random randgen( seed ); /* use constant seed to prevent weirdness */ for( unsigned long i = 0; i < p.columns(); i++ ) { if( seed > 0 ) { w_p(i) = randgen.normal(); // w_p(i)=randgen.drand32(); } else { w_p(i) = 1.0; } } } w_p = w_p / p.columns(); return w_p; } template typename antsSCCANObject::VectorType antsSCCANObject ::SpatiallySmoothVector( typename antsSCCANObject::VectorType vec, typename TInputImage::Pointer mask, bool surface ) { if( mask.IsNull() || vnl_math_abs( this->m_Smoother ) < 1.e-9 ) { return vec; } RealType vecnorm = vec.two_norm(); ImagePointer image = this->ConvertVariateToSpatialImage( vec, mask, false ); RealType spacingsize = 0; for( unsigned int d = 0; d < ImageDimension; d++ ) { RealType sp = mask->GetSpacing()[d]; spacingsize += sp * sp; } spacingsize = sqrt( spacingsize ); if ( this->m_Smoother < 0.0 ) { typedef itk::GradientAnisotropicDiffusionImageFilter< TInputImage, TInputImage > FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput( image ); filter->SetNumberOfIterations( vnl_math_abs( this->m_Smoother ) ); TRealType mytimestep = spacingsize / std::pow( 2.0 , static_cast(ImageDimension+1) ); TRealType reftimestep = 0.5 / std::pow( 2.0 , static_cast(ImageDimension+1) ); if ( mytimestep > reftimestep ) mytimestep = reftimestep; filter->SetTimeStep( mytimestep ); filter->SetConductanceParameter( 0.25 ); // might need to change this filter->Update(); VectorType gradvec = this->ConvertImageToVariate( filter->GetOutput(), mask ); return gradvec; } if ( ( surface ) && ( ImageDimension == 3 ) && ( false ) ) { unsigned int sigma = ( unsigned int ) this->m_Smoother; if ( this->m_Smoother > 0.0001 ) if ( sigma < 1 ) sigma = 1; typedef itk::SurfaceImageCurvature ParamType; typename ParamType::Pointer Parameterizer = ParamType::New(); Parameterizer->SetInputImage(mask); Parameterizer->SetFunctionImage(image); Parameterizer->SetNeighborhoodRadius( 1 ); Parameterizer->SetSigma( 1.0 ); Parameterizer->SetUseGeodesicNeighborhood(false); Parameterizer->SetUseLabel(false); Parameterizer->SetThreshold(0.5); Parameterizer->IntegrateFunctionOverSurface(true); for( unsigned int i = 0; i < sigma; i++ ) { Parameterizer->IntegrateFunctionOverSurface(true); } VectorType svec = this->ConvertImageToVariate( Parameterizer->GetFunctionImage(), mask ); return svec; } else { typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetUseImageSpacingOn(); filter->SetVariance( this->m_Smoother * spacingsize ); filter->SetMaximumError( .01f ); filter->SetInput( image ); filter->Update(); typename ImageType::Pointer imgout = filter->GetOutput(); // WriteImage( imgout , "ccaout_s.nii.gz" ); VectorType gradvec = this->ConvertImageToVariate( imgout, mask ); gradvec = gradvec * vecnorm / gradvec.two_norm(); // if ( ! this->m_Silent ) std::cout << ImageDimension << " gvec " << gradvec[1] << " " << gradvec[100] << " " << gradvec[1000] << std::endl; return gradvec; } } template typename antsSCCANObject::VectorType antsSCCANObject ::ComputeVectorLaplacian( typename antsSCCANObject::VectorType vec, typename TInputImage::Pointer mask ) { if( mask.IsNull() ) { return vec; } ImagePointer image = this->ConvertVariateToSpatialImage( vec, mask, false ); typedef itk::LaplacianRecursiveGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); RealType spacingsize = 0; for( unsigned int d = 0; d < ImageDimension; d++ ) { RealType sp = mask->GetSpacing()[d]; spacingsize += sp * sp; } spacingsize = sqrt( spacingsize ); filter->SetSigma( 0.333 * spacingsize ); filter->SetInput(image); filter->Update(); image = filter->GetOutput(); VectorType gradvec = this->ConvertImageToVariate( image, this->m_MaskImageP ); return gradvec; } template typename antsSCCANObject::VectorType antsSCCANObject ::ComputeVectorGradMag( typename antsSCCANObject::VectorType vec, typename TInputImage::Pointer mask ) { ImagePointer image = this->ConvertVariateToSpatialImage( vec, mask, false ); typedef itk::GradientMagnitudeRecursiveGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); RealType spacingsize = 0; for( unsigned int d = 0; d < ImageDimension; d++ ) { RealType sp = mask->GetSpacing()[d]; spacingsize += sp * sp; } spacingsize = sqrt( spacingsize ); filter->SetSigma( 0.333 * spacingsize ); filter->SetInput(image); filter->Update(); image = filter->GetOutput(); VectorType gradvec = this->ConvertImageToVariate( image, this->m_MaskImageP ); return gradvec; } template typename antsSCCANObject::MatrixType antsSCCANObject ::NormalizeMatrix( typename antsSCCANObject::MatrixType p, bool makepositive ) { return(p); MatrixType np( p.rows(), p.columns() ); for( unsigned long i = 0; i < p.columns(); i++ ) { VectorType wpcol = p.get_column(i); VectorType wpcol2 = wpcol - wpcol.mean(); double sd = wpcol2.squared_magnitude(); sd = sqrt( sd / (p.rows() - 1) ); if( sd <= 0.0 && i == static_cast(0) ) { if ( ! this->m_Silent ) std::cout << " row " << i << " has zero variance --- exiting " << std::endl; std::exception(); } if( sd <= 0 && i > 0 ) { if ( ! this->m_Silent ) std::cout << " row " << i << " has zero variance --- copying the previous row " << std::endl; if ( ! this->m_Silent ) std::cout << " the row is " << wpcol << std::endl; np.set_column(i, np.get_column(i - 1) ); } else { wpcol = wpcol2 / sd; np.set_column(i, wpcol); } } /** cast to a non-negative space */ if( makepositive ) { np = np - np.min_value(); } return np; } template typename antsSCCANObject::MatrixType antsSCCANObject ::VNLPseudoInverse( typename antsSCCANObject::MatrixType rin, bool take_sqrt ) { double pinvTolerance = this->m_PinvTolerance; MatrixType dd = rin; unsigned int ss = dd.rows(); if( dd.rows() > dd.columns() ) { ss = dd.columns(); } vnl_svd eig(dd, pinvTolerance); if( !take_sqrt ) { return eig.pinverse( ss ); } else { for( unsigned int j = 0; j < ss; j++ ) { RealType eval = eig.W(j, j); if( eval > pinvTolerance ) { // FIXME -- check tolerances against matlab pinv eig.W(j, j) = 1 / (eval); // need eval for inv cov if( take_sqrt ) { eig.W(j, j) = 1 / sqrt(eval); } } else { eig.W(j, j) = 0; } } /** there is a scaling problem with the pseudoinverse --- this is a cheap fix!! it is based on the theoretical frobenious norm of the inverse matrix */ MatrixType pinv = ( eig.recompose() ).transpose(); double a = sqrt( static_cast(dd.rows()) ); double b = (pinv * dd).frobenius_norm(); pinv = pinv * a / b; return pinv; } } template void antsSCCANObject ::SoftClustThreshold( typename antsSCCANObject::VectorType & v_in, TRealType soft_thresh, bool keep_positive , unsigned int clust, typename TInputImage::Pointer mask ) { // here , we apply the minimum threshold to the data. for( unsigned int i = 0; i < v_in.size(); i++ ) { RealType val = v_in(i); if ( keep_positive && val < 0 ) val = 0 ; // if ( keep_positive && val < 0 ) val *= ( -1 ); if( vnl_math_abs( val ) < soft_thresh ) { v_in(i) = 0; } else { v_in(i) = val; if( this->m_UseL1 ) { if ( v_in(i) > 0 ) v_in(i) = v_in(i) - soft_thresh; if ( v_in(i) < 0 ) v_in(i) = v_in(i) + soft_thresh; } } } this->ClusterThresholdVariate( v_in, mask, clust ); // if ( ! this->m_Silent ) std::cout <<" resoft " << this->CountNonZero( v_in ) << std::endl; return; } template void antsSCCANObject ::ReSoftThreshold( typename antsSCCANObject::VectorType & v_in, TRealType fractional_goal, bool keep_positive ) { // if ( ! this->m_Silent ) std::cout <<" resoft " << fractional_goal << std::endl; if( fabs(fractional_goal) >= 1 || fabs( (float)(v_in.size() ) * fractional_goal) <= 1 ) { return; } RealType maxv = v_in.max_value(); if( fabs(v_in.min_value() ) > maxv ) { maxv = fabs(v_in.min_value() ); } RealType lambg = this->m_Epsilon; RealType frac = 0; unsigned int its = 0, ct = 0; RealType soft_thresh = lambg; for( unsigned int i = 0; i < v_in.size(); i++ ) { if( keep_positive && v_in(i) < 0 ) { v_in(i) = 0; } } RealType minthresh = 0, minfdiff = 1; unsigned int maxits = 1000; for( its = 0; its < maxits; its++ ) { soft_thresh = (its / (float)maxits) * maxv; ct = 0; for( unsigned int i = 0; i < v_in.size(); i++ ) { RealType val = v_in(i); if( !keep_positive ) { val = fabs(val); } else if( val < 0 ) { val = 0; } if( val < soft_thresh ) { ct++; } } frac = (float)(v_in.size() - ct) / (float)v_in.size(); // if ( ! this->m_Silent ) std::cout << " cur " << frac << " goal " << fractional_goal << " st " << soft_thresh << " th " << // minthresh // << std::endl; if( fabs(frac - fractional_goal) < minfdiff ) { minthresh = soft_thresh; minfdiff = fabs(frac - fractional_goal); } } // if ( ! this->m_Silent ) std::cout << " goal " << fractional_goal << " st " << soft_thresh << " th " << minthresh << " minfdiff " // << // minfdiff << std::endl; // here , we apply the minimum threshold to the data. ct = 0; for( unsigned int i = 0; i < v_in.size(); i++ ) { RealType val = v_in(i); if( !keep_positive ) { val = fabs(val); } else if( val < 0 ) { val = 0; } if( val < minthresh ) { v_in(i) = 0; ct++; } else { if( this->m_UseL1 ) { v_in(i) = v_in(i) - minthresh; } } } // if ( ! this->m_Silent ) std::cout << " post minv " << tminv << " post maxv " << tmaxv << std::endl; frac = (float)(v_in.size() - ct) / (float)v_in.size(); // if ( ! this->m_Silent ) std::cout << " frac non-zero " << frac << " wanted " << fractional_goal << std::endl; // if ( v_in.two_norm() > this->m_Epsilon ) v_in=v_in/v_in.two_norm(); // if ( ! this->m_Silent ) std::cout << v_in < void antsSCCANObject ::ConstantProbabilityThreshold( typename antsSCCANObject::VectorType & v_in, TRealType probability_goal, bool keep_positive ) { bool debug = false; v_in = v_in / v_in.two_norm(); VectorType v_out(v_in); RealType maxv = v_in.max_value(); if( fabs(v_in.min_value() ) > maxv ) { maxv = fabs(v_in.min_value() ); } RealType lambg = this->m_Epsilon; RealType frac = 0; unsigned int its = 0; RealType probability = 0; RealType probability_sum = 0; RealType soft_thresh = lambg; unsigned int nzct = 0; for( unsigned int i = 0; i < v_in.size(); i++ ) { if( keep_positive && v_in(i) < 0 ) { v_in(i) = 0; } v_out(i) = v_in(i); probability_sum += fabs(v_out(i) ); if( fabs( v_in( i ) ) > 0 ) { nzct++; } } if( debug ) { if ( ! this->m_Silent ) std::cout << " prob sum " << probability_sum << std::endl; } if( debug ) { if ( ! this->m_Silent ) std::cout << " nzct " << nzct << std::endl; } RealType minthresh = 0, minfdiff = 1; unsigned int maxits = 1000; for( its = 0; its < maxits; its++ ) { soft_thresh = (its / (float)maxits) * maxv; probability = 0; for( unsigned int i = 0; i < v_in.size(); i++ ) { RealType val = v_in(i); if( !keep_positive ) { val = fabs(val); } else if( val < 0 ) { val = 0; } if( val < soft_thresh ) { v_out(i) = 0; } else { v_out(i) = v_in(i); probability += fabs(v_out(i) ); } } probability /= probability_sum; if( debug ) { if ( ! this->m_Silent ) std::cout << " cur " << probability << " goal " << probability_goal << " st " << soft_thresh << " th " << minthresh << std::endl; } if( fabs(probability - probability_goal) < minfdiff ) { minthresh = soft_thresh; minfdiff = fabs(probability - probability_goal); } } // here , we apply the minimum threshold to the data. probability = 0; unsigned long ct = 0; for( unsigned int i = 0; i < v_in.size(); i++ ) { RealType val = v_in(i); if( !keep_positive ) { val = fabs(val); } else if( val < 0 ) { val = 0; } if( val < minthresh ) { v_in(i) = 0; ct++; } else { // v_in(i)-=minthresh; probability += fabs(v_in(i) ); } } if( debug ) { if ( ! this->m_Silent ) std::cout << " frac non-zero " << probability << " wanted " << probability_goal << " Keep+ " << keep_positive << std::endl; } frac = (float)(v_in.size() - ct) / (float)v_in.size(); if( frac < 1 ) { if ( ! this->m_Silent ) std::cout << " const prob " << probability / probability_sum << " sparseness " << frac << std::endl; } // if ( v_in.two_norm() > this->m_Epsilon ) v_in=v_in/v_in.sum(); return; } template typename antsSCCANObject::VectorType antsSCCANObject ::TrueCCAPowerUpdate( TRealType penalty1, typename antsSCCANObject::MatrixType p, typename antsSCCANObject::VectorType w_q, typename antsSCCANObject::MatrixType q, bool keep_pos, bool factorOutR ) { RealType norm = 0; // recall that the matrices below have already be whitened .... // we bracket the computation and use associativity to make sure its done efficiently // vVector wpnew=( (CppInv.transpose()*p.transpose())*(CqqInv*q) )*w_q; VectorType wpnew; if( factorOutR ) { VectorType temp = q * w_q; wpnew = p.transpose() * ( temp - this->m_MatrixRRt * temp ); } else { VectorType temp = q * w_q; wpnew = p.transpose() * temp; } this->ReSoftThreshold( wpnew, penalty1, keep_pos ); norm = wpnew.two_norm(); if( norm > this->m_Epsilon ) { wpnew = wpnew / (norm); } return wpnew; } template void antsSCCANObject ::UpdatePandQbyR() { // R is already whitened switch( this->m_SCCANFormulation ) { case PQ: { // do nothing } break; case PminusRQ: { this->m_MatrixP = (this->m_MatrixP - this->m_MatrixRRt * this->m_MatrixP); } break; case PQminusR: { this->m_MatrixQ = (this->m_MatrixQ - this->m_MatrixRRt * this->m_MatrixQ); } break; case PminusRQminusR: { /** P_R = P - R_w R_w^T P */ /** Q_R = Q - R_w R_w^T Q */ this->m_MatrixP = (this->m_MatrixP - this->m_MatrixRRt * this->m_MatrixP); this->m_MatrixQ = (this->m_MatrixQ - this->m_MatrixRRt * this->m_MatrixQ); } break; case PQR: { if ( ! this->m_Silent ) std::cout << " You should call mscca not pscca " << std::endl; } break; } } template void antsSCCANObject ::RunDiagnostics( unsigned int n_vecs ) { if ( ! this->m_Silent ) std::cout << "Quantitative diagnostics: " << std::endl; if ( ! this->m_Silent ) std::cout << "Type 1: correlation from canonical variate to confounding vector " << std::endl; if ( ! this->m_Silent ) std::cout << "Type 2: correlation from canonical variate to canonical variate " << std::endl; RealType corrthresh = 0.3; MatrixType omatP = this->NormalizeMatrix(this->m_OriginalMatrixP); MatrixType omatQ; bool doq = true; if( this->m_OriginalMatrixQ.size() > 0 ) { omatQ = this->NormalizeMatrix(this->m_OriginalMatrixQ); } else { doq = false; omatQ = omatP; } if( this->m_OriginalMatrixR.size() > 0 ) { for( unsigned int wv = 0; wv < n_vecs; wv++ ) { for( unsigned int col = 0; col < this->m_MatrixR.columns(); col++ ) { RealType a = this->PearsonCorr(omatP * this->m_VariatesP.get_column(wv), this->m_OriginalMatrixR.get_column(col) ); RealType b = this->PearsonCorr(omatQ * this->m_VariatesQ.get_column(wv), this->m_OriginalMatrixR.get_column(col) ); // if ( ! this->m_Silent ) std::cout << "Pvec " << wv << " confound " << col << " : " << a <m_Silent ) std::cout << "Qvec " << wv << " confound " << col << " : " << b < corrthresh && fabs(b) > corrthresh ) { if ( ! this->m_Silent ) std::cout << " correlation with confound too high for variate " << wv << " corrs " << a << " and " << b << std::endl; // this->m_CanonicalCorrelations[wv]=0; } } } } for( unsigned int wv = 0; wv < n_vecs; wv++ ) { for( unsigned int yv = wv + 1; yv < n_vecs; yv++ ) { RealType a = this->PearsonCorr(omatP * this->m_VariatesP.get_column(wv), omatP * this->m_VariatesP.get_column(yv) ); if( fabs(a) > corrthresh ) { if ( ! this->m_Silent ) std::cout << " not orthogonal p " << a << std::endl; // this->m_CanonicalCorrelations[yv]=0; } if ( ! this->m_Silent ) std::cout << "Pvec " << wv << " Pvec " << yv << " : " << a << std::endl; if( doq ) { RealType b = this->PearsonCorr(omatQ * this->m_VariatesQ.get_column(wv), omatQ * this->m_VariatesQ.get_column(yv) ); if( fabs(b) > corrthresh ) { if ( ! this->m_Silent ) std::cout << " not orthogonal q " << a << std::endl; // this->m_CanonicalCorrelations[yv]=0; } if ( ! this->m_Silent ) std::cout << "Qvec " << wv << " Qvec " << yv << " : " << b << std::endl; } // doq } } } template TRealType antsSCCANObject ::SparseCCA(unsigned int /* nvecs */) { if ( ! this->m_Silent ) std::cout << " ed sparse cca " << std::endl; unsigned int nsubj = this->m_MatrixP.rows(); this->m_MatrixP = this->NormalizeMatrix(this->m_OriginalMatrixP); this->m_MatrixQ = this->NormalizeMatrix(this->m_OriginalMatrixQ); RealType tau = 0.01; if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " inv view mats " << std::endl; } MatrixType inviewcovmatP = ( (this->m_MatrixP * this->m_MatrixP.transpose() ) * (this->m_MatrixP * this->m_MatrixP.transpose() ) ) * (1 - tau) + ( this->m_MatrixP * this->m_MatrixP.transpose() ) * tau * (RealType)nsubj; MatrixType inviewcovmatQ = ( (this->m_MatrixQ * this->m_MatrixQ.transpose() ) * (this->m_MatrixQ * this->m_MatrixQ.transpose() ) ) * (1 - tau) + ( this->m_MatrixQ * this->m_MatrixQ.transpose() ) * tau * (RealType)nsubj; /** standard cca */ // MatrixType CppInv=this->PseudoInverseCovMat(this->m_MatrixP); // MatrixType CqqInv=this->PseudoInverseCovMat(this->m_MatrixQ); // MatrixType cov=(CppInv*this->m_MatrixP).transpose()*(CqqInv*this->m_MatrixQ); // MatrixType // TT=(this->m_MatrixP.transpose()*this->m_MatrixQ)*(this->m_MatrixP.transpose()*this->m_MatrixQ).transpose(); // MatrixType TT=( (CppInv*this->m_MatrixP).transpose()*(CqqInv*this->m_MatrixQ))* // (this->m_MatrixP.transpose()*this->m_MatrixQ).transpose(); /** dual cca */ MatrixType CppInv = this->PseudoInverse(inviewcovmatP); MatrixType CqqInv = this->PseudoInverse(inviewcovmatQ); /** need the eigenvectors of this reduced matrix */ // eMatrix pccain=CppInv*Cpq*CqqInv*Cqp; MatrixType ccap = ( (CppInv * (this->m_MatrixP * this->m_MatrixP.transpose() ) ).transpose() * (CqqInv * (this->m_MatrixQ * this->m_MatrixQ.transpose() ) ) * ( (this->m_MatrixP * this->m_MatrixP.transpose() ).transpose() * (this->m_MatrixQ * this->m_MatrixQ.transpose() ) ).transpose() ); // convert to eigen3 format /* eMatrix pccain=this->mVtoE(ccap); typedef Eigen::EigenSolver eigsolver; eigsolver pEG( pccain ); // eigsolver qEG( qccain ); eMatrix pccaVecs = pEG.pseudoEigenvectors(); eMatrix pccaSquaredCorrs=pEG.pseudoEigenvalueMatrix(); // call this function to check we are doing conversions correctly , matrix-wise this->mEtoV(pccaVecs); // map the variates back to P, Q space and sort them this->m_CanonicalCorrelations.set_size(nvecs); this->m_CanonicalCorrelations.fill(0); // copy to stl vector so we can sort the results MatrixType projToQ=( CqqInv*( (this->m_MatrixQ*this->m_MatrixQ.transpose() )* (this->m_MatrixP*this->m_MatrixP.transpose() ) )); std::vector evals(pccaSquaredCorrs.cols(),0); std::vector oevals(pccaSquaredCorrs.cols(),0); for ( long j=0; j 0.05 ){ VectorType temp=this->vEtoV( pccaVecs.col( j ) ); VectorType tempq=projToQ*temp; VectorType pvar=temp*this->m_MatrixP; this->ReSoftThreshold( pvar , this->m_FractionNonZeroP , this->m_KeepPositiveP ); VectorType qvar= tempq*this->m_MatrixQ; this->ReSoftThreshold( qvar , this->m_FractionNonZeroQ , this->m_KeepPositiveQ ); evals[j]=fabs(this->PearsonCorr(this->m_MatrixP*pvar,this->m_MatrixQ*qvar)); oevals[j]=evals[j]; } } // sort and reindex the eigenvectors/values sort (evals.begin(), evals.end(), my_sccan_sort_object); std::vector sorted_indices(nvecs,-1); for (unsigned int i=0; im_VariatesP.set_size(this->m_MatrixP.cols(),nvecs); this->m_VariatesQ.set_size(this->m_MatrixQ.cols(),nvecs); for (unsigned int i=0; ivEtoV( pccaVecs.col( sorted_indices[i] ) ); VectorType tempq=projToQ*temp; VectorType pvar= temp*this->m_MatrixP; this->ReSoftThreshold(pvar , this->m_FractionNonZeroP , this->m_KeepPositiveP ); VectorType qvar= tempq*this->m_MatrixQ ; this->ReSoftThreshold(qvar , this->m_FractionNonZeroQ , this->m_KeepPositiveQ ); this->m_VariatesP.set_column( i, pvar ); this->m_VariatesQ.set_column( i, qvar ); } for (unsigned int i=0; im_CanonicalCorrelations[i]= this->PearsonCorr(this->m_MatrixP*this->GetVariateP(i),this->m_MatrixQ*this->GetVariateQ(i) ); if ( ! this->m_Silent ) std::cout << "correlation of mapped back data " << this->m_CanonicalCorrelations[i] << " eval " << pccaSquaredCorrs(sorted_indices[i],sorted_indices[i]) << std::endl; } for (unsigned int i=0; im_Silent ) std::cout << "inner prod of projections 0 vs i " << this->PearsonCorr( this->m_MatrixP*this->GetVariateP(0) , this->m_MatrixP*this->GetVariateP(i) ) << std::endl; } */ // this->RunDiagnostics(nvecs); return this->m_CanonicalCorrelations[0]; } template TRealType antsSCCANObject ::SparsePartialCCA(unsigned int /* nvecs */) { /* if ( ! this->m_Silent ) std::cout <<" ed sparse partial cca " << std::endl; unsigned int nsubj=this->m_MatrixP.rows(); this->m_MatrixP=this->NormalizeMatrix(this->m_OriginalMatrixP); this->m_MatrixQ=this->NormalizeMatrix(this->m_OriginalMatrixQ); this->m_MatrixR=this->NormalizeMatrix(this->m_OriginalMatrixR); RealType tau=0.01; if (this->m_Debug) if ( ! this->m_Silent ) std::cout << " inv view mats " << std::endl; this->m_MatrixRRt=this->ProjectionMatrix(this->m_OriginalMatrixR); MatrixType PslashR=this->m_MatrixP; if ( this->m_SCCANFormulation == PminusRQ || this->m_SCCANFormulation == PminusRQminusR ) PslashR=this->m_MatrixP-(this->m_MatrixRRt*this->m_MatrixP); MatrixType QslashR=this->m_MatrixQ; if ( this->m_SCCANFormulation == PQminusR || this->m_SCCANFormulation == PminusRQminusR ) QslashR=this->m_MatrixQ-this->m_MatrixRRt*this->m_MatrixQ; if (this->m_Debug) { if ( ! this->m_Silent ) std::cout <<" corr-pre " << this->PearsonCorr( this->m_MatrixP.get_column(0) , this->m_OriginalMatrixR.get_column(0) ) << std::endl ;if ( ! this->m_Silent ) std::cout <<" corr-post " << this->PearsonCorr( PslashR.get_column(0) , this->m_OriginalMatrixR.get_column(0) ) << std::endl; if ( ! this->m_Silent ) std::cout <<" corr-pre " << this->PearsonCorr( this->m_MatrixQ.get_column(0) , this->m_OriginalMatrixR.get_column(0) ) << std::endl ;if ( ! this->m_Silent ) std::cout <<" corr-post " << this->PearsonCorr( QslashR.get_column(0) , this->m_OriginalMatrixR.get_column(0) ) << std::endl; } MatrixType inviewcovmatP=( (PslashR*PslashR.transpose())*(PslashR*PslashR.transpose()) )*(1-tau)+ ( PslashR*PslashR.transpose() )*tau*(RealType)nsubj; MatrixType inviewcovmatQ=( (QslashR*QslashR.transpose())*(QslashR*QslashR.transpose()) )*(1-tau)+( QslashR*QslashR.transpose() )*tau*(RealType)nsubj; // dual cca if (this->m_Debug) if ( ! this->m_Silent ) std::cout << " inv view mats pinv " << std::endl; MatrixType CppInv=this->PseudoInverse(inviewcovmatP); MatrixType CqqInv=this->PseudoInverse(inviewcovmatQ); if (this->m_Debug) if ( ! this->m_Silent ) std::cout << " inv view mats pinv done " << std::endl; // need the eigenvectors of this reduced matrix MatrixType ccap=( (CppInv*(PslashR*PslashR.transpose())).transpose() * (CqqInv*(QslashR*QslashR.transpose()))* ( (PslashR*PslashR.transpose() ).transpose()* (QslashR*QslashR.transpose() ) ).transpose() ); // convert to eigen3 format eMatrix pccain=this->mVtoE(ccap); typedef Eigen::EigenSolver eigsolver; eigsolver pEG( pccain ); eMatrix pccaVecs = pEG.pseudoEigenvectors(); eMatrix pccaSquaredCorrs=pEG.pseudoEigenvalueMatrix(); // call this function to check we are doing conversions correctly , matrix-wise this->mEtoV(pccaVecs); // map the variates back to P, Q space and sort them this->m_CanonicalCorrelations.set_size(nvecs); this->m_CanonicalCorrelations.fill(0); MatrixType projToQ=( CqqInv*( (QslashR*QslashR.transpose() )* (PslashR*PslashR.transpose() ) )); // copy to stl vector so we can sort the results std::vector evals(pccaSquaredCorrs.cols(),0); std::vector oevals(pccaSquaredCorrs.cols(),0); for ( long j=0; j 0.05 ){ VectorType temp=this->vEtoV( pccaVecs.col( j ) ); VectorType tempq=projToQ*temp; VectorType pvar= temp*PslashR; this->ReSoftThreshold(pvar , this->m_FractionNonZeroP , this->m_KeepPositiveP ); VectorType qvar=tempq*QslashR; this->ReSoftThreshold(qvar , this->m_FractionNonZeroQ , this->m_KeepPositiveQ ); evals[j]=fabs(this->PearsonCorr(PslashR*pvar,QslashR*qvar)); oevals[j]=evals[j]; } } // sort and reindex the eigenvectors/values sort (evals.begin(), evals.end(), my_sccan_sort_object); std::vector sorted_indices(nvecs,-1); for (unsigned int i=0; im_VariatesP.set_size(PslashR.cols(),nvecs); this->m_VariatesQ.set_size(QslashR.cols(),nvecs); for (unsigned int i=0; ivEtoV( pccaVecs.col( sorted_indices[i] ) ); VectorType tempq=projToQ*temp; VectorType pvar=temp*PslashR ; this->ReSoftThreshold(pvar , this->m_FractionNonZeroP , this->m_KeepPositiveP ); VectorType qvar=tempq*QslashR; this->ReSoftThreshold(qvar , this->m_FractionNonZeroQ , this->m_KeepPositiveQ ); this->m_VariatesP.set_column( i, pvar ); this->m_VariatesQ.set_column( i, qvar ); } for (unsigned int i=0; im_CanonicalCorrelations[i]= this->PearsonCorr(PslashR*this->GetVariateP(i),QslashR*this->GetVariateQ(i) ); if ( ! this->m_Silent ) std::cout << "correlation of mapped back data " << this->m_CanonicalCorrelations[i] << " eval " << pccaSquaredCorrs(sorted_indices[i],sorted_indices[i]) << std::endl; } for (unsigned int i=0; im_Silent ) std::cout << "inner prod of projections 0 vs i " << this->PearsonCorr( PslashR*this->GetVariateP(0) , PslashR*this->GetVariateP(i) ) << std::endl; } this->RunDiagnostics(nvecs); */ return this->m_CanonicalCorrelations[0]; } template void antsSCCANObject ::SortResults(unsigned int n_vecs) { bool debug = false; // sort and reindex the eigenvectors/values std::vector evals(n_vecs, 0); std::vector oevals(n_vecs, 0); if( debug ) { if ( ! this->m_Silent ) std::cout << "sort-a" << std::endl; } for( unsigned long j = 0; j < n_vecs; ++j ) { RealType val = fabs(this->m_CanonicalCorrelations[j]); evals[j] = val; oevals[j] = val; } if( debug ) { if ( ! this->m_Silent ) std::cout << "sort-b" << std::endl; } sort(evals.begin(), evals.end() , std::greater() ); std::vector sorted_indices( n_vecs, n_vecs - 1 ); for( unsigned int i = 0; i < evals.size(); i++ ) { for( unsigned int j = 0; j < evals.size(); j++ ) { if( fabs( evals[i] - oevals[j] ) < 1.e-9 && sorted_indices[i] == static_cast( n_vecs - 1 ) ) { sorted_indices[i] = j; oevals[j] = 0; } } } if( debug ) { for( unsigned int i = 0; i < evals.size(); i++ ) { if ( ! this->m_Silent ) std::cout << " sorted " << i << " is " << sorted_indices[i] << " ev " << evals[i] << " oev " << oevals[i] << std::endl; } } if( debug ) { if ( ! this->m_Silent ) std::cout << "sort-c" << std::endl; } VectorType newcorrs(n_vecs, 0); MatrixType varp( this->m_VariatesP.rows(), this->m_VariatesP.columns(), 0); MatrixType varq( this->m_VariatesQ.rows(), this->m_VariatesQ.columns(), 0); MatrixType varu( this->m_MatrixU.rows(), this->m_MatrixU.columns(), 0); if( debug ) { if ( ! this->m_Silent ) std::cout << "sort-d" << std::endl; } for( unsigned int i = 0; i < n_vecs; i++ ) { // if ( sorted_indices[i] > 0 ) { varp.set_column(i, this->m_VariatesP.get_column( sorted_indices[i] ) ); if( varq.columns() > i ) { varq.set_column(i, this->m_VariatesQ.get_column( sorted_indices[i] ) ); } if( varu.columns() > i ) { varu.set_column(i, this->m_MatrixU.get_column( sorted_indices[i] ) ); } newcorrs[i] = (this->m_CanonicalCorrelations[sorted_indices[i]]); // } } if( debug ) { if ( ! this->m_Silent ) std::cout << "sort-e" << std::endl; } for( unsigned int i = 0; i < n_vecs; i++ ) { if( debug ) { if ( ! this->m_Silent ) std::cout << "sort-e-a" << std::endl; } this->m_VariatesP.set_column(i, varp.get_column( i ) ); if( debug ) { if ( ! this->m_Silent ) std::cout << "sort-e-b" << std::endl; } if( this->m_VariatesQ.columns() > i ) { if( debug ) { if ( ! this->m_Silent ) std::cout << "sort-e-c" << i << " varqcol " << varq.columns() << " VarQcol " << this->m_VariatesQ.columns() << std::endl; } this->m_VariatesQ.set_column( i, varq.get_column( i ) ); } if( this->m_MatrixU.columns() > i ) { this->m_MatrixU.set_column( i, varu.get_column( i ) ); } } if( debug ) { if ( ! this->m_Silent ) std::cout << "sort-f" << std::endl; } this->m_CanonicalCorrelations = newcorrs; } template TRealType antsSCCANObject ::SparseArnoldiSVD_z(unsigned int n_vecs ) { this->m_CanonicalCorrelations.set_size(n_vecs); this->m_CanonicalCorrelations.fill(0); if ( ! this->m_Silent ) std::cout << " arnoldi sparse svd : cg " << std::endl; std::vector vexlist; this->m_MatrixP = this->NormalizeMatrix(this->m_OriginalMatrixP); this->m_MatrixQ = this->m_MatrixP; if( this->m_OriginalMatrixR.size() > 0 ) { this->m_MatrixRRt = this->ProjectionMatrix(this->m_OriginalMatrixR); this->m_MatrixP = this->m_MatrixP - (this->m_MatrixRRt * this->m_MatrixP); } this->m_ClusterSizes.set_size(n_vecs); this->m_ClusterSizes.fill(0); double trace = 0; for( unsigned int i = 0; i < this->m_MatrixP.cols(); i++ ) { trace += inner_product(this->m_MatrixP.get_column(i), this->m_MatrixP.get_column(i) ); } this->m_VariatesP.set_size(this->m_MatrixP.cols(), n_vecs); VariateType myGradients; this->m_SparseVariatesP.set_size(this->m_MatrixP.cols(), n_vecs); this->m_SparseVariatesP.fill(0); myGradients.set_size(this->m_MatrixP.cols(), n_vecs); myGradients.fill(0); MatrixType init = this->GetCovMatEigenvectors( this->m_MatrixP ); for( unsigned int kk = 0; kk < n_vecs; kk++ ) { this->m_VariatesP.set_column(kk, this->InitializeV(this->m_MatrixP) ); if( kk < init.columns() ) { VectorType initv = init.get_column(kk) * this->m_MatrixP; this->m_VariatesP.set_column(kk, initv); } } const unsigned int maxloop = this->m_MaximumNumberOfIterations; // Arnoldi Iteration SVD/SPCA unsigned int loop = 0; bool debug = false; double convcrit = 1; RealType fnp = 1; while( loop 1.e-8 ) { fnp = this->m_FractionNonZeroP; for( unsigned int k = 0; k < n_vecs; k++ ) { VectorType ptemp = this->m_SparseVariatesP.get_column(k); // if ( loop == 0 ) ptemp = this->m_VariatesP.get_column(k); MatrixType pmod = this->m_MatrixP; VectorType pveck = pmod.transpose() * ( pmod * ptemp ); // classic RealType hkkm1 = pveck.two_norm(); if( hkkm1 > this->m_Epsilon ) { pveck = pveck / hkkm1; } for( unsigned int j = 0; j < k; j++ ) // \forall j \ne i x_j \perp x_i { VectorType qj = this->m_VariatesP.get_column(j); RealType ip = inner_product(qj, qj); if( ip < this->m_Epsilon ) { ip = 1; } RealType hjk = inner_product(qj, pveck) / ip; pveck = pveck - qj * hjk; } VectorType pvecknew(pveck); // x_i is sparse if( fnp < 1 ) { if( this->m_KeepPositiveP ) { this->ConstantProbabilityThreshold( pvecknew, fnp, this->m_KeepPositiveP ); } else { this->ReSoftThreshold( pvecknew, fnp, !this->m_KeepPositiveP ); } this->ClusterThresholdVariate( pvecknew, this->m_MaskImageP, this->m_MinClusterSizeP ); pvecknew = pvecknew / pvecknew.two_norm(); } /** this is the difference between the updated & orthogonalized (not sparse) pveck and the sparse pveck */ /** put another way - minimize the difference between the sparse solution and the pca solution */ // myGradients.set_column(k,pvecknew*this->m_CanonicalCorrelations[k]-pveck); myGradients.set_column(k, pvecknew - pveck / pveck.two_norm() ); // residual between sparse and pca solution if( loop == 0 ) { this->m_VariatesP.set_column(k, pvecknew); } } // kloop /** Now update the solution by this gradient */ double recuravg = 0.5; // 0.99; //1/(double)(loop+1); double recuravg2 = 1 - recuravg; for( unsigned int k = 0; k < n_vecs; k++ ) { VectorType proj = this->m_MatrixP * this->m_VariatesP.get_column( k ); double denom = inner_product( proj, proj ); if( denom < this->m_Epsilon ) { denom = 1.e9; } VectorType pveck = myGradients.get_column(k); VectorType newsol = this->m_SparseVariatesP.get_column(k) * recuravg2 + pveck * recuravg; // VectorType newsol = this->m_SparseVariatesP.get_column(k) - pveck * alphak; this->m_SparseVariatesP.set_column(k, newsol); } /** Project the solution space to the sub-space */ for( unsigned int k = 0; k < n_vecs; k++ ) { VectorType ptemp = this->m_SparseVariatesP.get_column(k); if( fnp < 1 ) { if( this->m_KeepPositiveP ) { this->ConstantProbabilityThreshold( ptemp, fnp, this->m_KeepPositiveP ); } else { this->ReSoftThreshold( ptemp, fnp, !this->m_KeepPositiveP ); } this->ClusterThresholdVariate( ptemp, this->m_MaskImageP, this->m_MinClusterSizeP ); ptemp = ptemp / ptemp.two_norm(); } MatrixType pmod = this->m_MatrixP; VectorType pveck = pmod.transpose() * ( pmod * ptemp ); // classic RealType hkkm1 = pveck.two_norm(); if( hkkm1 > this->m_Epsilon ) { pveck = pveck / hkkm1; } for( unsigned int j = 0; j < k; j++ ) { VectorType qj = this->m_VariatesP.get_column(j); RealType ip = inner_product(qj, qj); if( ip < this->m_Epsilon ) { ip = 1; } RealType hjk = inner_product(qj, pveck) / ip; pveck = pveck - qj * hjk; } if( fnp < 1 ) { if( this->m_KeepPositiveP ) { this->ConstantProbabilityThreshold( pveck, fnp, this->m_KeepPositiveP ); } else { this->ReSoftThreshold( pveck, fnp, !this->m_KeepPositiveP ); } this->ClusterThresholdVariate( pveck, this->m_MaskImageP, this->m_MinClusterSizeP ); pveck = pveck / pveck.two_norm(); } this->m_VariatesP.set_column(k, pveck); } // kloop this->m_VariatesQ = this->m_VariatesP; if( debug ) { if ( ! this->m_Silent ) std::cout << " get evecs " << std::endl; } RealType vex = this->ComputeSPCAEigenvalues(n_vecs, trace, true); vexlist.push_back( vex ); this->SortResults( n_vecs ); convcrit = ( this->ComputeEnergySlope(vexlist, 5) ); if ( ! this->m_Silent ) std::cout << "Iteration: " << loop << " Eigenval_0: " << this->m_CanonicalCorrelations[0] << " Eigenval_N: " << this->m_CanonicalCorrelations[n_vecs - 1] << " Sparseness: " << fnp << " convergence-criterion: " << convcrit << " vex " << vex << std::endl; loop++; if( debug ) { if ( ! this->m_Silent ) std::cout << "wloopdone" << std::endl; } } // opt-loop // if ( ! this->m_Silent ) std::cout << " cluster-sizes " << this->m_ClusterSizes << std::endl; for( unsigned int i = 0; i < vexlist.size(); i++ ) { if ( ! this->m_Silent ) std::cout << vexlist[i] << ","; } if ( ! this->m_Silent ) std::cout << std::endl; return fabs(this->m_CanonicalCorrelations[0]); } template TRealType antsSCCANObject ::ReconstructionError( typename antsSCCANObject::MatrixType mymat, typename antsSCCANObject::MatrixType myvariate ) { this->m_CanonicalCorrelations.set_size( myvariate.cols() ); this->m_CanonicalCorrelations.fill( 0 ); RealType reconerr = 0; RealType onenorm = 0; unsigned int n_vecs = myvariate.cols(); RealType clnum = ( RealType ) this->m_MatrixP.cols(); for( unsigned int a = 0; a < mymat.rows(); a++ ) { VectorType x_i = mymat.get_row( a ); onenorm += x_i.one_norm() / clnum; VectorType lmsolv( n_vecs, 1 ); // row for B (void) this->ConjGrad( myvariate, lmsolv, x_i, 0, 10000 ); VectorType x_recon = ( this->m_VariatesP * lmsolv + this->m_Intercept ); reconerr += ( x_i - x_recon ).one_norm() / clnum; for( unsigned int i = 0; i < n_vecs; i++ ) { this->m_CanonicalCorrelations[i] = this->m_CanonicalCorrelations[i] + fabs( lmsolv[i] ); } } return ( onenorm - reconerr ) / onenorm; } template TRealType antsSCCANObject ::SparseReconPrior(unsigned int n_vecs, bool prior) { VectorType sparsenessparams( n_vecs, this->m_FractionNonZeroP ); double lambda = this->GetPriorWeight(); // Make it a parameter if ( ! this->m_Silent ) std::cout << "lambda" << lambda << " GradStep " << this->m_GradStep << std::endl; if( prior ) { this->m_MatrixPriorROI = this->m_OriginalMatrixPriorROI; n_vecs = this->m_MatrixPriorROI.rows(); for( unsigned int x = 0; x < this->m_OriginalMatrixPriorROI.rows(); x++ ) { VectorType priorrow = this->m_OriginalMatrixPriorROI.get_row( x ); RealType fnz = 0; for( unsigned int y = 0; y < this->m_OriginalMatrixPriorROI.cols(); y++ ) { if( vnl_math_abs( priorrow( y ) ) > 1.e-10 ) { fnz += 1; } } if( this->m_FractionNonZeroP < 1.e-10 ) { sparsenessparams( x ) = (RealType) fnz / (RealType) this->m_OriginalMatrixPriorROI.cols(); } priorrow = priorrow/priorrow.two_norm(); this->m_MatrixPriorROI.set_row( x , priorrow ); } } RealType reconerr = 0; this->m_CanonicalCorrelations.set_size( n_vecs ); this->m_CanonicalCorrelations.fill( 0 ); if ( ! this->m_Silent ) std::cout << " sparse recon prior " << this->m_MinClusterSizeP << std::endl; MatrixType matrixB( this->m_OriginalMatrixP.rows(), n_vecs ); matrixB.fill( 0 ); this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP ); this->m_VariatesP.set_size( this->m_MatrixP.cols(), n_vecs ); this->m_VariatesP.fill( 0 ); VectorType icept( this->m_MatrixP.rows(), 0 ); if( prior ) { for( unsigned int i = 0; i < n_vecs; i++ ) { VectorType initvec = this->m_MatrixPriorROI.get_row(i); initvec = initvec / initvec.two_norm(); this->SparsifyP( initvec ); this->m_VariatesP.set_column( i, initvec ); } } else { for( unsigned int i = 0; i < n_vecs; i++ ) { VectorType initvec = this->InitializeV( this->m_MatrixP, i + 1 ); initvec = initvec / initvec.two_norm(); this->m_VariatesP.set_column( i, initvec ); } } /** now initialize B */ reconerr = this->SparseReconB( matrixB, icept ); for( unsigned int overit = 0; overit < this->m_MaximumNumberOfIterations; overit++ ) { // update V matrix /** a power iteration method --- depends on the following given any nonzero $z \in \mathbb{R}^n$, the Rayleigh quotient $x^T X x / x^T x $ minimizes the function $\| \lambda x - X x \|^2 $ wrt $\lambda$. so, if we find the vector x ( by sparse power iteration ) then we have a vector that is a close approximation to the first eigenvector of X. If X is a residual matrix then x is a good approximation of the $n^th$ eigenvector. **/ VectorType zero( this->m_MatrixP.cols(), 0 ); VectorType zerob( this->m_MatrixP.rows(), 0 ); for( unsigned int a = 0; a < n_vecs; a++ ) { this->m_FractionNonZeroP = sparsenessparams( a ); VectorType bvec = matrixB.get_column( a ); matrixB.set_column( a, zerob ); // if X = U V^T + Error then matrixB = U MatrixType tempMatrix = this->m_VariatesP; tempMatrix.set_column( a, zero ); MatrixType partialmatrix = matrixB * tempMatrix.transpose(); for( unsigned int interc = 0; interc < this->m_MatrixP.rows(); interc++ ) { partialmatrix.set_row( interc, partialmatrix.get_row( interc ) + icept( interc ) ); } partialmatrix = this->m_MatrixP - partialmatrix; VectorType priorVec = this->m_MatrixPriorROI.get_row(a); if( priorVec.one_norm() < 1.e-12 ) { if ( ! this->m_Silent ) std::cout << "Prior Norm too small, could be a bug: " << priorVec.one_norm() << std::endl; } VectorType evec = this->m_VariatesP.get_column( a ); priorVec = priorVec / priorVec.two_norm(); this->m_CanonicalCorrelations[a] = this->IHTPowerIterationPrior( partialmatrix, evec, priorVec , 10, a, lambda ); this->m_VariatesP.set_column( a, evec ); matrixB.set_column( a, bvec ); // ///////////////////// // update B matrix by linear regression reconerr = this->SparseReconB( matrixB, icept ); } if ( ! this->m_Silent ) std::cout << overit << ": %var " << reconerr << std::endl; } this->m_VariatesQ = matrixB; return 1.0 / reconerr; } template TRealType antsSCCANObject ::IHTPowerIterationPrior( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& evec, typename antsSCCANObject::VectorType& priorin, unsigned int maxits, unsigned int maxorth, double lambda ) { /* // d/dv \| X_p - u_a v^t \| = u_a^t ( X_p - u_a v^t ) // u_a^t X_p - u_a^t u_a v^t // d/dv \| M - v v^t \| = v^t ( M - v v^t ) // v^t M - ip( v, v ) v^t VectorType bvec = this->m_MatrixU.get_column( maxorth ); VectorType mygrad1 = ( bvec * A - evec * inner_product( bvec, bvec ) ) * lam1; VectorType mygrad2 = ( this->FastOuterProductVectorMultiplication( priorin, evec ) - evec * inner_product( evec, evec ) ) * lam2; VectorType mygrad = mygrad1 + mygrad2 ; evec = evec + mygrad * this->m_GradStep; this->SparsifyP( evec ); return 1; */ /** This computes a hard-thresholded gradient descent on the eigenvector criterion. max x over x^t A^t A x s.t. x^t x = 1 success of the optimization is measured by rayleigh quotient. derivative is: d.dx ( x^t A^t A x ) = A^t A x , x \leftarrow x / \| x \| we use a conjugate gradient version of this optimization. */ VectorType prior( priorin ); if( evec.two_norm() == 0 ) { evec = this->InitializeV( this->m_MatrixP, false ); } evec = evec / evec.two_norm(); if( inner_product( prior, evec ) == 0 ) { evec.update( prior, 0 ); this->SparsifyP( evec ); } VectorType mgrad = this->FastOuterProductVectorMultiplication( prior, evec ); VectorType proj = ( A * prior ); VectorType lastgrad = evec; VectorType mlastgrad = mgrad; RealType rayquo = 0, rayquold = -1; RealType denom = inner_product( evec, evec ); if( denom > 0 ) { rayquo = inner_product( proj, proj ) / denom; } MatrixType At = A.transpose(); unsigned int powerits = 0; VectorType bestevec = evec; RealType basevscale = 1; RealType basepscale = 1; VectorType di; while( ( ( powerits < maxits ) && ( rayquo > rayquold ) ) || ( powerits < 4 ) ) { VectorType pvec = this->FastOuterProductVectorMultiplication( prior , evec ); VectorType nvec = ( At * ( A * evec ) ); if ( powerits == 0 ) { basevscale = pvec.two_norm() / nvec.two_norm() * this->m_GradStep; basepscale = this->m_GradStep * lambda; } VectorType g1 = nvec * basevscale; VectorType g2 = pvec * basepscale; RealType gamma = 0.1; nvec = g1 + g2; if ( powerits == 0 ) di = nvec; if( ( lastgrad.two_norm() > 0 ) ) { gamma = inner_product( nvec, nvec ) / inner_product( lastgrad, lastgrad ); } lastgrad = nvec; VectorType diplus1 = nvec + di * gamma ; evec = evec + diplus1; di = diplus1; for( unsigned int orth = 0; orth < maxorth; orth++ ) { VectorType zv = this->m_VariatesP.get_column( orth ); evec = this->Orthogonalize( evec, zv ); } this->SparsifyP( evec ); if( evec.two_norm() > 0 ) { evec = evec / evec.two_norm(); } rayquold = rayquo; denom = inner_product( evec, evec ); if( denom > 0 ) { // fast way to compute evec^T * ( A^T A + M M^T ) evec proj = ( At * ( A * evec ) ) * basevscale + ( this->FastOuterProductVectorMultiplication( prior , evec ) ) * basepscale; rayquo = inner_product( evec, proj ) / denom; } powerits++; if( rayquo > rayquold || powerits == 1 ) { bestevec = evec; } } // if ( ! this->m_Silent ) std::cout << "rayleigh-quotient: " << rayquo << " in " << powerits << " num " << maxorth; evec = bestevec; if( inner_product( prior, evec ) == 0 ) { evec.update( prior, 0 ); this->SparsifyP( evec ); if ( ! this->m_Silent ) std::cout << " recompute " << maxorth << " new ip " << inner_product( prior, evec ); } return rayquo; } template TRealType antsSCCANObject ::SparseReconB( typename antsSCCANObject::MatrixType& matrixB, typename antsSCCANObject::VectorType& icept ) { MatrixType temp( this->m_MatrixP ); icept.fill( 0 ); RealType meancorr = 0; matrixB.set_size( this->m_MatrixP.rows(), this->m_VariatesP.cols() ); matrixB.fill( 0 ); for( unsigned int a = 0; a < this->m_MatrixP.rows(); a++ ) { VectorType x_i = this->m_MatrixP.get_row( a ); // 1 by p VectorType lmsolv = matrixB.get_row( a ); // 1 by k .... variatesp is p by k ... // solve ( p by k ) ( k by 1 ) = ( p by 1 ) // which is \| V U_j - X_j \|^2 where j is the j^th subject (void) this->ConjGrad( this->m_VariatesP, lmsolv, x_i, 0, 100 ); // A x = b icept( a ) = this->m_Intercept; VectorType x_recon = ( this->m_VariatesP * lmsolv + icept( a ) ); matrixB.set_row( a, lmsolv ); RealType localcorr = this->PearsonCorr( x_recon, x_i ); temp.set_row( a, x_recon ); meancorr += localcorr; } if( false ) { std::vector ColumnHeaders; for( unsigned int nv = 0; nv < this->m_MatrixP.cols(); nv++ ) { std::stringstream ss; ss << (nv + 1); std::string stringout = ss.str(); std::string colname = std::string("X") + stringout; ColumnHeaders.push_back( colname ); } MatrixType recon = ( matrixB * this->m_VariatesP.transpose() ); typedef itk::CSVNumericObjectFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName( "Zrecon.csv" ); writer->SetInput( &recon ); writer->Write(); } this->m_MatrixU = matrixB; RealType matpfrobnorm = this->m_MatrixP.frobenius_norm(); RealType rr = ( temp - this->m_MatrixP ).frobenius_norm(); return ( 1.0 - rr * rr / ( matpfrobnorm * matpfrobnorm ) ); } template TRealType antsSCCANObject ::SparseRecon(unsigned int n_vecs) { bool prior = false; RealType reconerr = 0; VectorType sparsenessparams( n_vecs, this->m_FractionNonZeroP ); if ( ( this->m_OriginalMatrixPriorROI.rows() > 0 ) && ( this->m_OriginalMatrixPriorROI.cols() > 0 ) ) { prior = true; if ( ! this->m_Silent ) std::cout << " image-driven initialization " << std::endl; this->m_MatrixPriorROI = this->m_OriginalMatrixPriorROI; n_vecs = this->m_OriginalMatrixPriorROI.rows(); for( unsigned int x = 0; x < this->m_OriginalMatrixPriorROI.rows(); x++ ) { VectorType priorrow = this->m_OriginalMatrixPriorROI.get_row( x ); RealType fnz = 0; for( unsigned int y = 0; y < this->m_OriginalMatrixPriorROI.cols(); y++ ) { if(vnl_math_abs( priorrow( y ) ) > 1.e-8 ) { fnz += 1; } } if ( vnl_math_abs( this->m_FractionNonZeroP ) < 1.e-11 ) sparsenessparams( x ) = 1.0 * (RealType) fnz / (RealType) this->m_OriginalMatrixPriorROI.cols(); priorrow = priorrow/priorrow.two_norm(); this->m_MatrixPriorROI.set_row( x , priorrow ); } this->m_FractionNonZeroP = sparsenessparams.mean(); if ( ! this->m_Silent ) std::cout <<"sparseness: " << sparsenessparams << std::endl; this->m_VariatesP = this->m_MatrixPriorROI.transpose(); } this->m_CanonicalCorrelations.set_size( n_vecs ); this->m_CanonicalCorrelations.fill( 0 ); if ( ! this->m_Silent ) std::cout << " sparse recon : clust " << this->m_MinClusterSizeP << " UseL1 " << this->m_UseL1 << " Keep+ " << this->m_KeepPositiveP << " covering " << this->m_Covering << " smooth " << this->m_Smoother << std::endl; MatrixType matrixB( this->m_OriginalMatrixP.rows(), n_vecs ); matrixB.fill( 0 ); this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP ); VectorType icept( this->m_MatrixP.rows(), 0 ); if ( this->m_FractionNonZeroP < 1.e-11 ) { // estimate sparseness from PCA if ( ! this->m_Silent ) std::cout << " data-driven initialization from PCA " << std::endl; VectorType maxvals( this->m_MatrixP.cols() , 0 ); MatrixType cov = this->m_MatrixP * this->m_MatrixP.transpose(); vnl_svd eig( cov, 1.e-6 ); this->m_VariatesP.set_size( this->m_MatrixP.cols(), n_vecs ); this->m_VariatesP.fill( 0 ); for( unsigned int i = 0; i < n_vecs; i++ ) { if( i < this->m_MatrixP.rows() ) { VectorType u = eig.U().get_column( i ); VectorType up = u * this->m_MatrixP; up = up / up.two_norm(); this->m_VariatesP.set_column( i, up ); } } for( unsigned int i = 0; i < maxvals.size(); i++ ) { RealType maxval = 0; for( unsigned int j = 0; j < n_vecs; j++ ) { RealType myval = vnl_math_abs( this->m_VariatesP( i, j ) ); if ( myval > maxval ) { maxvals( i ) = j; maxval = myval; } } } sparsenessparams.fill( 0 ); for( unsigned int i = 0; i < maxvals.size(); i++ ) { unsigned int index = (unsigned int) ( maxvals( i ) + 0.5 ); sparsenessparams( index ) = sparsenessparams( index ) + 1; } for( unsigned int i = 0; i < n_vecs; i++ ) { sparsenessparams( i ) = 1.01 * ( sparsenessparams( i ) / this->m_MatrixP.cols() ); this->m_FractionNonZeroP = sparsenessparams( i ); VectorType vec = this->m_VariatesP.get_column( i ); this->SparsifyP( vec ); this->m_VariatesP.set_column( i, vec ); if ( ! this->m_Silent ) std::cout <<" Estimated-Sparseness " << i << " is " << sparsenessparams( i ) << std::endl; } if ( ! this->m_Silent ) std::cout <<" Estimated-Sparseness Sum " << sparsenessparams.sum() << std::endl; } // done estimate sparseness else if ( this->m_VariatesP.rows() < 1 ) { if ( ! this->m_Silent ) std::cout << " data-driven initialization " << std::endl; this->m_VariatesP.set_size( this->m_MatrixP.cols(), n_vecs ); this->m_VariatesP.fill( 0 ); for( unsigned int i = 0; i < n_vecs; i++ ) { VectorType initvec = this->InitializeV( this->m_MatrixP, i + 1 ); for( unsigned int j = 0; j < i; j++ ) { initvec = this->Orthogonalize( initvec, this->m_VariatesP.get_column( j ) ); } initvec = this->SpatiallySmoothVector( initvec, this->m_MaskImageP ); initvec = initvec / initvec.two_norm(); this->m_VariatesP.set_column( i, initvec ); } } /** now initialize B */ reconerr = this->SparseReconB( matrixB, icept ); this->SparseArnoldiSVD_Other( matrixB ); if ( ! this->m_Silent ) std::cout << "begin : %var " << reconerr << std::endl; RealType matpfrobnorm = this->m_MatrixP.frobenius_norm(); if ( false ) for( unsigned int overit = 0; overit < this->m_MaximumNumberOfIterations; overit++ ) { MatrixType vgrad = matrixB.transpose() * this->m_MatrixP - ( matrixB.transpose() * matrixB ) * this->m_VariatesP.transpose(); this->m_VariatesP = this->m_VariatesP + vgrad.transpose(); for( unsigned int a = 0; a < n_vecs; a++ ) { VectorType evec = this->m_VariatesP.get_column( a ); this->SparsifyP( evec ); this->m_VariatesP.set_column( a , evec ); } reconerr = this->SparseReconB( matrixB, icept ); this->SparseArnoldiSVD_Other( matrixB ); this->m_MatrixU = matrixB; if ( ! this->m_Silent ) std::cout << overit << ": %var " << reconerr << std::endl; } if ( true ) for( unsigned int overit = 0; overit < this->m_MaximumNumberOfIterations; overit++ ) { /** a power iteration method --- depends on the following given any nonzero $z \in \mathbb{R}^n$, the Rayleigh quotient $x^T X x / x^T x $ minimizes the function $\| \lambda x - X x \|^2 $ wrt $\lambda$. so, if we find the vector x ( by sparse power iteration ) then we have a vector that is a close approximation to the first eigenvector of X. If X is a residual matrix then x is a good approximation of the $n^th$ eigenvector. */ VectorType zero( this->m_MatrixP.cols(), 0 ); VectorType zerob( this->m_MatrixP.rows(), 0 ); unsigned int a = 0; while( a < n_vecs ) { this->m_FractionNonZeroP = sparsenessparams( a ); VectorType bvec = matrixB.get_column( a ); matrixB.set_column( a, zerob ); MatrixType tempMatrix = this->m_VariatesP; tempMatrix.set_column( a, zero ); MatrixType partialmatrix = matrixB * tempMatrix.transpose(); for( unsigned int interc = 0; interc < this->m_MatrixP.rows(); interc++ ) { partialmatrix.set_row( interc, partialmatrix.get_row( interc ) + icept( interc ) ); } partialmatrix = this->m_MatrixP - partialmatrix; this->m_CanonicalCorrelations[a] = 1 - ( partialmatrix.frobenius_norm() ) / matpfrobnorm; VectorType evec = this->m_VariatesP.get_column( a ); this->m_VariatesP.set_column( a, zero ); this->m_CanonicalCorrelations[a] = this->IHTPowerIteration( partialmatrix, evec, 5, a ); // 0 => a this->m_VariatesP.set_column( a, evec ); matrixB.set_column( a, bvec ); // update B matrix by linear regression reconerr = this->SparseReconB( matrixB, icept ); this->SparseArnoldiSVD_Other( matrixB ); a++; } // while // update B matrix by linear regression reconerr = this->SparseReconB( matrixB, icept ); this->SparseArnoldiSVD_Other( matrixB ); this->m_MatrixU = matrixB; if ( ! this->m_Silent ) std::cout << overit << ": %var " << reconerr << std::endl; if ( ( ! prior ) && ( overit == ( this->m_MaximumNumberOfIterations - 1 ) ) ) this->SortResults( n_vecs ); } this->m_VariatesQ = matrixB; for( unsigned int i = 0; i < n_vecs; i++ ) { VectorType v = this->m_VariatesP.get_column( i ); if( v.min_value() < 0 ) { this->m_VariatesP.set_column( i, v * ( -1 ) ); } } return 1.0 / reconerr; /** a regression-based method for( unsigned int a = 0; a < this->m_MatrixP.cols(); a++ ) { VectorType x_i = this->m_MatrixP.get_column( a ); VectorType lmsolv = this->m_VariatesP.get_row( a ); // good initialization should increase convergence speed (void) this->ConjGrad( matrixB, lmsolv, x_i, 0, 10000 ); // A x = b VectorType x_recon = ( matrixB * lmsolv + this->m_Intercept ); onenorm += x_i.one_norm() / this->m_MatrixP.rows(); reconerr += ( x_i - x_recon ).one_norm() / this->m_MatrixP.rows(); this->m_VariatesP.set_row( a , lmsolv ); } */ /* a gradient method E & = \langle X - U V^T , X - U V^T \rangle \\ \partial E/\partial V & = \langle -U , X - U V^T \rangle \\ & = -U^T X + U^T U V^T \rangle if ( overit == 0 ) this->m_VariatesP.fill( 0 ); MatrixType vgrad = matrixB.transpose() * this->m_MatrixP - ( matrixB.transpose() * matrixB ) * this->m_VariatesP.transpose(); // this->m_VariatesP = this->m_VariatesP + vgrad.transpose() * 0.01; this->m_VariatesP = this->m_VariatesP + vgrad.transpose() ; for( unsigned int a = 0; a < this->m_MatrixP.rows(); a++ ) { VectorType evec = this->m_VariatesP.get_column( a ); this->SparsifyP( evec ); this->m_VariatesP.set_column( a, evec ); } */ } template TRealType antsSCCANObject ::IHTPowerIterationHome( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& evec, // typename antsSCCANObject::VectorType& y, unsigned int maxits, unsigned int maxorth ) { /** This computes a hard-thresholded gradient descent on the eigenvector criterion. max x over x^t A^t A x s.t. x^t x = 1 success of the optimization is measured by rayleigh quotient. derivative is: d.dx ( x^t A^t A x ) = A^t A x , x \leftarrow x / \| x \| we use a conjugate gradient version of this optimization. */ if( evec.two_norm() == 0 ) { evec = this->InitializeV( this->m_MatrixP, true ); } VectorType proj = ( A * evec ); VectorType lastgrad = evec; RealType rayquo = 0, rayquold = -1; RealType denom = inner_product( evec, evec ); if( denom > 0 ) { rayquo = inner_product( proj, proj ) / denom; } MatrixType At = A.transpose(); unsigned int powerits = 0; bool conjgrad = true; VectorType bestevec = evec; while( ( ( rayquo > rayquold ) && ( powerits < maxits ) ) ) { VectorType nvec = At * proj; for( unsigned int orth = 0; orth < maxorth; orth++ ) { nvec = this->Orthogonalize( nvec, this->m_VariatesP.get_column( orth ) ); } RealType gamma = 0.1; nvec = this->SpatiallySmoothVector( nvec, this->m_MaskImageP ); if( ( lastgrad.two_norm() > 0 ) && ( conjgrad ) ) { gamma = inner_product( nvec, nvec ) / inner_product( lastgrad, lastgrad ); } lastgrad = nvec; evec = evec * gamma + nvec; evec = this->SpatiallySmoothVector( evec, this->m_MaskImageP ); // this->CurvatureSparseness( evec, ( 1 - this->m_FractionNonZeroP ) * 100, 5 , this->m_MaskImageP ); this->SparsifyP( evec ); // VectorType gradvec = this->ComputeVectorGradMag( evec, this->m_MaskImageP ); if( evec.two_norm() > 0 ) { evec = evec / evec.two_norm(); } proj = ( A * evec ); rayquold = rayquo; denom = inner_product( evec, evec ); if( denom > 0 ) { rayquo = inner_product( proj, proj ) / denom; // - gradvec.two_norm() / gradvec.size() * 1.e2 ; } powerits++; if( rayquo > rayquold ) { bestevec = evec; } } evec = bestevec; if ( ! this->m_Silent ) std::cout << "rayleigh-quotient: " << rayquo << " in " << powerits << " num " << maxorth << std::endl; return rayquo; } template TRealType antsSCCANObject ::SparseReconHome(unsigned int n_vecs) { // this->m_UseL1 = false; RealType reconerr = 0; RealType onenorm = 0; this->m_CanonicalCorrelations.set_size( n_vecs ); this->m_CanonicalCorrelations.fill( 0 ); if ( ! this->m_Silent ) std::cout << " sparse recon " << this->m_MinClusterSizeP << " useL1 " << this->m_UseL1 << std::endl; MatrixType matrixB( this->m_OriginalMatrixP.rows(), n_vecs ); matrixB.fill( 0 ); this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP ); MatrixType cov = this->m_MatrixP * this->m_MatrixP.transpose(); vnl_svd eig( cov, 1.e-6 ); this->m_VariatesP.set_size( this->m_MatrixP.cols(), n_vecs ); this->m_VariatesP.fill( 0 ); for( unsigned int i = 0; i < n_vecs; i++ ) { if( i < this->m_MatrixP.rows() ) { VectorType u = eig.U().get_column( i ); VectorType up = u * this->m_MatrixP; this->SparsifyP( up ); this->m_VariatesP.set_column( i, up ); matrixB.set_column( i, u ); } else { this->m_VariatesP.set_column( i, this->InitializeV( this->m_MatrixP, false ) ); } } VectorType icept( this->m_MatrixP.rows(), 0 ); RealType matpfrobnorm = this->m_MatrixP.frobenius_norm(); for( unsigned int overit = 0; overit < this->m_MaximumNumberOfIterations; overit++ ) { // cov = this->m_VariatesP.transpose() * this->m_VariatesP; // vnl_svd qr( cov ); // this->m_VariatesP = this->m_VariatesP * qr.U(); // update V matrix /** a power iteration method --- depends on the following given any nonzero $z \in \mathbb{R}^n$, the Rayleigh quotient $x^T X x / x^T x $ minimizes the function $\| \lambda x - X x \|^2 $ wrt $\lambda$. so, if we find the vector x ( by sparse power iteration ) then we have a vector that is a close approximation to the first eigenvector of X. If X is a residual matrix then x is a good approximation of the $n^th$ eigenvector. **/ VectorType zero( this->m_MatrixP.cols(), 0 ); VectorType zerob( this->m_MatrixP.rows(), 0 ); for( unsigned int a = 0; a < n_vecs; a++ ) { VectorType bvec = matrixB.get_column( a ); matrixB.set_column( a, zerob ); MatrixType tempMatrix = this->m_VariatesP; tempMatrix.set_column( a, zero ); MatrixType partialmatrix = matrixB * tempMatrix.transpose(); for( unsigned int interc = 0; interc < this->m_MatrixP.rows(); interc++ ) { partialmatrix.set_row( interc, partialmatrix.get_row( interc ) + icept( interc ) ); } this->m_CanonicalCorrelations[a] = ( partialmatrix.frobenius_norm() ) / matpfrobnorm; partialmatrix = this->m_MatrixP - partialmatrix; VectorType evec = this->m_VariatesP.get_column( a ); this->m_VariatesP.set_column( a, zero ); if( evec.two_norm() > 0 ) { evec = evec / evec.two_norm(); } // get 1st eigenvector ... how should this be done? how about svd? if( ( a >= this->m_MatrixP.rows() ) && ( overit == 0 ) ) { ( void ) this->PowerIteration( partialmatrix, evec, 10, false ); } this->m_CanonicalCorrelations[a] = this->IHTPowerIterationHome( partialmatrix, evec, 20, 0 ); // 0 => a this->m_VariatesP.set_column( a, evec ); matrixB.set_column( a, bvec ); } // update B matrix by linear regression reconerr = onenorm = 0; icept.fill( 0 ); for( unsigned int a = 0; a < this->m_MatrixP.rows(); a++ ) { VectorType x_i = this->m_MatrixP.get_row( a ); VectorType lmsolv = matrixB.get_row( a ); // good initialization should increase // convergence speed (void) this->ConjGrad( this->m_VariatesP, lmsolv, x_i, 0, 10000 ); // A x = b VectorType x_recon = ( this->m_VariatesP * lmsolv + this->m_Intercept ); icept( a ) = this->m_Intercept; onenorm += x_i.one_norm() / this->m_MatrixP.cols(); reconerr += ( x_i - x_recon ).one_norm() / this->m_MatrixP.cols(); matrixB.set_row( a, lmsolv ); } RealType rr = ( onenorm - reconerr ) / onenorm; if ( ! this->m_Silent ) std::cout << overit << ": %var " << rr << " raw-reconerr " << reconerr << std::endl; } this->m_VariatesQ = matrixB; this->SortResults( n_vecs ); // this->m_UseL1 = false; return 1.0 / reconerr; } template TRealType antsSCCANObject ::SparseArnoldiSVDGreedy(unsigned int n_vecs) { if ( ! this->m_Silent ) std::cout << " arnoldi sparse svd : greedy " << this->m_MinClusterSizeP << " sparseness " << this->m_FractionNonZeroP << std::endl; std::vector vexlist; vexlist.push_back( 0 ); this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP ); this->m_MatrixQ = this->m_MatrixP; if( this->m_OriginalMatrixR.size() > 0 ) { this->m_MatrixRRt = this->ProjectionMatrix(this->m_OriginalMatrixR); if ( ! this->m_Silent ) std::cout << " Subtracting nuisance matrix from P matrix " << std::endl; this->m_MatrixP = this->m_MatrixP - (this->m_MatrixRRt * this->m_MatrixP); } this->m_VariatesP.set_size(this->m_MatrixP.cols(), n_vecs); MatrixType bmatrix = this->GetCovMatEigenvectors( this->m_MatrixP ); MatrixType bmatrix_big; bmatrix_big.set_size( this->m_MatrixP.cols(), n_vecs ); // double trace = vnl_trace( this->m_MatrixP * this->m_MatrixP.transpose() ); for( unsigned int kk = 0; kk < n_vecs; kk++ ) { this->m_VariatesP.set_column( kk, this->InitializeV( this->m_MatrixP ) ); if( kk < bmatrix.columns() && true ) { VectorType initv = bmatrix.get_column( kk ) * this->m_MatrixP; this->SparsifyP( initv ); this->m_VariatesP.set_column( kk, initv ); } } const unsigned int maxloop = this->m_MaximumNumberOfIterations; // Arnoldi Iteration SVD/SPCA unsigned int loop = 0; bool debug = false; double convcrit = 1; RealType fnp = 1; bool negate = false; MatrixType lastV = this->m_VariatesP; lastV.fill( 0 ); while( loop 1.e-8 ) { /** Compute the gradient estimate according to standard Arnoldi */ fnp = this->m_FractionNonZeroP; // MatrixType cov = this->m_VariatesP.transpose() * this->m_VariatesP; // vnl_svd qr( cov ); // this->m_VariatesP = this->m_VariatesP * qr.U(); for( unsigned int k = 0; k < n_vecs; k++ ) { VectorType pveck = this->m_VariatesP.get_column(k); MatrixType pmod = this->NormalizeMatrix( this->m_OriginalMatrixP ); if( k > 1 && loop > 1 && k < this->m_MatrixP.rows() - 1 && false ) { MatrixType m( this->m_MatrixP.rows(), k, 0 ); for( unsigned int mm = 0; mm < k; mm++ ) { m.set_column( mm, this->m_MatrixP * this->m_VariatesP.get_column( mm ) ); } MatrixType projmat = this->ProjectionMatrix( m, 1.e-8 ); pmod = pmod - projmat * pmod; } for( unsigned int powerit = 0; powerit < 5; powerit++ ) { pveck = ( pmod * pveck ); // classic pveck = pmod.transpose() * ( pveck ); RealType hkkm1 = pveck.two_norm(); if( hkkm1 > 0 ) { pveck = pveck / hkkm1; } for( unsigned int j = 0; j < k; j++ ) { VectorType qj = this->m_VariatesP.get_column(j); pveck = this->Orthogonalize( pveck, qj ); } } // powerit /** penalize by gradient RealType spgoal = ( 1 - fnp ) * 100 ; this->CurvatureSparseness( pveck , spgoal );*/ /** Project to the feasible sub-space */ if( negate ) { pveck = pveck * ( -1 ); } if( fnp < 1 ) { if( this->m_KeepPositiveP ) { this->ConstantProbabilityThreshold( pveck, fnp, this->m_KeepPositiveP ); } else { this->ReSoftThreshold( pveck, fnp, !this->m_KeepPositiveP ); } this->ClusterThresholdVariate( pveck, this->m_MaskImageP, this->m_MinClusterSizeP ); if( pveck.two_norm() > 0 ) { pveck = pveck / pveck.two_norm(); } } if( negate ) { pveck = pveck * ( -1 ); } /******************************** unsigned int bcolind = 0; RealType maxcorr = 0; for ( unsigned int cc = 0 ; cc < bmatrix_big.cols(); cc ++ ) { RealType corr = fabs( this->PearsonCorr( this->m_MatrixP * pveck , bmatrix.get_column( cc ) ) ); if ( corr > maxcorr ) bcolind = cc; } VectorType b = bmatrix_big.get_column( bcolind ) ; RealType cgerr = this->SparseConjGrad( pveck , b , 1.e-3 ); ********************************/ /** Update the solution */ this->m_VariatesP.set_column(k, pveck); } // kloop // compute the reconstruction error for each voxel /* Now get the LSQ regression solution X - n \times p matrix of subjects by voxels thickness measures B - k \times p matrix of roi measures --- some row has p voxels and that row is binary i.e. non-zero in the voxels where that label is non-zero ( out of e.g. the whole cortex) \beta - 1 \times k vector of beta values computed from the regression solution X_i --- 1 \times n some voxel for each subject then solve , for each voxel , \| X_i - \beta B X \| 1xn 1xk kxp pxn \| X - \Beta B X \| pxn pxk kxp pxn voxels - requires you to have n > k --- which you should have .... and easily done in R b.c you precomputed B X => a k \times n matrix this means that you can reconstruct all voxels for a subject given the measures of his ROI values --- so it's basically a regression across all voxels given the ROI ( or PCA ) values from that subject. the reconstruction error is very descriptive about the information gained by the dimensionality reduction used for that data. RealType totalerr = 0; MatrixType A = ( this->m_MatrixP * this->m_VariatesP ); MatrixType reconmat( this->m_MatrixP.rows(), this->m_MatrixP.cols(), 0 ); for ( unsigned int vox = 0; vox < this->m_MatrixP.cols(); vox++ ) { VectorType lmsol( n_vecs, 0); VectorType voxels = this->m_MatrixP.get_column( vox ); RealType lmerror = this->ConjGrad( A , lmsol , voxels , 0 , 100 ); VectorType proj = A * lmsol; if ( vnl_math_isnan( lmerror ) ) { lmerror = 0 ; proj.fill(0); } reconmat.set_column( vox , proj ); totalerr += lmerror; } totalerr /= this->m_MatrixP.cols() ; if ( ! this->m_Silent ) std::cout << " totalerr " << totalerr << std::endl; { VectorType vvv = reconmat.get_row( 10 ); typename TInputImage::Pointer image=this->ConvertVariateToSpatialImage( vvv , this->m_MaskImageP , false ); typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( "temp.nii.gz" ); writer->SetInput( image ); writer->Update(); } { VectorType vvv = this->m_MatrixP.get_row( 10 ); typename TInputImage::Pointer image=this->ConvertVariateToSpatialImage( vvv , this->m_MaskImageP , false ); typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( "temp2.nii.gz" ); writer->SetInput( image ); writer->Update(); } */ this->m_VariatesQ = this->m_VariatesP; /** Estimate eigenvalues , then sort */ RealType bestvex = // this->ComputeSPCAEigenvalues( n_vecs, trace, false ); this->ReconstructionError( this->m_MatrixP, this->m_VariatesP ); /* begin the crude line search */ if( bestvex < vexlist[loop] ) { RealType f = -0.5, enew = 0, eold = -1, delt = 0.05; bestvex = 0; MatrixType bestV = this->m_VariatesP; while( ( enew - eold ) > 0 && f <= 0.5 && loop > 0 && true ) { this->m_VariatesP = ( this->m_VariatesQ * f + lastV * ( 1 - f ) ); for( unsigned int m = 0; m < this->m_VariatesP.cols(); m++ ) { VectorType mm = this->m_VariatesP.get_column(m); this->SparsifyP( mm ); if( mm.two_norm() > 0 ) { mm = mm / mm.two_norm(); } this->m_VariatesP.set_column( m, mm ); } eold = enew; enew = this->ReconstructionError( this->m_MatrixP, this->m_VariatesP ); if( enew > bestvex ) { bestvex = enew; bestV = this->m_VariatesP; } // if ( ! this->m_Silent ) std::cout <<" vex " << enew << " f " << f << std::endl; f = f + delt; } this->m_VariatesP = bestV; } if( bestvex < vexlist[loop] ) { this->m_VariatesP = lastV; bestvex = vexlist[loop]; } lastV = this->m_VariatesP; RealType vex = bestvex; vexlist.push_back( vex ); this->SortResults(n_vecs); convcrit = ( this->ComputeEnergySlope(vexlist, 10) ); if( bestvex == vexlist[loop] ) { convcrit = 0; } if ( ! this->m_Silent ) std::cout << "Iteration: " << loop << " Eval_0: " << this->m_CanonicalCorrelations[0] << " Eval_N: " << this->m_CanonicalCorrelations[n_vecs - 1] << " Sp: " << fnp << " conv-crit: " << convcrit << " %var " << bestvex << std::endl; loop++; if( debug ) { if ( ! this->m_Silent ) std::cout << "wloopdone" << std::endl; } } // opt-loop for( unsigned int i = 0; i < vexlist.size(); i++ ) { if ( ! this->m_Silent ) std::cout << vexlist[i] << ","; } if ( ! this->m_Silent ) std::cout << std::endl; return fabs(this->m_CanonicalCorrelations[0]); } template TRealType antsSCCANObject ::SparseArnoldiSVD_x(unsigned int n_vecs) { this->m_CanonicalCorrelations.set_size(n_vecs); this->m_CanonicalCorrelations.fill(0); if ( ! this->m_Silent ) std::cout << " arnoldi sparse svd " << std::endl; std::vector vexlist; this->m_MatrixP = this->NormalizeMatrix(this->m_OriginalMatrixP); this->m_MatrixQ = this->m_MatrixP; if( this->m_OriginalMatrixR.size() > 0 ) { this->m_MatrixRRt = this->ProjectionMatrix(this->m_OriginalMatrixR); if( this->m_SCCANFormulation == PminusRQ || this->m_SCCANFormulation == PminusRQminusR ) { if ( ! this->m_Silent ) std::cout << " Subtracting nuisance matrix from P matrix " << std::endl; this->m_MatrixP = this->m_MatrixP - (this->m_MatrixRRt * this->m_MatrixP); } } this->m_ClusterSizes.set_size(n_vecs); this->m_ClusterSizes.fill(0); double trace = 0; for( unsigned int i = 0; i < this->m_MatrixP.cols(); i++ ) { trace += inner_product(this->m_MatrixP.get_column(i), this->m_MatrixP.get_column(i) ); } this->m_VariatesP.set_size(this->m_MatrixP.cols(), n_vecs); VariateType myGradients; this->m_SparseVariatesP.set_size(this->m_MatrixP.cols(), n_vecs); this->m_SparseVariatesP.fill(0); myGradients.set_size(this->m_MatrixP.cols(), n_vecs); myGradients.fill(0); for( unsigned int kk = 0; kk < n_vecs; kk++ ) { this->m_VariatesP.set_column(kk, this->InitializeV(this->m_MatrixP) ); } const unsigned int maxloop = this->m_MaximumNumberOfIterations; // Arnoldi Iteration SVD/SPCA unsigned int loop = 0; bool debug = false; double convcrit = 1; RealType fnp = 1; while( loop 1.e-8 ) { /** Compute the gradient estimate */ fnp = this->m_FractionNonZeroP; for( unsigned int k = 0; k < n_vecs; k++ ) { // the old solution VectorType pveck = this->m_VariatesP.get_column(k); pveck = this->m_MatrixP.transpose() * ( this->m_MatrixP * pveck ); RealType hkkm1 = pveck.two_norm(); if( hkkm1 > 0 ) { pveck = pveck / hkkm1; } for( unsigned int j = 0; j < k; j++ ) { VectorType qj = this->m_VariatesP.get_column(j); RealType ip = inner_product(qj, qj); if( ip < this->m_Epsilon ) { ip = 1; } RealType hjk = inner_product(qj, pveck) / ip; pveck = pveck - qj * hjk; } // pveck is now the standard arnoldi pca update to the solution VectorType ppp(pveck); if( fnp < 1 ) { if( this->m_KeepPositiveP ) { this->ConstantProbabilityThreshold( ppp, fnp, this->m_KeepPositiveP ); } else { this->ReSoftThreshold( ppp, fnp, !this->m_KeepPositiveP ); } this->ClusterThresholdVariate( ppp, this->m_MaskImageP, this->m_MinClusterSizeP ); ppp = ppp / ppp.two_norm(); } myGradients.set_column(k, ppp - pveck); myGradients.set_column(k, ppp); if( loop == 0 ) { this->m_VariatesP.set_column(k, pveck); } if( loop == 0 ) { myGradients.set_column(k, pveck); } // this->m_VariatesP.set_column(k, this->m_SparseVariatesP.get_column(k) ); // this->m_SparseVariatesP.set_column(k,pveck); } // kloop /** Update the full solution space */ double recuravg = .9; // /sqrt(sqrt((double)(loop+1))); double recuravg2 = 1 - recuravg; for( unsigned int k = 0; k < n_vecs; k++ ) { VectorType pveck = myGradients.get_column(k); VectorType newsol = this->m_SparseVariatesP.get_column(k) * recuravg2 + pveck * recuravg; this->m_SparseVariatesP.set_column(k, newsol); // /newsol.two_norm() } /** Update the projected solution in sub-space */ for( unsigned int k = 0; k < n_vecs; k++ ) { VectorType pveck = this->m_SparseVariatesP.get_column(k); pveck = this->m_MatrixP.transpose() * ( this->m_MatrixP * pveck ); RealType hkkm1 = pveck.two_norm(); if( hkkm1 > 0 ) { pveck = pveck / hkkm1; } for( unsigned int j = 0; j < k; j++ ) { VectorType qj = this->m_VariatesP.get_column(j); RealType ip = inner_product(qj, qj); if( ip < this->m_Epsilon ) { ip = 1; } RealType hjk = inner_product(qj, pveck) / ip; pveck = pveck - qj * hjk; } if( fnp < 1 ) { if( this->m_KeepPositiveP ) { this->ConstantProbabilityThreshold( pveck, fnp, this->m_KeepPositiveP ); } else { this->ReSoftThreshold( pveck, fnp, !this->m_KeepPositiveP ); } this->ClusterThresholdVariate( pveck, this->m_MaskImageP, this->m_MinClusterSizeP ); pveck = pveck / pveck.two_norm(); } this->m_VariatesP.set_column(k, pveck); } // kloop for update this->m_VariatesQ = this->m_VariatesP; if( debug ) { if ( ! this->m_Silent ) std::cout << " get evecs " << std::endl; } RealType vex = this->ComputeSPCAEigenvalues(n_vecs, trace, true); vexlist.push_back( vex ); this->SortResults(n_vecs); convcrit = ( this->ComputeEnergySlope(vexlist, 5) ); if ( ! this->m_Silent ) std::cout << "Iteration: " << loop << " Eigenval_0: " << this->m_CanonicalCorrelations[0] << " Eigenval_N: " << this->m_CanonicalCorrelations[n_vecs - 1] << " Sparseness: " << fnp << " convergence-criterion: " << convcrit << " vex " << vex << std::endl; loop++; if( debug ) { if ( ! this->m_Silent ) std::cout << "wloopdone" << std::endl; } } // opt-loop for( unsigned int i = 0; i < vexlist.size(); i++ ) { if ( ! this->m_Silent ) std::cout << vexlist[i] << ","; } if ( ! this->m_Silent ) std::cout << std::endl; return fabs(this->m_CanonicalCorrelations[0]); } template TRealType antsSCCANObject ::ConjGrad( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& x_k, typename antsSCCANObject::VectorType b_in, TRealType convcrit, unsigned int maxits) { /** This will use the normal equations */ /** Based on Golub CONJUGATE G R A D I E N T A N D LANCZOS HISTORY * http://www.matematicas.unam.mx/gfgf/cg2010/HISTORY-conjugategradient.pdf */ bool debug = false; // minimize the following error : \| A^T*A * vec_i - b \| + sparseness_penalty MatrixType At = A.transpose(); VectorType b = b_in - b_in.mean(); b = b * A; VectorType r_k = At * ( A * x_k ); r_k = b - r_k; VectorType p_k = r_k; double approxerr = 1.e9; unsigned int ct = 0; VectorType bestsol = x_k; RealType starterr = b.two_norm(); RealType minerr = starterr, deltaminerr = 1, lasterr = starterr * 2; while( deltaminerr > 0 && approxerr > convcrit && ct < maxits ) { RealType alpha_denom = inner_product( p_k, At * ( A * p_k ) ); RealType iprk = inner_product( r_k, r_k ); if( debug ) { if ( ! this->m_Silent ) std::cout << " iprk " << iprk << std::endl; } if( alpha_denom < 1.e-12 ) { alpha_denom = 1; } RealType alpha_k = iprk / alpha_denom; if( debug ) { if ( ! this->m_Silent ) std::cout << " alpha_k " << alpha_k << std::endl; } VectorType x_k1 = x_k + alpha_k * p_k; // this adds the scaled residual to the current solution // this->SparsifyOther( x_k1 ); // this can be used to approximate NMF VectorType r_k1 = b - At * (A * x_k1 ); approxerr = r_k1.two_norm(); if( approxerr < minerr ) { minerr = approxerr; bestsol = ( x_k1 ); } deltaminerr = ( lasterr - approxerr ); if( debug ) { if ( ! this->m_Silent ) std::cout << " ConjGrad " << approxerr << " minerr " << minerr << std::endl; } lasterr = approxerr; // measures the change in the residual --- this is the Fletcher-Reeves form for nonlinear CG // see Table 1.1 \Beta^FR in A SURVEY OF NONLINEAR CONJUGATE GRADIENT METHODS // in this paper's notation d => p, g => r , alpha, beta, x the same , so y = rk1 - rk // measures the change in the residual VectorType yk = r_k1 - r_k; //RealType bknd = inner_product( p_k, yk ); //RealType beta_k = inner_product( ( yk - p_k * 2 * yk.two_norm() / bknd ) , r_k1 / bknd ); // Hager and Zhang RealType beta_k = inner_product( r_k1, r_k1 ) / inner_product( r_k, r_k ); // classic cg VectorType p_k1 = r_k1 + beta_k * p_k; if( debug ) { if ( ! this->m_Silent ) std::cout << " p_k1 " << p_k1.two_norm() << std::endl; } r_k = r_k1; p_k = p_k1; x_k = x_k1; ct++; } x_k = bestsol; // this->SparsifyOther( x_k ); this->m_Intercept = this->ComputeIntercept( A, x_k, b_in ); VectorType soln = A * x_k + this->m_Intercept; return ( soln - b_in).one_norm() / b_in.size(); } template TRealType antsSCCANObject ::EvaluateEnergy( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& x_k, typename antsSCCANObject::VectorType& p_k, typename antsSCCANObject::VectorType& b, TRealType minalph, TRealType lambda ) { VectorType x_k1 = x_k + minalph * p_k; this->SparsifyP( x_k1 ); VectorType r_k1 = ( b - A.transpose() * ( A * x_k1 ) ); RealType e = r_k1.two_norm() + x_k1.two_norm() * lambda; return e; } template TRealType antsSCCANObject ::GoldenSection( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& x_k, typename antsSCCANObject::VectorType& p_k, typename antsSCCANObject::VectorType& bsol, TRealType a, TRealType b, TRealType c, TRealType tau, TRealType lambda ) { if( this->m_GoldenSectionCounter == 0 ) { this->m_GSBestSol = a; } this->m_GoldenSectionCounter++; if( this->m_GoldenSectionCounter > 20 ) { return this->m_GSBestSol; } double phi = (1 + sqrt(5.0) ) / 2; double resphi = 2 - phi; double x; if( c - b > b - a ) { x = b + resphi * (c - b); } else { x = b - resphi * (b - a); } if( fabs(c - a) < tau * ( fabs(b) + fabs(x) ) ) { return (c + a) / 2; } if( this->EvaluateEnergy( A, x_k, p_k, bsol, x, lambda ) < this->EvaluateEnergy( A, x_k, p_k, bsol, b, lambda ) ) { this->m_GSBestSol = x; if( c - b > b - a ) { return GoldenSection( A, x_k, p_k, bsol, b, x, c, tau, lambda ); } else { this->m_GSBestSol = b; return GoldenSection( A, x_k, p_k, bsol, a, x, b, tau, lambda ); } } else { if( c - b > b - a ) { return GoldenSection( A, x_k, p_k, bsol, a, b, x, tau, lambda ); } else { return GoldenSection( A, x_k, p_k, bsol, x, b, c, tau, lambda ); } } } template TRealType antsSCCANObject ::LineSearch( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& x_k, typename antsSCCANObject::VectorType& p_k, typename antsSCCANObject::VectorType& b, TRealType minalph, TRealType maxalph, TRealType lambda ) { bool dogs = true; if( dogs ) { RealType big = maxalph; this->m_GoldenSectionCounter = 0; RealType tau = 0.00001; // sets accuracy , lower => more accurate return this->GoldenSection( A, x_k, p_k, b, 0, big / 2, big, tau, lambda); } // otherwise just search across a line ... RealType minerr = 1.e99; RealType bestalph = 0; RealType step; step = ( maxalph - minalph ) / 10; for( RealType eb = minalph; eb <= maxalph; eb = eb + step ) { RealType e = this->EvaluateEnergy( A, x_k, p_k, b, eb, lambda ); if( e < minerr ) { minerr = e; bestalph = eb; } } return bestalph; } template TRealType antsSCCANObject ::SparseNLConjGrad( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& x_k, typename antsSCCANObject::VectorType b, TRealType convcrit, unsigned int maxits, bool makeprojsparse, unsigned int loorth, unsigned int hiorth ) { bool negate = false; if( b.max_value() <= 0 ) { negate = true; } if( negate ) { b = b * ( -1 ); } bool debug = false; RealType intercept = 0; VectorType r_k = ( b - A.transpose() * ( A * x_k ) ); VectorType p_k = r_k; double approxerr = 1.e22; unsigned int ct = 0; VectorType bestsol = x_k; RealType starterr = r_k.two_norm(); RealType minerr = starterr, deltaminerr = 1; while( deltaminerr > 1.e-4 && minerr > convcrit && ct < maxits ) { RealType alpha_denom = inner_product( p_k, A.transpose() * ( A * p_k ) ); RealType iprk = inner_product( r_k, r_k ); RealType alpha_k = iprk / alpha_denom; if( debug ) { if ( ! this->m_Silent ) std::cout << " alpha_k " << alpha_k << " iprk " << iprk << " alpha_denom " << alpha_denom << std::endl; } // HACK NOT USED RealType stepsize = alpha_k / 100; // HACK NOT USED RealType minalph = stepsize; // HACK NOT USED RealType maxalph = alpha_k * 2; RealType best_alph = alpha_k; // RealType best_alph = this->LineSearch( A, x_k, p_k, b, minalph, maxalph, keeppos ); VectorType x_k1 = x_k + best_alph * p_k; // this adds the scaled residual to the current solution if( debug ) { if ( ! this->m_Silent ) std::cout << " xk12n " << x_k1.two_norm() << " alpha_k " << alpha_k << " pk2n " << p_k.two_norm() << " xk1-min " << x_k1.min_value() << std::endl; } if( hiorth > loorth ) { for( unsigned int wv = loorth; wv < hiorth; wv++ ) { x_k1 = this->Orthogonalize( x_k1, this->m_VariatesP.get_column( wv ) ); } } this->SparsifyP( x_k1 ); if( debug ) { if ( ! this->m_Silent ) std::cout << " xk12n " << x_k1.two_norm() << " alpha_k " << alpha_k << " pk2n " << p_k.two_norm() << std::endl; } VectorType proj = A.transpose() * (A * x_k1 ); VectorType r_k1 = ( b - proj ); if( makeprojsparse ) { this->SparsifyP( r_k1 ); } approxerr = r_k1.two_norm(); RealType newminerr = minerr; if( approxerr < newminerr || ct == 0 ) { newminerr = approxerr; bestsol = ( x_k1 ); this->m_Intercept = intercept; } deltaminerr = ( minerr - newminerr ); if( newminerr < minerr ) { minerr = newminerr; } if( debug ) { if ( ! this->m_Silent ) std::cout << " r_k12n " << r_k1.two_norm() << " r_k2n " << r_k.two_norm() << std::endl; } if( debug ) { if ( ! this->m_Silent ) std::cout << " x_k2n " << x_k.two_norm() << " x_k12n " << x_k1.two_norm() << std::endl; } if( debug ) { if ( ! this->m_Silent ) std::cout << " r_k1 " << minerr << " derr " << deltaminerr << " ba " << best_alph / alpha_k << std::endl; } // measures the change in the residual --- this is the Fletcher-Reeves form for nonlinear CG // see Table 1.1 \Beta^FR in A SURVEY OF NONLINEAR CONJUGATE GRADIENT METHODS // in this paper's notation d => p, g => r , alpha, beta, x the same , so y = rk1 - rk // RealType beta_k = inner_product( r_k1 , r_k1 ) / inner_product( r_k , r_k ); // classic cg VectorType yk = r_k1 - r_k; RealType bknd = inner_product( p_k, yk ); RealType beta_k = inner_product( ( yk - p_k * 2 * yk.two_norm() / bknd ), r_k1 / bknd ); // Hager and Zhang // RealType beta_k = inner_product( r_k1 , r_k1 ) / inner_product( r_k , r_k ); // classic cg VectorType p_k1 = r_k1 + beta_k * p_k; // if ( makeprojsparse ) this->SparsifyP( p_k1, keeppos ); r_k = r_k1; p_k = p_k1; x_k = x_k1; ct++; } x_k = bestsol; if( negate ) { x_k = x_k * ( -1 ); b = b * ( -1 ); } return minerr / A.rows(); } template TRealType antsSCCANObject ::SparseNLPreConjGrad( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& x_k, typename antsSCCANObject::VectorType b, TRealType convcrit, unsigned int maxits ) { if( this->m_PreC.size() <= 0 ) { vnl_diag_matrix prec( A.cols(), 0 ); this->m_PreC = prec; for( unsigned int i = 0; i < A.cols(); i++ ) { this->m_PreC( i, i ) = inner_product( A.get_column( i ), A.get_column( i ) ); if( this->m_PreC( i, i ) > 1.e-8 ) { this->m_PreC( i, i ) = 1 / this->m_PreC( i, i ); } else { this->m_PreC( i, i ) = 0; } } } RealType intercept = 0; VectorType r_k = ( b - A.transpose() * ( A * x_k ) ); VectorType z_k = this->m_PreC * r_k; VectorType p_k = z_k; double approxerr = 1.e22; unsigned int ct = 0; VectorType bestsol = x_k; RealType starterr = 1.e99; RealType minerr = starterr, deltaminerr = 1; while( ( ct < 5 ) || ( ( deltaminerr > 1.e-10 ) && ( minerr > convcrit ) && ( ct < maxits ) ) ) { // RealType spgoal = 100.0 * ( 1 - vnl_math_abs( this->m_FractionNonZeroP ) ); RealType alpha_denom = inner_product( p_k, A.transpose() * ( A * p_k ) ); RealType iprk = inner_product( r_k, z_k ); RealType alpha_k = iprk / alpha_denom; RealType best_alph = alpha_k; // RealType stepsize = alpha_k / 10; // RealType minalph = stepsize; // RealType maxalph = alpha_k * 4; // best_alph = this->LineSearch( A, x_k, p_k, b, minalph, maxalph, false ); // this->CurvatureSparseness( p_k , spgoal * 0.9 , A , b , 50 ); VectorType x_k1 = x_k + best_alph * p_k; this->SparsifyP( x_k1 ); // VectorType x_k2( x_k1 ); // this->CurvatureSparseness( x_k1 , spgoal , 500 ); // x_k1 = x_k2; VectorType proj = A.transpose() * (A * x_k1 ); VectorType r_k1 = ( b - proj ) - x_k1 * 100; // this->CurvatureSparseness( r_k1 , spgoal , A , b , 5 ); approxerr = r_k1.two_norm(); VectorType z_k1 = this->m_PreC * r_k1; RealType newminerr = minerr; if( approxerr < newminerr || ct == 0 ) { newminerr = approxerr; bestsol = ( x_k1 ); this->m_Intercept = intercept; } deltaminerr = ( minerr - newminerr ); if( newminerr < minerr ) { minerr = newminerr; } VectorType yk = r_k1 - r_k; RealType temp = inner_product( z_k, r_k ); if( vnl_math_abs( temp ) < 1.e-9 ) { temp = 1; } RealType beta_k = inner_product( z_k1, r_k1 ) / temp; VectorType p_k1 = z_k1 + beta_k * p_k; r_k = r_k1; p_k = p_k1; x_k = x_k1; z_k = z_k1; // preconditioning ct++; if ( ! this->m_Silent ) std::cout << " approxerr " << approxerr << " ct " << ct << " nz " << this->CountNonZero( x_k ) << " deltaminerr " << deltaminerr << std::endl; } x_k = bestsol; RealType finalerr = minerr / A.rows(); // if ( ! this->m_Silent ) std::cout << " NZ1 " << this->CountNonZero( x_k ) << " err " << finalerr << std::endl; return finalerr; } template TRealType antsSCCANObject ::PowerIteration( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& evec, unsigned int maxits, bool makesparse ) { unsigned int n_vecs = this->m_VariatesP.cols(); for( unsigned int powerits = 0; powerits < maxits; powerits++ ) { if( evec.two_norm() > 0 ) { evec = evec / evec.two_norm(); } evec = ( A.transpose() ) * ( A * evec ); // power iteration for( unsigned int orth = 0; orth < n_vecs; orth++ ) { evec = this->Orthogonalize( evec, this->m_VariatesP.get_column( orth ) ); } } // (void) this->CurvatureSparseness( evec , 95 , 10 ); if( makesparse ) { this->SparsifyP( evec ); } if( evec.two_norm() > 0 ) { evec = evec / evec.two_norm(); } return inner_product( A * evec, A * evec ); } template TRealType antsSCCANObject ::IHTPowerIteration( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& evecin, // typename antsSCCANObject::VectorType& y, unsigned int maxits, unsigned int maxorth ) { /** This computes a hard-thresholded gradient descent on the eigenvector criterion. max x over x^t A^t A x s.t. x^t x = 1 success of the optimization is measured by rayleigh quotient. derivative is: d.dx ( x^t A^t A x ) = A^t A x , x \leftarrow x / \| x \| we use a conjugate gradient version of this optimization. */ A = A - A.min_value(); VectorType evec = evecin; unsigned int maxcoltoorth = ( unsigned int ) ( 1.0 / vnl_math_abs( this->m_FractionNonZeroP ) ) - 1; if( evecin.two_norm() == 0 ) { evec = this->InitializeV( this->m_MatrixP, false ); } VectorType proj = ( A * evecin ); VectorType lastgrad = evecin; RealType rayquo = 0; RealType denom = inner_product( evecin, evecin ); if( denom > 0 ) { rayquo = inner_product( proj, proj ) / denom; } else if ( ! this->m_Silent ) std::cout << "Denom < 0: probable trouble." << std::endl; RealType bestrayquo = 0; MatrixType At = A.transpose(); unsigned int powerits = 0; bool conjgrad = true; VectorType bestevec = evecin; RealType relfac = 1.0; VectorType di; while( ( ( rayquo > bestrayquo ) && ( powerits < maxits ) ) || powerits < 2 ) // while ( ( relfac > 1.e-4 ) && ( powerits < maxits ) ) { VectorType nvec = At * proj; RealType gamma = 0.1; // nvec = this->SpatiallySmoothVector( nvec, this->m_MaskImageP ); if ( powerits == 0 ) di = nvec; if( ( lastgrad.two_norm() > 0 ) && ( conjgrad ) ) { gamma = inner_product( nvec, nvec ) / inner_product( lastgrad, lastgrad ); } lastgrad = nvec; VectorType diplus1 = nvec + di * gamma; evec = evec + diplus1 * relfac ; unsigned int startingm = 0; if ( maxorth > maxcoltoorth ) startingm = maxorth - maxcoltoorth; for( unsigned int orth = startingm; orth < maxorth; orth++ ) { VectorType zv = this->m_VariatesP.get_column( orth ); evec = this->Orthogonalize( evec, zv ); if ( this->m_Covering == 1 ) this->ZeroProduct( evec, zv ); /** alternative --- orthogonalize in n-space VectorType v = A * this->m_VariatesP.get_column( orth ); RealType ip1 = inner_product( A * evec, v ); RealType ip2 = inner_product( v, v ); // evec = evec - this->m_VariatesP.get_column( orth ) * ip1 / ip2;*/ } this->SparsifyP( evec ); if( evec.two_norm() > 0 ) { evec = evec / evec.two_norm(); } proj = ( A * evec ); denom = inner_product( evec, evec ); if( denom > 0 ) { rayquo = inner_product( proj, proj ) / denom; // - gradvec.two_norm() / gradvec.size() * 1.e2 ; } if( rayquo > bestrayquo ) { bestevec = evec; bestrayquo = rayquo; } else { relfac *= 0.9; evec = bestevec; } powerits++; } if ( ! this->m_Silent ) std::cout << "rayleigh-quotient: " << bestrayquo << " in " << powerits << " num " << maxorth << " fnz " << this->CountNonZero( evec ) << std::endl; evecin = bestevec; return bestrayquo; } template TRealType antsSCCANObject ::IHTPowerIterationU( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& evecin, unsigned int maxits, unsigned int maxorth ) { unsigned int maxcoltoorth = ( unsigned int ) ( 1.0 / vnl_math_abs( this->m_FractionNonZeroP ) ) - 1; VectorType proj = evecin; RealType rayquo = 0; RealType bestrayquo = 0; MatrixType At = A.transpose(); VectorType lastgrad = At * proj; VectorType evec = At * proj; RealType denom = inner_product( evec, evec ); if( denom > 0 ) { rayquo = inner_product( proj, proj ) / denom; } else if ( ! this->m_Silent ) std::cout << "Denom < 0: probable trouble." << std::endl; unsigned int powerits = 0; bool conjgrad = true; VectorType bestevec = evecin; RealType relfac = 1.0; VectorType di; while( ( ( rayquo > bestrayquo ) && ( powerits < maxits ) ) || powerits < 2 ) { VectorType nvec = At * proj; RealType gamma = 0.1; if ( powerits == 0 ) di = nvec; if( ( lastgrad.two_norm() > 0 ) && ( conjgrad ) ) { gamma = inner_product( nvec, nvec ) / inner_product( lastgrad, lastgrad ); } lastgrad = nvec; VectorType diplus1 = nvec + di * gamma; evec = evec + diplus1 * relfac ; unsigned int startingm = 0; if ( maxorth > maxcoltoorth ) startingm = maxorth - maxcoltoorth; for( unsigned int orth = startingm; orth < maxorth; orth++ ) { VectorType zv = this->m_VariatesP.get_column( orth ); evec = this->Orthogonalize( evec, zv ); if ( this->m_Covering == 1 ) this->ZeroProduct( evec, zv ); } this->SparsifyP( evec ); if( evec.two_norm() > 0 ) { evec = evec / evec.two_norm(); } proj = ( A * evec ); denom = inner_product( evec, evec ); if( denom > 0 ) { rayquo = inner_product( proj, proj ) / denom; // - gradvec.two_norm() / gradvec.size() * 1.e2 ; } powerits++; if( rayquo > bestrayquo ) { bestevec = evec; bestrayquo = rayquo; } else { relfac *= 0.9; evec = bestevec; } } evecin = bestevec; if ( ! this->m_Silent ) std::cout << "rayleigh-quotient: " << bestrayquo << " in " << powerits << " num " << maxorth << " fnz " << this->m_FractionNonZeroP << std::endl; return bestrayquo; } template TRealType antsSCCANObject ::RidgeCCA(unsigned int n_vecs) { RealType taup = vnl_math_abs( this->m_FractionNonZeroP ); RealType tauq = vnl_math_abs( this->m_FractionNonZeroQ ); if ( ! this->m_Silent ) std::cout << " ridge cca : taup " << taup << " tauq " << tauq << std::endl; this->m_CanonicalCorrelations.set_size( n_vecs ); this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP, false ); this->m_MatrixQ = this->NormalizeMatrix( this->m_OriginalMatrixQ, false ); this->m_VariatesP.set_size( this->m_MatrixP.cols(), n_vecs ); this->m_VariatesQ.set_size( this->m_MatrixQ.cols(), n_vecs ); if ( ! this->m_Silent ) std::cout << " begin " << std::endl; unsigned int maxloop = this->m_MaximumNumberOfIterations; if( maxloop < 25 ) { maxloop = 25; } RealType totalcorr = 0; RealType bestcorr = 0; unsigned int bestseed = 1; for( unsigned int seeder = 1; seeder < 100; seeder++ ) { totalcorr = this->InitializeSCCA( n_vecs, seeder ); if( totalcorr > bestcorr ) { bestseed = seeder; bestcorr = totalcorr; } totalcorr = 0; } if ( ! this->m_Silent ) std::cout << " Best initial corr " << bestcorr << std::endl; this->InitializeSCCA( n_vecs, bestseed ); unsigned int loop = 0; bool energyincreases = true; RealType energy = 0; RealType lastenergy = 0; while( ( ( loop < maxloop ) && ( energyincreases ) ) || loop < 3 ) { for( unsigned int k = 0; k < n_vecs; k++ ) { VectorType ptemp = this->m_VariatesP.get_column(k); VectorType qtemp = this->m_VariatesQ.get_column(k); VectorType pveck = this->m_MatrixQ * qtemp; VectorType qveck = this->m_MatrixP * ptemp; pveck = pveck * this->m_MatrixP; qveck = qveck * this->m_MatrixQ; for( unsigned int j = 0; j < k; j++ ) { VectorType qj = this->m_VariatesP.get_column(j); // this->ZeroProduct( pveck, qj ); pveck = this->Orthogonalize( pveck, qj ); qj = this->m_VariatesQ.get_column(j); // this->ZeroProduct( qveck, qj ); qveck = this->Orthogonalize( qveck, qj ); } pveck = pveck / pveck.two_norm(); qveck = qveck / qveck.two_norm(); pveck = ptemp + pveck * 0.01; qveck = qtemp + qveck * 0.01; this->m_VariatesP.set_column( k, pveck ); this->m_VariatesQ.set_column( k, qveck ); this->NormalizeWeightsByCovariance( k, taup, tauq ); VectorType proj1 = this->m_MatrixP * this->m_VariatesP.get_column( k ); VectorType proj2 = this->m_MatrixQ * this->m_VariatesQ.get_column( k ); this->m_CanonicalCorrelations[k] = this->PearsonCorr( proj1, proj2 ); } this->SortResults( n_vecs ); lastenergy = energy; energy = this->m_CanonicalCorrelations.one_norm() / n_vecs; if ( ! m_Silent ) { if ( ! this->m_Silent ) std::cout << " Loop " << loop << " Corrs : " << this->m_CanonicalCorrelations << " CorrMean : " << energy << std::endl; } if( energy < lastenergy ) { energyincreases = false; } else { energyincreases = true; } loop++; } // outer loop double ccasum = 0; for( unsigned int i = 0; i < this->m_CanonicalCorrelations.size(); i++ ) { ccasum += fabs(this->m_CanonicalCorrelations[i]); } if( n_vecs > 1 ) { return ccasum; } else { return fabs(this->m_CanonicalCorrelations[0]); } return this->m_CanonicalCorrelations[0]; } template TRealType antsSCCANObject ::RidgeRegression( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& x_k, typename antsSCCANObject::VectorType b, TRealType lambda, unsigned int maxits, bool makesparse ) { MatrixType At = A.transpose(); VectorType r_k = At * ( A * x_k ); r_k = b - r_k - x_k * lambda; VectorType p_k = r_k; double approxerr = 1.e9; unsigned int ct = 0; VectorType bestsol = x_k; RealType starterr = 1.e99; RealType minerr = starterr, deltaminerr = 1, lasterr = starterr * 2; while( deltaminerr > 1.e-4 && approxerr > 0 && ct < maxits ) { RealType alpha_denom = inner_product( p_k, At * ( A * p_k ) ); RealType iprk = inner_product( r_k, r_k ); if( alpha_denom < 1.e-12 ) { alpha_denom = 1; } RealType alpha_k = iprk / alpha_denom; VectorType x_k1 = x_k + alpha_k * p_k; // this adds the scaled residual to the current solution x_k1 = x_k + alpha_k * p_k * .2; // this adds the scaled residual to the current solution VectorType r_k1 = b - At * (A * x_k1 ) - x_k1 * lambda; approxerr = (b - At * (A * x_k1 ) ).two_norm() + x_k1.two_norm() * lambda; if( approxerr < minerr ) { minerr = approxerr; bestsol = ( x_k1 ); } deltaminerr = ( lasterr - approxerr ); lasterr = approxerr; RealType bkdenom = inner_product( r_k, r_k ); if( bkdenom < 1.e-12 ) { bkdenom = 1; } RealType beta_k = inner_product( r_k1, r_k1 ) / bkdenom; // classic cg VectorType p_k1 = r_k1 + beta_k * p_k; r_k = r_k1; p_k = p_k1; x_k = x_k1; ct++; } x_k = bestsol; if( makesparse ) { this->SparsifyP( x_k ); } return minerr; } template TRealType antsSCCANObject ::CGSPCA(unsigned int n_vecs ) { if ( ! this->m_Silent ) std::cout << " conjugate gradient sparse-pca approx to pca " << std::endl; std::vector vexlist; this->m_MatrixP = this->NormalizeMatrix(this->m_OriginalMatrixP); this->m_MatrixQ = this->m_MatrixP; if( this->m_OriginalMatrixR.size() > 0 ) { this->m_MatrixRRt = this->ProjectionMatrix( this->m_OriginalMatrixR ); this->m_MatrixP = this->m_MatrixP - ( this->m_MatrixRRt * this->m_MatrixP ); } MatrixType nspaceevecs = this->GetCovMatEigenvectors( this->m_MatrixP ); VectorType nspaceevals = this->m_Eigenvalues; this->BasicSVD(); MatrixType variatesInit = this->m_VariatesP; // unsigned int n_vecs = this->m_VariatesP.cols(); VectorType evalInit( n_vecs ); unsigned int evct = 0; for( unsigned int i = 0; i < this->m_Eigenvalues.size(); i++ ) { if( evct < n_vecs ) { evalInit[evct] = this->m_Eigenvalues( i ); } evct++; if( evct < n_vecs ) { evalInit[evct] = this->m_Eigenvalues( i ); } evct++; } unsigned int repspervec = this->m_MaximumNumberOfIterations; this->m_VariatesP.set_size( this->m_MatrixP.cols(), repspervec * n_vecs ); this->m_VariatesP.fill( 0 ); RealType fnp = this->m_FractionNonZeroP; for( unsigned int colind = 0; colind < n_vecs; colind++ ) { if ( ! this->m_Silent ) std::cout << " colind " << colind << std::endl; for( unsigned int whichevec = 0; whichevec < repspervec; whichevec++ ) { VectorType b = this->m_Eigenvectors.get_column( colind ); unsigned int baseind = colind * repspervec; // unsigned int lastbaseind = colind * repspervec; // if( colind % 2 == 1 ) // { // lastbaseind = ( colind - 1 ) * repspervec; // } unsigned int locind = baseind + whichevec; VectorType x_k = this->InitializeV( this->m_MatrixP, false ); this->m_FractionNonZeroP = fnp + fnp * whichevec; RealType minerr1 = this->SparseNLConjGrad( this->m_MatrixP, x_k, b, 1.e-1, 10, true ); // , 0 , colind ); // // , baseind , // locind bool keepgoing = true; unsigned int kgct = 0; while( keepgoing && kgct < 100 ) { VectorType x_k2 = x_k; RealType minerr2 = this->SparseNLConjGrad( this->m_MatrixP, x_k2, b, 1.e-1, 10, true ); // , 0 , colind // ); keepgoing = false; // if ( fabs( minerr2 - minerr1 ) < 1.e-9 ) { x_k = x_k2; keepgoing = true; minerr1 = minerr2 ; } if( minerr2 < minerr1 ) { x_k = x_k2; keepgoing = true; minerr1 = minerr2; } // if ( ! this->m_Silent ) std::cout << " minerr1 " << minerr1 << " wev " << whichevec << std::endl; kgct++; } for( unsigned int j = baseind; j < locind; j++ ) { VectorType temp = this->m_VariatesP.get_column( j ); this->ZeroProduct( x_k, temp ); } this->m_VariatesP.set_column( locind, x_k ); } // repspervec } // colind RealType reconstruction_error = this->ReconstructionError( this->m_MatrixP, this->m_VariatesP ); if ( ! this->m_Silent ) std::cout << "%Var " << reconstruction_error << std::endl; for( unsigned int k = 0; k < this->m_VariatesP.cols(); k++ ) { VectorType v = this->m_VariatesP.get_column( k ); this->m_VariatesP.set_column( k, v / v.sum() ); } this->m_Eigenvalues = this->m_CanonicalCorrelations = evalInit; return this->m_CanonicalCorrelations[0]; } template void antsSCCANObject ::DeleteRow( typename antsSCCANObject::MatrixType& p_in, unsigned int row ) { unsigned int nrows = p_in.rows() - 1; if( row >= nrows ) { nrows = p_in.rows(); } MatrixType p( nrows, p_in.columns() ); unsigned int rowct = 0; for( long i = 0; i < p.rows(); ++i ) // loop over rows { if( i != row ) { p.set_row( rowct, p_in.get_row( i ) ); rowct++; } } p_in = p; return; } template void antsSCCANObject ::LASSO_alg( typename antsSCCANObject::MatrixType& X, typename antsSCCANObject::VectorType& y, typename antsSCCANObject::VectorType& beta_lasso, TRealType gamma, unsigned int maxits ) { VectorType ypred; VectorType yresid; RealType besterr = 1.e20; RealType curerr = besterr; unsigned int added = 0; for( unsigned int its = 0; its < maxits; its++ ) { added = 0; ypred = X * beta_lasso; for( unsigned int j = 0; j < beta_lasso.size(); j++ ) { VectorType xj = X.get_column( j ); ypred = ypred - xj * beta_lasso( j ); RealType regbeta = this->SimpleRegression( y, ypred ); yresid = y - ( ypred * regbeta + this->m_Intercept ); RealType beta = beta_lasso( j ); for( unsigned int i = 0; i < xj.size(); i++ ) { beta += ( xj( i ) * yresid( i ) ); } beta = this->LASSOSoft( beta, gamma ); ypred = ypred + xj * beta; regbeta = this->SimpleRegression( y, ypred ); yresid = y - ( ypred * regbeta + this->m_Intercept ); RealType sparseness = gamma * ( beta_lasso.one_norm() + fabs( beta ) - fabs( beta_lasso( j ) ) ); curerr = yresid.two_norm() * 0.5 + sparseness; /** Eq 7 Friedman pathwise */ // if ( ! this->m_Silent ) std::cout << "Err: " << yresid.one_norm() * n << " sp: " << sparseness << " cur " << curerr << " best " // << besterr << std::endl; if( curerr < besterr ) { beta_lasso( j ) = beta; besterr = curerr; } else { ypred = ypred - xj * beta; ypred = ypred + xj * beta_lasso( j ); } if( fabs( beta_lasso( j ) ) > 0 ) { added++; } if( j % 1000 == 0 && false ) { if ( ! this->m_Silent ) std::cout << "progress: " << 100 * j / X.cols() << " lasso-loop " << its << " : " << besterr << " num_vars_added " << added << std::endl; } } // if ( ! this->m_Silent ) std::cout << " lasso-iteration: " << its << " minerr: " << besterr << " num_vars_added: " << added // << std::endl; } // iterations } template TRealType antsSCCANObject ::LASSO_Cross() { RealType gamma = this->m_FractionNonZeroP; if ( ! this->m_Silent ) std::cout << " Cross-validation - LASSO " << std::endl; this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP ); this->m_MatrixR = this->NormalizeMatrix( this->m_OriginalMatrixR ); if( this->m_OriginalMatrixR.size() <= 0 ) { if ( ! this->m_Silent ) std::cout << " You need to define a reference matrix " << std::endl; std::exception(); } VectorType predictions( this->m_MatrixR.rows(), 0 ); unsigned int foldnum = this->m_MaximumNumberOfIterations; if( foldnum <= 1 ) { foldnum = 1; } VectorType folds( this->m_MatrixP.rows(), 0 ); for( unsigned int f = 0; f < this->m_MatrixP.rows(); f++ ) { folds( f ) = f % foldnum; } // Variates holds the average over all folds this->m_VariatesP( this->m_MatrixP.cols(), 1 ); this->m_VariatesP.fill( 0 ); this->m_VariatesQ = this->m_VariatesP; for( unsigned int fold = 0; fold < foldnum; fold++ ) { unsigned int foldct = 0; for( unsigned int f = 0; f < this->m_MatrixP.rows(); f++ ) { if( folds( f ) == fold ) { foldct++; } } MatrixType p_leave_out( foldct, this->m_MatrixP.cols(), 0 ); MatrixType r_leave_out( foldct, this->m_MatrixR.cols(), 0 ); unsigned int leftoutsize = this->m_MatrixP.rows() - foldct; MatrixType matrixP( leftoutsize, this->m_MatrixP.cols() ); MatrixType matrixR( leftoutsize, this->m_MatrixR.cols() ); unsigned int leave_out = 0; unsigned int dont_leave_out = 0; for( unsigned int f = 0; f < this->m_MatrixP.rows(); f++ ) { if( folds( f ) == fold ) { p_leave_out.set_row( leave_out, this->m_MatrixP.get_row( f ) ); r_leave_out.set_row( leave_out, this->m_OriginalMatrixR.get_row( f ) ); leave_out++; } else { matrixP.set_row( dont_leave_out, this->m_MatrixP.get_row( f ) ); matrixR.set_row( dont_leave_out, this->m_MatrixR.get_row( f ) ); dont_leave_out++; } } if( foldnum <= 1 ) { matrixP = p_leave_out; matrixR = r_leave_out; } VectorType rout = r_leave_out.get_column( 0 ); VectorType y = matrixR.get_column( 0 ); VectorType yreal = matrixR.get_column( 0 ); unsigned int fleave_out = 0; for( unsigned int f = 0; f < this->m_MatrixP.rows(); f++ ) { if( folds( f ) != fold ) { yreal( fleave_out ) = this->m_OriginalMatrixR( f, 0 ); fleave_out++; } } /** train the lasso + regression model */ VectorType beta_lasso( matrixP.cols(), 0 ); this->LASSO_alg( matrixP, y, beta_lasso, gamma, 2 ); VectorType ypred = matrixP * beta_lasso; RealType regbeta = this->SimpleRegression( yreal, ypred ); RealType intercept = yreal.mean() - ypred.mean() * regbeta; /** test the lasso + regression model */ VectorType localpredictions = p_leave_out * beta_lasso; localpredictions = localpredictions * regbeta + intercept; fleave_out = 0; for( unsigned int f = 0; f < this->m_MatrixP.rows(); f++ ) { if( folds( f ) == fold ) { predictions( f ) = localpredictions( fleave_out ); fleave_out++; } } this->m_VariatesP.set_column( 0, this->m_VariatesP.get_column( 0 ) + beta_lasso * regbeta ); if ( ! this->m_Silent ) std::cout << " fold " << fold << " mean-abs-error: " << ( localpredictions - rout ).one_norm() / foldct << std::endl; } RealType regbeta = this->SimpleRegression( predictions, this->m_OriginalMatrixR.get_column( 0 ) ); RealType intercept = this->m_OriginalMatrixR.get_column( 0 ).mean() - predictions.mean() * regbeta; RealType predictionerror = ( predictions * regbeta + intercept - this->m_OriginalMatrixR.get_column( 0 ) ).one_norm() / this->m_OriginalMatrixR.rows(); if ( ! this->m_Silent ) std::cout << " overall-mean-abs-prediction-error: " << predictionerror << std::endl; this->m_Eigenvalues = predictions * regbeta + intercept; this->m_CanonicalCorrelations = predictions * regbeta + intercept; return predictionerror; } template TRealType antsSCCANObject ::LASSO( unsigned int n_vecs ) { /** Based on Friedman's pathwise coordinate optimization */ RealType gamma = this->m_FractionNonZeroP; if ( ! this->m_Silent ) std::cout << " Pathwise LASSO : penalty = " << gamma << " veclimit = " << n_vecs << std::endl; this->m_CanonicalCorrelations.set_size( 1 ); this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP ); this->m_MatrixR = this->NormalizeMatrix( this->m_OriginalMatrixR ); if( this->m_OriginalMatrixR.size() <= 0 ) { if ( ! this->m_Silent ) std::cout << " You need to define a reference matrix " << std::endl; std::exception(); } VectorType y = this->m_MatrixR.get_column( 0 ); RealType n = 1.0 / static_cast( y.size() ); VectorType beta_lasso( this->m_MatrixP.cols(), 0 ); for( unsigned int i = 0; i < 1; i++ ) { if ( ! this->m_Silent ) std::cout << i << std::endl; this->LASSO_alg( this->m_MatrixP, y, beta_lasso, gamma, this->m_MaximumNumberOfIterations ); // RealType spgoal = ( RealType ) n_vecs ; // this->CurvatureSparseness( beta_lasso , spgoal , 100 ); } /** post-process the result to select the largest n_vecs entries std::vector post( beta_lasso.size() , 0 ); for( unsigned long j = 0; j < beta_lasso.size(); ++j ) post[ j ] = fabs( beta_lasso( j ) ); // sort and reindex the values sort( post.begin(), post.end() ); RealType thresh = 0; for( unsigned long j = 0; ( ( j < beta_lasso.size() ) && ( j < n_vecs ) ); ++j ) { thresh = post[j]; } unsigned int added = 0; if ( thresh > 0 ) { added = 0; for( unsigned long j = 0; j < beta_lasso.size(); ++j ) { if ( fabs( beta_lasso( j ) ) < thresh ) beta_lasso( j ) = 0; else added++; } } */ /** now get the original y value and result to find prediction error in true units */ y = this->m_OriginalMatrixR.get_column( 0 ); this->m_Intercept = this->ComputeIntercept( this->m_MatrixP, beta_lasso, y ); VectorType ypred = this->m_MatrixP * beta_lasso + this->m_Intercept; VectorType yresid = y - ( ypred ); this->m_CanonicalCorrelations( 0 ) = yresid.one_norm() * n; this->m_VariatesP.set_size( this->m_MatrixP.cols(), 1 ); this->m_VariatesP.set_column( 0, beta_lasso ); if ( ! this->m_Silent ) std::cout << "final error " << this->m_CanonicalCorrelations( 0 ) << std::endl; return this->m_CanonicalCorrelations( 0 ); } template TRealType antsSCCANObject ::SparseConjGradRidgeRegression( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& x_k, typename antsSCCANObject::VectorType b_in, TRealType convcrit, unsigned int maxits, bool makesparse ) { x_k.fill( 0 ); RealType spgoal = 100.0 * ( 1 - vnl_math_abs( this->m_FractionNonZeroP ) ); RealType lambda = 1.e2; if ( ! this->m_Silent ) std::cout << "SparseConjGradRidgeRegression: lambda " << lambda << " Soft? " << this->m_UseL1 << " fnp " << this->m_FractionNonZeroP << std::endl; MatrixType At = A.transpose(); VectorType b( b_in ); /** The data term b = b_n * A may be noisy. here we optionally add a penalty * \kappa \| \nabla b \|^2 * which smooths the data term and eases optimization */ this->CurvatureSparseness( b, spgoal, 10 , this->m_MaskImageP ); VectorType r_k = At * ( A * x_k ); /** this is the gradient of the objective : * \| b - A^T A x \| + \lambda \| x \|^2 */ r_k = b - r_k - x_k * lambda; VectorType p_k = r_k; double approxerr = 1.e9; unsigned int ct = 0; VectorType bestsol = x_k; RealType starterr = 1.e99; RealType minerr = starterr, deltaminerr = 1, lasterr = starterr * 2; if( makesparse ) { this->SparsifyP( x_k ); } /** Here, should be more careful to make sure energy improves, need better command line interface for specifying objective */ while( ( ( deltaminerr > 0.1 ) && ( approxerr > convcrit ) && ( ct < maxits ) ) || ( ct < 3 ) ) { /** standard conjugate gradient defintion of \alpha_k and p_k * see http://www.matematicas.unam.mx/gfgf/cg2010/HISTORY-conjugategradient.pdf */ RealType alpha_denom = inner_product( p_k, At * ( A * p_k ) ); RealType iprk = inner_product( r_k, r_k ); if( alpha_denom < 1.e-12 ) { alpha_denom = 1; } /** A line search on the objective term , given search direction p_k * see http://en.wikipedia.org/wiki/Nonlinear_conjugate_gradient_method */ RealType alpha_k = iprk / alpha_denom; VectorType x_k1 = x_k + alpha_k * p_k; /** To be careful (not necessary) orthogonalize with previous solutions for( unsigned int col = 0; col < this->m_VariatesP.cols(); col++ ) { x_k1 = this->Orthogonalize( x_k1, this->m_VariatesP.get_column( col ) ); } */ // VectorType kvec( x_k1 ); // RealType gradnorm = this->ComputeVectorGradMag( x_k1 , this->m_MaskImageP ).two_norm() ; this->CurvatureSparseness( x_k1, spgoal, 5, this->m_MaskImageP ); // VectorType gradvec = this->ComputeVectorLaplacian( x_k1 , this->m_MaskImageP ); if( makesparse ) { /** use a hard thresholding on the update to enforce l_0 */ this->SparsifyP( x_k1 ); } RealType dataterm = (b - At * (A * x_k1 ) ).two_norm(); // VectorType r_k1 = b - At * (A * x_k1 ) + gradvec * lambda; // approxerr = dataterm + gradnorm * lambda; /** compute the new gradient term */ VectorType r_k1 = b - At * (A * x_k1 ) - x_k1 * lambda; // ridge approxerr = dataterm + x_k1.two_norm() * lambda; if( approxerr < minerr ) { minerr = approxerr; bestsol = ( x_k1 ); } if( approxerr > minerr || ct % 20 == 0 ) { this->m_GoldenSectionCounter = 0; RealType tau = 0.00001; RealType loa = alpha_k * 0.25; RealType hia = alpha_k * 1.e10; RealType mida = ( loa + hia ) * 0.5; alpha_k = this->GoldenSection( A, x_k, p_k, b, loa, mida, hia, tau, lambda); x_k1 = x_k + alpha_k * p_k; this->CurvatureSparseness( x_k1, spgoal, 5, this->m_MaskImageP ); this->SparsifyP( x_k1 ); dataterm = (b - At * (A * x_k1 ) ).two_norm(); r_k1 = b - At * (A * x_k1 ) - x_k1 * lambda; // ridge approxerr = dataterm + x_k1.two_norm() * lambda; if( approxerr < minerr ) { minerr = approxerr; bestsol = ( x_k1 ); } } deltaminerr = ( lasterr - approxerr ); if ( ! this->m_Silent ) std::cout << " SparseRidgeConjGrad " << approxerr << " deltaminerr " << deltaminerr << " ct " << ct << " dataterm " << dataterm << std::endl; lasterr = approxerr; /** compute the new conjugate gradient term using standard approaches */ RealType bkdenom = inner_product( r_k, r_k ); if( bkdenom < 1.e-12 ) { bkdenom = 1; } RealType beta_k = inner_product( r_k1, r_k1 ) / bkdenom; // classic cg VectorType p_k1 = r_k1 + beta_k * p_k; r_k = r_k1; p_k = p_k1; x_k = x_k1; ct++; } return minerr; } template TRealType antsSCCANObject ::IHTRegression( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& x_k, typename antsSCCANObject::VectorType b_in, TRealType itkNotUsed( convcrit ), unsigned int maxits, TRealType mu, bool isp, bool verbose ) { if( b_in.size() != x_k.size() ) { if ( ! this->m_Silent ) std::cout << " ITHRegression error --- b_in is the wrong size " << std::endl; return 1.e9; } vnl_diag_matrix mudiag( A.cols(), mu ); RealType lambda = 0.e2; MatrixType At = A.transpose(); VectorType b( b_in ); VectorType p_k = At * ( A * x_k ); p_k = b - p_k - x_k * lambda; VectorType curdir = p_k; VectorType lastdir = p_k; double approxerr = 1.e9; unsigned int ct = 0; VectorType bestsol = x_k; RealType starterr = 1.e99; RealType minerr = starterr, deltaminerr = 1, lasterr = starterr * 2; if( isp ) { this->SparsifyP( x_k ); } else { this->SparsifyQ( x_k ); } RealType alpha_k = 1.e-4 / A.frobenius_norm(); if( verbose ) { if ( ! this->m_Silent ) std::cout << " alpha_k " << alpha_k << " xknorm " << x_k.two_norm() << " Anorm " << A.frobenius_norm() << std::endl; } RealType gamma = 0; bool conjgrad = true; VectorType lastgrad = x_k; // while( ( ( deltaminerr > 0.1 ) && ( approxerr > convcrit ) && ( ct < maxits ) ) // || ( ct < 4 ) ) while( ( ct < maxits ) && alpha_k > 1.e-14 ) { if( ( lastgrad.two_norm() > 0 ) && ( conjgrad ) && ( ct > 2 ) ) { gamma = inner_product( p_k, p_k ) / inner_product( lastgrad, lastgrad ); } lastdir = curdir; curdir = ( p_k + gamma * lastdir ); if( !conjgrad ) { curdir = p_k; } VectorType x_k1 = x_k + alpha_k * curdir; for ( unsigned int vp = 0; vp < this->m_VariatesP.cols(); vp++ ) x_k1 = this->Orthogonalize( x_k1, this->m_VariatesP.get_column( vp ) ); VectorType gradvec; if( isp ) { // x_k1 = this->SpatiallySmoothVector( x_k1, this->m_MaskImageP ); this->SparsifyP( x_k1 ); gradvec = this->ComputeVectorLaplacian( x_k1, this->m_MaskImageP ); } else { // x_k1 = this->SpatiallySmoothVector( x_k1, this->m_MaskImageQ ); this->SparsifyQ( x_k1 ); gradvec = this->ComputeVectorLaplacian( x_k1, this->m_MaskImageQ ); } VectorType update = At * (A * x_k1 ) - mudiag * x_k1; RealType dataterm = (b - update ).two_norm(); lastgrad = p_k; p_k = b - update - x_k1 * lambda + gradvec * 0.e4; // ridge approxerr = dataterm + x_k1.two_norm() * lambda; if( approxerr < minerr ) { minerr = approxerr; bestsol = ( x_k1 ); } else { alpha_k *= 0.5; } deltaminerr = ( lasterr - approxerr ); if( verbose ) { if ( ! this->m_Silent ) std::cout << " IHTRegression " << approxerr << " deltaminerr " << deltaminerr << " ct " << ct << " dataterm " << dataterm << " alpha_k " << alpha_k << std::endl; } lasterr = approxerr; x_k = x_k1; ct++; } return minerr; } template TRealType antsSCCANObject ::MatchingPursuit( typename antsSCCANObject::MatrixType& A, typename antsSCCANObject::VectorType& x_k, TRealType convcrit, unsigned int maxits ) { double approxerr = 1.e9; unsigned int ct = 0; VectorType bestsol = x_k; RealType starterr = 1.e99; RealType minerr = starterr, deltaminerr = 1, lasterr = starterr * 2; this->m_Indicator.set_size( A.cols() ); this->m_Indicator.fill( 0 ); MatrixType Ai; VectorType resid = this->m_OriginalB; while( ( ( deltaminerr > 1.e-3 ) && ( approxerr > convcrit ) && ( ct < maxits ) ) || ( ct < 10 ) ) { VectorType corrb( x_k ); for( unsigned int mm = 0; mm < A.cols(); mm++ ) { corrb( mm ) = vnl_math_abs( this->PearsonCorr( resid, A.get_column( mm ) ) ); } this->ReSoftThreshold( corrb, vnl_math_abs( this->m_FractionNonZeroP ), true ); /** this debiases the solution */ unsigned int nzct = 0; for( unsigned int mm = 0; mm < A.cols(); mm++ ) { if( vnl_math_abs( corrb( mm ) ) > 0 ) { this->m_Indicator( mm, mm ) = 1; } } this->GetSubMatrix( A, Ai ); VectorType lmsolv = this->InitializeV( Ai, true ); (void) this->ConjGrad( Ai, lmsolv, resid, 0, 10000 ); x_k.fill( 0 ); for( unsigned int mm = 0; mm < A.cols(); mm++ ) { if( this->m_Indicator( mm, mm ) > 0 ) { x_k( mm ) = lmsolv( nzct ); nzct++; } } this->ReSoftThreshold( x_k, vnl_math_abs( this->m_FractionNonZeroP ), true ); this->ClusterThresholdVariate( x_k, this->m_MaskImageP, this->m_MinClusterSizeP ); this->m_Intercept = this->ComputeIntercept( A, x_k, this->m_OriginalB ); VectorType soln = A * x_k + this->m_Intercept; resid = ( this->m_OriginalB - soln ); approxerr = resid.one_norm(); if( approxerr < minerr ) { minerr = approxerr; bestsol = ( x_k ); } deltaminerr = ( lasterr - approxerr ); if ( ! this->m_Silent ) std::cout << " MatchingPursuit " << approxerr << " deltaminerr " << deltaminerr << " ct " << ct << " diag " << this->m_Indicator.diagonal().sum() << std::endl; lasterr = approxerr; ct++; } return minerr; } template TRealType antsSCCANObject ::NetworkDecomposition(unsigned int n_vecs ) { // MatrixType rmat = this->m_OriginalMatrixR.extract( // this->m_OriginalMatrixR.rows() , this->m_OriginalMatrixR.cols() - 1 , 0 , 1 ); // rmat = this->NormalizeMatrix( rmat ); /** Based on Golub CONJUGATE G R A D I E N T A N D LANCZOS HISTORY * http://www.matematicas.unam.mx/gfgf/cg2010/HISTORY-conjugategradient.pdf */ if ( ! this->m_Silent ) std::cout << " network decomposition using nlcg & normal equations " << std::endl; this->m_CanonicalCorrelations.set_size( this->m_OriginalMatrixP.rows() ); this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP ); this->m_MatrixR = this->NormalizeMatrix( this->m_OriginalMatrixR ); this->m_MatrixR = this->m_OriginalMatrixR; if( this->m_OriginalMatrixR.size() <= 0 ) { if ( ! this->m_Silent ) std::cout << " You need to define a reference matrix " << std::endl; std::exception(); } unsigned int foldnum = this->m_MaximumNumberOfIterations; if( foldnum <= 1 ) { foldnum = 1; } VectorType folds( this->m_MatrixP.rows(), 0 ); for( unsigned int f = 0; f < this->m_MatrixP.rows(); f++ ) { folds( f ) = f % foldnum; } RealType avgprederr = 0.0; RealType avgfiterr = 0.0; unsigned int extra_cols = 0; // VariatesQ holds the average over all folds this->m_VariatesQ.set_size( this->m_MatrixP.cols(), n_vecs + extra_cols ); this->m_VariatesQ.fill( 0 ); RealType totalloerror = 0; RealType loerror = 0; unsigned int predct = 0; for( unsigned int fold = 0; fold < foldnum; fold++ ) { unsigned int foldct = 0; for( unsigned int f = 0; f < this->m_MatrixP.rows(); f++ ) { if( folds( f ) == fold ) { foldct++; } } MatrixType p_leave_out( foldct, this->m_MatrixP.cols(), 0 ); MatrixType r_leave_out( foldct, this->m_MatrixR.cols(), 0 ); unsigned int leftoutsize = this->m_MatrixP.rows() - foldct; MatrixType matrixP( leftoutsize, this->m_MatrixP.cols() ); MatrixType matrixR( leftoutsize, this->m_MatrixR.cols() ); unsigned int leave_out = 0; unsigned int dont_leave_out = 0; for( unsigned int f = 0; f < this->m_MatrixP.rows(); f++ ) { if( folds( f ) == fold ) { p_leave_out.set_row( leave_out, this->m_MatrixP.get_row( f ) ); r_leave_out.set_row( leave_out, this->m_MatrixR.get_row( f ) ); leave_out++; } else { matrixP.set_row( dont_leave_out, this->m_MatrixP.get_row( f ) ); matrixR.set_row( dont_leave_out, this->m_MatrixR.get_row( f ) ); dont_leave_out++; } } if( foldnum <= 1 ) { matrixP = p_leave_out; matrixR = r_leave_out; } if( matrixR.cols() > 1 && false ) { MatrixType m( matrixR.rows(), matrixR.cols() - 1, 0 ); for( unsigned int mm = 1; mm < matrixR.cols(); mm++ ) { m.set_row( mm, matrixR.get_column( mm ) ); } MatrixType projmat = this->ProjectionMatrix( m, 1.e-2 ); matrixP = matrixP - projmat * matrixP; MatrixType m2( r_leave_out.rows(), r_leave_out.cols() - 1, 0 ); for( unsigned int mm = 1; mm < r_leave_out.cols(); mm++ ) { m2.set_row( mm, r_leave_out.get_column( mm ) ); } projmat = this->ProjectionMatrix( m2, 1.e-2 ); p_leave_out = p_leave_out - projmat * p_leave_out; } VectorType soln; MatrixType A; this->m_VariatesP.set_size( matrixP.cols(), n_vecs + extra_cols ); this->m_VariatesP.fill( 0 ); VectorType intercept( matrixP.cols(), 1 ); if( extra_cols > 0 ) { this->m_VariatesP.set_column( 0, intercept ); } VectorType original_b = matrixR.get_column( 0 ); unsigned int colind = extra_cols; RealType minerr1; bool addcol = false; matrixP = this->NormalizeMatrix( matrixP ); while( colind < this->m_VariatesP.cols() ) { VectorType b = original_b; this->m_OriginalB = original_b; VectorType x_k = this->m_VariatesP.get_column( colind ); /***************************************/ if( colind > 0 ) { A = matrixP * this->m_VariatesP.extract( matrixP.cols(), colind, 0, 0); if( addcol ) { this->AddColumnsToMatrix( A, matrixR, 1, this->m_MatrixR.cols() - 1 ); } VectorType lmsolv( A.cols(), 1 ); (void) this->ConjGrad( A, lmsolv, original_b, 0, 10000 ); VectorType v = ( A * lmsolv + this->m_Intercept ); b = this->Orthogonalize( b, v ); } unsigned int adder = 0; b = b - b.mean(); for( unsigned int cl = colind; cl < colind + 2; cl++ ) { VectorType randv = this->InitializeV( matrixP, 0 ); VectorType bp = b * matrixP; VectorType bpneg( bp ); this->PosNegVector( bp, true ); this->PosNegVector( bpneg, false ); if( cl % 2 != 0 ) { bp = bpneg * (-1); } // minerr1 = this->SparseNLPreConjGrad( matrixP, randv, bp, 1.e-1, 100 ); /**/ // minerr1 = this->SparseNLConjGrad( matrixP, randv, bp, 1.e-5, 500, true , true ); /** miccai good? */ // minerr1 = this->MatchingPursuit( matrixP , randv, 0, 90 );/** bad */ // minerr1 = this->SparseConjGradRidgeRegression( matrixP, randv, bp, 0, 150, true );/** good */ // randv.fill( 0 ); // minerr1 = this->RidgeRegression( matrixP , randv, bp, 1.e4 , 100 , false );/** biased but ok */ // minerr1 = this->ConjGrad( matrixP , randv, matrixP * bp , 0, 10000 ); this->SparsifyP( randv ); minerr1 = this->IHTRegression( matrixP, randv, bp, 0, 500, 0, true, true ); /** good */ if( cl < this->m_VariatesP.cols() ) { this->m_VariatesP.set_column( cl, randv ); } adder++; if ( ! this->m_Silent ) std::cout << " cl " << cl << " bp mean " << bp.mean() << " randvmean " << randv.mean() << std::endl; } colind = colind + adder; /***************************************/ /* Training : Now get the LSQ regression solution */ /***************************************/ A = matrixP * this->m_VariatesP.extract( matrixP.cols(), colind, 0, 0); if( addcol ) { this->AddColumnsToMatrix( A, matrixR, 1, this->m_MatrixR.cols() - 1 ); } VectorType lmsolv( A.cols(), 1 ); ( void ) this->ConjGrad( A, lmsolv, original_b, 0, 10000 ); this->m_Intercept = this->ComputeIntercept( A, lmsolv, original_b ); soln = A * lmsolv + this->m_Intercept; RealType fiterr = ( original_b - soln ).one_norm() / original_b.size(); avgfiterr += ( fiterr * ( 1.0 / ( RealType ) foldnum ) ); /** Testing */ p_leave_out = this->NormalizeMatrix( p_leave_out ); A = p_leave_out * this->m_VariatesP.extract( matrixP.cols(), colind, 0, 0); if( addcol ) { this->AddColumnsToMatrix( A, r_leave_out, 1, this->m_MatrixR.cols() - 1 ); } soln = ( A * lmsolv ) + this->m_Intercept; loerror = ( soln - r_leave_out.get_column( 0 ) ).one_norm() / soln.size(); unsigned int fleave_out = 0; if( foldnum == 1 ) { this->m_CanonicalCorrelations = soln; } else { for( unsigned int f = 0; f < this->m_MatrixP.rows(); f++ ) { if( folds( f ) == fold ) { this->m_CanonicalCorrelations( f ) = soln( fleave_out ); RealType temp = fabs( soln( fleave_out ) - r_leave_out.get_column( 0 ) ( fleave_out ) ); if( colind == this->m_VariatesP.cols() ) { avgprederr += temp; predct++; } fleave_out++; } } } if( predct > 0 ) { if ( ! this->m_Silent ) std::cout << "Fold: " << fold << " minerr," << loerror << ",col," << colind << " totalpredictionerr " << avgprederr / predct << " ,fiterr, " << fiterr << " Gap: " << ( avgprederr / predct ) - fiterr << std::endl; } else { if ( ! this->m_Silent ) std::cout << "Fold: " << fold << " minerr," << loerror << ",col," << colind << " ,fiterr, " << fiterr << std::endl; } } RealType wt = ( ( soln - r_leave_out.get_column( 0 ) ).one_norm() / soln.size() ) / ( ( r_leave_out.get_column( 0 ) ).one_norm() / soln.size() ); this->m_VariatesQ = this->m_VariatesQ + this->m_VariatesP * ( 1 / wt ); totalloerror += ( 1 / wt ); } this->m_VariatesQ = this->m_VariatesQ / totalloerror; RealType regbeta = this->SimpleRegression( this->m_CanonicalCorrelations, this->m_OriginalMatrixR.get_column( 0 ) ); RealType intercept = this->m_OriginalMatrixR.get_column( 0 ).mean() - this->m_CanonicalCorrelations.mean() * regbeta; VectorType predicted = this->m_CanonicalCorrelations * regbeta + intercept; RealType predictionerror = ( predicted - this->m_OriginalMatrixR.get_column( 0 ) ).one_norm() / this->m_OriginalMatrixR.rows(); if ( ! this->m_Silent ) std::cout << " overall-mean-abs-prediction-error: " << predictionerror << " correlation " << this->PearsonCorr( predicted, this->m_OriginalMatrixR.get_column( 0 ) ) << " Gap " << avgprederr / predct - avgfiterr << std::endl; this->m_VariatesP = this->m_VariatesQ; for( unsigned int i = 0; i < this->m_VariatesP.cols(); i++ ) { VectorType col = this->m_VariatesP.get_column( i ); this->SparsifyP( col ); // make averaged predictors sparse! this->m_VariatesP.set_column( i, col ); } return avgprederr / predct; } /* MatrixType pmod = this->m_MatrixP; this->m_Indicator.set_size(this->m_MatrixP.cols()); this->m_Indicator.fill(1); if ( whichevec > 0 && false ) { // zero out already used parts of matrix for ( unsigned int mm = baseind; mm < locind; mm++ ) { VectorType u = this->m_VariatesP.get_column( mm ); for ( unsigned int j=0; j< u.size(); j++) if ( fabs(u(j)) > 0 ) this->m_Indicator( j , j ) = 0; } pmod = pmod * this->m_Indicator; b = b * this->m_Indicator; } if( whichevec > 0 && false ) { MatrixType m( this->m_MatrixP.rows(), locind - baseind , 0 ); for( unsigned int mm = baseind; mm < locind; mm++ ) { m.set_row( mm, this->m_MatrixP * this->m_VariatesP.get_column( mm ) ); } MatrixType projmat = this->ProjectionMatrix( m, 1.e-2 ); pmod = pmod - projmat * pmod; } if ( whichevec > 0 ) { MatrixType A = this->m_MatrixP * this->m_VariatesP.get_n_columns( baseind , whichevec ); VectorType lmsolv( A.cols() , 1 ); VectorType bproj = this->m_MatrixP * b; this->ConjGrad( A , lmsolv, bproj , 0, 10000 ); bproj = bproj - A * lmsolv; bp = bproj * this->m_MatrixP; for( unsigned int vv = 0; vv < bp.size(); vv++ ) if ( colind % 2 == 0 & bp( vv ) > 0 ) bp( vv ) = 0; else if ( colind % 2 > 0 & bp( vv ) < 0 ) bp( vv ) = 0; } VectorType bnspace = this->m_MatrixP * this->m_Eigenvectors.get_column( colind ); if ( ! this->m_Silent ) std::cout << " vecerr-a " << this->PearsonCorr( this->m_MatrixP * x_k , bnspace )<< " norm " << ( this->m_MatrixP * x_k - bnspace ).two_norm() << std::endl; if ( ! this->m_Silent ) std::cout << " vecerr-a-init " << this->PearsonCorr( this->m_MatrixP * variatesInit.get_column( colind ) , bnspace ) << " norm " << ( this->m_MatrixP * variatesInit.get_column( colind ) - bnspace ).two_norm() << std::endl; if ( ! this->m_Silent ) std::cout << " col " << colind << " of " << n_vecs << " nspaceevecssz " << nspaceevecs.cols() << " mnv " << x_k.min_value() << " mxv " << x_k.max_value() << " colind " << colind << std::endl; this->m_Indicator.set_size(this->m_MatrixP.cols()); this->m_Indicator.fill(1); if ( colind > 0) { // zero out already used parts of matrix for ( unsigned int mm = 0; mm < colind; mm++ ) { VectorType u = this->m_VariatesP.get_column( mm ); for ( unsigned int j=0; j< u.size(); j++) if ( fabs(u(j)) >= this->m_Epsilon ) this->m_Indicator( j , j ) = 0; } // pmod = pmod * this->m_Indicator; } */ template TRealType antsSCCANObject ::SparseArnoldiSVD_Other( typename antsSCCANObject::MatrixType& A ) { if ( vnl_math_abs(this->m_RowSparseness) <= 1.e-9 ) return 0; unsigned int maxrowtoorth = ( unsigned int ) ( 1.0 / vnl_math_abs( this->m_RowSparseness ) ) - 0; unsigned int maxloop = 10; unsigned int loop = 0; double convcrit = 1; MatrixType Asparse( A ); while( loop < maxloop && convcrit > 1.e-8 ) { for( unsigned int k = 0; k < A.columns(); k++ ) { VectorType pveck = Asparse.get_column( k ); if ( !this->m_GetSmall ) pveck = ( pveck * A ) * A.transpose(); if ( this->m_GetSmall ) { RealType traceest = 0; for ( unsigned int kcol = 0; kcol < Asparse.columns(); kcol++ ) traceest += inner_product( Asparse.get_column( kcol ), Asparse.get_column( kcol ) ); traceest = traceest + 1; VectorType p1 = ( pveck * A ) * A.transpose(); vnl_diag_matrix trid( this->m_MatrixP.cols(), traceest ); VectorType p2 = ( pveck * trid ); pveck = p2 - p1; } pveck = pveck / pveck.two_norm(); unsigned int startingm = 0; if ( k > maxrowtoorth ) startingm = k - maxrowtoorth; for( unsigned int m = startingm; m < k; m++ ) { VectorType orthagainst = Asparse.get_column( m ); pveck = this->Orthogonalize( pveck, orthagainst ); // this->ZeroProduct( pveck, orthagainst ); } pveck = pveck / pveck.two_norm(); this->SparsifyOther( pveck , false ); Asparse.set_column(k,pveck); } loop++; } for( unsigned int k = 0; k < A.columns(); k++ ) { A.set_column(k, Asparse.get_column( k ) ); } return 0; } template TRealType antsSCCANObject ::BasicSVD() { unsigned int n_vecs = this->m_MatrixP.rows() - 1; if( ( this->m_MatrixP.cols() - 1 ) < n_vecs ) { n_vecs = this->m_MatrixP.cols() - 1; } this->m_CanonicalCorrelations.set_size(n_vecs); this->m_CanonicalCorrelations.fill(0); if ( ! this->m_Silent ) std::cout << " basic svd " << std::endl; std::vector vexlist; this->m_MatrixP = this->NormalizeMatrix(this->m_OriginalMatrixP); this->m_MatrixQ = this->m_MatrixP; if( this->m_OriginalMatrixR.size() > 0 ) { this->m_MatrixRRt = this->ProjectionMatrix(this->m_OriginalMatrixR); this->m_MatrixP = this->m_MatrixP - (this->m_MatrixRRt * this->m_MatrixP); } this->m_VariatesP.set_size( this->m_MatrixP.cols(), n_vecs * 2 ); MatrixType init = this->GetCovMatEigenvectors( this->m_MatrixP ); if ( ! this->m_Silent ) std::cout << "got initial svd " << std::endl; m_Eigenvectors.set_size( this->m_MatrixP.cols(), n_vecs * 2 ); unsigned int svdct = 0; RealType fnp = this->m_FractionNonZeroP; for( unsigned int kk = 0; kk < n_vecs; kk++ ) { if( kk < init.columns() ) { VectorType initvpos = init.get_column(kk) * this->m_MatrixP; VectorType initvneg = init.get_column(kk) * this->m_MatrixP; if( true ) { for( unsigned int vv = 0; vv < initvneg.size(); vv++ ) { if( initvneg( vv ) > 0 ) { initvneg( vv ) = 0; } else { initvneg( vv ) = fabs( initvneg( vv ) ); } if( initvpos( vv ) < 0 ) { initvpos( vv ) = 0; } } m_Eigenvectors.set_column( svdct, initvneg * (-1) ); if( fnp < 1 && false ) { if( this->m_KeepPositiveP ) { this->ConstantProbabilityThreshold( initvneg, fnp, this->m_KeepPositiveP ); } else { this->ReSoftThreshold( initvneg, fnp, !this->m_KeepPositiveP ); } this->ClusterThresholdVariate( initvneg, this->m_MaskImageP, this->m_MinClusterSizeP ); } this->m_VariatesP.set_column( svdct, initvneg * (-1) ); svdct++; m_Eigenvectors.set_column( svdct, initvpos ); if( fnp < 1 && false ) { if( this->m_KeepPositiveP ) { this->ConstantProbabilityThreshold( initvpos, fnp, this->m_KeepPositiveP ); } else { this->ReSoftThreshold( initvpos, fnp, !this->m_KeepPositiveP ); } this->ClusterThresholdVariate( initvpos, this->m_MaskImageP, this->m_MinClusterSizeP ); } this->m_VariatesP.set_column( svdct, initvpos ); } // separate the eigenvectors into + / - else { this->m_VariatesP.set_column( svdct, initvpos ); } svdct++; } } // double ktrace = vnl_trace( this->m_MatrixP * this->m_MatrixP.transpose() ); // RealType vex = this->ComputeSPCAEigenvalues( this->m_VariatesP.cols(), this->m_Eigenvalues.sum(), true ); // if ( ! this->m_Silent ) std::cout << "original-vex : " << this->m_Eigenvalues.sum() / ktrace << " sparse-vex: " << vex << // std::endl; return this->m_CanonicalCorrelations[0]; } template TRealType antsSCCANObject ::SparseArnoldiSVD(unsigned int n_vecs ) { this->m_CanonicalCorrelations.set_size(n_vecs); this->m_CanonicalCorrelations.fill(0); if ( ! this->m_Silent ) std::cout << " arnoldi sparse svd : cg " << std::endl; std::vector vexlist; this->m_MatrixP = this->NormalizeMatrix(this->m_OriginalMatrixP); this->m_MatrixQ = this->m_MatrixP; if( this->m_OriginalMatrixR.size() > 0 ) { this->m_MatrixRRt = this->ProjectionMatrix(this->m_OriginalMatrixR); this->m_MatrixP = this->m_MatrixP - (this->m_MatrixRRt * this->m_MatrixP); } this->m_ClusterSizes.set_size(n_vecs); this->m_ClusterSizes.fill(0); double trace = 0; for( unsigned int i = 0; i < this->m_MatrixP.cols(); i++ ) { trace += inner_product(this->m_MatrixP.get_column(i), this->m_MatrixP.get_column(i) ); } this->m_VariatesP.set_size(this->m_MatrixP.cols(), n_vecs); VariateType myGradients; this->m_SparseVariatesP.set_size(this->m_MatrixP.cols(), n_vecs); this->m_SparseVariatesP.fill(0); myGradients.set_size(this->m_MatrixP.cols(), n_vecs); myGradients.fill(0); MatrixType init = this->GetCovMatEigenvectors( this->m_MatrixP ); for( unsigned int kk = 0; kk < n_vecs; kk++ ) { this->m_VariatesP.set_column(kk, this->InitializeV(this->m_MatrixP) ); if( kk < init.columns() ) { VectorType initv = init.get_column(kk) * this->m_MatrixP; this->m_VariatesP.set_column(kk, initv); this->m_SparseVariatesP.set_column(kk, initv); } } const unsigned int maxloop = this->m_MaximumNumberOfIterations; // Arnoldi Iteration SVD/SPCA unsigned int loop = 0; bool debug = false; double convcrit = 1; RealType fnp = 1; while( loop 1.e-8 ) { fnp = this->m_FractionNonZeroP; for( unsigned int k = 0; k < n_vecs; k++ ) { VectorType pveck = this->m_SparseVariatesP.get_column(k); if( fnp < 1 ) { if( this->m_KeepPositiveP ) { this->ConstantProbabilityThreshold( pveck, fnp, this->m_KeepPositiveP ); } else { this->ReSoftThreshold( pveck, fnp, !this->m_KeepPositiveP ); } this->ClusterThresholdVariate( pveck, this->m_MaskImageP, this->m_MinClusterSizeP ); } // for ( unsigned int i=0; iOrthogonalize(pveck, this->m_VariatesP.get_column(i) ); RealType alpha = 0.1; VectorType resid = init.get_column(k) - this->m_MatrixP * pveck; VectorType newsol = this->m_SparseVariatesP.get_column(k) + this->m_MatrixP.transpose() * resid * alpha; this->m_SparseVariatesP.set_column(k, newsol); if( fnp < 1 ) { if( this->m_KeepPositiveP ) { this->ConstantProbabilityThreshold( newsol, fnp, this->m_KeepPositiveP ); } else { this->ReSoftThreshold( newsol, fnp, !this->m_KeepPositiveP ); } this->ClusterThresholdVariate( newsol, this->m_MaskImageP, this->m_MinClusterSizeP ); } this->m_VariatesP.set_column(k, newsol); } // kloop this->m_VariatesQ = this->m_VariatesP; if( debug ) { if ( ! this->m_Silent ) std::cout << " get evecs " << std::endl; } RealType vex = this->ComputeSPCAEigenvalues(n_vecs, trace, true); vexlist.push_back( vex ); this->SortResults( n_vecs ); convcrit = ( this->ComputeEnergySlope(vexlist, 5) ); if ( ! this->m_Silent ) std::cout << "Iteration: " << loop << " Eigenval_0: " << this->m_CanonicalCorrelations[0] << " Eigenval_1: " << this->m_CanonicalCorrelations[1] << " Eigenval_N: " << this->m_CanonicalCorrelations[n_vecs - 1] << " Sparseness: " << fnp << " convergence-criterion: " << convcrit << " vex " << vex << std::endl; loop++; if( debug ) { if ( ! this->m_Silent ) std::cout << "wloopdone" << std::endl; } } // opt-loop // if ( ! this->m_Silent ) std::cout << " cluster-sizes " << this->m_ClusterSizes << std::endl; for( unsigned int i = 0; i < vexlist.size(); i++ ) { if ( ! this->m_Silent ) std::cout << vexlist[i] << ","; } if ( ! this->m_Silent ) std::cout << std::endl; return fabs(this->m_CanonicalCorrelations[0]); } template TRealType antsSCCANObject ::ComputeSPCAEigenvalues(unsigned int n_vecs, TRealType trace, bool orth ) { this->m_CanonicalCorrelations.set_size( n_vecs ); double evalsum = 0; // we have variates P = X , Q = X^T , Cov \approx \sum_i eval_i E_i^t E_i // where E_i - eigenvector, eval_i eigenvalue unsigned long mind = this->m_MatrixP.rows(); if( mind > this->m_MatrixP.cols() ) { mind = this->m_MatrixP.cols(); } // we estimate variance explained by \sum_i eigenvalue_i / trace(A) ... // shen and huang // MatrixType kcovmat=this->VNLPseudoInverse( this->m_VariatesP.transpose()*this->m_VariatesP // )*this->m_VariatesP.transpose(); // kcovmat=(this->m_MatrixP*this->m_VariatesP)*kcovmat; // double ktrace=vnl_trace( kcovmat ); // if ( ! this->m_Silent ) std::cout <<" ktr " << ktrace << std::endl; for( unsigned int i = 0; i < n_vecs; i++ ) { VectorType u = this->m_VariatesP.get_column(i); // vnl_diag_matrix indicator(this->m_MatrixP.cols(),1); // for ( unsigned int j=0; j< u.size(); j++) if ( fabs(u(j)) <= this->m_Epsilon ) indicator(j,j)=0; VectorType proj = this->m_MatrixP.transpose() * ( this->m_MatrixP * u ); double eigenvalue_i = 0; double p2n = proj.two_norm(); double oeig = p2n; proj = this->m_MatrixP * u; eigenvalue_i = oeig; u = this->m_MatrixP * u; double unorm = u.two_norm(); if( unorm > 0 ) { u = u / u.two_norm(); } // factor out the % of the eigenvector that is not orthogonal to higher ranked eigenvectors for( unsigned int j = 0; j < i; j++ ) { VectorType v = this->m_MatrixP * this->m_VariatesP.get_column(j); if( v.two_norm() > 0 ) { v = v / v.two_norm(); } double ip = inner_product( u, v ); ip = 1 - fabs( ip ); if( orth ) { eigenvalue_i *= ip; } } if( eigenvalue_i == 0 ) { this->m_VariatesP.set_column( i, this->InitializeV( this->m_MatrixP ) ); } evalsum += oeig; // eigenvalue_i; this->m_CanonicalCorrelations[i] = eigenvalue_i; } evalsum /= trace; // if ( ! this->m_Silent ) std::cout << this->m_CanonicalCorrelations << std::endl; double vex = this->m_CanonicalCorrelations.sum() / trace; // if ( ! this->m_Silent ) std::cout<<" adjusted variance explained " << vex << std::endl; // if ( ! this->m_Silent ) std::cout<<" raw variance explained " << evalsum << std::endl; /* MatrixType mmm = this->m_MatrixP * this->m_VariatesP ; MatrixType projmat = this->ProjectionMatrix( mmm ); projmat = projmat * this->m_MatrixP ; projmat = projmat * this->m_MatrixP.frobenius_norm() / projmat.frobenius_norm(); MatrixType resid = this->m_MatrixP - projmat; double ktrace=vnl_trace( resid ); // if we factored out everything then ktrace will be small if ( ! this->m_Silent ) std::cout <<" ktr " << ktrace << " trace " << this->m_Eigenvalues.sum() << std::endl; */ return vex; /*****************************************/ MatrixType ptemp(this->m_MatrixP); VectorType d_i(n_vecs, 0); for( unsigned int i = 0; i < n_vecs; i++ ) { // find d_i MatrixType m = outer_product(this->m_VariatesQ.get_column(i), this->m_VariatesP.get_column(i) ); // now find d_i to bring m close to ptemp RealType a = ptemp.frobenius_norm(); RealType b = m.frobenius_norm(); // m=m*(a/b); RealType hypod = inner_product(this->m_VariatesQ.get_column(i), this->m_MatrixP * this->m_VariatesP.get_column(i) ); if ( ! this->m_Silent ) std::cout << " hypod " << hypod << " a " << a << " b " << b << " a/b " << a / b << " " << std::endl; ptemp = ptemp + m * a; } return 0; } template typename antsSCCANObject::MatrixType antsSCCANObject ::GetCovMatEigenvectors( typename antsSCCANObject::MatrixType rin ) { double pinvTolerance = this->m_PinvTolerance; MatrixType dd = this->NormalizeMatrix(rin); MatrixType cov = dd * dd.transpose(); cov.set_identity(); TRealType regularization = 0; cov = cov * regularization + rin * rin.transpose(); vnl_svd eig(cov, pinvTolerance); VectorType vec1 = eig.U().get_column(0); VectorType vec2 = eig.V().get_column(0); double evalsum = 0; this->m_Eigenvalues.set_size(cov.rows() ); this->m_Eigenvalues.fill(0); for( unsigned int i = 0; i < cov.rows(); i++ ) { this->m_Eigenvalues[i] = eig.W(i, i); evalsum += eig.W(i, i); // if ( ! this->m_Silent ) std::cout <<" variance-explained-eval " << i << " = " << evalsum/trace*100 << std::endl; } // if ( ! this->m_Silent ) std::cout <<" W 1 " << eig.W(0,0) << " W 2 " << eig.W(1,1) << std::endl; if( vec2.size() == rin.rows() ) { this->m_Eigenvectors = eig.V(); return eig.V(); } else { this->m_Eigenvectors = eig.U(); return eig.U(); } } template bool antsSCCANObject ::CCAUpdate( unsigned int n_vecs, bool allowchange , bool normbycov , unsigned int k ) { // srand (time(NULL)); RealType gsteP = this->m_GradStepP; RealType gsteQ = this->m_GradStepQ; this->m_FractionNonZeroP = this->m_SparsenessP( k ); this->m_FractionNonZeroQ = this->m_SparsenessQ( k ); VectorType pprior; VectorType qprior; if ( ( this->m_MatrixPriorROI.rows() > 0 ) && ( this->m_MatrixPriorROI.cols() > 0 ) ) { pprior = this->m_MatrixPriorROI.transpose().get_column( k ); pprior = pprior / pprior.two_norm(); } if ( ( this->m_MatrixPriorROI2.rows() > 0 ) && ( this->m_MatrixPriorROI2.cols() > 0 ) ) { qprior = this->m_MatrixPriorROI2.transpose().get_column( k ); qprior = qprior / qprior.two_norm(); } this->m_Debug = false; bool secondSO = true; // for( unsigned int k = 0; k < n_vecs; k++ ) { // residualize against previous vectors // 0 - vox orth + resid, 1 - only vox orth , 2 - only resid if ( ( k > 0 ) && ( this->m_Covering == 0 || this->m_Covering == 2 ) ) { VectorType temp = this->m_MatrixP * this->m_VariatesP.get_column( k-1 ); this->SparsifyOther( temp ); if ( k < (this->m_MatrixP.columns()-1) ) this->m_MatrixP = this->OrthogonalizeMatrix( this->m_MatrixP, temp ); temp = this->m_MatrixQ * this->m_VariatesQ.get_column( k-1 ); this->SparsifyOther( temp ); if ( n_vecs < this->m_MatrixQ.columns() ) this->m_MatrixQ = this->OrthogonalizeMatrix( this->m_MatrixQ, temp ); this->m_MatrixP = this->NormalizeMatrix( this->m_MatrixP, false ); this->m_MatrixQ = this->NormalizeMatrix( this->m_MatrixQ, false ); } VectorType ptemp = this->m_VariatesP.get_column(k); VectorType qtemp = this->m_VariatesQ.get_column(k); VectorType pveck = this->m_MatrixQ * qtemp; if ( secondSO ) this->SparsifyOther( pveck ); // zeromatch VectorType qveck = this->m_MatrixP * ptemp; if ( secondSO ) this->SparsifyOther( qveck ); // zeromatch // get list of all zeroes std::vector zeromatch( qveck.size(), 0); unsigned int zct = 0; for ( unsigned int zm = 0; zm < qveck.size(); zm++ ) { if ( ( this->Close2Zero( pveck(zm) ) || this->Close2Zero( qveck(zm) ) ) && ( false ) ) { zct++; zeromatch[ zm ] = 1; pveck(zm) = 0; qveck(zm) = 0; } } /** the gradient of ( x X , y Y ) * ( x X , x X )^{-1} * ( y Y , y Y )^{-1} * where we constrain ( x X , x X ) = ( y Y , y Y ) = 1 */ RealType ccafactor = inner_product( pveck, qveck ) * 0.5; pveck = pveck * this->m_MatrixP; VectorType pproj = ( this->m_MatrixP * ptemp ); if ( secondSO ) this->SparsifyOther( pproj ); // zeromatch // for ( unsigned int zm = 0; zm < qveck.size(); zm++ ) // if ( this->Close2Zero( zeromatch[ zm ] - 1 ) ) pproj( zm ) = 0; pveck = pveck - this->m_MatrixP.transpose() * pproj * ccafactor; qveck = qveck * this->m_MatrixQ; VectorType qproj = ( this->m_MatrixQ * qtemp ); if ( secondSO ) this->SparsifyOther( qproj ); // zeromatch // for ( unsigned int zm = 0; zm < qveck.size(); zm++ ) // if ( this->Close2Zero( zeromatch[ zm ] - 1 ) ) qproj( zm ) = 0; qveck = qveck - this->m_MatrixQ.transpose() * ( qproj ) * ccafactor; RealType sclp = ( static_cast( pveck.size() ) * this->m_FractionNonZeroP ); RealType sclq = ( static_cast( qveck.size() ) * this->m_FractionNonZeroQ ); bool genomics = false; if( genomics ) { VectorType y = this->m_MatrixP * ptemp; this->LASSO_alg( this->m_MatrixQ, y, qveck, 1.e-3, 2 ); } pveck = ptemp + pveck * ( gsteP / sclp ); qveck = qtemp + qveck * ( gsteQ / sclq ); if ( this->m_Covering != 2 ) // 0 - vox orth + resid, 1 - only vox orth , 2 - only resid for( unsigned int j = 0; j < k; j++ ) { if ( j != k ) { VectorType qj = this->m_VariatesP.get_column( j ); pproj = ( this->m_MatrixP * pveck ); if ( secondSO ) this->SparsifyOther( pproj ); pveck = this->Orthogonalize( pveck / ( pproj ).two_norm() , qj ); // pveck = this->Orthogonalize( pveck, qj, &this->m_MatrixP, &this->m_MatrixP); // if ( this->m_Covering ) this->ZeroProduct( pveck, qj ); qj = this->m_VariatesQ.get_column( j ); qproj = ( this->m_MatrixQ * qveck ); if ( secondSO ) this->SparsifyOther( qproj ); qveck = this->Orthogonalize( qveck / ( qproj ).two_norm() , qj ); // qveck = this->Orthogonalize( qveck, qj, &this->m_MatrixQ, &this->m_MatrixQ); // if ( this->m_Covering ) this->ZeroProduct( qveck, qj ); } } if ( ( this->m_UseLongitudinalFormulation > 1.e-9 ) && ( pveck.size() == qveck.size() ) ) { VectorType lpveck = pveck + ( qveck - pveck ) * this->m_UseLongitudinalFormulation; VectorType lqveck = qveck + ( pveck - qveck ) * this->m_UseLongitudinalFormulation; pveck = lpveck; qveck = lqveck; } if ( pprior.size() == pveck.size() ) { pveck = pveck / pveck.two_norm(); pveck = pveck * (1.0 - this->m_PriorWeight ) + pprior * this->m_PriorWeight; } if ( qprior.size() == qveck.size() ) { qveck = qveck / qveck.two_norm(); qveck = qveck * (1.0 - this->m_PriorWeight ) + qprior * this->m_PriorWeight; } this->SparsifyP( pveck ); this->SparsifyQ( qveck ); if ( this->m_UseLongitudinalFormulation > 1.e-9 ) { if ( k == 0 && ( pveck.size() != qveck.size() ) ) { std::cout << "Cannot Use Longitudinal formulation if input matrix sizes dont match. "; } else if ( k == 0 ) { if ( ! this->m_Silent ) std::cout << "Longitudinal=>p=q: " << ( pveck - qveck ).one_norm(); } } if( n_vecs == 0 ) { RealType mup = inner_product( this->m_MatrixP * ptemp, this->m_MatrixP * ptemp ) / ptemp.two_norm(); RealType muq = inner_product( this->m_MatrixQ * qtemp, this->m_MatrixQ * qtemp ) / qtemp.two_norm(); if ( ! this->m_Silent ) std::cout << " USE-IHT FORMULATION " << mup << " " << muq << std::endl; this->IHTRegression( this->m_MatrixP, ptemp, pveck, 0, 1, mup, true, false ); pveck = ptemp; this->IHTRegression( this->m_MatrixQ, qtemp, qveck, 0, 1, muq, false, false ); qveck = qtemp; } /* randomly drop-out some entries in pveck for ( unsigned int i = 0; i < pveck.size(); i++ ) if ( ( rand() % 100 ) < 2 ) pveck[i] = 0; for ( unsigned int i = 0; i < qveck.size(); i++ ) if ( ( rand() % 100 ) < 2 ) qveck[i] = 0; */ // test 4 cases of updates pproj = this->m_MatrixP * ptemp; if ( secondSO ) this->SparsifyOther( pproj ); VectorType pproj2 = this->m_MatrixP * pveck; if ( secondSO ) this->SparsifyOther( pproj2 ); qproj = this->m_MatrixQ * qtemp; if ( secondSO ) this->SparsifyOther( qproj ); VectorType qproj2 = this->m_MatrixQ * qveck; if ( secondSO ) this->SparsifyOther( qproj2 ); RealType corr0 = this->PearsonCorr( pproj , qproj ); RealType corr1 = this->PearsonCorr( pproj2 , qproj2 ); RealType corr2 = this->PearsonCorr( pproj2, qproj ); RealType corr3 = this->PearsonCorr( pproj, qproj2 ); this->m_Debug = true; if( corr1 > corr0 ) { this->m_VariatesP.set_column( k, pveck ); this->m_VariatesQ.set_column( k, qveck ); if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " corr1 " << corr1 << " v " << corr2 << std::endl; } } else if( ( corr2 > corr0 ) && ( corr2 > corr3 ) ) { this->m_VariatesP.set_column( k, pveck ); this->m_GradStepQ *= 0.5; if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " corr2 " << corr2 << " v " << corr1 << std::endl; } } else if( corr3 > corr0 ) { this->m_VariatesQ.set_column( k, qveck ); this->m_GradStepP *= 0.5; if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " corr3 " << corr3 << " v " << corr0 << std::endl; } } else if( allowchange ) { this->m_GradStepP *= 0.5; this->m_GradStepQ *= 0.5; if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " corr0 " << corr0 << " v " << corr1 << " NewGrad " << gsteP << " & " << gsteQ << std::endl; } } this->m_Debug = false; if ( normbycov ) this->NormalizeWeightsByCovariance( k, 0, 0 ); else this->NormalizeWeights( k ); VectorType proj1 = this->m_MatrixP * this->m_VariatesP.get_column( k ); if ( secondSO ) this->SparsifyOther( proj1 ); VectorType proj2 = this->m_MatrixQ * this->m_VariatesQ.get_column( k ); if ( secondSO ) this->SparsifyOther( proj2 ); this->m_CanonicalCorrelations[k] = this->PearsonCorr( proj1, proj2 ); } // this->SortResults( n_vecs ); return this->m_CanonicalCorrelations.mean(); } template TRealType antsSCCANObject ::InitializeSCCA_simple( unsigned int n_vecs ) { /** get the row mean for each matrix , then correlate the other matrix with that, then sparsify * for 2nd,3rd,etc evecs, orthogonalize initial vector against sparsified. */ // this->InitializeSCCA( n_vecs , 20 );// arbitrary initialization RealType totalcorr = 0; unsigned int pmax = this->m_MatrixP.rows(); unsigned int qmax = this->m_MatrixQ.rows(); VectorType prowmean( pmax , 0 ); for( unsigned long i = 0; i < pmax; i++ ) { prowmean( i ) = this->m_MatrixP.get_row( i ).mean(); } VectorType qrowmean( qmax, 0 ); for( unsigned long i = 0; i < qmax; i++ ) { qrowmean( i ) = this->m_MatrixQ.get_row( i ).mean(); } if ( prowmean.two_norm() > this->m_Epsilon ) prowmean = prowmean / prowmean.two_norm(); if ( qrowmean.two_norm() > this->m_Epsilon ) qrowmean = qrowmean / qrowmean.two_norm(); this->SparsifyOther( prowmean ); this->SparsifyOther( qrowmean ); VectorType ipvec = ( prowmean ) * this->m_MatrixP; VectorType iqvec = ( qrowmean ) * this->m_MatrixQ; for( unsigned int kk = 0; kk < n_vecs; kk++ ) { if ( ( kk > 0 ) && ( this->m_Covering == 0 || this->m_Covering == 2 ) ) { VectorType temp = this->m_MatrixP * this->m_VariatesP.get_column( kk-1 ); this->SparsifyOther( temp ); if ( n_vecs < this->m_MatrixP.columns() ) this->m_MatrixP = this->OrthogonalizeMatrix( this->m_MatrixP, temp ); temp = this->m_MatrixQ * this->m_VariatesQ.get_column( kk-1 ); this->SparsifyOther( temp ); if ( n_vecs < this->m_MatrixQ.columns() ) this->m_MatrixQ = this->OrthogonalizeMatrix( this->m_MatrixQ, temp ); } VectorType qvec = ( this->m_MatrixP * ipvec ); this->SparsifyOther( qvec ); qvec = qvec * this->m_MatrixQ; if ( qvec.two_norm() > this->m_Epsilon ) qvec = qvec / qvec.two_norm(); VectorType vec = ( this->m_MatrixQ * qvec ); this->SparsifyOther( vec ); vec = vec * this->m_MatrixP; if ( vec.two_norm() > this->m_Epsilon ) vec = vec / vec.two_norm(); VectorType vec2 = ( this->m_MatrixQ * iqvec ); this->SparsifyOther( vec2 ); vec2 = vec2 * this->m_MatrixP; if ( vec2.two_norm() > this->m_Epsilon ) vec2 = vec2 / vec2.two_norm(); VectorType qvec2 = ( this->m_MatrixP * vec2 ); this->SparsifyOther( qvec2 ); qvec2 = qvec2 * this->m_MatrixQ; if ( qvec2.two_norm() > this->m_Epsilon ) qvec2 = qvec2 / qvec2.two_norm(); if ( vnl_math_abs( this->PearsonCorr( this->m_MatrixP * vec2, this->m_MatrixQ * qvec2 ) ) > vnl_math_abs( this->PearsonCorr( this->m_MatrixP * vec, this->m_MatrixQ * qvec ) ) ) { vec = vec2; qvec = qvec2; } // if ( ! this->m_Silent ) std::cout << " vec-y " << vec.two_norm() << std::endl; // if ( ! this->m_Silent ) std::cout << "qvec-y " << qvec.two_norm() << std::endl; for( unsigned int j = 0; j < kk; j++ ) { VectorType qj = this->m_VariatesP.get_column(j); if ( j < (this->m_MatrixP.cols()-1) ) vec = this->Orthogonalize( vec, qj ); qj = this->m_VariatesQ.get_column(j); if ( n_vecs < this->m_MatrixQ.columns() ) qvec = this->Orthogonalize( qvec, qj ); } // if ( ! this->m_Silent ) std::cout << " vec-z " << vec.two_norm() << std::endl; // if ( ! this->m_Silent ) std::cout << "qvec-z " << qvec.two_norm() << std::endl; vec = this->SpatiallySmoothVector( vec, this->m_MaskImageP ); qvec = this->SpatiallySmoothVector( qvec, this->m_MaskImageQ ); if ( qvec.two_norm() > 0 ) qvec = qvec / qvec.two_norm(); if ( vec.two_norm() > 0 ) vec = vec / vec.two_norm(); if ( this->m_UseLongitudinalFormulation > 1.e-9 ) { vec = ( vec + qvec ) * 0.5; qvec = vec; } this->SparsifyP( vec ); this->SparsifyQ( qvec ); RealType locor = vnl_math_abs( this->PearsonCorr( this->m_MatrixP * vec, this->m_MatrixQ * qvec ) ); if ( vnl_math_isnan( qvec.two_norm() ) ) { qvec = this->m_VariatesQ.get_column( kk ); locor = vnl_math_abs( this->PearsonCorr( this->m_MatrixP * vec, this->m_MatrixQ * qvec ) ); } if ( vnl_math_isnan( vec.two_norm() ) ) { vec = this->m_VariatesP.get_column( kk ); locor = vnl_math_abs( this->PearsonCorr( this->m_MatrixP * vec, this->m_MatrixQ * qvec ) ); } this->m_VariatesP.set_column( kk, vec ); this->m_VariatesQ.set_column( kk, qvec ); this->NormalizeWeightsByCovariance( kk, 1, 1 ); totalcorr += locor; } return totalcorr; } template TRealType antsSCCANObject ::InitializeSCCA( unsigned int n_vecs, unsigned int seeder ) { RealType totalcorr = 0; for( unsigned int kk = 0; kk < n_vecs; kk++ ) { unsigned int theseed = ( kk + 1 ) * seeder; VectorType vec = this->InitializeV( this->m_MatrixP, theseed ); vec = this->m_MatrixP * vec; this->SparsifyOther( vec ); vec = vec * this->m_MatrixP; vec = vec / vec.two_norm(); VectorType qvec = ( this->m_MatrixP * vec ); this->SparsifyOther( qvec ); qvec = qvec * this->m_MatrixQ; qvec = qvec / qvec.two_norm(); vec = ( this->m_MatrixQ * qvec ) * this->m_MatrixP; vec = vec / vec.two_norm(); for( unsigned int j = 0; j < kk; j++ ) { VectorType qj = this->m_VariatesP.get_column(j); if ( j < (this->m_MatrixP.columns()-1) ) vec = this->Orthogonalize( vec, qj ); qj = this->m_VariatesQ.get_column(j); if ( n_vecs < this->m_MatrixQ.columns() ) qvec = this->Orthogonalize( qvec, qj ); } vec = this->SpatiallySmoothVector( vec, this->m_MaskImageP ); qvec = this->SpatiallySmoothVector( qvec, this->m_MaskImageQ ); qvec = qvec / qvec.two_norm(); vec = vec / vec.two_norm(); this->SparsifyP( vec ); this->SparsifyQ( qvec ); this->m_VariatesP.set_column( kk, vec ); this->m_VariatesQ.set_column( kk, qvec ); this->NormalizeWeights( kk ); totalcorr += vnl_math_abs( this->PearsonCorr( this->m_MatrixP * vec, this->m_MatrixQ * qvec ) ); } return totalcorr*0.5; // this->CCAUpdate( n_vecs , false ); return this->m_CanonicalCorrelations.sum(); } template TRealType antsSCCANObject ::SparsePartialArnoldiCCA(unsigned int n_vecs_in) { RealType basegradstep = this->m_GradStep; this->m_Debug = false; unsigned int n_vecs = n_vecs_in; if( n_vecs < 1 ) { n_vecs = 1; } this->m_CanonicalCorrelations.set_size(n_vecs); this->m_CanonicalCorrelations.fill(0); this->m_SparsenessP.set_size( n_vecs ); this->m_SparsenessP.fill( this->m_FractionNonZeroP ); this->m_SparsenessQ.set_size( n_vecs ); this->m_SparsenessQ.fill( this->m_FractionNonZeroQ ); if ( ! m_Silent ) { if ( ! this->m_Silent ) std::cout << " arnoldi sparse partial cca : L1?" << this->m_UseL1 << " GradStep " << this->m_GradStep << " p+ " << this->GetKeepPositiveP() << " q+ " << this->GetKeepPositiveQ() << " covering " << this->m_Covering << std::endl; } this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP, false ); this->m_MatrixQ = this->NormalizeMatrix( this->m_OriginalMatrixQ, false ); this->m_MatrixR = this->NormalizeMatrix( this->m_OriginalMatrixR, false ); if( this->m_OriginalMatrixR.size() > 0 ) { if ( ! this->m_Silent ) std::cout << "Partialing-Pre : -P-Norm " << this->m_MatrixP.frobenius_norm() << " -Q-Norm " << this->m_MatrixQ.frobenius_norm() << std::endl; this->m_MatrixRRt = this->ProjectionMatrix(this->m_OriginalMatrixR); if( this->m_SCCANFormulation == PminusRQ || this->m_SCCANFormulation == PminusRQminusR ) { if ( ! this->m_Silent ) std::cout << " Subtract R from P " << std::endl; this->m_MatrixP = this->m_MatrixP - (this->m_MatrixRRt * this->m_MatrixP); if ( ! this->m_Silent ) std::cout << "Partialing-Post : -P-Norm " << this->m_MatrixP.frobenius_norm() << std::endl; } if( this->m_SCCANFormulation == PQminusR || this->m_SCCANFormulation == PminusRQminusR ) { if ( ! this->m_Silent ) std::cout << " Subtract R from Q " << std::endl; this->m_MatrixQ = this->m_MatrixQ - this->m_MatrixRRt * this->m_MatrixQ; if ( ! this->m_Silent ) std::cout << " -Q-Norm " << this->m_MatrixQ.frobenius_norm() << std::endl; } } this->m_VariatesP.set_size(this->m_MatrixP.cols(), n_vecs); this->m_VariatesQ.set_size(this->m_MatrixQ.cols(), n_vecs); RealType initReturn = this->InitializeSCCA_simple( n_vecs ); if ( !m_Silent ) { if ( ! this->m_Silent ) std::cout << "Initialization: " << initReturn << std::endl; } /* RealType bestcorr = 0; RealType totalcorr = 0; int bestseed = -1; for( unsigned int seeder = 0; seeder < 100; seeder++ ) { totalcorr = this->InitializeSCCA( n_vecs, seeder ); if( totalcorr > bestcorr ) { bestseed = seeder; bestcorr = totalcorr; if ( ! this->m_Silent ) std::cout << " seed " << seeder << " corr " << bestcorr << std::endl; } } if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " Best initial corr " << bestcorr << std::endl; } if ( bestseed >= 0 ) totalcorr = this->InitializeSCCA( n_vecs, bestseed ); if ( ! this->m_Silent ) std::cout << " seed " << bestseed << " corr " << totalcorr << std::endl; */ bool imagedriver = false; if ( ( this->m_OriginalMatrixPriorROI.rows() > 0 ) && ( this->m_OriginalMatrixPriorROI.cols() > 0 ) ) { imagedriver = true; n_vecs = this->m_OriginalMatrixPriorROI.rows(); this->m_VariatesP = this->m_MatrixPriorROI.transpose(); for ( unsigned int i = 0; i < n_vecs; i++ ) { VectorType vec = this->m_VariatesP.get_column( i ); RealType spar = this->CountNonZero( vec ); if ( fabs( this->m_FractionNonZeroP ) < 1.e-11 ) // learn from prior this->m_SparsenessP( i ) = spar; this->SparsifyP( vec ); vec = this->SpatiallySmoothVector( vec, this->m_MaskImageP ); vec = vec / vec.two_norm(); this->m_VariatesP.set_column( i , vec ); } } if ( ( this->m_MatrixPriorROI2.rows() > 0 ) && ( this->m_MatrixPriorROI2.cols() > 0 ) ) { imagedriver = true; this->m_VariatesQ = this->m_MatrixPriorROI2.transpose(); for ( unsigned int i = 0; i < n_vecs; i++ ) { VectorType vec = this->m_VariatesQ.get_column( i ); RealType spar = this->CountNonZero( vec ); if ( fabs( this->m_FractionNonZeroQ ) < 1.e-11 ) this->m_SparsenessQ( i ) = spar; this->SparsifyQ( vec ); vec = this->SpatiallySmoothVector( vec, this->m_MaskImageQ ); vec = vec / vec.two_norm(); this->m_VariatesQ.set_column( i , vec ); } } if ( ! this->m_Silent ) { std::cout << " P-sparseness : " << this->m_SparsenessP << std::endl; std::cout << " Q-sparseness : " << this->m_SparsenessQ << std::endl; } unsigned int maxloop = this->m_MaximumNumberOfIterations; unsigned int innerloop = 1; unsigned int loop = 0; RealType energy = 0; RealType lastenergy = 0; VectorType gradstepsp( n_vecs , basegradstep ); VectorType gradstepsq( n_vecs , basegradstep ); if ( this->m_Covering == 0 || this->m_Covering == 2 ) { innerloop = maxloop; maxloop = 1; } for ( unsigned int oo = 0; oo < maxloop; oo++ ) { for ( unsigned int k = 0; k < n_vecs; k++ ) { this->m_GradStepP = gradstepsp[ k ]; this->m_GradStepQ = gradstepsq[ k ]; if ( k == 0 ) this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP, false ); if ( k == 0 ) this->m_MatrixQ = this->NormalizeMatrix( this->m_OriginalMatrixQ, false ); loop=0; this->m_GradStep = basegradstep; while( ( ( loop < innerloop ) ) ) // && ( energyincreases ) ) ) { // Arnoldi Iteration SCCA bool normbycov = true; bool changedgrad = this->CCAUpdate( n_vecs_in, true , normbycov, k ); lastenergy = energy; energy = this->m_CanonicalCorrelations.one_norm() / ( float ) n_vecs_in; if( this->m_GradStep < 1.e-12 ) // || ( vnl_math_abs( energy - lastenergy ) < this->m_Epsilon && !changedgrad ) ) { if ( ! this->m_Silent ) std::cout << " this->m_GradStep : " << this->m_GradStep << " energy : " << energy << " changedgrad : " << changedgrad << std::endl; } gradstepsp[ k ] = this->m_GradStepP; gradstepsq[ k ] = this->m_GradStepQ; loop++; } // outer loop } if ( ! imagedriver ) this->SortResults( n_vecs_in ); // if ( ! m_Silent ) { if ( ! this->m_Silent ) std::cout << " Loop " << oo << " Corrs : " << this->m_CanonicalCorrelations << " CorrMean : " << energy << std::endl; } } // oo // this->RunDiagnostics(n_vecs); double ccasum = 0; for( unsigned int i = 0; i < this->m_CanonicalCorrelations.size(); i++ ) { if ( ! this->m_Silent ) std::cout << i << " P: " << this->CountNonZero( this->m_VariatesP.get_column( i ) ) << " Q: " << this->CountNonZero( this->m_VariatesQ.get_column( i ) ) << std::endl; ccasum += fabs(this->m_CanonicalCorrelations[i]); } if( n_vecs_in > 1 ) { return ccasum; } else { return fabs(this->m_CanonicalCorrelations[0]); } /* ************************************************************************************************** // now deal with covariates --- this could work but needs to be fixed. for ( unsigned int j=0; j< this->m_MatrixR.cols(); j++) { // FIXME is this really what qj should be? it should be the projection of the nuisance variable // into the space of qj ... VectorType cov=this->m_MatrixR.get_column(j); VectorType qj=cov*this->m_MatrixP; RealType hjk=inner_product(cov,this->m_MatrixP*pveck)/ inner_product(cov,cov); if ( this->m_SCCANFormulation == PminusRQ || this->m_SCCANFormulation == PminusRQminusR ) for (unsigned int i=0; im_MatrixQ; hjk=inner_product(cov,this->m_MatrixQ*qveck)/ inner_product(cov,cov); if ( this->m_SCCANFormulation == PQminusR || this->m_SCCANFormulation == PminusRQminusR ) for (unsigned int i=0; im_SCCANFormulation != PQ ) { for ( unsigned int dd=0; dd<20 ; dd++) { VectorType upp(this->m_MatrixP.cols(),0); VectorType upq(this->m_MatrixQ.cols(),0); for ( unsigned int rr=0; rrm_MatrixR.cols(); rr++) { pveck=this->m_MatrixQ*qtemp; qveck=this->m_MatrixP*ptemp; pveck=pveck/pveck.two_norm(); qveck=qveck/qveck.two_norm(); pveck=this->Orthogonalize(pveck,this->m_OriginalMatrixR.get_column(rr)); qveck=this->Orthogonalize(qveck,this->m_OriginalMatrixR.get_column(rr)); VectorType tempp=this->m_MatrixP.transpose()*pveck; VectorType tempq=this->m_MatrixQ.transpose()*qveck; pveck=pveck/pveck.two_norm(); upp=upp+tempp/tempp.two_norm()*1.0/(this->m_MatrixR.cols()); qveck=qveck/qveck.two_norm(); upq=upq+tempq/tempq.two_norm()*1.0/(this->m_MatrixR.cols()); } //rr RealType eps=.01; if ( dd % 2 == 0 ) { qtemp=qtemp+eps*upq; // RealType c1=this->PearsonCorr(this->m_OriginalMatrixR.get_column(rr),this->m_MatrixQ*qtemp); // RealType c2=this->PearsonCorr(this->m_OriginalMatrixR.get_column(rr),this->m_MatrixQ*(qtemp+eps*upq)); // RealType c3=this->PearsonCorr(this->m_OriginalMatrixR.get_column(rr),this->m_MatrixQ*(qtemp-eps*upq)); // if ( fabs(c2) < fabs(c1) ) qtemp=qtemp+eps*upq; // if ( fabs(c3) < fabs(c1) ) qtemp=qtemp-eps*upq; } else { ptemp=ptemp+eps*upp; // RealType c1=this->PearsonCorr(this->m_OriginalMatrixR.get_column(rr),this->m_MatrixP*ptemp); // RealType c2=this->PearsonCorr(this->m_OriginalMatrixR.get_column(rr),this->m_MatrixP*(ptemp+eps*upp)); // RealType c3=this->PearsonCorr(this->m_OriginalMatrixR.get_column(rr),this->m_MatrixP*(ptemp-eps*upp)); // if ( fabs(c2) < fabs(c1) ) ptemp=ptemp+eps*upp; // if ( fabs(c3) < fabs(c1) ) ptemp=ptemp-eps*upp; } ptemp=ptemp/ptemp.two_norm(); qtemp=qtemp/qtemp.two_norm(); pveck=this->m_MatrixQ*qtemp; qveck=this->m_MatrixP*ptemp; } //dd } // if ( this->m_SCCANFormulation != PQ ) { VectorType proj=this->m_MatrixQ*this->m_WeightsQ; if ( false && ( this->m_SCCANFormulation == PminusRQ || this->m_SCCANFormulation == PminusRQminusR ) ) for (unsigned int kk=0; kk< this->m_OriginalMatrixR.cols(); kk++) proj=this->Orthogonalize(proj,this->m_MatrixR.get_column(kk)); this->m_WeightsP=this->m_MatrixP.transpose()*(proj); */ } template TRealType antsSCCANObject ::IHTCCA(unsigned int n_vecs_in) { if ( ! this->m_Silent ) std::cout << " IHTCCA " << std::endl; unsigned int n_vecs = n_vecs_in; if( n_vecs < 1 ) { n_vecs = 1; } this->m_CanonicalCorrelations.set_size(n_vecs); this->m_CanonicalCorrelations.fill(0); if ( ! this->m_Silent ) std::cout << " iht cca " << std::endl; if ( ! this->m_Silent ) std::cout << " pos-p " << this->GetKeepPositiveP() << " pos-q " << this->GetKeepPositiveQ() << std::endl; this->m_MatrixP = this->NormalizeMatrix( this->m_OriginalMatrixP, false ); this->m_MatrixQ = this->NormalizeMatrix( this->m_OriginalMatrixQ, false ); this->m_MatrixR = this->NormalizeMatrix( this->m_OriginalMatrixR, false ); if( this->m_OriginalMatrixR.size() > 0 ) { if ( ! this->m_Silent ) std::cout << "Partialing-Pre : -P-Norm " << this->m_MatrixP.frobenius_norm() << " -Q-Norm " << this->m_MatrixQ.frobenius_norm() << std::endl; this->m_MatrixRRt = this->ProjectionMatrix(this->m_OriginalMatrixR); if( this->m_SCCANFormulation == PminusRQ || this->m_SCCANFormulation == PminusRQminusR ) { if ( ! this->m_Silent ) std::cout << " Subtract R from P " << std::endl; this->m_MatrixP = this->m_MatrixP - (this->m_MatrixRRt * this->m_MatrixP); if ( ! this->m_Silent ) std::cout << "Partialing-Post : -P-Norm " << this->m_MatrixP.frobenius_norm() << std::endl; } if( this->m_SCCANFormulation == PQminusR || this->m_SCCANFormulation == PminusRQminusR ) { if ( ! this->m_Silent ) std::cout << " Subtract R from Q " << std::endl; this->m_MatrixQ = this->m_MatrixQ - this->m_MatrixRRt * this->m_MatrixQ; if ( ! this->m_Silent ) std::cout << " -Q-Norm " << this->m_MatrixQ.frobenius_norm() << std::endl; } } this->m_VariatesP.set_size(this->m_MatrixP.cols(), n_vecs); this->m_VariatesQ.set_size(this->m_MatrixQ.cols(), n_vecs); RealType totalcorr = 0; RealType bestcorr = 0; unsigned int bestseed = 1; for( unsigned int seeder = 1; seeder < 100; seeder++ ) { totalcorr = this->InitializeSCCA( n_vecs, seeder ); if( totalcorr > bestcorr ) { bestseed = seeder; bestcorr = totalcorr; } totalcorr = 0; } if ( ! this->m_Silent ) std::cout << " Best initial corr " << bestcorr << std::endl; this->InitializeSCCA( n_vecs, bestseed ); const unsigned int maxloop = this->m_MaximumNumberOfIterations; unsigned int loop = 0; bool energyincreases = true; RealType energy = 0, lastenergy = 0; while( ( ( loop < maxloop ) && ( energyincreases ) ) || loop < 1 ) // { // Arnoldi Iteration SCCA for( unsigned int k = 0; k < n_vecs; k++ ) { VectorType ptemp = this->m_VariatesP.get_column(k); VectorType qtemp = this->m_VariatesQ.get_column(k); VectorType pveck = this->m_MatrixQ * qtemp; VectorType qveck = this->m_MatrixP * ptemp; pveck = this->m_MatrixP.transpose() * pveck; qveck = this->m_MatrixQ.transpose() * qveck; for( unsigned int j = 0; j < k; j++ ) { VectorType qj = this->m_VariatesP.get_column(j); pveck = this->Orthogonalize( pveck, qj ); qj = this->m_VariatesQ.get_column(j); qveck = this->Orthogonalize( qveck, qj ); } RealType mup = inner_product( this->m_MatrixP * pveck, this->m_MatrixP * pveck ) / pveck.two_norm(); RealType muq = inner_product( this->m_MatrixQ * qveck, this->m_MatrixQ * qveck ) / qveck.two_norm(); this->IHTRegression( this->m_MatrixP, ptemp, pveck, mup, 5, 0, true, false ); this->IHTRegression( this->m_MatrixQ, qtemp, qveck, muq, 5, 0, false, false ); this->m_VariatesP.set_column( k, ptemp ); this->m_VariatesQ.set_column( k, qtemp ); this->NormalizeWeightsByCovariance( k, 0.05, 0.05 ); VectorType proj1 = this->m_MatrixP * this->m_VariatesP.get_column( k ); VectorType proj2 = this->m_MatrixQ * this->m_VariatesQ.get_column( k ); this->m_CanonicalCorrelations[k] = this->PearsonCorr( proj1, proj2 ); } this->SortResults( n_vecs ); lastenergy = energy; energy = this->m_CanonicalCorrelations.one_norm() / n_vecs; if ( ! this->m_Silent ) std::cout << " Loop " << loop << " Corrs : " << this->m_CanonicalCorrelations << " CorrMean : " << energy << std::endl; if( vnl_math_abs( energy - lastenergy ) < 1.e-8 || energy < lastenergy ) { energyincreases = false; } else { energyincreases = true; } loop++; } // outer loop this->SortResults(n_vecs); // this->RunDiagnostics(n_vecs); double ccasum = 0; for( unsigned int i = 0; i < this->m_CanonicalCorrelations.size(); i++ ) { ccasum += fabs(this->m_CanonicalCorrelations[i]); } if( n_vecs_in > 1 ) { return ccasum; } else { return fabs(this->m_CanonicalCorrelations[0]); } } template void antsSCCANObject ::WhitenDataSetForRunSCCANMultiple(unsigned int nvecs) { if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " now whiten and apply R " << std::endl; } if( this->m_OriginalMatrixR.size() > 0 || nvecs > 0 ) { this->m_MatrixP = this->NormalizeMatrix(this->m_OriginalMatrixP); if( this->m_VariatesP.size() > 0 ) { this->m_MatrixRp.set_size(this->m_MatrixP.rows(), this->m_OriginalMatrixR.cols() + nvecs); this->m_MatrixRp.fill(0); if( this->m_OriginalMatrixR.size() > 0 && ( this->m_SCCANFormulation == PminusRQ || this->m_SCCANFormulation == PminusRQminusR ) ) { this->m_MatrixRp.set_columns(0, this->m_OriginalMatrixR); this->m_MatrixRp.set_columns(this->m_OriginalMatrixR.cols(), this->m_MatrixP * (this->m_VariatesP.get_n_columns(0, nvecs) ) ); } else { this->m_MatrixRp.set_columns(0, this->m_MatrixP * (this->m_VariatesP.get_n_columns(0, nvecs) ) ); } } else { this->m_MatrixRp = this->NormalizeMatrix(this->m_OriginalMatrixR); } this->m_MatrixRp = ProjectionMatrix(this->m_MatrixRp); if( this->m_Debug && this->m_VariatesP.cols() > 1 ) { if ( ! this->m_Silent ) std::cout << " corr-pre " << this->PearsonCorr( (this->m_OriginalMatrixP * this->m_VariatesP).get_column(0), (this->m_OriginalMatrixP * this->m_VariatesP).get_column(1) ) << std::endl; if ( ! this->m_Silent ) std::cout << " corr-post " << this->PearsonCorr( ( this-> m_MatrixP * this->m_VariatesP).get_column(0), (this->m_MatrixP * this->m_VariatesP).get_column(1) ) << std::endl; } this->m_MatrixQ = this->NormalizeMatrix(this->m_OriginalMatrixQ); if( this->m_VariatesQ.size() > 0 ) { this->m_MatrixRq.set_size(this->m_MatrixQ.rows(), this->m_OriginalMatrixR.cols() + nvecs); this->m_MatrixRq.fill(0); if( this->m_OriginalMatrixR.size() > 0 && ( this->m_SCCANFormulation == PQminusR || this->m_SCCANFormulation == PminusRQminusR ) ) { this->m_MatrixRq.set_columns(0, this->m_OriginalMatrixR); this->m_MatrixRq.set_columns(this->m_OriginalMatrixR.cols(), this->m_MatrixQ * (this->m_VariatesQ.get_n_columns(0, nvecs) ) ); } else { this->m_MatrixRq.set_columns(0, this->m_MatrixQ * (this->m_VariatesQ.get_n_columns(0, nvecs) ) ); } } else { this->m_MatrixRq = this->NormalizeMatrix(this->m_OriginalMatrixR); } this->m_MatrixRq = ProjectionMatrix(this->m_MatrixRq); } else { this->m_MatrixP = this->NormalizeMatrix(this->m_OriginalMatrixP); this->m_MatrixQ = this->NormalizeMatrix(this->m_OriginalMatrixQ); // this->m_MatrixP=this->WhitenMatrix(this->m_MatrixP); // this->m_MatrixQ=this->WhitenMatrix(this->m_MatrixQ); } if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " whiten and apply R done " << std::endl; } } template void antsSCCANObject ::NormalizeWeights(const unsigned int k ) { this->m_WeightsP = this->m_VariatesP.get_column( k ); this->m_WeightsP = this->m_WeightsP / sqrt(this->m_WeightsP.two_norm()); // to scale gradient down this->m_VariatesP.set_column( k, this->m_WeightsP ); this->m_WeightsQ = this->m_VariatesQ.get_column( k ); this->m_WeightsQ = this->m_WeightsQ / sqrt(this->m_WeightsQ.two_norm()); // to scale gradient down this->m_VariatesQ.set_column( k, this->m_WeightsQ ); } template void antsSCCANObject ::NormalizeWeightsByCovariance(const unsigned int k, const TRealType taup, const TRealType tauq) { // for ( unsigned int k=0; km_VariatesP.cols(); k++) { this->m_WeightsP = this->m_VariatesP.get_column( k ); this->m_WeightsQ = this->m_VariatesQ.get_column( k ); RealType normP = 0; if( this->m_MatrixRp.size() > 0 ) { VectorType w = this->m_MatrixP * this->m_WeightsP; normP = inner_product( w, (this->m_MatrixP - this->m_MatrixRp * this->m_MatrixP) * this->m_WeightsP ); } else { // v^t ( X^t X + k * Id ) v = v^t ( X X^t v + k * Id * v ) // = v^t ( X X^t v + k * Id * v ) vnl_diag_matrix regdiagp( this->m_MatrixP.cols(), taup ); VectorType w = this->m_MatrixP.transpose() * ( this->m_MatrixP * this->m_WeightsP ) + regdiagp * this->m_WeightsP; normP = inner_product( this->m_WeightsP, w ); } if( normP > 0 ) { if( this->m_Debug ) if ( ! this->m_Silent ) std::cout << "normP " << normP ; this->m_WeightsP = this->m_WeightsP / sqrt(normP); this->m_VariatesP.set_column( k, this->m_WeightsP ); } RealType normQ = 0; if( this->m_MatrixRq.size() > 0 ) { VectorType w = this->m_MatrixQ * this->m_WeightsQ; normQ = inner_product( w, (this->m_MatrixQ - this->m_MatrixRq * this->m_MatrixQ) * this->m_WeightsQ ); } else { vnl_diag_matrix regdiagq( this->m_MatrixQ.cols(), tauq ); VectorType w = this->m_MatrixQ.transpose() * ( this->m_MatrixQ * this->m_WeightsQ ) + regdiagq * this->m_WeightsQ; normQ = inner_product( this->m_WeightsQ, w ); } if( normQ > 0 ) { if( this->m_Debug ) if ( ! this->m_Silent ) std::cout << " normQ " << normQ << " "; this->m_WeightsQ = this->m_WeightsQ / sqrt(normQ); this->m_VariatesQ.set_column( k, this->m_WeightsQ ); } } } template TRealType antsSCCANObject ::RunSCCAN2multiple( unsigned int n_vecs ) { this->m_Debug = false; // this->m_Debug=true; if ( ! this->m_Silent ) std::cout << " power iteration (partial) scca " << std::endl; this->m_CanonicalCorrelations.set_size(n_vecs); this->m_CanonicalCorrelations.fill(0); RealType truecorr = 0; unsigned int nr1 = this->m_MatrixP.rows(); unsigned int nr2 = this->m_MatrixQ.rows(); this->m_VariatesP.set_size(0, 0); this->m_VariatesQ.set_size(0, 0); if( nr1 != nr2 ) { if ( ! this->m_Silent ) std::cout << " P rows " << this->m_MatrixP.rows() << " cols " << this->m_MatrixP.cols() << std::endl; if ( ! this->m_Silent ) std::cout << " Q rows " << this->m_MatrixQ.rows() << " cols " << this->m_MatrixQ.cols() << std::endl; if ( ! this->m_Silent ) std::cout << " R rows " << this->m_MatrixR.rows() << " cols " << this->m_MatrixR.cols() << std::endl; if ( ! this->m_Silent ) std::cout << " N-rows for MatrixP does not equal N-rows for MatrixQ " << nr1 << " vs " << nr2 << std::endl; std::exception(); } if( !this->m_AlreadyWhitened ) { if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " whiten " << std::endl; } this->WhitenDataSetForRunSCCANMultiple(); this->m_AlreadyWhitened = true; if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " whiten done " << std::endl; } } this->m_VariatesP.set_size(this->m_MatrixP.cols(), n_vecs); this->m_VariatesQ.set_size(this->m_MatrixQ.cols(), n_vecs); for( unsigned int kk = 0; kk < n_vecs; kk++ ) { this->m_VariatesP.set_column(kk, this->InitializeV(this->m_MatrixP) ); this->m_VariatesQ.set_column(kk, this->InitializeV(this->m_MatrixQ) ); } // begin computing solution for( unsigned int makesparse = 1; makesparse < 2; makesparse++ ) { unsigned int which_e_vec = 0; bool notdone = true; while( notdone ) { if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " get canonical variate number " << which_e_vec + 1 << std::endl; } double initcorr = 1.e-5; truecorr = initcorr; double deltacorr = 1, lastcorr = initcorr * 0.5; this->m_WeightsP = this->m_VariatesP.get_column(which_e_vec); this->m_WeightsQ = this->m_VariatesQ.get_column(which_e_vec); unsigned long its = 0, min_its = 5; if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " Begin " << std::endl; } while( (itsm_MaximumNumberOfIterations && deltacorr> this->m_ConvergenceThreshold) || its < min_its ) { if( its == 0 ) { this->WhitenDataSetForRunSCCANMultiple(which_e_vec); } bool doorth = true; // this->m_Debug=true; { VectorType proj = this->m_MatrixQ * this->m_WeightsQ; if( this->m_MatrixRp.size() > 0 && ( this->m_SCCANFormulation == PminusRQ || this->m_SCCANFormulation == PminusRQminusR ) ) { this->m_WeightsP = this->m_MatrixP.transpose() * (proj - this->m_MatrixRp * proj); } else { this->m_WeightsP = this->m_MatrixP.transpose() * (proj); } if( doorth ) { for( unsigned int kk = 0; kk < which_e_vec; kk++ ) { this->m_WeightsP = this->Orthogonalize(this->m_WeightsP, this->m_VariatesP.get_column( kk), &this->m_MatrixP, &this->m_MatrixP); } } this->m_WeightsP = this->SoftThreshold( this->m_WeightsP, this->m_FractionNonZeroP, !this->m_KeepPositiveP ); if( its > 0 ) { this->m_WeightsP = this->ClusterThresholdVariate( this->m_WeightsP, this->m_MaskImageP ); } if( which_e_vec > 0 && this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " p orth-b " << this->PearsonCorr( this->m_MatrixP * this->m_WeightsP, this->m_MatrixP * this->m_VariatesP.get_column( 0) ) << std::endl; } } VectorType projp = this->m_MatrixQ * this->m_WeightsQ; VectorType projq = this->m_MatrixP * this->m_WeightsP; { VectorType proj = this->m_MatrixP * this->m_WeightsP; if( this->m_MatrixRq.size() > 0 && ( this->m_SCCANFormulation == PQminusR || this->m_SCCANFormulation == PminusRQminusR ) ) { this->m_WeightsQ = this->m_MatrixQ.transpose() * (proj - this->m_MatrixRq * proj); } else { this->m_WeightsQ = this->m_MatrixQ.transpose() * (proj); } if( doorth ) { for( unsigned int kk = 0; kk < which_e_vec; kk++ ) { this->m_WeightsQ = this->Orthogonalize(this->m_WeightsQ, this->m_VariatesQ.get_column( kk), &this->m_MatrixQ, &this->m_MatrixQ); } } this->m_WeightsQ = this->SoftThreshold( this->m_WeightsQ, this->m_FractionNonZeroQ, !this->m_KeepPositiveQ ); if( which_e_vec > 0 && this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " q orth-b " << this->PearsonCorr( this->m_MatrixQ * this->m_WeightsQ, this->m_MatrixQ * this->m_VariatesQ.get_column( 0) ) << std::endl; } } this->m_WeightsP = this->m_MatrixP.transpose() * (projp); this->m_WeightsQ = this->m_MatrixQ.transpose() * (projq); this->ReSoftThreshold( this->m_WeightsP, this->m_FractionNonZeroP, this->m_KeepPositiveP ); this->ReSoftThreshold( this->m_WeightsQ, this->m_FractionNonZeroQ, this->m_KeepPositiveQ ); if( its > 1 ) { this->m_WeightsP = this->ClusterThresholdVariate( this->m_WeightsP, this->m_MaskImageP, this->m_MinClusterSizeP ); this->m_WeightsQ = this->ClusterThresholdVariate( this->m_WeightsQ, this->m_MaskImageQ, this->m_MinClusterSizeQ ); } for( unsigned int kk = 0; kk < which_e_vec; kk++ ) { this->m_WeightsP = this->Orthogonalize(this->m_WeightsP, this->m_VariatesP.get_column( kk), &this->m_MatrixP, &this->m_MatrixP); } if( ( this->m_SCCANFormulation == PminusRQ || this->m_SCCANFormulation == PminusRQminusR ) ) { for( unsigned int kk = 0; kk < this->m_OriginalMatrixR.cols(); kk++ ) { this->m_WeightsP = this->Orthogonalize(this->m_WeightsP, this->m_MatrixR.get_column( kk) * this->m_MatrixP, &this->m_MatrixP, &this->m_MatrixP); } } for( unsigned int kk = 0; kk < which_e_vec; kk++ ) { this->m_WeightsQ = this->Orthogonalize(this->m_WeightsQ, this->m_VariatesQ.get_column( kk), &this->m_MatrixQ, &this->m_MatrixQ); } if( ( this->m_SCCANFormulation == PQminusR || this->m_SCCANFormulation == PminusRQminusR ) ) { for( unsigned int kk = 0; kk < this->m_OriginalMatrixR.cols(); kk++ ) { this->m_WeightsQ = this->Orthogonalize(this->m_WeightsQ, this->m_MatrixR.get_column( kk) * this->m_MatrixQ, &this->m_MatrixQ, &this->m_MatrixQ); } } this->NormalizeWeightsByCovariance(which_e_vec); this->m_VariatesP.set_column(which_e_vec, this->m_WeightsP); this->m_VariatesQ.set_column(which_e_vec, this->m_WeightsQ); truecorr = this->PearsonCorr( this->m_MatrixP * this->m_WeightsP, this->m_MatrixQ * this->m_WeightsQ ); if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " corr " << truecorr << " it " << its << std::endl; } deltacorr = fabs(truecorr - lastcorr); lastcorr = truecorr; ++its; this->m_CanonicalCorrelations[which_e_vec] = truecorr; if ( ! this->m_Silent ) std::cout << " canonical variate number " << which_e_vec + 1 << " corr " << this->m_CanonicalCorrelations[which_e_vec] << " kept cluster " << this->m_KeptClusterSize << std::endl; } // inner_it if( fabs(truecorr) < 1.e-2 || (which_e_vec + 1) == n_vecs ) { notdone = false; } else { which_e_vec++; } } if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " done with loop " << std::endl; } std::vector evals(n_vecs, 0); std::vector oevals(n_vecs, 0); for( unsigned long j = 0; j < n_vecs; ++j ) { RealType val = fabs(this->m_CanonicalCorrelations[j]); evals[j] = val; oevals[j] = val; } // sort and reindex the eigenvectors/values sort(evals.begin(), evals.end() , std::greater() ); std::vector sorted_indices(n_vecs, -1); for( unsigned int i = 0; i < evals.size(); i++ ) { for( unsigned int j = 0; j < evals.size(); j++ ) { if( evals[i] == oevals[j] && sorted_indices[i] == -1 ) { sorted_indices[i] = j; oevals[j] = 0; } } } VectorType newcorrs(n_vecs, 0); MatrixType varp(this->m_MatrixP.cols(), n_vecs, 0); MatrixType varq(this->m_MatrixQ.cols(), n_vecs, 0); for( unsigned int i = 0; i < n_vecs; i++ ) { varp.set_column(i, this->m_VariatesP.get_column( sorted_indices[i] ) ); varq.set_column(i, this->m_VariatesQ.get_column( sorted_indices[i] ) ); newcorrs[i] = (this->m_CanonicalCorrelations[sorted_indices[i]]); } for( unsigned int i = 0; i < n_vecs; i++ ) { this->m_VariatesP.set_column(i, varp.get_column( i ) ); this->m_VariatesQ.set_column(i, varq.get_column( i ) ); } this->m_CanonicalCorrelations = newcorrs; // this->RunDiagnostics(n_vecs); } // makesparse RealType corrsum = 0; for( unsigned int i = 0; i < this->m_CanonicalCorrelations.size(); i++ ) { corrsum += fabs(this->m_CanonicalCorrelations[i]); } return this->m_CanonicalCorrelations[0]; // corrsum; } template TRealType antsSCCANObject ::RunSCCAN2() { RealType truecorr = 0; unsigned int nr1 = this->m_MatrixP.rows(); unsigned int nr2 = this->m_MatrixQ.rows(); if( nr1 != nr2 ) { if ( ! this->m_Silent ) std::cout << " P rows " << this->m_MatrixP.rows() << " cols " << this->m_MatrixP.cols() << std::endl; if ( ! this->m_Silent ) std::cout << " Q rows " << this->m_MatrixQ.rows() << " cols " << this->m_MatrixQ.cols() << std::endl; if ( ! this->m_Silent ) std::cout << " R rows " << this->m_MatrixR.rows() << " cols " << this->m_MatrixR.cols() << std::endl; if ( ! this->m_Silent ) std::cout << " N-rows for MatrixP does not equal N-rows for MatrixQ " << nr1 << " vs " << nr2 << std::endl; std::exception(); } else { // if ( ! this->m_Silent ) std::cout << " P-positivity constraints? " << this->m_KeepPositiveP << " frac " << this->m_FractionNonZeroP // << " // Q-positivity constraints? " << m_KeepPositiveQ << " frac " << this->m_FractionNonZeroQ << std::endl; } this->m_WeightsP = this->InitializeV(this->m_MatrixP); this->m_WeightsQ = this->InitializeV(this->m_MatrixQ); // if ( !this->m_AlreadyWhitened ) { if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " norm P " << std::endl; } this->m_MatrixP = this->NormalizeMatrix(this->m_MatrixP); if( this->m_Debug ) { if ( ! this->m_Silent ) std::cout << " norm Q " << std::endl; } this->m_MatrixQ = this->NormalizeMatrix(this->m_MatrixQ); if( this->m_OriginalMatrixR.size() > 0 ) { this->m_MatrixR = this->NormalizeMatrix(this->m_OriginalMatrixR); this->m_MatrixR = this->WhitenMatrix(this->m_MatrixR); this->m_MatrixRRt = this->m_MatrixR * this->m_MatrixR.transpose(); this->UpdatePandQbyR(); } this->m_MatrixP = this->WhitenMatrix(this->m_MatrixP); this->m_MatrixQ = this->WhitenMatrix(this->m_MatrixQ); this->m_AlreadyWhitened = true; } for( unsigned int outer_it = 0; outer_it < 2; outer_it++ ) { truecorr = 0; double deltacorr = 1, lastcorr = 1; unsigned long its = 0; while( itsm_MaximumNumberOfIterations && deltacorr> this->m_ConvergenceThreshold ) { this->m_WeightsP = this->TrueCCAPowerUpdate(this->m_FractionNonZeroP, this->m_MatrixP, this->m_WeightsQ, this->m_MatrixQ, this->m_KeepPositiveP, false); this->m_WeightsQ = this->TrueCCAPowerUpdate(this->m_FractionNonZeroQ, this->m_MatrixQ, this->m_WeightsP, this->m_MatrixP, this->m_KeepPositiveQ, false); truecorr = this->PearsonCorr( this->m_MatrixP * this->m_WeightsP, this->m_MatrixQ * this->m_WeightsQ ); deltacorr = fabs(truecorr - lastcorr); lastcorr = truecorr; ++its; } // inner_it } // outer_it this->m_CorrelationForSignificanceTest = truecorr; return truecorr; } template TRealType antsSCCANObject ::RunSCCAN3() { unsigned int nc1 = this->m_MatrixP.rows(); unsigned int nc2 = this->m_MatrixQ.rows(); unsigned int nc3 = this->m_MatrixR.rows(); if( nc1 != nc2 || nc1 != nc3 || nc3 != nc2 ) { if ( ! this->m_Silent ) std::cout << " P rows " << this->m_MatrixP.rows() << " cols " << this->m_MatrixP.cols() << std::endl; if ( ! this->m_Silent ) std::cout << " Q rows " << this->m_MatrixQ.rows() << " cols " << this->m_MatrixQ.cols() << std::endl; if ( ! this->m_Silent ) std::cout << " R rows " << this->m_MatrixR.rows() << " cols " << this->m_MatrixR.cols() << std::endl; if ( ! this->m_Silent ) std::cout << " N-rows do not match " << std::endl; std::exception(); } this->m_WeightsP = this->InitializeV(this->m_MatrixP); this->m_WeightsQ = this->InitializeV(this->m_MatrixQ); this->m_WeightsR = this->InitializeV(this->m_MatrixR); if( !this->m_AlreadyWhitened ) { this->m_MatrixP = this->NormalizeMatrix(this->m_MatrixP); this->m_MatrixP = this->WhitenMatrix(this->m_MatrixP); this->m_MatrixQ = this->NormalizeMatrix(this->m_MatrixQ); this->m_MatrixQ = this->WhitenMatrix(this->m_MatrixQ); this->m_MatrixR = this->NormalizeMatrix(this->m_MatrixR); this->m_MatrixR = this->WhitenMatrix(this->m_MatrixR); this->m_AlreadyWhitened = true; } RealType truecorr = 0; RealType norm = 0, deltacorr = 1, lastcorr = 1; unsigned long its = 0; while( itsm_MaximumNumberOfIterations && deltacorr> this->m_ConvergenceThreshold ) { /** for sparse mcca * w_i \leftarrow \frac{ S( X_i^T ( \sum_{j \ne i} X_j w_j ) }{norm of above } */ this->m_WeightsP = this->m_MatrixP.transpose() * (this->m_MatrixQ * this->m_WeightsQ + this->m_MatrixR * this->m_WeightsR); this->ReSoftThreshold( this->m_WeightsP, this->m_FractionNonZeroP, this->m_KeepPositiveP); norm = this->m_WeightsP.two_norm(); this->m_WeightsP = this->m_WeightsP / (norm); this->m_WeightsQ = this->m_MatrixQ.transpose() * (this->m_MatrixP * this->m_WeightsP + this->m_MatrixR * this->m_WeightsR); this->ReSoftThreshold( this->m_WeightsQ, this->m_FractionNonZeroQ, this->m_KeepPositiveQ); norm = this->m_WeightsQ.two_norm(); this->m_WeightsQ = this->m_WeightsQ / (norm); this->m_WeightsR = this->m_MatrixR.transpose() * (this->m_MatrixP * this->m_WeightsP + this->m_MatrixQ * this->m_WeightsQ); this->ReSoftThreshold( this->m_WeightsR, this->m_FractionNonZeroR, this->m_KeepPositiveR); norm = this->m_WeightsR.two_norm(); this->m_WeightsR = this->m_WeightsR / (norm); VectorType pvec = this->m_MatrixP * this->m_WeightsP; VectorType qvec = this->m_MatrixQ * this->m_WeightsQ; VectorType rvec = this->m_MatrixR * this->m_WeightsR; double corrpq = this->PearsonCorr( pvec, qvec ); double corrpr = this->PearsonCorr( pvec, rvec ); double corrqr = this->PearsonCorr( rvec, qvec ); truecorr = corrpq + corrpr + corrqr; deltacorr = fabs(truecorr - lastcorr); lastcorr = truecorr; // if ( ! this->m_Silent ) std::cout << " correlation of projections: pq " << corrpq << " pr " << corrpr << " qr " << corrqr << " // at-it " << // its << std::endl; its++; } // if ( ! this->m_Silent ) std::cout << " PNZ-Frac " << this->CountNonZero(this->m_WeightsP) << std::endl; // if ( ! this->m_Silent ) std::cout << " QNZ-Frac " << this->CountNonZero(this->m_WeightsQ) << std::endl; // if ( ! this->m_Silent ) std::cout << " RNZ-Frac " << this->CountNonZero(this->m_WeightsR) << std::endl; this->m_CorrelationForSignificanceTest = truecorr; return truecorr; } template void antsSCCANObject ::MRFFilterVariateMatrix() { // 1. compute the label for each voxel --- the label is the col + 1 // recall this->m_VariatesP.set_size(this->m_MatrixP.cols(), n_vecs); VectorType labels( this->m_MatrixP.cols(), 0 ); VectorType maxweight( this->m_MatrixP.cols(), 0 ); unsigned int n_vecs = this->m_VariatesP.cols(); for( unsigned int i = 0; i < this->m_MatrixP.cols(); i++ ) { RealType maxval = 0; VectorType pvec = this->m_VariatesP.get_row( i ); for( unsigned int j = 0; j < pvec.size(); j++ ) { /** FIXME should this be fabs or not? */ // RealType val = fabs( this->m_VariatesP( j , i ) ); RealType val = pvec( j ); if( val > maxval ) { maxval = val; labels( i ) = ( j + 1 ); maxweight( i ) = val; } } } typename TInputImage::Pointer flabelimage = this->ConvertVariateToSpatialImage( labels, this->m_MaskImageP, false ); typename TInputImage::Pointer maxweightimage = this->ConvertVariateToSpatialImage( maxweight, this->m_MaskImageP, false ); typedef unsigned int LabelType; typedef itk::Image LabelImageType; typename LabelImageType::Pointer labelimage = LabelImageType::New(); labelimage->SetRegions( flabelimage->GetRequestedRegion() ); labelimage->CopyInformation( flabelimage ); labelimage->Allocate(); labelimage->FillBuffer( 0 ); typename LabelImageType::Pointer maskimage = LabelImageType::New(); maskimage->SetRegions( flabelimage->GetRequestedRegion() ); maskimage->CopyInformation( flabelimage ); maskimage->Allocate(); maskimage->FillBuffer( 0 ); itk::ImageRegionConstIterator Itf( flabelimage, flabelimage->GetLargestPossibleRegion() ); for( Itf.GoToBegin(); !Itf.IsAtEnd(); ++Itf ) { if( this->m_MaskImageP->GetPixel( Itf.GetIndex() ) > 0 ) { labelimage->SetPixel( Itf.GetIndex(), static_cast( Itf.Get() + 0.5 ) ); maskimage->SetPixel( Itf.GetIndex(), 1 ); } } // simple MRF style update for( unsigned int mrfct = 0; mrfct < 2; mrfct++ ) { typedef itk::NeighborhoodIterator iteratorType; typename iteratorType::RadiusType rad; rad.Fill(1); iteratorType GHood(rad, labelimage, labelimage->GetLargestPossibleRegion() ); GHood.GoToBegin(); while( !GHood.IsAtEnd() ) { VectorType countlabels( n_vecs, 0 ); LabelType p = GHood.GetCenterPixel(); if( p >= 0.5 && this->m_MaskImageP->GetPixel( GHood.GetIndex() ) >= 0.5 ) { for( unsigned int i = 0; i < GHood.Size(); i++ ) { LabelType p2 = GHood.GetPixel(i); if( p2 > 0 ) { countlabels[p2 - 1] = countlabels[p2 - 1] + 1; } } LabelType jj = 0; unsigned long maxlabcount = 0; for( unsigned int i = 0; i < countlabels.size(); i++ ) { if( countlabels[i] > maxlabcount ) { jj = i + 1; maxlabcount = countlabels[i]; } } GHood.SetCenterPixel( jj ); } ++GHood; } } // mrfct unsigned int vecind = 0; for( Itf.GoToBegin(); !Itf.IsAtEnd(); ++Itf ) { if( this->m_MaskImageP->GetPixel( Itf.GetIndex() ) >= 0.5 ) { labels( vecind ) = labelimage->GetPixel( Itf.GetIndex() ); vecind++; } } { typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( labelimage ); writer->SetFileName( "label1.nii.gz" ); writer->Update(); } { typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( maxweightimage ); writer->SetFileName( "weight.nii.gz" ); writer->Update(); } for( unsigned int i = 0; i < this->m_MatrixP.cols(); i++ ) { VectorType pvec = this->m_VariatesP.get_row( i ); LabelType lab = static_cast( labels( i ) + 0.5 ); if( lab > 0 ) { lab = lab - 1; } for( unsigned int j = 0; j < pvec.size(); j++ ) { if( j != lab ) { pvec( j ) = 0; } } pvec = pvec / pvec.two_norm(); this->m_VariatesP.set_row( i, pvec ); } /* // 2. now do a simple MRF style update typedef itk::ants::AtroposSegmentationImageFilter SegmentationFilterType; typename SegmentationFilterType::Pointer segmenter = SegmentationFilterType::New(); typename SegmentationFilterType::ArrayType radius; radius.Fill( 1 ); segmenter->SetMinimizeMemoryUsage( false ); segmenter->SetNumberOfTissueClasses( this->m_VariatesP.cols() ); segmenter->SetInitializationStrategy( SegmentationFilterType::PriorLabelImage ); segmenter->SetPriorProbabilityWeight( 0.5 ); segmenter->SetPriorLabelImage( labelimage ); segmenter->SetPosteriorProbabilityFormulation( SegmentationFilterType::Socrates ); segmenter->SetMaximumNumberOfIterations( 5 ); segmenter->SetConvergenceThreshold( 0 ); segmenter->SetMaskImage( maskimage ); // Check to see that the labels in the prior label image or the non-zero // probability voxels in the prior probability images encompass the entire // mask region. if( segmenter->GetInitializationStrategy() == SegmentationFilterType::PriorLabelImage ) { itk::ImageRegionConstIterator ItM( segmenter->GetMaskImage(), segmenter->GetMaskImage()->GetLargestPossibleRegion() ); itk::ImageRegionConstIterator ItP( segmenter->GetPriorLabelImage(), segmenter->GetPriorLabelImage()->GetLargestPossibleRegion() ); for( ItM.GoToBegin(), ItP.GoToBegin(); !ItM.IsAtEnd(); ++ItM, ++ItP ) { if( ItM.Get() == segmenter->GetMaskLabel() && ItP.Get() == 0 ) { if ( ! this->m_Silent ) std::cout << std::endl; if ( ! this->m_Silent ) std::cout << "Warning: the labels in the the prior label image do " << "not encompass the entire mask region. As a result each unlabeled voxel will be " << "initially assigned a random label. The user might want to consider " << "various alternative strategies like assigning an additional " << "\"background\" label to the unlabeled voxels or propagating " << "the labels within the mask region." << std::endl; if ( ! this->m_Silent ) std::cout << std::endl; break; } } } segmenter->SetIntensityImage( 0 , maxweightimage ); segmenter->SetMRFSmoothingFactor( 0.2 ); segmenter->SetMRFRadius( radius ); typedef typename SegmentationFilterType::SampleType SampleType; typedef itk::ants::Statistics::GaussianListSampleFunction LikelihoodType; for( unsigned int n = 0; n < segmenter->GetNumberOfTissueClasses(); n++ ) { typename LikelihoodType::Pointer gaussianLikelihood = LikelihoodType::New(); segmenter->SetLikelihoodFunction( n, gaussianLikelihood ); } segmenter->SetUsePartialVolumeLikelihoods( false ); segmenter->Update(); typename LabelImageType::Pointer labelimage2 = segmenter->GetOutput(); { typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetInput( labelimage2 ); writer->SetFileName( "label2.nii.gz" ); writer->Update(); } */ } } // namespace ants } // namespace itk /* if (its == 0 ) if ( which_e_vec > 0 && false ) { // here, factor out previous evecs globally MatrixType temp; MatrixType pp; unsigned int basect=this->m_MatrixR.columns(); basect=0; pp.set_size(this->m_MatrixP.rows(),which_e_vec+basect); // for (unsigned int kk=0; kkm_MatrixR.columns(); kk++) // pp.set_column(kk,this->m_MatrixR.get_column(kk)); unsigned int colcount=0; //this->m_MatrixR.columns(); for (unsigned int kk=which_e_vec-1; kkm_MatrixP*this->m_VariatesP.get_column(kk)); colcount++; } temp=this->NormalizeMatrix(pp); temp=this->WhitenMatrix(temp); temp=temp*temp.transpose(); this->m_MatrixP=(this->m_MatrixP-temp*this->m_MatrixP); MatrixType qq; qq.set_size(this->m_MatrixQ.rows(),which_e_vec+this->m_MatrixR.columns()); for (unsigned int kk=0; kkm_MatrixR.columns(); kk++) qq.set_column(kk,this->m_MatrixR.get_column(kk)); colcount=this->m_MatrixR.columns(); for (unsigned int kk=which_e_vec-1; kkm_MatrixQ*this->m_VariatesQ.get_column(kk)); colcount++; } temp=this->NormalizeMatrix(qq); temp=this->WhitenMatrix(temp); temp=temp*temp.transpose(); this->m_MatrixQ=(this->m_MatrixQ-temp*this->m_MatrixQ); } //m_Debug=true; double ip=1; unsigned long ct=0,max_ip_its=50; double deltaip=1,lastip=0; while ( (deltaip) > 1.e-3 && ct < max_ip_its && which_e_vec > 0 || ct < 4 ) { ip=0; this->ReSoftThreshold( this->m_WeightsP , this->m_FractionNonZeroP , this->m_KeepPositiveP ); VectorType ptem=this->m_WeightsP; if ( which_e_vec >= 1 ) ptem=this->Orthogonalize(ptem,this->m_VariatesP.get_column(0),&this->m_MatrixP); if ( which_e_vec >= 2 ) ptem=this->Orthogonalize(ptem,this->m_VariatesP.get_column(1),&this->m_MatrixP); this->m_WeightsP=ptem; this->ReSoftThreshold( this->m_WeightsP , this->m_FractionNonZeroP , this->m_KeepPositiveP ); ip+=this->PearsonCorr(this->m_MatrixP*this->m_WeightsP,this->m_MatrixP*this->m_VariatesP.get_column(0)); if ( which_e_vec >= 2) ip+=this->PearsonCorr(this->m_MatrixP*this->m_WeightsP,this->m_MatrixP*this->m_VariatesP.get_column(1)); deltaip=fabs(lastip)-fabs(ip); lastip=ip; ct++; if ( this->m_Debug ) if ( ! this->m_Silent ) std::cout << " pip-b " << ip << " delt " << deltaip << std::endl; } ip=1; ct=0; deltaip=1;lastip=0; while ( (deltaip) > 1.e-3 && ct < max_ip_its && which_e_vec > 0 || ct < 4 ) { ip=0; this->ReSoftThreshold( this->m_WeightsQ , this->m_FractionNonZeroQ , this->m_KeepPositiveQ ); VectorType ptem=this->m_WeightsQ; if ( which_e_vec >= 1 ) ptem=this->Orthogonalize(ptem,this->m_VariatesQ.get_column(0),&this->m_MatrixQ); if ( which_e_vec >= 2 ) ptem=this->Orthogonalize(ptem,this->m_VariatesQ.get_column(1),&this->m_MatrixQ); this->m_WeightsQ=ptem; this->ReSoftThreshold( this->m_WeightsQ , this->m_FractionNonZeroQ , this->m_KeepPositiveQ ); ip+=this->PearsonCorr(this->m_MatrixQ*this->m_WeightsQ,this->m_MatrixQ*this->m_VariatesQ.get_column(0)); if ( which_e_vec >= 2) ip+=this->PearsonCorr(this->m_MatrixQ*this->m_WeightsQ,this->m_MatrixQ*this->m_VariatesQ.get_column(1)); deltaip=fabs(lastip)-fabs(ip); lastip=ip; ct++; if ( this->m_Debug ) if ( ! this->m_Silent ) std::cout << " qip-b " << ip << " delt " << deltaip << std::endl; } // alternative tools for factoring out evecs // if ( its == 0 && which_e_vec > 0 ) { // this->WhitenDataSetForRunSCCANMultiple(); q_evecs_factor=this->m_MatrixQ*this->m_VariatesQ.get_n_columns(0,which_e_vec); q_evecs_factor=this->NormalizeMatrix( q_evecs_factor ); q_evecs_factor=this->WhitenMatrix(q_evecs_factor); q_evecs_factor=q_evecs_factor*q_evecs_factor.transpose(); // MatrixType temp=this->m_MatrixP-q_evecs_factor*this->m_MatrixP; // temp=this->InverseCovarianceMatrix(temp,&this->m_MatrixP); // this->m_MatrixP=temp; p_evecs_factor=this->m_MatrixP*this->m_VariatesP.get_n_columns(0,which_e_vec); p_evecs_factor=this->NormalizeMatrix( p_evecs_factor ); p_evecs_factor=this->WhitenMatrix(p_evecs_factor); p_evecs_factor=p_evecs_factor*p_evecs_factor.transpose(); // temp=this->m_MatrixQ-p_evecs_factor*this->m_MatrixQ; // temp=this->InverseCovarianceMatrix(temp,&this->m_MatrixQ); // this->m_MatrixQ=temp; } */ /* bool precond = true; MatrixType Cinv; if ( precond ) { // Preconditioned Conjugate Gradient Method // Tuesday 25 July 2006, by Nadir SOUALEM if ( ! this->m_Silent ) std::cout << " begin chol " << std::endl; MatrixType AAt = A * A.transpose() ; MatrixType kcovmat=this->VNLPseudoInverse( this->m_VariatesP.transpose()*this->m_VariatesP vnl_ldl_cholesky chol( AAt ); if ( ! this->m_Silent ) std::cout << " done chol " << std::endl; MatrixType chollow = chol.lower_triangle(); vnl_diag_matrix diag( chol.diagonal() ); vnl_diag_matrix diaginv( chol.diagonal() ); for ( unsigned int i = 0; i < diag.cols(); i++ ) if ( diaginv(i,i) > 1.e-6 ) { diaginv = 1.0 / diaginv( i , i ); chollow( i , i ) = chollow( i , i ) + diag( i, i ); } else diaginv(i,i) = diag(i,i) = 0; if ( ! this->m_Silent ) std::cout << " precon " << std::endl; // preconditioner MatrixType temp = ( chollow * diaginv ) * chollow.transpose(); Cinv = vnl_matrix_inverse( temp ); if ( ! this->m_Silent ) std::cout << " precon done " << std::endl; A = Cinv * A; if ( ! this->m_Silent ) std::cout << " got A precond " << std::endl; } bool dosvdinit = true; if ( n_vecs > ( this->m_MatrixP.rows() - 1 ) ) dosvdinit = false; if ( dosvdinit ) { MatrixType cov = this->m_MatrixP * this->m_MatrixP.transpose(); vnl_svd qr( cov ); matrixB = qr.U().extract( this->m_MatrixP.rows(), n_vecs, 0, 0); this->m_VariatesP = this->m_MatrixP.transpose( ) * matrixB; for( unsigned int i = 0; i < n_vecs; i++ ) { VectorType evec = this->m_VariatesP.get_column( i ); this->SparsifyP( evec ); this->m_VariatesP.set_column( i, evec ); } } else { */ ants-2.2.0/Utilities/itkAdaptiveNonLocalMeansDenoisingImageFilter.h000066400000000000000000000162611311104306400254330ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkAdaptiveNonLocalMeansDenoisingImageFilter_h #define itkAdaptiveNonLocalMeansDenoisingImageFilter_h #include "itkNonLocalPatchBasedImageFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkGaussianOperator.h" namespace itk { /** * \class AdaptiveNonLocalMeansDenoisingImageFilter * \brief Implementation of a denoising image filter. * * \author Jose V. Manjon with ITK porting by Nick Tustison * * Contributed by * * \par REFERENCE * * J. V. Manjon, P. Coupe, Luis Marti-Bonmati, D. L. Collins, * and M. Robles. "Adaptive Non-Local Means Denoising of MR Images With * Spatially Varying Noise Levels, Journal of Magnetic Resonance Imaging, * 31:192-203, June 2010. * * \ingroup ITKNoiseFiltering */ template > class AdaptiveNonLocalMeansDenoisingImageFilter : public NonLocalPatchBasedImageFilter { public: /** Standard class typedefs. */ typedef AdaptiveNonLocalMeansDenoisingImageFilter Self; typedef NonLocalPatchBasedImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Runtime information support. */ itkTypeMacro( AdaptiveNonLocalMeansDenoisingImageFilter, NonLocalPatchBasedImageFilter ); /** Standard New method. */ itkNewMacro( Self ); /** ImageDimension constants */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); /** Some convenient typedefs. */ typedef TInputImage InputImageType; typedef typename InputImageType::PixelType InputPixelType; typedef TOutputImage OutputImageType; typedef typename Superclass::RegionType RegionType; typedef TMaskImage MaskImageType; typedef typename MaskImageType::PixelType MaskPixelType; typedef typename MaskImageType::PixelType LabelType; typedef typename Superclass::RealType RealType; typedef typename Superclass::RealImageType RealImageType; typedef typename Superclass::RealImagePointer RealImagePointer; typedef typename Superclass::IndexType IndexType; typedef typename Superclass::ConstNeighborhoodIteratorType ConstNeighborhoodIteratorType; typedef typename Superclass::NeighborhoodRadiusType NeighborhoodRadiusType; typedef typename Superclass::NeighborhoodOffsetType NeighborhoodOffsetType; typedef GaussianOperator ModifiedBesselCalculatorType; /** * The image expected for input for noise correction. */ void SetInput1( const InputImageType *image ) { this->SetInput( image ); } /** * Set mask image function. If a binary mask image is specified, only * those input image voxels corresponding with the mask image. */ void SetMaskImage( const MaskImageType *mask ) { this->SetNthInput( 1, const_cast( mask ) ); } void SetInput2( const MaskImageType *mask ) { this->SetMaskImage( mask ); } /** * Get mask image function. If a binary mask image is specified, only * those input image voxels corresponding with the mask image. */ const MaskImageType* GetMaskImage() const { return static_cast( this->ProcessObject::GetInput( 1 ) ); } /** * Employ Rician noise model. Otherwise use a Gaussian noise model. * Default = true. */ itkSetMacro( UseRicianNoiseModel, bool ); itkGetConstMacro( UseRicianNoiseModel, bool ); itkBooleanMacro( UseRicianNoiseModel ); /** * Smoothing factor for noise. Default = 1.0. */ itkSetMacro( SmoothingFactor, RealType ); itkGetConstMacro( SmoothingFactor, RealType ); /** * Smoothing variance for Rician noise. Default = 2.0. */ itkSetMacro( SmoothingVariance, RealType ); itkGetConstMacro( SmoothingVariance, RealType ); /** * Epsilon for minimum value of mean and variance at a pixel. * Default = 0.00001. */ itkSetMacro( Epsilon, RealType ); itkGetConstMacro( Epsilon, RealType ); /** * Mean threshold. * Default = 0.95. */ itkSetMacro( MeanThreshold, RealType ); itkGetConstMacro( MeanThreshold, RealType ); /** * Variance threshold. * Default = 0.5. */ itkSetMacro( VarianceThreshold, RealType ); itkGetConstMacro( VarianceThreshold, RealType ); /** * Neighborhood for computing local mean and variance images. * Default = 1x1x... */ itkSetMacro( NeighborhoodRadiusForLocalMeanAndVariance, NeighborhoodRadiusType ); itkGetConstMacro( NeighborhoodRadiusForLocalMeanAndVariance, NeighborhoodRadiusType ); protected: AdaptiveNonLocalMeansDenoisingImageFilter(); ~AdaptiveNonLocalMeansDenoisingImageFilter() {} void PrintSelf( std::ostream & os, Indent indent ) const ITK_OVERRIDE; void ThreadedGenerateData( const RegionType &, ThreadIdType ) ITK_OVERRIDE; void BeforeThreadedGenerateData() ITK_OVERRIDE; void AfterThreadedGenerateData() ITK_OVERRIDE; private: AdaptiveNonLocalMeansDenoisingImageFilter( const Self& ) ITK_DELETE_FUNCTION; void operator=( const Self& ) ITK_DELETE_FUNCTION; RealType CalculateCorrectionFactor( RealType ); bool m_UseRicianNoiseModel; ModifiedBesselCalculatorType m_ModifiedBesselCalculator; RealType m_Epsilon; RealType m_MeanThreshold; RealType m_VarianceThreshold; RealType m_SmoothingFactor; RealType m_SmoothingVariance; RealType m_MaximumInputPixelIntensity; RealType m_MinimumInputPixelIntensity; RealImagePointer m_MeanImage; RealImagePointer m_RicianBiasImage; RealImagePointer m_VarianceImage; RealImagePointer m_ThreadContributionCountImage; RealImagePointer m_IntensitySquaredDistanceImage; NeighborhoodRadiusType m_NeighborhoodRadiusForLocalMeanAndVariance; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkAdaptiveNonLocalMeansDenoisingImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkAdaptiveNonLocalMeansDenoisingImageFilter.hxx000066400000000000000000000431511311104306400260110ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkAdaptiveNonLocalMeansDenoisingImageFilter_hxx #define itkAdaptiveNonLocalMeansDenoisingImageFilter_hxx #include "itkAdaptiveNonLocalMeansDenoisingImageFilter.h" #include "itkArray.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkMath.h" #include "itkMeanImageFilter.h" #include "itkNeighborhoodIterator.h" #include "itkProgressReporter.h" #include "itkStatisticsImageFilter.h" #include "itkVarianceImageFilter.h" #include namespace itk { template AdaptiveNonLocalMeansDenoisingImageFilter ::AdaptiveNonLocalMeansDenoisingImageFilter() : m_UseRicianNoiseModel( true ), m_Epsilon( 0.00001 ), m_MeanThreshold( 0.95 ), m_VarianceThreshold( 0.5 ), m_SmoothingFactor( 1.0 ), m_SmoothingVariance( 2.0 ), m_MaximumInputPixelIntensity( NumericTraits::NonpositiveMin() ), m_MinimumInputPixelIntensity( NumericTraits::max() ) { this->SetNumberOfRequiredInputs( 1 ); this->m_MeanImage = ITK_NULLPTR; this->m_VarianceImage = ITK_NULLPTR; this->m_IntensitySquaredDistanceImage = ITK_NULLPTR; this->m_ThreadContributionCountImage = ITK_NULLPTR; this->m_RicianBiasImage = ITK_NULLPTR; this->m_NeighborhoodRadiusForLocalMeanAndVariance.Fill( 1 ); } template void AdaptiveNonLocalMeansDenoisingImageFilter ::BeforeThreadedGenerateData() { Superclass::BeforeThreadedGenerateData(); const InputImageType *inputImage = this->GetInput(); typedef MeanImageFilter MeanImageFilterType; typename MeanImageFilterType::Pointer meanImageFilter = MeanImageFilterType::New(); meanImageFilter->SetInput( inputImage ); meanImageFilter->SetRadius( this->GetNeighborhoodRadiusForLocalMeanAndVariance() ); this->m_MeanImage = meanImageFilter->GetOutput(); this->m_MeanImage->Update(); this->m_MeanImage->DisconnectPipeline(); typedef VarianceImageFilter VarianceImageFilterType; typename VarianceImageFilterType::Pointer varianceImageFilter = VarianceImageFilterType::New(); varianceImageFilter->SetInput( inputImage ); varianceImageFilter->SetRadius( this->GetNeighborhoodRadiusForLocalMeanAndVariance() ); this->m_VarianceImage = varianceImageFilter->GetOutput(); this->m_VarianceImage->Update(); this->m_VarianceImage->DisconnectPipeline(); typedef StatisticsImageFilter StatsFilterType; typename StatsFilterType::Pointer statsFilter = StatsFilterType::New(); statsFilter->SetInput( inputImage ); statsFilter->Update(); this->m_MaximumInputPixelIntensity = static_cast( statsFilter->GetMaximum() ); this->m_MinimumInputPixelIntensity = static_cast( statsFilter->GetMinimum() ); this->m_ThreadContributionCountImage = RealImageType::New(); this->m_ThreadContributionCountImage->CopyInformation( inputImage ); this->m_ThreadContributionCountImage->SetRegions( inputImage->GetRequestedRegion() ); this->m_ThreadContributionCountImage->Allocate( true ); if( this->m_UseRicianNoiseModel ) { this->m_RicianBiasImage = RealImageType::New(); this->m_RicianBiasImage->CopyInformation( inputImage ); this->m_RicianBiasImage->SetRegions( inputImage->GetRequestedRegion() ); this->m_RicianBiasImage->Allocate( true ); } this->AllocateOutputs(); //Output buffer needs to be zero initialized this->GetOutput()->FillBuffer( 0.0 ); } template void AdaptiveNonLocalMeansDenoisingImageFilter ::ThreadedGenerateData( const RegionType ®ion, ThreadIdType threadId ) { ProgressReporter progress( this, threadId, region.GetNumberOfPixels(), 100 ); const InputImageType *inputImage = this->GetInput(); const MaskImageType *maskImage = this->GetMaskImage(); OutputImageType *outputImage = this->GetOutput(); ConstNeighborhoodIterator ItV( this->GetNeighborhoodSearchRadius(), this->m_VarianceImage, region ); ConstNeighborhoodIterator ItM( this->GetNeighborhoodSearchRadius(), this->m_MeanImage, region ); ConstNeighborhoodIterator ItBI( this->GetNeighborhoodPatchRadius(), inputImage, region ); ConstNeighborhoodIterator ItBM( this->GetNeighborhoodPatchRadius(), this->m_MeanImage, region ); NeighborhoodIterator ItBO( this->GetNeighborhoodPatchRadius(), outputImage, region ); NeighborhoodIterator ItBL( this->GetNeighborhoodPatchRadius(), this->m_ThreadContributionCountImage, region ); const unsigned int neighborhoodSearchSize = this->GetNeighborhoodSearchSize(); const unsigned int neighborhoodPatchSize = this->GetNeighborhoodPatchSize(); Array weightedAverageIntensities( neighborhoodPatchSize ); ItM.GoToBegin(); ItV.GoToBegin(); ItBI.GoToBegin(); ItBM.GoToBegin(); ItBO.GoToBegin(); ItBL.GoToBegin(); while( !ItM.IsAtEnd() ) { InputPixelType inputCenterPixel = ItBI.GetCenterPixel(); RealType meanCenterPixel = ItM.GetCenterPixel(); RealType varianceCenterPixel = ItV.GetCenterPixel(); RealType maxWeight = NumericTraits::ZeroValue(); RealType sumOfWeights = NumericTraits::ZeroValue(); weightedAverageIntensities.Fill( NumericTraits::ZeroValue() ); RealType meanNeighborhoodPixel = NumericTraits::ZeroValue(); RealType varianceNeighborhoodPixel = NumericTraits::ZeroValue(); if( inputCenterPixel > 0 && meanCenterPixel > this->m_Epsilon && varianceCenterPixel > this->m_Epsilon && ( !maskImage || maskImage->GetPixel( ItM.GetIndex() ) != NumericTraits::ZeroValue() ) ) { // Calculate the minimum distance RealType minimumDistance = NumericTraits::max(); for( unsigned int m = 0; m < neighborhoodSearchSize; m++ ) { if( ! ItM.IndexInBounds( m ) || m == static_cast( 0.5 * neighborhoodSearchSize ) ) { continue; } meanNeighborhoodPixel = ItM.GetPixel( m ); varianceNeighborhoodPixel = ItV.GetPixel( m ); IndexType neighborhoodIndex = ItM.GetIndex( m ); if( inputImage->GetPixel( neighborhoodIndex ) <= 0 || meanNeighborhoodPixel <= this->m_Epsilon || varianceNeighborhoodPixel <= this->m_Epsilon ) { continue; } const RealType meanRatio = meanCenterPixel / meanNeighborhoodPixel; const RealType meanRatioInverse = ( this->m_MaximumInputPixelIntensity - meanCenterPixel ) / ( this->m_MaximumInputPixelIntensity - meanNeighborhoodPixel ); const RealType varianceRatio = varianceCenterPixel / varianceNeighborhoodPixel; if( ( ( meanRatio > this->m_MeanThreshold && meanRatio < 1.0 / this->m_MeanThreshold ) || ( meanRatioInverse > this->m_MeanThreshold && meanRatioInverse < 1.0 / this->m_MeanThreshold ) ) && varianceRatio > this->m_VarianceThreshold && varianceRatio < 1.0 / this->m_VarianceThreshold ) { RealType averageDistance = 0.0; RealType count = 0.0; for( unsigned int n = 0; n < neighborhoodPatchSize; n++ ) { IndexType neighborhoodPatchIndex = neighborhoodIndex + this->GetNeighborhoodPatchOffsetList()[n]; if( ! this->GetTargetImageRegion().IsInside( neighborhoodPatchIndex ) ) { continue; } averageDistance += vnl_math_sqr( ( ItBI.GetPixel( n ) - ItBM.GetPixel( n ) ) - ( inputImage->GetPixel( neighborhoodPatchIndex ) - this->m_MeanImage->GetPixel( neighborhoodPatchIndex ) ) ); count += 1.0; } averageDistance /= count; minimumDistance = vnl_math_min( averageDistance, minimumDistance ); } } if( itk::Math::AlmostEquals( minimumDistance, NumericTraits::ZeroValue() ) ) { minimumDistance = NumericTraits::OneValue(); } // Rician correction if( this->m_UseRicianNoiseModel ) { for( unsigned int n = 0; n < neighborhoodPatchSize; n++ ) { if( ! ItBM.IndexInBounds( n ) ) { continue; } if( itk::Math::AlmostEquals( minimumDistance, NumericTraits::max() ) ) { this->m_RicianBiasImage->SetPixel( ItBM.GetIndex( n ), 0.0 ); } else { this->m_RicianBiasImage->SetPixel( ItBM.GetIndex( n ), minimumDistance ); } } } // Patch filtering for( unsigned int m = 0; m < neighborhoodSearchSize; m++ ) { if( ! ItM.IndexInBounds( m ) || m == static_cast( 0.5 * neighborhoodSearchSize ) ) { continue; } meanNeighborhoodPixel = ItM.GetPixel( m ); varianceNeighborhoodPixel = ItV.GetPixel( m ); IndexType neighborhoodIndex = ItM.GetIndex( m ); if( inputImage->GetPixel( neighborhoodIndex ) <= 0 || meanNeighborhoodPixel < this->m_Epsilon || varianceNeighborhoodPixel < this->m_Epsilon ) { continue; } const RealType meanRatio = meanCenterPixel / meanNeighborhoodPixel; const RealType meanRatioInverse = ( this->m_MaximumInputPixelIntensity - meanCenterPixel ) / ( this->m_MaximumInputPixelIntensity - meanNeighborhoodPixel ); const RealType varianceRatio = varianceCenterPixel / varianceNeighborhoodPixel; if( ( ( meanRatio > this->m_MeanThreshold && meanRatio < 1.0 / this->m_MeanThreshold ) || ( meanRatioInverse > this->m_MeanThreshold && meanRatioInverse < 1.0 / this->m_MeanThreshold ) ) && varianceRatio > this->m_VarianceThreshold && varianceRatio < 1.0 / this->m_VarianceThreshold ) { RealType averageDistance = 0.0; RealType count = 0.0; for( unsigned int n = 0; n < neighborhoodPatchSize; n++ ) { IndexType neighborhoodPatchIndex = neighborhoodIndex + this->GetNeighborhoodPatchOffsetList()[n]; if( ! this->GetTargetImageRegion().IsInside( neighborhoodPatchIndex ) ) { continue; } averageDistance += vnl_math_sqr( inputImage->GetPixel( neighborhoodPatchIndex ) - ItBI.GetPixel( n ) ); count += 1.0; } averageDistance /= count; RealType weight = 0.0; if( averageDistance <= 3.0 * minimumDistance ) { weight = std::exp( -averageDistance / minimumDistance ); } if( weight > maxWeight ) { maxWeight = weight; } if( weight > 0.0 ) { for( unsigned int n = 0; n < neighborhoodPatchSize; n++ ) { IndexType neighborhoodPatchIndex = neighborhoodIndex + this->GetNeighborhoodPatchOffsetList()[n]; if( ! this->GetTargetImageRegion().IsInside( neighborhoodPatchIndex ) ) { continue; } if( this->m_UseRicianNoiseModel ) { weightedAverageIntensities[n] += weight * vnl_math_sqr( inputImage->GetPixel( neighborhoodPatchIndex ) ); } else { weightedAverageIntensities[n] += weight * inputImage->GetPixel( neighborhoodPatchIndex ); } } sumOfWeights += weight; } } } if( itk::Math::AlmostEquals( maxWeight, NumericTraits::ZeroValue() ) ) { maxWeight = NumericTraits::OneValue(); } } else { maxWeight = NumericTraits::OneValue(); } for( unsigned int n = 0; n < neighborhoodPatchSize; n++ ) { if( ! ItBI.IndexInBounds( n ) ) { continue; } if( this->m_UseRicianNoiseModel ) { weightedAverageIntensities[n] += maxWeight * vnl_math_sqr( ItBI.GetPixel( n ) ); } else { weightedAverageIntensities[n] += maxWeight * ItBI.GetPixel( n ); } } sumOfWeights += maxWeight; if( sumOfWeights > 0.0 ) { for( unsigned int n = 0; n < neighborhoodPatchSize; n++ ) { if( ! ItBO.IndexInBounds( n ) ) { continue; } typename OutputImageType::PixelType estimate = ItBO.GetPixel( n ); estimate += ( weightedAverageIntensities[n] / sumOfWeights ); ItBO.SetPixel( n, estimate ); ItBL.SetPixel( n, ItBL.GetPixel( n ) + 1.0 ); } } ++ItM; ++ItV; ++ItBI; ++ItBL; ++ItBM; ++ItBO; progress.CompletedPixel(); } } template void AdaptiveNonLocalMeansDenoisingImageFilter ::AfterThreadedGenerateData() { const MaskImageType * maskImage = this->GetMaskImage(); if( this->m_UseRicianNoiseModel ) { typedef DiscreteGaussianImageFilter SmootherType; typename SmootherType::Pointer smoother = SmootherType::New(); smoother->SetInput( this->m_RicianBiasImage ); smoother->SetVariance( this->m_SmoothingVariance ); smoother->SetUseImageSpacingOn(); smoother->Update(); ImageRegionConstIterator ItS( smoother->GetOutput(), smoother->GetOutput()->GetRequestedRegion() ); ImageRegionConstIteratorWithIndex ItM( this->m_MeanImage, this->m_MeanImage->GetRequestedRegion() ); ImageRegionIterator ItB( this->m_RicianBiasImage, this->m_RicianBiasImage->GetRequestedRegion() ); ItS.GoToBegin(); ItM.GoToBegin(); ItB.GoToBegin(); while( !ItS.IsAtEnd() ) { if( ItS.Get() > 0.0 && ( !maskImage || maskImage->GetPixel( ItM.GetIndex() ) != NumericTraits::ZeroValue() ) ) { const RealType snr = ItM.Get() / std::sqrt( ItS.Get() ); RealType bias = 2.0 * ItS.Get() / this->CalculateCorrectionFactor( snr ); if( vnl_math_isnan( bias ) || vnl_math_isinf( bias ) ) { bias = 0.0; } ItB.Set( bias ); } ++ItS; ++ItM; ++ItB; } } ImageRegionIteratorWithIndex ItO( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); ImageRegionConstIterator ItL( this->m_ThreadContributionCountImage, this->m_ThreadContributionCountImage->GetRequestedRegion() ); for( ItO.GoToBegin(), ItL.GoToBegin(); !ItO.IsAtEnd(); ++ItO, ++ItL ) { RealType estimate = ItO.Get(); if( ItL.Get() == 0.0 ) { continue; } estimate /= ItL.Get(); if( this->m_UseRicianNoiseModel ) { RealType bias = this->m_RicianBiasImage->GetPixel( ItO.GetIndex() ); estimate -= bias; if( estimate < 0.0 ) { estimate = 0.0; } estimate = std::sqrt( estimate ); } ItO.Set( estimate ); } } template typename AdaptiveNonLocalMeansDenoisingImageFilter::RealType AdaptiveNonLocalMeansDenoisingImageFilter ::CalculateCorrectionFactor( RealType snr ) { const RealType snrSquared = vnl_math_sqr( snr ); RealType value = 2.0 + snrSquared - 0.125 * Math::pi * std::exp( -0.5 * snrSquared ) * vnl_math_sqr( ( 2.0 + snrSquared ) * this->m_ModifiedBesselCalculator.ModifiedBesselI0( 0.25 * snrSquared ) + snrSquared * this->m_ModifiedBesselCalculator.ModifiedBesselI1( 0.25 * snrSquared ) ); if( value < 0.001 || value > 10.0 ) { value = 1.0; } return value; } template void AdaptiveNonLocalMeansDenoisingImageFilter ::PrintSelf( std::ostream &os, Indent indent ) const { Superclass::PrintSelf( os, indent ); if( this->m_UseRicianNoiseModel ) { os << indent << "Using Rician noise model." << std::endl; } else { os << indent << "Using Gaussian noise model." << std::endl; } os << indent << "Epsilon = " << this->m_Epsilon << std::endl; os << indent << "Mean threshold = " << this->m_MeanThreshold << std::endl; os << indent << "Variance threshold = " << this->m_VarianceThreshold << std::endl; os << indent << "Smoothing variance = " << this->m_SmoothingVariance << std::endl; os << indent << "Neighborhood radius for local mean and variance = " << this->m_NeighborhoodRadiusForLocalMeanAndVariance << std::endl; } } // end namespace itk #endif ants-2.2.0/Utilities/itkAlternatingValueDifferenceImageFilter.h000066400000000000000000000163651311104306400246510ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkAlternatingValueDifferenceImageFilter_h #define __itkAlternatingValueDifferenceImageFilter_h #include "itkImageToImageFilter.h" #include "itkExtrapolateImageFunction.h" #include "itkInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" namespace itk { /** \class AlternatingValueDifferenceImageFilter * \brief Finds difference signal from alternating signal * * This filter is templated over the input image type and the output image * type. Each signal is interpolated over the entire range of the * subtraction dimension. The output image is the difference between * the two intepolated signals. * * \ingroup GeometricTransform * \ingroup MultiThreaded * \ingroup Streamed * * \author Jeffrey Duda * */ template class AlternatingValueDifferenceImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef AlternatingValueDifferenceImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(AlternatingValueDifferenceImageFilter, ImageToImageFilter); /** Compiler can't inherit typedef? */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::OutputImageType OutputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::RegionType OutputImageRegionType; typedef LinearInterpolateImageFunction DefaultInterpolatorType; typedef typename DefaultInterpolatorType::Pointer DefaultInterpolatorPointerType; typedef InterpolateImageFunction< InputImageType, double > InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointerType; /** Compiler can't inherit ImageDimension enumeration? */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); itkGetMacro( SubtractionDimension, unsigned int ); itkSetMacro( SubtractionDimension, unsigned int ); itkGetMacro( IndexPadding, unsigned int ); itkSetMacro( IndexPadding, unsigned int ); /** Set the interpolator function. The default is * LinearInterpolateImageFunction. Some * other options are NearestNeighborInterpolateImageFunction * (useful for binary masks and other images with a small number of * possible pixel values), and BSplineInterpolateImageFunction * (which provides a higher order of interpolation). */ itkSetObjectMacro(ControlInterpolator, InterpolatorType); itkSetObjectMacro(LabelInterpolator, InterpolatorType); /** Get a pointer to the interpolator function. */ itkGetConstObjectMacro(ControlInterpolator, InterpolatorType); itkGetConstObjectMacro(LabelInterpolator, InterpolatorType); itkGetModifiableObjectMacro(ControlImage, InputImageType); itkGetModifiableObjectMacro(LabelImage, InputImageType); itkGetModifiableObjectMacro(ControlOutputImage, InputImageType); itkGetModifiableObjectMacro(LabelOutputImage, InputImageType); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro( InputConvertibleToOutputCheck, ( Concept::Convertible ) ); itkConceptMacro( DimensionCheck, ( Concept::SameDimension ) ); /** End concept checking */ #endif protected: AlternatingValueDifferenceImageFilter(); ~AlternatingValueDifferenceImageFilter() { } void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; /** Override VeriyInputInformation() to add the additional check * that all inputs have the same number of components. * * \sa ProcessObject::VerifyInputInformation */ virtual void VerifyInputInformation() ITK_OVERRIDE; /** Overrides GenerateOutputInformation() in order to produce * an image which has a different information than the first input. * \sa ProcessObject::GenerateOutputInformaton() */ virtual void GenerateOutputInformation() ITK_OVERRIDE; /** Overrides GenerateInputRequestedRegion() in order to inform * the pipeline execution model of different input requested regions * than the output requested region. * \sa ImageToImageFilter::GenerateInputRequestedRegion() */ //virtual void GenerateInputRequestedRegion(); /** This method is used to set the state of the filter before * multi-threading. */ virtual void BeforeThreadedGenerateData() ITK_OVERRIDE; /** AlternatingValueDifferenceImageFilter can be implemented as a multithreaded filter. * \sa ImageSource::ThreadedGenerateData(), * ImageSource::GenerateData() */ virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) ITK_OVERRIDE; private: AlternatingValueDifferenceImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** IndexValueType is used to switch among the inputs and * is used as the index value of the new dimension */ typedef unsigned int IndexValueType; unsigned int m_SubtractionDimension; unsigned int m_IndexPadding; InputImagePointer m_ControlImage; InputImagePointer m_LabelImage; InputImagePointer m_ControlOutputImage; InputImagePointer m_LabelOutputImage; InterpolatorPointerType m_ControlInterpolator; // Image function for // interpolation InterpolatorPointerType m_LabelInterpolator; // Image function for // interpolation // ExtrapolatorPointerType m_Extrapolator; // Image function for // extrapolation }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkAlternatingValueDifferenceImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkAlternatingValueDifferenceImageFilter.hxx000066400000000000000000000307451311104306400252270ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkAlternatingValueDifferenceImageFilter_hxx #define __itkAlternatingValueDifferenceImageFilter_hxx #include "itkAlternatingValueDifferenceImageFilter.h" #include "itkProgressReporter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIterator.h" namespace itk { template AlternatingValueDifferenceImageFilter ::AlternatingValueDifferenceImageFilter() { m_ControlInterpolator = dynamic_cast< InterpolatorType * > ( DefaultInterpolatorType::New().GetPointer() ); m_LabelInterpolator = dynamic_cast< InterpolatorType * > ( DefaultInterpolatorType::New().GetPointer() ); m_IndexPadding = 1; } template void AlternatingValueDifferenceImageFilter ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "SubtractionDimension: " << m_SubtractionDimension << std::endl; } template void AlternatingValueDifferenceImageFilter ::VerifyInputInformation() { Superclass::VerifyInputInformation(); // Verify that all input have the same number of components typename InputImageType::ConstPointer image = this->GetInput(); if( image.IsNull() ) { itkExceptionMacro( << "Input not set as expected!" ); } const unsigned int numComponents = image->GetNumberOfComponentsPerPixel(); for( IndexValueType idx = 1; idx < this->GetNumberOfInputs(); ++idx ) { image = this->GetInput(idx); // if the input was not set it could still be null if( image.IsNull() ) { // an invalid requested region exception will be generated later. continue; } if( numComponents != image->GetNumberOfComponentsPerPixel() ) { itkExceptionMacro( << "Primary input has " << numComponents << " numberOfComponents " << "but input " << idx << " has " << image->GetNumberOfComponentsPerPixel() << "!" ); } } } /** * \sa UnaryFunctorImageFilter::GenerateOutputInformation() */ template void AlternatingValueDifferenceImageFilter ::GenerateOutputInformation() { // do not call the superclass' implementation of this method since // this filter allows the input and output to be of different dimensions // get pointers to the input and output typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); typename Superclass::InputImageConstPointer inputPtr = this->GetInput(); if( !outputPtr || !inputPtr ) { return; } // Set the output image largest possible region. Use a RegionCopier // so that the input and output images can be different dimensions. OutputImageRegionType outputLargestPossibleRegion; this->CallCopyInputRegionToOutputRegion( outputLargestPossibleRegion, inputPtr->GetLargestPossibleRegion() ); // for the subtraction dimension unsigned int nPairs = this->GetInput()->GetLargestPossibleRegion().GetSize()[m_SubtractionDimension] - 2 * m_IndexPadding; outputLargestPossibleRegion.SetSize( m_SubtractionDimension, nPairs ); outputPtr->SetLargestPossibleRegion(outputLargestPossibleRegion); // Set the output spacing and origin const ImageBase *phyData; phyData = dynamic_cast *>( this->GetInput() ); if( phyData ) { // Copy what we can from the image from spacing and origin of the input // This logic needs to be augmented with logic that select which // dimensions to copy unsigned int ii; const typename InputImageType::SpacingType & inputSpacing = inputPtr->GetSpacing(); const typename InputImageType::PointType & inputOrigin = inputPtr->GetOrigin(); typename OutputImageType::SpacingType outputSpacing; typename OutputImageType::PointType outputOrigin; // copy the input to the output and fill the rest of the // output with zeros. for( ii = 0; ii < OutputImageDimension; ++ii ) { outputSpacing[ii] = inputSpacing[ii]; outputOrigin[ii] = inputOrigin[ii]; } outputOrigin[ m_SubtractionDimension ] += outputSpacing[ m_SubtractionDimension ]*m_IndexPadding; // set the spacing and origin outputPtr->SetSpacing(outputSpacing); outputPtr->SetOrigin(outputOrigin); // // Copy the direction cosines from the input to the output. // On join, the output dim is always >= input dim typedef typename InputImageType::DirectionType InputDirectionType; typedef typename OutputImageType::DirectionType OutputDirectionType; InputDirectionType inputDir = inputPtr->GetDirection(); unsigned int outputdim = OutputImageType::GetImageDimension(); OutputDirectionType outputDir = outputPtr->GetDirection(); for( unsigned int i = 0; i < outputdim; i++ ) { for( unsigned int j = 0; j < outputdim; j++ ) { outputDir[i][j] = inputDir[i][j]; } } outputPtr->SetDirection(outputDir); } else { // pointer could not be cast back down itkExceptionMacro( << "itk::DimensionSimpleSubtractionImageFilter::GenerateOutputInformation " << "cannot cast input to " << typeid( ImageBase * ).name() ); } // Support VectorImages by setting number of components on output. const unsigned int numComponents = inputPtr->GetNumberOfComponentsPerPixel(); if( numComponents != outputPtr->GetNumberOfComponentsPerPixel() ) { outputPtr->SetNumberOfComponentsPerPixel( numComponents ); } } /* template void AlternatingValueDifferenceImageFilter ::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); if( !this->GetOutput() ) { return; } OutputImageRegionType outputRegion = this->GetOutput()->GetRequestedRegion(); IndexValueType begin = outputRegion.GetIndex(InputImageDimension); IndexValueType end = begin + outputRegion.GetSize(InputImageDimension); for( IndexValueType idx = 0; idx < this->GetNumberOfIndexedInputs(); ++idx ) { InputImagePointer inputPtr = const_cast( this->GetInput(idx) ); if( !inputPtr ) { // Because DataObject::PropagateRequestedRegion() allows only // InvalidRequestedRegionError, it's impossible to write simply: // itkExceptionMacro(<< "Missing input " << idx); InvalidRequestedRegionError e(__FILE__, __LINE__); e.SetLocation(ITK_LOCATION); e.SetDescription("Missing input."); e.SetDataObject( this->GetOutput() ); throw e; } InputImageRegionType inputRegion; if( begin <= idx && idx < end ) { this->CallCopyOutputRegionToInputRegion(inputRegion, outputRegion); } else { // to tell the pipeline that updating this input is unncesseary inputRegion = inputPtr->GetBufferedRegion(); } inputPtr->SetRequestedRegion(inputRegion); } } */ /** * Set up state of filter before multi-threading. * InterpolatorType::SetInputImage is not thread-safe and hence * has to be set up before ThreadedGenerateData */ template void AlternatingValueDifferenceImageFilter ::BeforeThreadedGenerateData() { if( !m_ControlInterpolator ) { itkExceptionMacro(<< "Control interpolator not set"); } if( !m_LabelInterpolator ) { itkExceptionMacro(<< "Label interpolator not set"); } // Split image into control and label images typename InputImageType::RegionType region = this->GetInput()->GetLargestPossibleRegion(); typename InputImageType::RegionType::SizeType size = region.GetSize(); size[InputImageDimension - 1] = size[InputImageDimension - 1] / 2; region.SetSize( size ); typename InputImageType::SpacingType spacing = this->GetInput()->GetSpacing(); spacing[InputImageDimension - 1] = spacing[InputImageDimension - 1] * 2.0; typename InputImageType::PointType origin = this->GetInput()->GetOrigin(); this->m_ControlImage = InputImageType::New(); this->m_ControlImage->SetRegions( region ); this->m_ControlImage->SetSpacing( spacing ); this->m_ControlImage->SetOrigin( origin ); this->m_ControlImage->SetDirection( this->GetInput()->GetDirection() ); this->m_ControlImage->Allocate(); origin[InputImageDimension - 1] = origin[InputImageDimension - 1] + spacing[InputImageDimension - 1]; this->m_LabelImage = InputImageType::New(); this->m_LabelImage->SetRegions( region ); this->m_LabelImage->SetSpacing( spacing ); this->m_LabelImage->SetOrigin( origin ); this->m_LabelImage->SetDirection( this->GetInput()->GetDirection() ); this->m_LabelImage->Allocate(); this->m_ControlOutputImage = InputImageType::New(); this->m_ControlOutputImage->SetRegions( this->GetOutput()->GetLargestPossibleRegion() ); this->m_ControlOutputImage->SetSpacing( this->GetOutput()->GetSpacing() ); this->m_ControlOutputImage->SetOrigin( this->GetOutput()->GetOrigin() ); this->m_ControlOutputImage->SetDirection( this->GetOutput()->GetDirection() ); this->m_ControlOutputImage->Allocate(); this->m_LabelOutputImage = InputImageType::New(); this->m_LabelOutputImage->SetRegions( this->GetOutput()->GetLargestPossibleRegion() ); this->m_LabelOutputImage->SetSpacing( this->GetOutput()->GetSpacing() ); this->m_LabelOutputImage->SetOrigin( this->GetOutput()->GetOrigin() ); this->m_LabelOutputImage->SetDirection( this->GetOutput()->GetDirection() ); this->m_LabelOutputImage->Allocate(); ImageRegionConstIterator it( this->GetInput(), this->GetInput()->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { typename InputImageType::IndexType idx = it.GetIndex(); unsigned int offset = idx[InputImageDimension - 1] % 2; unsigned int tp = idx[InputImageDimension - 1] / 2; idx[InputImageDimension - 1] = tp; if( offset ) { this->m_LabelImage->SetPixel( idx, it.Value() ); } else { this->m_ControlImage->SetPixel( idx, it.Value() ); } ++it; } // Connect input image to interpolator m_ControlInterpolator->SetInputImage( this->m_ControlImage ); m_LabelInterpolator->SetInputImage( this->m_LabelImage ); // Connect input image to extrapolator // if( !m_Extrapolator.IsNull() ) // { // m_Extrapolator->SetInputImage( this->GetInput() ); // } } template void AlternatingValueDifferenceImageFilter ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { itkDebugMacro(<< "Actually executing"); ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); OutputImageRegionType outputRegion = outputRegionForThread; InputImageRegionType inputRegion; this->CallCopyOutputRegionToInputRegion(inputRegion, outputRegionForThread); ImageRegionIterator outIt( this->GetOutput(), outputRegion); typename InputImageType::PointType pt; while( !outIt.IsAtEnd() ) { typename InputImageType::IndexType idx = outIt.GetIndex(); this->GetOutput()->TransformIndexToPhysicalPoint( idx, pt ); float cValue = this->m_ControlInterpolator->Evaluate( pt ); float lValue = this->m_LabelInterpolator->Evaluate( pt ); this->m_ControlOutputImage->SetPixel(outIt.GetIndex(), cValue ); this->m_LabelOutputImage->SetPixel(outIt.GetIndex(), lValue ); outIt.Set( cValue - lValue ); ++outIt; } } } // end namespace itk #endif ants-2.2.0/Utilities/itkAlternatingValueSimpleSubtractionImageFilter.h000066400000000000000000000122151311104306400262540ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkAlternatingValueSimpleSubtractionImageFilter_h #define __itkAlternatingValueSimpleSubtractionImageFilter_h #include "itkImageToImageFilter.h" namespace itk { /** \class AlternatingValueSimpleSubtractionImageFilter * \brief Finds difference signal from alternating signal * * This filter is templated over the input image type and the output image * type. The output image is the difference between a two alternating * signals that alternate over the subtraction dimension. * * \ingroup GeometricTransform * \ingroup MultiThreaded * \ingroup Streamed * * \author Jeffrey Duda * */ template class AlternatingValueSimpleSubtractionImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef AlternatingValueSimpleSubtractionImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(AlternatingValueSimpleSubtractionImageFilter, ImageToImageFilter); /** Compiler can't inherit typedef? */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::OutputImageType OutputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::RegionType OutputImageRegionType; /** Compiler can't inherit ImageDimension enumeration? */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); /** Set/Get dimension to subtract over */ itkGetMacro(SubtractionDimension, unsigned int); itkSetMacro(SubtractionDimension, unsigned int); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro( InputConvertibleToOutputCheck, ( Concept::Convertible ) ); itkConceptMacro( DimensionCheck, ( Concept::SameDimension ) ); /** End concept checking */ #endif protected: AlternatingValueSimpleSubtractionImageFilter(); ~AlternatingValueSimpleSubtractionImageFilter() { } void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; /** Override VeriyInputInformation() to add the additional check * that all inputs have the same number of components. * * \sa ProcessObject::VerifyInputInformation */ virtual void VerifyInputInformation() ITK_OVERRIDE; /** Overrides GenerateOutputInformation() in order to produce * an image which has a different information than the first input. * \sa ProcessObject::GenerateOutputInformaton() */ virtual void GenerateOutputInformation() ITK_OVERRIDE; /** Overrides GenerateInputRequestedRegion() in order to inform * the pipeline execution model of different input requested regions * than the output requested region. * \sa ImageToImageFilter::GenerateInputRequestedRegion() */ //virtual void GenerateInputRequestedRegion(); /** AlternatingValueSimpleSubtractionImageFilter can be implemented as a multithreaded filter. * \sa ImageSource::ThreadedGenerateData(), * ImageSource::GenerateData() */ virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) ITK_OVERRIDE; private: AlternatingValueSimpleSubtractionImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** IndexValueType is used to switch among the inputs and * is used as the index value of the new dimension */ typedef unsigned int IndexValueType; unsigned int m_SubtractionDimension; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkAlternatingValueSimpleSubtractionImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkAlternatingValueSimpleSubtractionImageFilter.hxx000066400000000000000000000214751311104306400266440ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkAlternatingValueSimpleSubtractionImageFilter_hxx #define __itkAlternatingValueSimpleSubtractionImageFilter_hxx #include "itkAlternatingValueSimpleSubtractionImageFilter.h" #include "itkProgressReporter.h" #include "itkImageRegionIterator.h" namespace itk { template AlternatingValueSimpleSubtractionImageFilter ::AlternatingValueSimpleSubtractionImageFilter() { m_SubtractionDimension = InputImageDimension - 1; } template void AlternatingValueSimpleSubtractionImageFilter ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "SubtractionDimension: " << m_SubtractionDimension << std::endl; } template void AlternatingValueSimpleSubtractionImageFilter ::VerifyInputInformation() { Superclass::VerifyInputInformation(); // Verify that all input have the same number of components typename InputImageType::ConstPointer image = this->GetInput(); if( image.IsNull() ) { itkExceptionMacro( << "Input not set as expected!" ); } const unsigned int numComponents = image->GetNumberOfComponentsPerPixel(); for( IndexValueType idx = 1; idx < this->GetNumberOfInputs(); ++idx ) { image = this->GetInput(idx); // if the input was not set it could still be null if( image.IsNull() ) { // an invalid requested region exception will be generated later. continue; } if( numComponents != image->GetNumberOfComponentsPerPixel() ) { itkExceptionMacro( << "Primary input has " << numComponents << " numberOfComponents " << "but input " << idx << " has " << image->GetNumberOfComponentsPerPixel() << "!" ); } } } /** * \sa UnaryFunctorImageFilter::GenerateOutputInformation() */ template void AlternatingValueSimpleSubtractionImageFilter ::GenerateOutputInformation() { // do not call the superclass' implementation of this method since // this filter allows the input and output to be of different dimensions // get pointers to the input and output typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); typename Superclass::InputImageConstPointer inputPtr = this->GetInput(); if( !outputPtr || !inputPtr ) { return; } // Set the output image largest possible region. Use a RegionCopier // so that the input and output images can be different dimensions. OutputImageRegionType outputLargestPossibleRegion; this->CallCopyInputRegionToOutputRegion( outputLargestPossibleRegion, inputPtr->GetLargestPossibleRegion() ); // for the subtraction dimension unsigned int nPairs = this->GetInput()->GetLargestPossibleRegion().GetSize()[m_SubtractionDimension] / 2; outputLargestPossibleRegion.SetSize( m_SubtractionDimension, nPairs ); outputPtr->SetLargestPossibleRegion(outputLargestPossibleRegion); // Set the output spacing and origin const ImageBase *phyData; phyData = dynamic_cast *>( this->GetInput() ); if( phyData ) { // Copy what we can from the image from spacing and origin of the input // This logic needs to be augmented with logic that select which // dimensions to copy unsigned int ii; const typename InputImageType::SpacingType & inputSpacing = inputPtr->GetSpacing(); const typename InputImageType::PointType & inputOrigin = inputPtr->GetOrigin(); typename OutputImageType::SpacingType outputSpacing; typename OutputImageType::PointType outputOrigin; // copy the input to the output and fill the rest of the // output with zeros. for( ii = 0; ii < OutputImageDimension; ++ii ) { outputSpacing[ii] = inputSpacing[ii]; outputOrigin[ii] = inputOrigin[ii]; } outputSpacing[m_SubtractionDimension] *= 2; // set the spacing and origin outputPtr->SetSpacing(outputSpacing); outputPtr->SetOrigin(outputOrigin); // // Copy the direction cosines from the input to the output. // On join, the output dim is always >= input dim typedef typename InputImageType::DirectionType InputDirectionType; typedef typename OutputImageType::DirectionType OutputDirectionType; InputDirectionType inputDir = inputPtr->GetDirection(); unsigned int outputdim = OutputImageType::GetImageDimension(); OutputDirectionType outputDir = outputPtr->GetDirection(); for( unsigned int i = 0; i < outputdim; i++ ) { for( unsigned int j = 0; j < outputdim; j++ ) { outputDir[i][j] = inputDir[i][j]; } } outputPtr->SetDirection(outputDir); } else { // pointer could not be cast back down itkExceptionMacro( << "itk::AlternatingValueSimpleSubtractionImageFilter::GenerateOutputInformation " << "cannot cast input to " << typeid( ImageBase * ).name() ); } // Support VectorImages by setting number of components on output. const unsigned int numComponents = inputPtr->GetNumberOfComponentsPerPixel(); if( numComponents != outputPtr->GetNumberOfComponentsPerPixel() ) { outputPtr->SetNumberOfComponentsPerPixel( numComponents ); } } /* template void AlternatingValueSimpleSubtractionImageFilter ::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); if( !this->GetOutput() ) { return; } OutputImageRegionType outputRegion = this->GetOutput()->GetRequestedRegion(); IndexValueType begin = outputRegion.GetIndex(InputImageDimension); IndexValueType end = begin + outputRegion.GetSize(InputImageDimension); for( IndexValueType idx = 0; idx < this->GetNumberOfIndexedInputs(); ++idx ) { InputImagePointer inputPtr = const_cast( this->GetInput(idx) ); if( !inputPtr ) { // Because DataObject::PropagateRequestedRegion() allows only // InvalidRequestedRegionError, it's impossible to write simply: // itkExceptionMacro(<< "Missing input " << idx); InvalidRequestedRegionError e(__FILE__, __LINE__); e.SetLocation(ITK_LOCATION); e.SetDescription("Missing input."); e.SetDataObject( this->GetOutput() ); throw e; } InputImageRegionType inputRegion; if( begin <= idx && idx < end ) { this->CallCopyOutputRegionToInputRegion(inputRegion, outputRegion); inputRegion.SetSize(m_SubtractionDimension, 2*inputRegion.GetSize(m_SubtractionDimension) ); } else { // to tell the pipeline that updating this input is unncesseary inputRegion = inputPtr->GetBufferedRegion(); } inputPtr->SetRequestedRegion(inputRegion); } } */ template void AlternatingValueSimpleSubtractionImageFilter ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { itkDebugMacro(<< "Actually executing"); ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); OutputImageRegionType outputRegion = outputRegionForThread; InputImageRegionType inputRegion; this->CallCopyOutputRegionToInputRegion(inputRegion, outputRegionForThread); ImageRegionIterator outIt( this->GetOutput(), outputRegion); while( !outIt.IsAtEnd() ) { typename InputImageType::IndexType idx1 = outIt.GetIndex(); typename InputImageType::IndexType idx2 = outIt.GetIndex(); idx1[m_SubtractionDimension] = 2*outIt.GetIndex()[m_SubtractionDimension]; idx2[m_SubtractionDimension] = 2*outIt.GetIndex()[m_SubtractionDimension] + 1; outIt.Set( this->GetInput()->GetPixel(idx1) - this->GetInput()->GetPixel(idx2) ); ++outIt; } } } // end namespace itk #endif ants-2.2.0/Utilities/itkAverageAffineTransformFunction.h000066400000000000000000000164351311104306400234030ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkAverageAffineTransformFunction_h #define __itkAverageAffineTransformFunction_h #include "itkMacro.h" #include "itkConceptChecking.h" #include "itkANTSCenteredAffine2DTransform.h" #include "itkANTSAffine3DTransform.h" #include namespace itk { /* */ namespace AverageAffineTransformFunctionHelperNameSpace { template struct Dispatcher { }; template struct HelperCommonType { typedef TAffine InternalAffineTransformType; typedef typename InternalAffineTransformType::Pointer InternalAffineTransformPointerType; typedef struct { InternalAffineTransformPointerType aff; double weight; } SingleInternalTransformItemType; typedef std::list InternalTransformListType; typedef typename InternalAffineTransformType::ParametersType ParametersType; static void ComputeAveragePartialParameters(InternalTransformListType & transform_list, ParametersType & average_parameters, unsigned int iStart, unsigned int iEnd); }; template class HelperType; // { // // purposely not include any types // }; // explicit specialization for 2D affine transform template <> class HelperType > { public: typedef ANTSCenteredAffine2DTransform InternalAffineTransformType; typedef HelperCommonType::InternalAffineTransformPointerType InternalAffineTransformPointerType; typedef HelperCommonType::SingleInternalTransformItemType SingleInternalTransformItemType; typedef HelperCommonType::InternalTransformListType InternalTransformListType; typedef HelperCommonType::ParametersType ParametersType; static void ComputeAverageScaleParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageShearingParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageRotationParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageTranslationParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); }; // explicit specialization for 3D affine transform template <> class HelperType > { public: typedef ANTSAffine3DTransform InternalAffineTransformType; typedef HelperCommonType::InternalAffineTransformPointerType InternalAffineTransformPointerType; typedef HelperCommonType::SingleInternalTransformItemType SingleInternalTransformItemType; typedef HelperCommonType::InternalTransformListType InternalTransformListType; typedef HelperCommonType::ParametersType ParametersType; static void ComputeAverageScaleParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageShearingParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageRotationParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageTranslationParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); }; } template class AverageAffineTransformFunction { public: typedef TTransform GenericAffineTransformType; itkStaticConstMacro(InputSpaceDimension, unsigned int, GenericAffineTransformType::InputSpaceDimension); itkStaticConstMacro(OutputSpaceDimension, unsigned int, GenericAffineTransformType::OutputSpaceDimension); itkStaticConstMacro(SpaceDimension, unsigned int, InputSpaceDimension); typedef double InternalScalarType; typedef Point PointType; AverageAffineTransformFunction(); ~AverageAffineTransformFunction() { } ; // void PrintSelf(std::ostream& os, Indent indent) const; typedef typename GenericAffineTransformType::Pointer GenericAffineTransformPointerType; typedef struct { GenericAffineTransformPointerType aff; double weight; } SingleTransformItemType; typedef std::list TransformListType; TransformListType & GetTransformList() { return m_TransformList; } void PrintTransformList(); void PushBackAffineTransform(const GenericAffineTransformType* t, double weight); void AverageMultipleAffineTransform(const PointType & center_output, GenericAffineTransformPointerType & affine_output); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(SameDimensionCheck1, (Concept::SameDimension ) ); /** End concept checking */ #endif protected: TransformListType m_TransformList; // type declaration to include support both 2D and 3D affine transform protected: typedef typename::itk::AverageAffineTransformFunctionHelperNameSpace::HelperType< ::itk::AverageAffineTransformFunctionHelperNameSpace::Dispatcher< SpaceDimension> > HelperType; typedef typename HelperType::InternalAffineTransformType InternalAffineTransformType; typedef typename HelperType::InternalAffineTransformPointerType InternalAffineTransformPointerType; typedef typename HelperType::SingleInternalTransformItemType SingleInternalTransformItemType; typedef typename HelperType::InternalTransformListType InternalTransformListType; InternalTransformListType m_InternalTransformList; void ConvertGenericAffineToInternalAffineByFixingCenter(GenericAffineTransformPointerType & aff, InternalAffineTransformPointerType & iaff, const PointType & center); void ConvertInternalAffineToGenericAffine(InternalAffineTransformPointerType & iaff, GenericAffineTransformPointerType & aff); }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkAverageAffineTransformFunction.hxx" #endif #endif /*__itkAverageAffineTransformFunction_h*/ ants-2.2.0/Utilities/itkAverageAffineTransformFunction.hxx000066400000000000000000000237331311104306400237620ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkAverageAffineTransformFunction_hxx #define __itkAverageAffineTransformFunction_hxx #include "itkAverageAffineTransformFunction.h" #include "itkNumericTraits.h" #include namespace itk { /** * Default constructor. */ template AverageAffineTransformFunction::AverageAffineTransformFunction() { } template void AverageAffineTransformFunction::PrintTransformList() { std::cout << "transform list: " << std::endl; typename TransformListType::iterator it = (m_TransformList.begin() ); for( int ii = 0; it != m_TransformList.end(); it++, ii++ ) { std::cout << '[' << ii << ":" << it->weight << "]:" << it->aff << std::endl; } } // /** // * Standard PrintSelf method. // */ // template // void // WarpImageMultiTransformFilter // ::PrintSelf(std::ostream& os, Indent indent) const // { // // Superclass::PrintSelf(os, indent); // } template void AverageAffineTransformFunction::PushBackAffineTransform( const GenericAffineTransformType* t, double weight) { if( t ) { SingleTransformItemType item; item.aff = const_cast(t); item.weight = weight; m_TransformList.push_back(SingleTransformItemType(item) ); } } template void AverageAffineTransformFunction::AverageMultipleAffineTransform( const PointType & reference_center, GenericAffineTransformPointerType & affine_output) { // std::cout << "test " ; // TransformTypePointer affine_output = TransformType::New(); affine_output->SetIdentity(); affine_output->SetCenter(reference_center); unsigned int number_of_affine = m_TransformList.size(); number_of_affine--; // std::cout << affine_output; typename TransformListType::iterator it = m_TransformList.begin(); // typename InternalAffineTransformType::InputPointType center_ref = m_ReferenceCenter; typename InternalAffineTransformType::Pointer average_iaff = InternalAffineTransformType::New(); typename InternalAffineTransformType::ParametersType average_parameters = average_iaff->GetParameters(); for( ; it != m_TransformList.end(); it++ ) { SingleInternalTransformItemType internal_item; internal_item.aff = InternalAffineTransformType::New(); ConvertGenericAffineToInternalAffineByFixingCenter(it->aff, internal_item.aff, reference_center); internal_item.weight = it->weight; m_InternalTransformList.push_back(internal_item); std::cout << "internal_transform: " << internal_item.aff << std::endl; } HelperType::ComputeAverageScaleParameters(m_InternalTransformList, average_parameters); HelperType::ComputeAverageShearingParameters(m_InternalTransformList, average_parameters); HelperType::ComputeAverageRotationParameters(m_InternalTransformList, average_parameters); HelperType::ComputeAverageTranslationParameters(m_InternalTransformList, average_parameters); average_iaff->SetParameters(average_parameters); average_iaff->SetCenter(reference_center); std::cout << "average_iaff" << average_iaff << std::endl; ConvertInternalAffineToGenericAffine(average_iaff, affine_output); std::cout << "affine_output" << affine_output << std::endl; return; } template void AverageAffineTransformFunction::ConvertGenericAffineToInternalAffineByFixingCenter( GenericAffineTransformPointerType & aff, InternalAffineTransformPointerType & iaff, const PointType & center) { iaff->SetCenter(center); iaff->SetMatrix(aff->GetMatrix() ); iaff->SetTranslation(aff->GetTranslation() ); return; } template void AverageAffineTransformFunction::ConvertInternalAffineToGenericAffine( InternalAffineTransformPointerType & iaff, GenericAffineTransformPointerType & aff) { aff->SetCenter(iaff->GetCenter() ); aff->SetTranslation(iaff->GetTranslation() ); aff->SetMatrix(iaff->GetMatrix() ); return; } namespace AverageAffineTransformFunctionHelperNameSpace { template void HelperCommonType::ComputeAveragePartialParameters( InternalTransformListType & transform_list, ParametersType & average_parameters, unsigned int istart, unsigned int iend) { double w = 0.0; // initialize partial parameters to zero for( unsigned int k = istart; k <= iend; k++ ) { average_parameters[k] = 0.0; } typename InternalTransformListType::iterator it = transform_list.begin(); unsigned int cnt = 0; for( ; it != transform_list.end(); it++ ) { ParametersType current_parameters = it->aff->GetParameters(); w += it->weight; std::cout << "[" << cnt++ << "]:" << it->weight << "\t"; for( unsigned int k = istart; k <= iend; k++ ) { average_parameters[k] += it->weight * current_parameters[k]; std::cout << current_parameters[k] << " "; } std::cout << std::endl; } if( w <= 0.0 ) { std::cout << "Total weight smaller than 0!!!" << std::endl; std::exception(); } // normalize by weight std::cout << "sum:w=" << w << "\t"; for( unsigned int k = istart; k <= iend; k++ ) { std::cout << average_parameters[k] << " "; } std::cout << std::endl; // normalize by weight std::cout << "average" << "\t"; for( unsigned int k = istart; k <= iend; k++ ) { average_parameters[k] /= w; std::cout << average_parameters[k] << " "; } std::cout << std::endl; return; } void HelperType >::ComputeAverageScaleParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 1; unsigned int iend = 2; std::cout << "average 2D scale parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageShearingParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 3; unsigned int iend = 3; std::cout << "average 2D shearing parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageRotationParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 0; unsigned int iend = 0; std::cout << "average 2D rotation parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageTranslationParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 6; unsigned int iend = 7; std::cout << "average 2D translation parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageScaleParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 4; unsigned int iend = 6; std::cout << "average 3D scale parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageShearingParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 7; unsigned int iend = 9; std::cout << "average 3D shearing parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageRotationParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 0; unsigned int iend = 3; std::cout << "average 3D rotation parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); // extra normalization for quaternion double quat_mag = 0.0; for( unsigned int j = istart; j <= iend; j++ ) { quat_mag += average_parameters[j] * average_parameters[j]; } quat_mag = sqrt(quat_mag); for( unsigned int j = 0; j < 4; j++ ) { average_parameters[j] /= quat_mag; } } void HelperType >::ComputeAverageTranslationParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 10; unsigned int iend = 12; std::cout << "average 3D translation parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } } // end namespace AverageAffineTransformFunctionHelperNameSpace } // end namespace itk #endif // __itkAverageAffineTransformFunction_hxx ants-2.2.0/Utilities/itkAverageAffineTransformNoRigidFunction.h000066400000000000000000000165431311104306400246570ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkAverageAffineTransformNoRigidFunction_h #define __itkAverageAffineTransformNoRigidFunction_h #include "itkMacro.h" #include "itkConceptChecking.h" #include "itkANTSCenteredAffine2DTransform.h" #include "itkANTSAffine3DTransform.h" #include namespace itk { /* */ namespace AverageAffineTransformNoRigidFunctionHelperNameSpace { template struct Dispatcher { }; template struct HelperCommonType { typedef TAffine InternalAffineTransformType; typedef typename InternalAffineTransformType::Pointer InternalAffineTransformPointerType; typedef struct { InternalAffineTransformPointerType aff; double weight; } SingleInternalTransformItemType; typedef std::list InternalTransformListType; typedef typename InternalAffineTransformType::ParametersType ParametersType; static void ComputeAveragePartialParameters(InternalTransformListType & transform_list, ParametersType & average_parameters, unsigned int iStart, unsigned int iEnd); }; template class HelperType; // { // // purposely not include any types // }; // explicit specialization for 2D affine transform template <> class HelperType > { public: typedef ANTSCenteredAffine2DTransform InternalAffineTransformType; typedef HelperCommonType::InternalAffineTransformPointerType InternalAffineTransformPointerType; typedef HelperCommonType::SingleInternalTransformItemType SingleInternalTransformItemType; typedef HelperCommonType::InternalTransformListType InternalTransformListType; typedef HelperCommonType::ParametersType ParametersType; static void ComputeAverageScaleParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageShearingParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageRotationParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageTranslationParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); }; // explicit specialization for 3D affine transform template <> class HelperType > { public: typedef ANTSAffine3DTransform InternalAffineTransformType; typedef HelperCommonType::InternalAffineTransformPointerType InternalAffineTransformPointerType; typedef HelperCommonType::SingleInternalTransformItemType SingleInternalTransformItemType; typedef HelperCommonType::InternalTransformListType InternalTransformListType; typedef HelperCommonType::ParametersType ParametersType; static void ComputeAverageScaleParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageShearingParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageRotationParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); static void ComputeAverageTranslationParameters(InternalTransformListType & transform_list, ParametersType & average_parameters); }; } template class AverageAffineTransformNoRigidFunction { public: typedef TTransform GenericAffineTransformType; itkStaticConstMacro(InputSpaceDimension, unsigned int, GenericAffineTransformType::InputSpaceDimension); itkStaticConstMacro(OutputSpaceDimension, unsigned int, GenericAffineTransformType::OutputSpaceDimension); itkStaticConstMacro(SpaceDimension, unsigned int, InputSpaceDimension); typedef double InternalScalarType; typedef Point PointType; AverageAffineTransformNoRigidFunction(); ~AverageAffineTransformNoRigidFunction() { } ; // void PrintSelf(std::ostream& os, Indent indent) const; typedef typename GenericAffineTransformType::Pointer GenericAffineTransformPointerType; typedef struct { GenericAffineTransformPointerType aff; double weight; } SingleTransformItemType; typedef std::list TransformListType; TransformListType & GetTransformList() { return m_TransformList; } void PrintTransformList(); void PushBackAffineTransform(const GenericAffineTransformType* t, double weight); void AverageMultipleAffineTransform(const PointType & center_output, GenericAffineTransformPointerType & affine_output); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(SameDimensionCheck1, (Concept::SameDimension ) ); /** End concept checking */ #endif protected: TransformListType m_TransformList; // type declaration to include support both 2D and 3D affine transform protected: typedef typename::itk::AverageAffineTransformNoRigidFunctionHelperNameSpace::HelperType< ::itk::AverageAffineTransformNoRigidFunctionHelperNameSpace::Dispatcher< SpaceDimension> > HelperType; typedef typename HelperType::InternalAffineTransformType InternalAffineTransformType; typedef typename HelperType::InternalAffineTransformPointerType InternalAffineTransformPointerType; typedef typename HelperType::SingleInternalTransformItemType SingleInternalTransformItemType; typedef typename HelperType::InternalTransformListType InternalTransformListType; InternalTransformListType m_InternalTransformList; void ConvertGenericAffineToInternalAffineByFixingCenter(GenericAffineTransformPointerType & aff, InternalAffineTransformPointerType & iaff, const PointType & center); void ConvertInternalAffineToGenericAffine(InternalAffineTransformPointerType & iaff, GenericAffineTransformPointerType & aff); }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkAverageAffineTransformNoRigidFunction.hxx" #endif #endif /*__itkAverageAffineTransformNoRigidFunction_h*/ ants-2.2.0/Utilities/itkAverageAffineTransformNoRigidFunction.hxx000066400000000000000000000241001311104306400252230ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkAverageAffineTransformNoRigidFunction_hxx #define __itkAverageAffineTransformNoRigidFunction_hxx #include "itkAverageAffineTransformNoRigidFunction.h" #include "itkNumericTraits.h" #include namespace itk { /** * Default constructor. */ template AverageAffineTransformNoRigidFunction::AverageAffineTransformNoRigidFunction() { } template void AverageAffineTransformNoRigidFunction::PrintTransformList() { std::cout << "transform list: " << std::endl; typename TransformListType::iterator it = (m_TransformList.begin() ); for( int ii = 0; it != m_TransformList.end(); it++, ii++ ) { std::cout << '[' << ii << ":" << it->weight << "]:" << it->aff << std::endl; } } // /** // * Standard PrintSelf method. // */ // template // void // WarpImageMultiTransformFilter // ::PrintSelf(std::ostream& os, Indent indent) const // { // // Superclass::PrintSelf(os, indent); // } template void AverageAffineTransformNoRigidFunction::PushBackAffineTransform( const GenericAffineTransformType* t, double weight) { if( t ) { SingleTransformItemType item; item.aff = const_cast(t); item.weight = weight; m_TransformList.push_back(SingleTransformItemType(item) ); } } template void AverageAffineTransformNoRigidFunction::AverageMultipleAffineTransform( const PointType & reference_center, GenericAffineTransformPointerType & affine_output) { // std::cout << "test " ; // TransformTypePointer affine_output = TransformType::New(); affine_output->SetIdentity(); affine_output->SetCenter(reference_center); unsigned int number_of_affine = m_TransformList.size(); number_of_affine--; // std::cout << affine_output; typename TransformListType::iterator it = m_TransformList.begin(); // typename InternalAffineTransformType::InputPointType center_ref = m_ReferenceCenter; typename InternalAffineTransformType::Pointer average_iaff = InternalAffineTransformType::New(); typename InternalAffineTransformType::ParametersType average_parameters = average_iaff->GetParameters(); for( ; it != m_TransformList.end(); it++ ) { SingleInternalTransformItemType internal_item; internal_item.aff = InternalAffineTransformType::New(); ConvertGenericAffineToInternalAffineByFixingCenter(it->aff, internal_item.aff, reference_center); internal_item.weight = it->weight; m_InternalTransformList.push_back(internal_item); std::cout << "internal_transform: " << internal_item.aff << std::endl; } HelperType::ComputeAverageScaleParameters(m_InternalTransformList, average_parameters); HelperType::ComputeAverageShearingParameters(m_InternalTransformList, average_parameters); // HelperType::ComputeAverageRotationParameters(m_InternalTransformList, // average_parameters); // HelperType::ComputeAverageTranslationParameters(m_InternalTransformList, // average_parameters); average_iaff->SetParameters(average_parameters); average_iaff->SetCenter(reference_center); std::cout << "average_iaff" << average_iaff << std::endl; ConvertInternalAffineToGenericAffine(average_iaff, affine_output); std::cout << "affine_output" << affine_output << std::endl; return; } template void AverageAffineTransformNoRigidFunction::ConvertGenericAffineToInternalAffineByFixingCenter( GenericAffineTransformPointerType & aff, InternalAffineTransformPointerType & iaff, const PointType & center) { iaff->SetCenter(center); iaff->SetMatrix(aff->GetMatrix() ); iaff->SetTranslation(aff->GetTranslation() ); return; } template void AverageAffineTransformNoRigidFunction::ConvertInternalAffineToGenericAffine( InternalAffineTransformPointerType & iaff, GenericAffineTransformPointerType & aff) { aff->SetCenter(iaff->GetCenter() ); aff->SetTranslation(iaff->GetTranslation() ); aff->SetMatrix(iaff->GetMatrix() ); return; } namespace AverageAffineTransformNoRigidFunctionHelperNameSpace { template void HelperCommonType::ComputeAveragePartialParameters( InternalTransformListType & transform_list, ParametersType & average_parameters, unsigned int istart, unsigned int iend) { double w = 0.0; // initialize partial parameters to zero for( unsigned int k = istart; k <= iend; k++ ) { average_parameters[k] = 0.0; } typename InternalTransformListType::iterator it = transform_list.begin(); unsigned int cnt = 0; for( ; it != transform_list.end(); it++ ) { ParametersType current_parameters = it->aff->GetParameters(); w += it->weight; std::cout << "[" << cnt++ << "]:" << it->weight << "\t"; for( unsigned int k = istart; k <= iend; k++ ) { average_parameters[k] += it->weight * current_parameters[k]; std::cout << current_parameters[k] << " "; } std::cout << std::endl; } if( w <= 0.0 ) { std::cout << "Total weight smaller than 0!!!" << std::endl; std::exception(); } // normalize by weight std::cout << "sum:w=" << w << "\t"; for( unsigned int k = istart; k <= iend; k++ ) { std::cout << average_parameters[k] << " "; } std::cout << std::endl; // normalize by weight std::cout << "average" << "\t"; for( unsigned int k = istart; k <= iend; k++ ) { average_parameters[k] /= w; std::cout << average_parameters[k] << " "; } std::cout << std::endl; return; } void HelperType >::ComputeAverageScaleParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 1; unsigned int iend = 2; std::cout << "average 2D scale parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageShearingParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 3; unsigned int iend = 3; std::cout << "average 2D shearing parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageRotationParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 0; unsigned int iend = 0; std::cout << "average 2D rotation parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageTranslationParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 6; unsigned int iend = 7; std::cout << "average 2D translation parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageScaleParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 4; unsigned int iend = 6; std::cout << "average 3D scale parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageShearingParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 7; unsigned int iend = 9; std::cout << "average 3D shearing parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } void HelperType >::ComputeAverageRotationParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 0; unsigned int iend = 3; std::cout << "average 3D rotation parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); // extra normalization for quaternion double quat_mag = 0.0; for( unsigned int j = istart; j <= iend; j++ ) { quat_mag += average_parameters[j] * average_parameters[j]; } quat_mag = sqrt(quat_mag); for( unsigned int j = 0; j < 4; j++ ) { average_parameters[j] /= quat_mag; } } void HelperType >::ComputeAverageTranslationParameters( InternalTransformListType & transform_list, ParametersType & average_parameters) { unsigned int istart = 10; unsigned int iend = 12; std::cout << "average 3D translation parameter " << std::endl; HelperCommonType::ComputeAveragePartialParameters( transform_list, average_parameters, istart, iend); } } // end namespace AverageAffineTransformNoRigidFunctionHelperNameSpace } // end namespace itk #endif // __itkAverageAffineTransformNoRigidFunction_hxx ants-2.2.0/Utilities/itkAverageOverDimensionImageFilter.h000066400000000000000000000302141311104306400234720ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkAverageOverDimensionImageFilter_h #define __itkAverageOverDimensionImageFilter_h #include "itkInPlaceImageFilter.h" #include "itkSmartPointer.h" #include "itkExtractImageFilterRegionCopier.h" namespace itk { /** \class AverageOverDimensionImageFilter * \brief Decrease the image size by cropping the image to the selected * region bounds. * * AverageOverDimensionImageFilter changes the image boundary of an image by removing * pixels outside the target region. The target region must be specified. * * AverageOverDimensionImageFilter also collapses dimensions so that the input image * may have more dimensions than the output image (i.e. 4-D input image * to a 3-D output image). To specify what dimensions to collapse, * the ExtractionRegion must be specified. For any dimension dim where * ExtractionRegion.Size[dim] = 0, that dimension is collapsed. The * index to collapse on is specified by ExtractionRegion.Index[dim]. * For example, we have a image 4D = a 4x4x4x4 image, and we want * to get a 3D image, 3D = a 4x4x4 image, specified as [x,y,z,2] from 4D * (i.e. the 3rd "time" slice from 4D). The ExtractionRegion.Size = * [4,4,4,0] and ExtractionRegion.Index = [0,0,0,2]. * * The number of dimension in ExtractionRegion.Size and Index must = * InputImageDimension. The number of non-zero dimensions in * ExtractionRegion.Size must = OutputImageDimension. * * The output image produced by this filter will have the same origin as the * input image, while the ImageRegion of the output image will start at the * starting index value provided in the ExtractRegion parameter. If you are * looking for a filter that will re-compute the origin of the output image, * and provide an output image region whose index is set to zeros, then you may * want to use the RegionOfInterestImageFilter. The output spacing is is * simply the collapsed version of the input spacing. * * Determining the direction of the collapsed output image from an larger * dimensional input space is an ill defined problem in general. It is * required that the application developer select the desired transformation * strategy for collapsing direction cosigns. It is REQUIRED that a strategy * be explicitly requested (i.e. there is no working default). * Direction Collapsing Strategies: * 1) DirectionCollapseToUnknown(); * This is the default and the filter can not run when this is set. * The reason is to explicitly force the application developer to * define their desired behavior. * 1) DirectionCollapseToIdentity(); * Output has identity direction no matter what * 2) DirectionCollapseToSubmatrix(); * Output direction is the sub-matrix if it is positive definite, else throw an exception. * * This filter is implemented as a multithreaded filter. It provides a * ThreadedGenerateData() method for its implementation. * * \note This filter is derived from InPlaceImageFilter. When the * input to this filter matched the output requirested region, like * with streaming filter for input, then setting this filter to run * in-place will result in no copying of the bulk pixel data. * * \sa CropImageFilter * \ingroup GeometricTransform * \ingroup ITKCommon * * \wiki * \wikiexample{ImageProcessing/AverageOverDimensionImageFilter,Crop an image by specifying the region to keep} * \endwiki */ template< class TInputImage, class TOutputImage > class AverageOverDimensionImageFilter: public InPlaceImageFilter< TInputImage, TOutputImage > { public: /** Standard class typedefs. */ typedef AverageOverDimensionImageFilter Self; typedef InPlaceImageFilter< TInputImage, TOutputImage > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(AverageOverDimensionImageFilter, ImageToImageFilter); /** Image type information. */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; /** Typedef to describe the output and input image region types. */ typedef typename TOutputImage::RegionType OutputImageRegionType; typedef typename TInputImage::RegionType InputImageRegionType; /** Typedef to describe the type of pixel. */ typedef typename TOutputImage::PixelType OutputImagePixelType; typedef typename TInputImage::PixelType InputImagePixelType; /** Typedef to describe the output and input image index and size types. */ typedef typename TOutputImage::IndexType OutputImageIndexType; typedef typename TInputImage::IndexType InputImageIndexType; typedef typename TOutputImage::SizeType OutputImageSizeType; typedef typename TInputImage::SizeType InputImageSizeType; typedef enum DirectionCollapseStrategyEnum { DIRECTIONCOLLAPSETOUNKOWN=0, DIRECTIONCOLLAPSETOIDENTITY=1, DIRECTIONCOLLAPSETOSUBMATRIX=2, DIRECTIONCOLLAPSETOGUESS=3 } DIRECTIONCOLLAPSESTRATEGY; /** * Set the strategy to be used to collapse pysical space dimensions. * * itk::itkAverageOverDimensionImageFilter::DIRECTIONCOLLAPSETOIDENTITY * Set the strategy so that all collapsed images have an identity direction. * Use this strategy when you know that retention of the physical space * orientation of the collapsed image is not important. * * itk::itkAverageOverDimensionImageFilter::DIRECTIONCOLLAPSETOGUESS * Set the strategy so that all collapsed images where * output direction is the sub-matrix it it is positive definite, else * return identity. This is backwards compatible with ITKv3, but * is highly discouraged because the results are difficult to * anticipate under differing data scenerios. * * itk::itkAverageOverDimensionImageFilter::DIRECTIONCOLLAPSETOSUBMATRIX * Set the strategy so that all collapsed images where * output direction is the sub-matrix it it is positive definite, * else throw an exception. Use this strategy when it is known * that properly identified physical space sub-volumes can be * reliably extracted from a higher dimensional space. For * example when the application programmer knows that a 4D image * is 3D+time, and that the 3D sub-space is properly defined. */ void SetDirectionCollapseToStrategy(const DIRECTIONCOLLAPSESTRATEGY choosenStrategy) { switch(choosenStrategy) { case DIRECTIONCOLLAPSETOGUESS: case DIRECTIONCOLLAPSETOIDENTITY: case DIRECTIONCOLLAPSETOSUBMATRIX: break; case DIRECTIONCOLLAPSETOUNKOWN: default: itkExceptionMacro( << "Invalid Strategy Chosen for itk::AverageOverDimensionImageFilter" ); } this->m_DirectionCollapseStrategy=choosenStrategy; this->Modified(); } /** NOTE: The SetDirectionCollapseToUknown is explicitly not defined. * It is a state that a filter can be in only when it is first instantiate * prior to being initialized. */ /** * Get the currently set strategy for collapsing directions of physical space. */ DIRECTIONCOLLAPSESTRATEGY GetDirectionCollapseToStrategy() const { return this->m_DirectionCollapseStrategy; } /** \sa SetDirectionCollapseToStrategy */ void SetDirectionCollapseToGuess() { this->SetDirectionCollapseToStrategy(DIRECTIONCOLLAPSETOGUESS); } /** \sa SetDirectionCollapseToStrategy */ void SetDirectionCollapseToIdentity() { this->SetDirectionCollapseToStrategy(DIRECTIONCOLLAPSETOIDENTITY); } /** \sa SetDirectionCollapseToStrategy */ void SetDirectionCollapseToSubmatrix() { this->SetDirectionCollapseToStrategy(DIRECTIONCOLLAPSETOSUBMATRIX); } /** ImageDimension enumeration */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); typedef ImageToImageFilterDetail::ExtractImageFilterRegionCopier< itkGetStaticConstMacro(InputImageDimension), itkGetStaticConstMacro(OutputImageDimension) > ExtractImageFilterRegionCopierType; /** Set/Get the output image region. * If any of the ExtractionRegion.Size = 0 for any particular dimension dim, * we have to collapse dimension dim. This means the output image will have * 'c' dimensions less than the input image, where c = number of * ExtractionRegion.Size = 0. */ void SetAveragingDimension(unsigned int averagingDimension); itkGetMacro(AveragingDimension, unsigned int); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro( InputCovertibleToOutputCheck, ( Concept::Convertible< InputImagePixelType, OutputImagePixelType > ) ); /** End concept checking */ #endif protected: AverageOverDimensionImageFilter(); ~AverageOverDimensionImageFilter() {} void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; /** AverageOverDimensionImageFilter can produce an image which is a different * resolution than its input image. As such, AverageOverDimensionImageFilter * needs to provide an implementation for * GenerateOutputInformation() in order to inform the pipeline * execution model. The original documentation of this method is * below. * * \sa ProcessObject::GenerateOutputInformaton() */ virtual void GenerateOutputInformation() ITK_OVERRIDE; /** This function calls the actual region copier to do the mapping from * output image space to input image space. It uses a * Function object used for dispatching to various routines to * copy an output region (start index and size) to an input region. * For most filters, this is a trivial copy because most filters * require the input dimension to match the output dimension. * However, some filters like itk::AverageOverDimensionImageFilter can * support output images of a lower dimension that the input. * * \sa ImageToImageFilter::CallCopyRegion() */ virtual void CallCopyOutputRegionToInputRegion(InputImageRegionType & destRegion, const OutputImageRegionType & srcRegion) ITK_OVERRIDE; /** AverageOverDimensionImageFilter can be implemented as a multithreaded filter. * Therefore, this implementation provides a ThreadedGenerateData() * routine which is called for each processing thread. The output * image data is allocated automatically by the superclass prior to * calling ThreadedGenerateData(). ThreadedGenerateData can only * write to the portion of the output image specified by the * parameter "outputRegionForThread" * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) ITK_OVERRIDE; /** Overridden to check if there is no work to be done, before * calling superclass' implementation. */ void GenerateData() ITK_OVERRIDE; unsigned int m_AveragingDimension; OutputImageRegionType m_OutputImageRegion; private: AverageOverDimensionImageFilter(const Self &); //purposely not implemented void operator=(const Self &); //purposely not implemented DIRECTIONCOLLAPSESTRATEGY m_DirectionCollapseStrategy; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkAverageOverDimensionImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkAverageOverDimensionImageFilter.hxx000066400000000000000000000257521311104306400240650ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkAverageOverDimensionImageFilter_hxx #define __itkAverageOverDimensionImageFilter_hxx #include "itkAverageOverDimensionImageFilter.h" #include "itkImageAlgorithm.h" #include "itkObjectFactory.h" #include "itkProgressReporter.h" #include "itkImageRegionIterator.h" namespace itk { /** * */ template< class TInputImage, class TOutputImage > AverageOverDimensionImageFilter< TInputImage, TOutputImage > ::AverageOverDimensionImageFilter(): #ifdef ITKV3_COMPATIBILITY m_DirectionCollapseStrategy(DIRECTIONCOLLAPSETOGUESS) #else m_DirectionCollapseStrategy(DIRECTIONCOLLAPSETOUNKOWN) #endif { Superclass::InPlaceOff(); } /** * */ template< class TInputImage, class TOutputImage > void AverageOverDimensionImageFilter< TInputImage, TOutputImage > ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "OutputImageRegion: " << m_OutputImageRegion << std::endl; os << indent << "DirectionCollapseStrategy: " << m_DirectionCollapseStrategy << std::endl; } template< class TInputImage, class TOutputImage > void AverageOverDimensionImageFilter< TInputImage, TOutputImage > ::CallCopyOutputRegionToInputRegion(InputImageRegionType & destRegion, const OutputImageRegionType & srcRegion) { typename InputImageRegionType::SizeType inputSize; typename InputImageRegionType::IndexType inputIndex; unsigned int countDim = 0; for (unsigned int i=0; im_AveragingDimension ) { inputSize[i] = srcRegion.GetSize()[countDim]; inputIndex[i] = srcRegion.GetIndex()[countDim]; countDim++; } } inputSize[this->m_AveragingDimension] = this->GetInput()->GetRequestedRegion().GetSize()[this->m_AveragingDimension]; inputIndex[this->m_AveragingDimension] = this->GetInput()->GetRequestedRegion().GetIndex()[this->m_AveragingDimension]; destRegion.SetSize( inputSize ); destRegion.SetIndex( inputIndex ); } template< class TInputImage, class TOutputImage > void AverageOverDimensionImageFilter< TInputImage, TOutputImage > ::SetAveragingDimension(unsigned int averagingDimension) { this->m_AveragingDimension = averagingDimension; if ( this->m_AveragingDimension > InputImageDimension ) { itkExceptionMacro("Averaging dimension is larger than input image dimension"); } InputImageSizeType inputSize = this->GetInput()->GetRequestedRegion().GetSize(); OutputImageSizeType outputSize; outputSize.Fill(0); OutputImageIndexType outputIndex; outputIndex.Fill(0); /** * check to see if the number of non-zero entries in the extraction region * matches the number of dimensions in the output image. */ unsigned int dimCount = 0; for ( unsigned int i = 0; i < InputImageDimension; ++i ) { if ( i != this->m_AveragingDimension ) { outputSize[dimCount] = inputSize[i]; outputIndex[dimCount] = this->GetInput()->GetRequestedRegion().GetIndex()[i]; dimCount++; } } m_OutputImageRegion.SetSize(outputSize); m_OutputImageRegion.SetIndex(outputIndex); this->Modified(); } /** * AverageOverDimensionImageFilter can produce an image which is a different resolution * than its input image. As such, AverageOverDimensionImageFilter needs to provide an * implementation for GenerateOutputInformation() in order to inform * the pipeline execution model. The original documentation of this * method is below. * * \sa ProcessObject::GenerateOutputInformaton() */ template< class TInputImage, class TOutputImage > void AverageOverDimensionImageFilter< TInputImage, TOutputImage > ::GenerateOutputInformation() { // do not call the superclass' implementation of this method since // this filter allows the input and the output to be of different dimensions // get pointers to the input and output typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); typename Superclass::InputImageConstPointer inputPtr = this->GetInput(); if ( !outputPtr || !inputPtr ) { return; } // Set the output image size to the same value as the extraction region. outputPtr->SetLargestPossibleRegion(m_OutputImageRegion); // Set the output spacing and origin const ImageBase< InputImageDimension > *phyData; phyData = dynamic_cast< const ImageBase< InputImageDimension > * >( this->GetInput() ); if ( phyData ) { // Copy what we can from the image from spacing and origin of the input // This logic needs to be augmented with logic that select which // dimensions to copy const typename InputImageType::SpacingType & inputSpacing = inputPtr->GetSpacing(); const typename InputImageType::DirectionType & inputDirection = inputPtr->GetDirection(); const typename InputImageType::PointType & inputOrigin = inputPtr->GetOrigin(); typename OutputImageType::SpacingType outputSpacing; typename OutputImageType::DirectionType outputDirection; typename OutputImageType::PointType outputOrigin; outputOrigin.Fill(0.0); outputDirection.SetIdentity(); unsigned int countDim = 0; for ( unsigned int i = 0; i < InputImageDimension; ++i ) { if ( i != this->m_AveragingDimension ) { outputSpacing[countDim] = inputSpacing[i]; outputOrigin[countDim] = inputOrigin[i]; unsigned int countDim2 = 0; for ( unsigned int dim = 0; dim < InputImageDimension; ++dim ) { if ( dim != this->m_AveragingDimension ) { outputDirection[countDim][countDim2] = inputDirection[i][dim]; ++countDim2; } } countDim++; } } // if the filter changes from a higher to a lower dimension, or // if, after rebuilding the direction cosines, there's a zero // length cosine vector, reset the directions to identity. switch(m_DirectionCollapseStrategy) { case DIRECTIONCOLLAPSETOIDENTITY: { outputDirection.SetIdentity(); } break; case DIRECTIONCOLLAPSETOSUBMATRIX: { if ( vnl_determinant( outputDirection.GetVnlMatrix() ) == 0.0 ) { itkExceptionMacro( << "Invalid submatrix extracted for collapsed direction." ); } } break; case DIRECTIONCOLLAPSETOGUESS: { if ( vnl_determinant( outputDirection.GetVnlMatrix() ) == 0.0 ) { outputDirection.SetIdentity(); } } break; case DIRECTIONCOLLAPSETOUNKOWN: default: { itkExceptionMacro( << "It is required that the strategy for collapsing the direction matrix be explicitly specified. " << "Set with either myfilter->SetDirectionCollapseToIdentity() or myfilter->SetDirectionCollapseToSubmatrix() " << typeid( ImageBase< InputImageDimension > * ).name() ); } } // set the spacing and origin outputPtr->SetSpacing(outputSpacing); outputPtr->SetDirection(outputDirection); outputPtr->SetOrigin(outputOrigin); outputPtr->SetNumberOfComponentsPerPixel( inputPtr->GetNumberOfComponentsPerPixel() ); } else { // pointer could not be cast back down itkExceptionMacro( << "itk::AverageOverDimensionImageFilter::GenerateOutputInformation " << "cannot cast input to " << typeid( ImageBase< InputImageDimension > * ).name() ); } } template< class TInputImage, class TOutputImage > void AverageOverDimensionImageFilter< TInputImage, TOutputImage > ::GenerateData() { // InPlace::AllocateOutputs set the running in place ivar. // This method will be called again, by GenerateData, but there is // no harm done. this->AllocateOutputs(); // The input matched the output, nothing to do. if ( this->GetRunningInPlace() ) { OutputImageType *outputPtr = this->GetOutput(); // the in-place grafting copies the meta data, this needs to be // set back. outputPtr->SetLargestPossibleRegion(m_OutputImageRegion); this->SetProgress( 1.0 ); return; } this->Superclass::GenerateData(); } /** * AverageOverDimensionImageFilter can be implemented as a multithreaded filter. * Therefore, this implementation provides a ThreadedGenerateData() * routine which is called for each processing thread. The output * image data is allocated automatically by the superclass prior to * calling ThreadedGenerateData(). ThreadedGenerateData can only * write to the portion of the output image specified by the * parameter "outputRegionForThread" * * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ template< class TInputImage, class TOutputImage > void AverageOverDimensionImageFilter< TInputImage, TOutputImage > ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { itkDebugMacro(<< "Actually executing"); // Get the input and output pointers const InputImageType *inputPtr = this->GetInput(); OutputImageType *outputPtr = this->GetOutput(); // support progress methods/callbacks ProgressReporter progress( this, threadId, 1 ); // Define the portion of the input to walk for this thread InputImageRegionType inputRegionForThread; this->CallCopyOutputRegionToInputRegion(inputRegionForThread, outputRegionForThread); unsigned int nValues = inputRegionForThread.GetSize()[this->m_AveragingDimension]; unsigned int offset = inputRegionForThread.GetIndex()[this->m_AveragingDimension]; ImageRegionIteratorWithIndex it( outputPtr, outputRegionForThread ); while ( !it.IsAtEnd() ) { typename InputImageType::IndexType idx; unsigned int dimCount = 0; for ( unsigned int i=0; im_AveragingDimension ) { idx[i] = it.GetIndex()[dimCount]; ++dimCount; } } typename OutputImageType::PixelType value = 0.0; typename OutputImageType::PixelType nTimes = (nValues - offset + 1); for ( unsigned int i=offset; im_AveragingDimension] = i; value += inputPtr->GetPixel( idx ); } if ( nTimes > 0 ) { value /= nTimes; } it.Set( value ); ++it; } progress.CompletedPixel(); } } // end namespace itk #endif ants-2.2.0/Utilities/itkComposeDiffeomorphismsImageFilter.h000066400000000000000000000106761311104306400241140ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkComposeDiffeomorphismsImageFilter_h #define __itkComposeDiffeomorphismsImageFilter_h #include "itkImageToImageFilter.h" #include "itkVectorInterpolateImageFunction.h" namespace itk { /** * \class ComposeDiffeomorphismsImageFilter * * \brief * * \par * * \author Nicholas J. Tustison */ template class ComposeDiffeomorphismsImageFilter : public ImageToImageFilter { public: typedef ComposeDiffeomorphismsImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Extract dimension from input image. */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); typedef TInputImage InputFieldType; typedef TOutputImage OutputFieldType; /** Image typedef support. */ typedef typename OutputFieldType::PixelType PixelType; typedef typename OutputFieldType::PixelType VectorType; typedef typename OutputFieldType::RegionType RegionType; typedef typename OutputFieldType::IndexType IndexType; typedef typename OutputFieldType::PointType PointType; typedef typename OutputFieldType::SpacingType SpacingType; typedef typename OutputFieldType::PointType OriginType; typedef typename OutputFieldType::SizeType SizeType; typedef typename OutputFieldType::DirectionType DirectionType; /** Other typedef */ typedef typename VectorType::ComponentType RealType; typedef VectorInterpolateImageFunction InterpolatorType; /** Get the interpolator. */ itkGetModifiableObjectMacro( Interpolator, InterpolatorType ); /** Set the deformation field */ void SetDisplacementField( const InputFieldType *field ) { itkDebugMacro( "setting deformation field to " << field ); if( field != this->GetInput( 0 ) ) { this->SetInput( 0, field ); this->Modified(); if( !this->m_Interpolator.IsNull() ) { this->m_Interpolator->SetInputImage( field ); } } } /** * Get the deformation field. */ const InputFieldType * GetDisplacementField() const { return this->GetInput( 0 ); } /** Set the warping field */ void SetWarpingField( const InputFieldType *field ) { itkDebugMacro( "setting warping field to " << field ); if( field != this->GetInput( 1 ) ) { this->SetInput( 1, field ); } } /** * Get the warping field. */ const InputFieldType * GetWarpingField() const { return this->GetInput( 1 ); } /* Set the interpolator. */ virtual void SetInterpolator( InterpolatorType* interpolator ); protected: /** Constructor */ ComposeDiffeomorphismsImageFilter(); /** Deconstructor */ virtual ~ComposeDiffeomorphismsImageFilter(); /** Standard print self function **/ void PrintSelf( std::ostream& os, Indent indent ) const; /** preprocessing function */ void BeforeThreadedGenerateData(); /** Multithreaded function which generates the output field. */ void ThreadedGenerateData( const RegionType &, ThreadIdType ); private: ComposeDiffeomorphismsImageFilter( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented /** The interpolator. */ typename InterpolatorType::Pointer m_Interpolator; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkComposeDiffeomorphismsImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkComposeDiffeomorphismsImageFilter.hxx000066400000000000000000000074241311104306400244710ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkComposeDiffeomorphismsImageFilter_hxx #define __itkComposeDiffeomorphismsImageFilter_hxx #include "itkComposeDiffeomorphismsImageFilter.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkVectorLinearInterpolateImageFunction.h" namespace itk { /* * ComposeDiffeomorphismsImageFilter class definitions */ template ComposeDiffeomorphismsImageFilter ::ComposeDiffeomorphismsImageFilter() { this->SetNumberOfRequiredInputs( 2 ); typedef VectorLinearInterpolateImageFunction DefaultInterpolatorType; typename DefaultInterpolatorType::Pointer interpolator = DefaultInterpolatorType::New(); this->m_Interpolator = interpolator; } template ComposeDiffeomorphismsImageFilter ::~ComposeDiffeomorphismsImageFilter() { } template void ComposeDiffeomorphismsImageFilter ::SetInterpolator( InterpolatorType *interpolator ) { itkDebugMacro( "setting Interpolator to " << interpolator ); if( this->m_Interpolator != interpolator ) { this->m_Interpolator = interpolator; this->Modified(); if( !this->GetDisplacementField() ) { this->m_Interpolator->SetInputImage( this->GetInput( 0 ) ); } } } template void ComposeDiffeomorphismsImageFilter ::BeforeThreadedGenerateData() { VectorType zeroVector( 0.0 ); this->AllocateOutputs(); this->GetOutput()->FillBuffer( zeroVector ); } template void ComposeDiffeomorphismsImageFilter ::ThreadedGenerateData( const RegionType & region, ThreadIdType itkNotUsed( threadId ) ) { VectorType zeroVector( 0.0 ); typename OutputFieldType::Pointer output = this->GetOutput(); ImageRegionConstIterator ItW( this->GetWarpingField(), region ); ImageRegionIteratorWithIndex ItF( output, region ); for( ItW.GoToBegin(), ItF.GoToBegin(); !ItW.IsAtEnd(); ++ItW, ++ItF ) { PointType point1; output->TransformIndexToPhysicalPoint( ItF.GetIndex(), point1 ); typename InputFieldType::PixelType tmpWarp = ItW.Get(); PointType point2 = point1; for( unsigned int d = 0; d < point2.PointDimension; d++ ) { point2[d] += tmpWarp[d]; } typename InterpolatorType::OutputType displacement; if( this->m_Interpolator->IsInsideBuffer( point2 ) ) { displacement = this->m_Interpolator->Evaluate( point2 ); typename OutputFieldType::PixelType tmpOut; tmpOut = ( point2 + displacement ) - point1; ItF.Set( tmpOut ); } } } template void ComposeDiffeomorphismsImageFilter ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf( os, indent ); } } // end namespace itk #endif ants-2.2.0/Utilities/itkDecomposeTensorFunction.h000066400000000000000000000057771311104306400221440ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkDecomposeTensorFunction_h #define __itkDecomposeTensorFunction_h #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif #include "itkProcessObject.h" #include "itkVariableSizeMatrix.h" namespace itk { /** \class DecomposeTensorFunction * */ template > class DecomposeTensorFunction : public ProcessObject { public: /** Standard class typedefs. */ typedef DecomposeTensorFunction Self; typedef ProcessObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro( DecomposeTensorFunction, ProcessObject ); /** Extract some information from the image types. Dimensionality * of the two images is assumed to be the same. */ typedef TInput InputMatrixType; typedef TOutput OutputMatrixType; /** Define the data type and the vector of data type used in calculations. */ typedef TRealType RealType; // Wrappers for vnl routines void EvaluateEigenDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateSymmetricEigenDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateQRDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateSVDDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateSVDEconomyDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateLeftPolarDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateRightPolarDecomposition( InputMatrixType &, OutputMatrixType &, OutputMatrixType & ); void EvaluateCholeskyDecomposition( InputMatrixType &, OutputMatrixType & ); RealType EvaluateDeterminant( InputMatrixType & ); DecomposeTensorFunction(); virtual ~DecomposeTensorFunction() { } protected: void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; private: DecomposeTensorFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkDecomposeTensorFunction.hxx" #endif #endif ants-2.2.0/Utilities/itkDecomposeTensorFunction.hxx000066400000000000000000000222551311104306400225120ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkDecomposeTensorFunction_hxx #define __itkDecomposeTensorFunction_hxx #include "itkDecomposeTensorFunction.h" #include "vnl/algo/vnl_cholesky.h" #include "vnl/algo/vnl_qr.h" #include "vnl/algo/vnl_real_eigensystem.h" #include "vnl/algo/vnl_svd.h" #include "vnl/algo/vnl_svd_economy.h" #include "vnl/algo/vnl_symmetric_eigensystem.h" #include "vnl/vnl_matrix.h" #include "vnl/vnl_matrix_fixed.h" #include "vnl/vnl_det.h" namespace itk { template DecomposeTensorFunction ::DecomposeTensorFunction() { } template void DecomposeTensorFunction ::EvaluateEigenDecomposition( InputMatrixType & M, OutputMatrixType & D, OutputMatrixType & V ) { unsigned int RowDimensions = M.Rows(); unsigned int ColumnDimensions = M.Cols(); D.SetSize( RowDimensions, RowDimensions ); V.SetSize( RowDimensions, RowDimensions ); D.Fill( 0.0 ); vnl_matrix v( RowDimensions, ColumnDimensions ); for( unsigned int j = 0; j < ColumnDimensions; j++ ) { for( unsigned int i = 0; i < RowDimensions; i++ ) { v.put( i, j, M[i][j] ); } } vnl_real_eigensystem eig( v ); for( unsigned int j = 0; j < ColumnDimensions; j++ ) { for( unsigned int i = 0; i < RowDimensions; i++ ) { V[i][j] = static_cast( (eig.Vreal).get( i, j ) ); if( i == j ) { D[i][j] = static_cast( std::real( eig.D(j) ) ); } } } } template void DecomposeTensorFunction ::EvaluateSymmetricEigenDecomposition( InputMatrixType & M, OutputMatrixType & D, OutputMatrixType & V ) { // The resulting eigenvectors and values are sorted in increasing order // so V.column(0) is the eigenvector corresponding to the // smallest eigenvalue. unsigned int RowDimensions = M.Rows(); unsigned int ColumnDimensions = M.Cols(); D.SetSize( RowDimensions, RowDimensions ); V.SetSize( RowDimensions, RowDimensions ); D.Fill( 0.0 ); vnl_symmetric_eigensystem eig( M.GetVnlMatrix() ); for( unsigned int j = 0; j < ColumnDimensions; j++ ) { for( unsigned int i = 0; i < RowDimensions; i++ ) { V[i][j] = (eig.V).get( i, j ); if( i == j ) { D[i][j] = eig.D(j); } } } } template void DecomposeTensorFunction ::EvaluateRightPolarDecomposition( InputMatrixType & M, OutputMatrixType & R, OutputMatrixType & S ) { OutputMatrixType U; OutputMatrixType W; OutputMatrixType V; this->EvaluateSVDDecomposition( M, U, W, V ); R = U * V.GetTranspose(); S = V * W * V.GetTranspose(); } template void DecomposeTensorFunction ::EvaluateLeftPolarDecomposition( InputMatrixType & M, OutputMatrixType & S, OutputMatrixType & R ) { OutputMatrixType U; OutputMatrixType W; OutputMatrixType V; this->EvaluateSVDDecomposition( M, U, W, V ); R = U * V.GetTranspose(); S = U * W * U.GetTranspose(); } template void DecomposeTensorFunction ::EvaluateQRDecomposition( InputMatrixType & M, OutputMatrixType & Q, OutputMatrixType & R ) { vnl_qr qr( M.GetVnlMatrix() ); Q.SetSize( qr.Q().rows(), qr.Q().cols() ); R.SetSize( qr.R().rows(), qr.R().cols() ); for( unsigned int i = 0; i < Q.Rows(); i++ ) { for( unsigned int j = 0; j < Q.Cols(); j++ ) { Q[i][j] = qr.Q() (i, j); } } for( unsigned int i = 0; i < R.Rows(); i++ ) { for( unsigned int j = 0; j < R.Cols(); j++ ) { R[i][j] = qr.R() (i, j); } } } template void DecomposeTensorFunction ::EvaluateSVDDecomposition( InputMatrixType & M, OutputMatrixType & U, OutputMatrixType & W, OutputMatrixType & V ) { unsigned int RowDimensions = M.Rows(); unsigned int ColumnDimensions = M.Cols(); U.SetSize( RowDimensions, RowDimensions ); V.SetSize( ColumnDimensions, ColumnDimensions ); W.SetSize( RowDimensions, ColumnDimensions ); vnl_svd svd( M.GetVnlMatrix() ); for( unsigned int i = 0; i < RowDimensions; i++ ) { for( unsigned int j = 0; j < RowDimensions; j++ ) { U[i][j] = svd.U(i, j); } } for( unsigned int i = 0; i < ColumnDimensions; i++ ) { for( unsigned int j = 0; j < ColumnDimensions; j++ ) { V[i][j] = svd.V(i, j); } } W.Fill( 0.0 ); unsigned int minDimensions = ColumnDimensions; if( static_cast( RowDimensions ) < static_cast( ColumnDimensions ) ) { minDimensions = RowDimensions; } for( unsigned int i = 0; i < minDimensions; i++ ) { W[i][i] = svd.W(i, i); } } template void DecomposeTensorFunction ::EvaluateSVDEconomyDecomposition( InputMatrixType & M, OutputMatrixType & W, OutputMatrixType & V ) { /** * Same as SVD except the routine does not return U --- * allows for faster computation. */ unsigned int RowDimensions = M.Rows(); unsigned int ColumnDimensions = M.Cols(); V.SetSize( ColumnDimensions, ColumnDimensions ); W.SetSize( RowDimensions, ColumnDimensions ); vnl_svd_economy svd( M.GetVnlMatrix() ); for( unsigned int i = 0; i < ColumnDimensions; i++ ) { for( unsigned int j = 0; j < ColumnDimensions; j++ ) { V[i][j] = svd.V() (i, j); } } W.Fill( 0.0 ); unsigned int minDimensions = ColumnDimensions; if( static_cast( RowDimensions ) < static_cast( ColumnDimensions ) ) { minDimensions = RowDimensions; } for( unsigned int i = 0; i < minDimensions; i++ ) { W[i][i] = svd.lambdas()[i]; } } template void DecomposeTensorFunction ::EvaluateCholeskyDecomposition( InputMatrixType & M, OutputMatrixType & L ) { // Assumes symmetric tensor of type double vnl_matrix m( M.Rows(), M.Cols() ); for( unsigned int j = 0; j < M.Cols(); j++ ) { for( unsigned int i = 0; i < M.Rows(); i++ ) { m.put( i, j, M[i][j] ); } } vnl_cholesky cholesky( m, vnl_cholesky::quiet ); L.SetSize( M.Rows(), M.Cols() ); for( unsigned int i = 0; i < L.Rows(); i++ ) { for( unsigned int j = 0; j < L.Cols(); j++ ) { L[i][j] = cholesky.L_badly_named_method().get(i, j); } } } template typename DecomposeTensorFunction::RealType DecomposeTensorFunction ::EvaluateDeterminant( InputMatrixType & M ) { RealType det = 0.0; if( M.Rows() == M.Cols() && M.Rows() >= 2 && M.Rows() <= 4 ) { vnl_matrix_fixed m2; vnl_matrix_fixed m3; vnl_matrix_fixed m4; for( unsigned int i = 0; i < M.Rows(); i++ ) { for( unsigned int j = 0; j < M.Cols(); j++ ) { switch( M.Rows() ) { case 2: { m2.put( i, j, M[i][j] ); } break; case 3: { m3.put( i, j, M[i][j] ); } break; case 4: { m4.put( i, j, M[i][j] ); } break; } } } switch( M.Rows() ) { case 2: { det = static_cast( vnl_det( m2 ) ); } break; case 3: { det = static_cast( vnl_det( m3 ) ); } break; case 4: { det = static_cast( vnl_det( m4 ) ); } break; } } else { vnl_qr qr( M.GetVnlMatrix() ); det = static_cast( qr.determinant() ); } return det; } template void DecomposeTensorFunction ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf( os, indent ); } } // end namespace itk #endif ants-2.2.0/Utilities/itkDeformationFieldGradientTensorImageFilter.h000077500000000000000000000151661311104306400255160ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 __itkDeformationFieldGradientTensorImageFilter_h #define __itkDeformationFieldGradientTensorImageFilter_h #include "itkConstNeighborhoodIterator.h" #include "itkImageToImageFilter.h" #include "itkMatrix.h" #include "itkVector.h" namespace itk { /** \class DeformationFieldGradientTensorImageFilter * */ template , TInputImage::ImageDimension> > class ITK_EXPORT DeformationFieldGradientTensorImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef DeformationFieldGradientTensorImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods) */ itkTypeMacro( DeformationFieldGradientTensorImageFilter, ImageToImageFilter ); /** Extract some information from the image types. Dimensionality * of the two images is assumed to be the same. */ typedef typename TOutputImage::PixelType OutputPixelType; typedef typename TInputImage::PixelType InputPixelType; /** Image typedef support */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; /** The dimensionality of the input and output images. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension); /** Length of the vector pixel type of the input image. */ itkStaticConstMacro(VectorDimension, unsigned int, InputPixelType::Dimension); /** Define the data type and the vector of data type used in calculations. */ typedef TRealType RealType; typedef Image RealImageType; typedef Vector RealVectorType; typedef Image RealVectorImageType; typedef Matrix RealMatrixType; /** Type of the iterator that will be used to move through the image. Also the type which will be passed to the evaluate function */ typedef ConstNeighborhoodIterator ConstNeighborhoodIteratorType; typedef typename ConstNeighborhoodIteratorType::RadiusType RadiusType; /** Superclass typedefs. */ typedef typename Superclass::OutputImageRegionType OutputImageRegionType; /** DeformationFieldGradientTensorImageFilter needs a larger input requested * region than the output requested region (larger by the kernel * size to calculate derivatives). As such, * DeformationFieldGradientTensorImageFilter needs to provide an * implementation for GenerateInputRequestedRegion() in order to inform the * pipeline execution model. * * \sa ImageToImageFilter::GenerateInputRequestedRegion() */ virtual void GenerateInputRequestedRegion() throw( InvalidRequestedRegionError ) ITK_OVERRIDE; itkSetClampMacro( Order, unsigned int, 1, 2 ); itkGetConstReferenceMacro( Order, unsigned int ); itkSetMacro( UseCenteredDifference, bool ); itkGetConstReferenceMacro( UseCenteredDifference, bool ); itkBooleanMacro( UseCenteredDifference ); itkSetMacro( UseImageSpacing, bool ); itkGetConstReferenceMacro( UseImageSpacing, bool ); itkBooleanMacro( UseImageSpacing ); itkSetMacro( CalculateJacobian, bool ); itkGetConstReferenceMacro( CalculateJacobian, bool ); itkBooleanMacro( CalculateJacobian ); /** Get access to the input image casted as real pixel values */ itkGetConstObjectMacro( RealValuedInputImage, RealVectorImageType ); protected: DeformationFieldGradientTensorImageFilter(); virtual ~DeformationFieldGradientTensorImageFilter() {} /** Do any necessary casting/copying of the input data. Input pixel types whose value types are not real number types must be cast to real number types.*/ void BeforeThreadedGenerateData() ITK_OVERRIDE; /** DeformationFieldGradientTensorImageFilter can be implemented as a * multithreaded filter (we're only using vnl_det(), which is trivially * thread safe). Therefore, this implementation provides a * ThreadedGenerateData() routine which is called for each * processing thread. The output image data is allocated * automatically by the superclass prior to calling * ThreadedGenerateData(). ThreadedGenerateData can only write to * the portion of the output image specified by the parameter * "outputRegionForThread" * * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ void ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) ITK_OVERRIDE; void PrintSelf ( std::ostream& os, Indent indent ) const ITK_OVERRIDE; private: bool m_UseImageSpacing; bool m_UseCenteredDifference; bool m_CalculateJacobian; unsigned int m_Order; RadiusType m_NeighborhoodRadius; Vector m_DerivativeWeights; typename RealVectorImageType::ConstPointer m_RealValuedInputImage; RealMatrixType EvaluateAtNeighborhood( const ConstNeighborhoodIteratorType & ) const; DeformationFieldGradientTensorImageFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkDeformationFieldGradientTensorImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkDeformationFieldGradientTensorImageFilter.hxx000077500000000000000000000231211311104306400260640ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 _itkDeformationFieldGradientTensorImageFilter_hxx #define _itkDeformationFieldGradientTensorImageFilter_hxx #include "itkDeformationFieldGradientTensorImageFilter.h" #include "itkImageRegionIterator.h" #include "itkNeighborhoodAlgorithm.h" #include "itkProgressReporter.h" #include "itkVectorCastImageFilter.h" #include "itkZeroFluxNeumannBoundaryCondition.h" #include "vnl/vnl_det.h" namespace itk { template DeformationFieldGradientTensorImageFilter ::DeformationFieldGradientTensorImageFilter() { this->m_UseImageSpacing = true; this->m_UseCenteredDifference = true; this->m_CalculateJacobian = false; this->m_Order = 1; this->m_DerivativeWeights.Fill( 1.0 ); } template void DeformationFieldGradientTensorImageFilter ::GenerateInputRequestedRegion() throw( InvalidRequestedRegionError ) { // call the superclass' implementation of this method Superclass::GenerateInputRequestedRegion(); // get pointers to the input and output InputImagePointer inputPtr = const_cast< InputImageType * >( this->GetInput() ); OutputImagePointer outputPtr = this->GetOutput(); if ( !inputPtr || !outputPtr ) { return; } // get a copy of the input requested region (should equal the output // requested region) typename TInputImage::RegionType inputRequestedRegion; inputRequestedRegion = inputPtr->GetRequestedRegion(); this->m_NeighborhoodRadius.Fill( this->m_Order ); // pad the input requested region by the operator radius inputRequestedRegion.PadByRadius( this->m_NeighborhoodRadius ); // crop the input requested region at the input's largest possible region if ( inputRequestedRegion.Crop( inputPtr->GetLargestPossibleRegion() ) ) { inputPtr->SetRequestedRegion( inputRequestedRegion ); return; } else { // Couldn't crop the region (requested region is outside the largest // possible region). Throw an exception. // store what we tried to request (prior to trying to crop) inputPtr->SetRequestedRegion( inputRequestedRegion ); // build an exception InvalidRequestedRegionError e( __FILE__, __LINE__ ); e.SetLocation( ITK_LOCATION ); e.SetDescription( "Requested region is outside the largest possible region." ); e.SetDataObject( inputPtr ); throw e; } } template< typename TInputImage, typename TRealType, typename TOutputImage > void DeformationFieldGradientTensorImageFilter ::BeforeThreadedGenerateData() { if ( this->m_UseImageSpacing ) { for ( unsigned int i = 0; i < ImageDimension; i++ ) { if ( this->GetInput()->GetSpacing()[i] == 0.0 ) { itkExceptionMacro(<< "Image spacing in dimension " << i << " is zero."); } this->m_DerivativeWeights[i] = 1.0 / static_cast( this->GetInput()->GetSpacing()[i] ); } } /** If the input needs casting to a real-valued vector type, create the appropriate image and set the m_RealValuedInputImage pointer to this image. Otherwise just point to the input image. */ if ( typeid( typename InputImageType::PixelType ) != typeid( RealVectorType ) ) { typename VectorCastImageFilter::Pointer caster = VectorCastImageFilter::New(); caster->SetInput( this->GetInput() ); caster->Update(); this->m_RealValuedInputImage = caster->GetOutput(); } else { this->m_RealValuedInputImage = dynamic_cast( this->GetInput() ); } } template< typename TInputImage, typename TRealType, typename TOutputImage > void DeformationFieldGradientTensorImageFilter< TInputImage, TRealType, TOutputImage > ::ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) { ZeroFluxNeumannBoundaryCondition nbc; ConstNeighborhoodIteratorType bit; ImageRegionIterator it; // Find the data-set boundary "faces" typename NeighborhoodAlgorithm:: ImageBoundaryFacesCalculator::FaceListType faceList; NeighborhoodAlgorithm::ImageBoundaryFacesCalculator bC; faceList = bC( dynamic_cast ( this->m_RealValuedInputImage.GetPointer() ), outputRegionForThread, this->m_NeighborhoodRadius ); typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator:: FaceListType::iterator fit; // Support progress methods/callbacks ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); const typename InputImageType::DirectionType R = this->m_RealValuedInputImage->GetDirection(); const typename InputImageType::DirectionType RI = this->m_RealValuedInputImage->GetInverseDirection(); // Process each of the data set faces. The iterator is reinitialized on each // face so that it can determine whether or not to check for boundary // conditions. for ( fit = faceList.begin(); fit != faceList.end(); ++fit ) { bit = ConstNeighborhoodIteratorType( this->m_NeighborhoodRadius, dynamic_cast ( m_RealValuedInputImage.GetPointer() ), *fit ); it = ImageRegionIterator( this->GetOutput(), *fit ); bit.OverrideBoundaryCondition( &nbc ); bit.GoToBegin(); while ( ! bit.IsAtEnd() ) { RealMatrixType F = this->EvaluateAtNeighborhood( bit ); it.Set( R * F * RI ); ++bit; ++it; progress.CompletedPixel(); } } } template< typename TInputImage, typename TRealType, typename TOutputImage > typename DeformationFieldGradientTensorImageFilter< TInputImage, TRealType, TOutputImage >::RealMatrixType DeformationFieldGradientTensorImageFilter< TInputImage, TRealType, TOutputImage > ::EvaluateAtNeighborhood( const ConstNeighborhoodIteratorType &it ) const { unsigned i, j; RealMatrixType F; RealVectorType physicalVectorCenter; this->m_RealValuedInputImage->TransformLocalVectorToPhysicalVector( it.GetCenterPixel(), physicalVectorCenter ); for ( i = 0; i < ImageDimension; ++i ) { RealVectorType physicalVectorNext1; this->m_RealValuedInputImage->TransformLocalVectorToPhysicalVector( it.GetNext( i, 1 ), physicalVectorNext1 ); RealVectorType physicalVectorNext2; this->m_RealValuedInputImage->TransformLocalVectorToPhysicalVector( it.GetNext( i, 2 ), physicalVectorNext2 ); RealVectorType physicalVectorPrevious1; this->m_RealValuedInputImage->TransformLocalVectorToPhysicalVector( it.GetPrevious( i, 1 ), physicalVectorPrevious1 ); RealVectorType physicalVectorPrevious2; this->m_RealValuedInputImage->TransformLocalVectorToPhysicalVector( it.GetPrevious( i, 2 ), physicalVectorPrevious2 ); RealType weight = this->m_DerivativeWeights[i]; for ( j = 0; j < VectorDimension; ++j ) { if ( this->m_UseCenteredDifference ) { switch( this->m_Order ) { case 1: default: F[i][j] = weight * 0.5 * ( physicalVectorNext1[j] - physicalVectorPrevious1[j] ); break; case 2: F[i][j] = weight * ( -physicalVectorNext2[j] + 8.0 * physicalVectorNext1[j] - 8.0 * physicalVectorPrevious1[j] + physicalVectorPrevious2[j] ) / 12.0; break; } } else // Forward difference schema { switch( this->m_Order ) { case 1: default: F[i][j] = weight * ( physicalVectorNext1[j] - physicalVectorCenter[j] ); break; } } } } if ( this->m_CalculateJacobian ) { unsigned int minDimension = ImageDimension; if ( static_cast( VectorDimension ) < static_cast( ImageDimension ) ) { minDimension = VectorDimension; } for ( i = 0; i < minDimension; i++ ) { F[i][i] += 1.0; } } return F; } template void DeformationFieldGradientTensorImageFilter ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf(os,indent); os << indent << "m_CalculateJacobian = " << this->m_CalculateJacobian << std::endl; os << indent << "m_UseImageSpacing = " << this->m_UseImageSpacing << std::endl; os << indent << "m_UseCenteredDifference = " << this->m_UseCenteredDifference << std::endl; os << indent << "m_Order = " << this->m_Order << std::endl; os << indent << "m_NeighborhoodRadius = " << this->m_NeighborhoodRadius << std::endl; os << indent << "m_RealValuedInputImage = " << m_RealValuedInputImage.GetPointer() << std::endl; } } // end namespace itk #endif ants-2.2.0/Utilities/itkDeterminantTensorImageFilter.h000077500000000000000000000070251311104306400230720ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 __itkDeterminantTensorImageFilter_h #define __itkDeterminantTensorImageFilter_h #include "itkImageToImageFilter.h" #include "itkMatrix.h" #include "itkVector.h" namespace itk { /** \class DeterminantTensorImageFilter * */ template > class ITK_EXPORT DeterminantTensorImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef DeterminantTensorImageFilter Self; typedef ImageToImageFilter< TInputImage, TOutputImage > Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods) */ itkTypeMacro( DeterminantTensorImageFilter, ImageToImageFilter ); /** Extract some information from the image types. Dimensionality * of the two images is assumed to be the same. */ typedef typename TOutputImage::PixelType OutputPixelType; typedef typename TInputImage::PixelType InputPixelType; /** Image typedef support */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename OutputImageType::RegionType OutputImageRegionType; /** The dimensionality of the input and output images. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension); /** Define the data type and the vector of data type used in calculations. */ typedef TRealType RealType; protected: DeterminantTensorImageFilter(); virtual ~DeterminantTensorImageFilter() {} /** DeterminantTensorImageFilter can be implemented as a * multithreaded filter (we're only using vnl_det(), which is trivially * thread safe). Therefore, this implementation provides a * ThreadedGenerateData() routine which is called for each * processing thread. The output image data is allocated * automatically by the superclass prior to calling * ThreadedGenerateData(). ThreadedGenerateData can only write to * the portion of the output image specified by the parameter * "outputRegionForThread" * * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ void ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) ITK_OVERRIDE; void PrintSelf ( std::ostream& os, Indent indent ) const ITK_OVERRIDE; private: DeterminantTensorImageFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkDeterminantTensorImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkDeterminantTensorImageFilter.hxx000077500000000000000000000040601311104306400234460ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 _itkDeterminantTensorImageFilter_hxx #define _itkDeterminantTensorImageFilter_hxx #include "itkDeterminantTensorImageFilter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIterator.h" #include "itkProgressReporter.h" #include "vnl/vnl_det.h" namespace itk { template DeterminantTensorImageFilter ::DeterminantTensorImageFilter(){} template< typename TInputImage, typename TRealType, typename TOutputImage > void DeterminantTensorImageFilter< TInputImage, TRealType, TOutputImage > ::ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) { ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); ImageRegionIterator ItD ( this->GetOutput(), outputRegionForThread ); ImageRegionConstIterator ItM ( this->GetInput(), outputRegionForThread ); ItD.GoToBegin(); ItM.GoToBegin(); while ( !ItD.IsAtEnd() && !ItM.IsAtEnd() ) { ItD.Set( static_cast( vnl_det( ( ItM.Get() ).GetVnlMatrix() ) ) ); progress.CompletedPixel(); ++ItD; ++ItM; } } template void DeterminantTensorImageFilter ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf(os,indent); } } // end namespace itk #endif ants-2.2.0/Utilities/itkDiReCTImageFilter.h000066400000000000000000000256451311104306400205040ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkDiReCTImageFilter_h #define __itkDiReCTImageFilter_h #include "itkImageToImageFilter.h" #include "itkVector.h" namespace itk { /** \class DiReCTImageFilter * \brief Diffeomorphic Registration-based Cortical Thickness measurement. * * To estimate the cortical thickness, the following inputs are required: * - Segmentation image in which the csf, grey matter, and white matter voxels * are all labeled with values of 1, 2, and 3, respectively. * - Corresponding grey matter and white matter probability maps. * * \author Nicholas J. Tustison * * \par REFERENCE * S. R. Das, B. B. Avants, M. Grossman, and J. C. Gee, "Registration based * cortical thickness measurement," Neuroimage 2009, 45:867--879. * */ template class DiReCTImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef DiReCTImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Extract dimension from input and output image. */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); /** Convenient typedefs for simplifying declarations. */ typedef TInputImage InputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename InputImageType::PixelType InputPixelType; typedef InputPixelType LabelType; typedef typename InputImageType::RegionType RegionType; typedef typename InputImageType::IndexType IndexType; typedef typename IndexType::IndexValueType IndexValueType; typedef TOutputImage OutputImageType; typedef TOutputImage RealImageType; typedef typename OutputImageType::PixelType RealType; typedef typename RealImageType::Pointer RealImagePointer; typedef Vector VectorType; typedef Image DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldPointer; typedef typename VectorType::ValueType VectorValueType; typedef typename DisplacementFieldType::PointType PointType; /** * Set the segmentation image. The segmentation image is a labeled image * with specified labels for the gray and white matters. */ void SetSegmentationImage( const InputImageType *seg ) { this->SetNthInput( 0, const_cast( seg ) ); } /** * Get the segmentation image. */ const InputImageType * GetSegmentationImage() const { return this->GetInput( 0 ); } /** * Get the label image. */ const RealImageType * GetThicknessPriorImage() const { return this->m_ThicknessPriorImage; } /** * Set the label image. */ void SetThicknessPriorImage( RealImagePointer seg ) { this->m_ThicknessPriorImage = seg; } /** * Set the grey matter probability image. */ void SetGrayMatterProbabilityImage( const RealImageType *gm ) { this->SetNthInput( 1, const_cast( gm ) ); this->Modified(); } /** * Get the grey matter probability image. */ const RealImageType * GetGrayMatterProbabilityImage() const { return static_cast( this->ProcessObject::GetInput( 1 ) ); } /** * Set the white matter probability image. */ void SetWhiteMatterProbabilityImage( const RealImageType *wm ) { this->SetNthInput( 2, const_cast( wm ) ); this->Modified(); } /** * Get the grey matter probability image. */ const RealImageType * GetWhiteMatterProbabilityImage() const { return static_cast( this->ProcessObject::GetInput( 2 ) ); } /** * Get the warped white matter probability map. */ const RealImageType * GetWarpedWhiteMatterProbabilityImage() const { return static_cast( this->ProcessObject::GetOutput( 1 ) ); } /** * Set/Get the maximum number of registration iterations. Default = 50. */ itkSetMacro( MaximumNumberOfIterations, unsigned int ); itkGetConstMacro( MaximumNumberOfIterations, unsigned int ); /** * Set/Get the maximum number of inversion iterations. Default = 20. */ itkSetMacro( MaximumNumberOfInvertDisplacementFieldIterations, unsigned int ); itkGetConstMacro( MaximumNumberOfInvertDisplacementFieldIterations, unsigned int ); /** * Set/Get the gray matter label in the segmentation image. Default = 2. */ itkSetMacro( GrayMatterLabel, LabelType ); itkGetConstMacro( GrayMatterLabel, LabelType ); /** * Set/Get the white matter label in the segmentation image. Default = 3. */ itkSetMacro( WhiteMatterLabel, LabelType ); itkGetConstMacro( WhiteMatterLabel, LabelType ); /** * Set/Get the convergence threshold. Default = 0.001. */ itkSetMacro( ConvergenceThreshold, RealType ); itkGetConstMacro( ConvergenceThreshold, RealType ); /** * Set/Get the convergence window size. Convergence is determined by fitting a * line to the normalized energy profile of the last N iterations (where * N is specified by the window size) and determining the slope which is * then compared with the convergence threshold. Default = 10. */ itkSetMacro( ConvergenceWindowSize, unsigned int ); itkGetConstMacro( ConvergenceWindowSize, unsigned int ); /** * Set/Get the thickness prior estimate---provides a constraint on the * final thickness measurement. References in the literature * give a normal thickness of typically 3 mm with normal range * from ~2 mm in the calcarine cortex to ~4 mm in the precentral * gyrus. Default = 10 mm. */ itkSetMacro( ThicknessPriorEstimate, RealType ); itkGetConstMacro( ThicknessPriorEstimate, RealType ); /** * Set/Get the gradient step size. Default = 0.025. */ itkSetClampMacro( InitialGradientStep, RealType, 0, NumericTraits::max() ); itkGetConstMacro( InitialGradientStep, RealType ); /** * Get the current gradient step. */ itkGetConstMacro( CurrentGradientStep, RealType ); /** * Set/Get the smoothing sigma for the total and hit images (in voxels). Default = 1.0. */ itkSetClampMacro( SmoothingVariance, RealType, 0, NumericTraits::max() ); itkGetConstMacro( SmoothingVariance, RealType ); /** * Set/Get the isotropic mesh spacing for smoothing the velocity field (in mm). Default = 5.75. */ itkSetClampMacro( BSplineSmoothingIsotropicMeshSpacing, RealType, 0, NumericTraits::max() ); itkGetConstMacro( BSplineSmoothingIsotropicMeshSpacing, RealType ); /** * Set/Get the B-spline smoothing sigma for the velocity field (in voxels). Default = 1.5. */ itkSetClampMacro( SmoothingVelocityFieldVariance, RealType, 0, NumericTraits::max() ); itkGetConstMacro( SmoothingVelocityFieldVariance, RealType ); /** * Set/Get the number of integration points. Default = 10. */ itkSetMacro( NumberOfIntegrationPoints, unsigned int ); itkGetConstMacro( NumberOfIntegrationPoints, unsigned int ); /** * Set/Get the option to use B-spline smoothing. Default = false. */ itkSetMacro( UseBSplineSmoothing, bool ); itkGetConstMacro( UseBSplineSmoothing, bool ); itkBooleanMacro( UseBSplineSmoothing ); /** * Get the number of elapsed iterations. This is a helper function for * reporting observations. */ itkGetConstMacro( ElapsedIterations, unsigned int ); /** * Get the current energy. This is a helper function for reporting * observations. */ itkGetConstMacro( CurrentEnergy, RealType ); /** * Get the current convergence measurement. This is a helper function for * reporting observations. */ itkGetConstMacro( CurrentConvergenceMeasurement, RealType ); protected: DiReCTImageFilter(); virtual ~DiReCTImageFilter(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void ThreadedGenerateData( const RegionType &, ThreadIdType ) ITK_OVERRIDE { }; void GenerateData() ITK_OVERRIDE; private: /** * Private function for extracting regions (e.g. gray or white). */ InputImagePointer ExtractRegion( const InputImageType *, LabelType ); /** * Private function for extracting regional contours (e.g. gray or white). */ InputImagePointer ExtractRegionalContours( const InputImageType *, LabelType ); /** * Private function for making thickness image. */ void MakeThicknessImage( RealImagePointer, RealImagePointer, InputImagePointer, RealImagePointer ); /** * Private function for warping an image. */ RealImagePointer WarpImage( const RealImageType *, const DisplacementFieldType * ); /** * Private function for inverting the deformation field. */ void InvertDisplacementField( const DisplacementFieldType *, DisplacementFieldType * ); /** * Private function for smoothing the deformation field. */ DisplacementFieldPointer GaussianSmoothDisplacementField( const DisplacementFieldType *, const RealType ); /** * Private function for smoothing the deformation field. */ DisplacementFieldPointer BSplineSmoothDisplacementField( const DisplacementFieldType *, const RealType ); /** * Private function for smoothing the image. */ RealImagePointer SmoothImage( const RealImageType *, const RealType ); RealType m_ThicknessPriorEstimate; RealType m_SmoothingVariance; RealType m_SmoothingVelocityFieldVariance; RealType m_BSplineSmoothingIsotropicMeshSpacing; RealType m_InitialGradientStep; RealType m_CurrentGradientStep; unsigned int m_NumberOfIntegrationPoints; LabelType m_GrayMatterLabel; LabelType m_WhiteMatterLabel; unsigned int m_ElapsedIterations; unsigned int m_MaximumNumberOfIterations; unsigned int m_MaximumNumberOfInvertDisplacementFieldIterations; RealType m_CurrentEnergy; RealType m_CurrentConvergenceMeasurement; RealType m_ConvergenceThreshold; unsigned int m_ConvergenceWindowSize; RealImagePointer m_ThicknessPriorImage; bool m_UseBSplineSmoothing; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkDiReCTImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkDiReCTImageFilter.hxx000066400000000000000000001132271311104306400210560ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkDiReCTImageFilter_hxx #define __itkDiReCTImageFilter_hxx #include "itkDiReCTImageFilter.h" #include "itkAddImageFilter.h" #include "itkBinaryContourImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkBinaryBallStructuringElement.h" #include "itkBinaryDilateImageFilter.h" #include "itkCastImageFilter.h" #include "itkComposeDisplacementFieldsImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkDisplacementFieldToBSplineImageFilter.h" #include "itkGaussianOperator.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkVectorMagnitudeImageFilter.h" #include "itkImageDuplicator.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkImportImageFilter.h" #include "itkInvertDisplacementFieldImageFilter.h" #include "itkIterationReporter.h" #include "itkMaximumImageFilter.h" #include "itkMultiplyByConstantImageFilter.h" #include "itkStatisticsImageFilter.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkVectorNeighborhoodOperatorImageFilter.h" #include "itkWarpImageFilter.h" #include "itkWindowConvergenceMonitoringFunction.h" #include "ReadWriteData.h" namespace itk { template DiReCTImageFilter ::DiReCTImageFilter() : m_ThicknessPriorEstimate( 10.0 ), m_SmoothingVariance( 1.0 ), m_SmoothingVelocityFieldVariance( 1.5 ), m_BSplineSmoothingIsotropicMeshSpacing( 5.75 ), m_InitialGradientStep( 0.025 ), m_CurrentGradientStep( 0.025 ), m_NumberOfIntegrationPoints( 10 ), m_GrayMatterLabel( 2 ), m_WhiteMatterLabel( 3 ), m_MaximumNumberOfIterations( 50 ), m_MaximumNumberOfInvertDisplacementFieldIterations( 20 ), m_CurrentEnergy( NumericTraits::max() ), m_ConvergenceThreshold( 0.001 ), m_ConvergenceWindowSize( 10 ), m_UseBSplineSmoothing( false ) { this->m_ThicknessPriorImage = ITK_NULLPTR; this->SetNumberOfRequiredInputs( 3 ); } template DiReCTImageFilter ::~DiReCTImageFilter() { } template void DiReCTImageFilter ::GenerateData() { if ( this->m_ThicknessPriorImage ) { std::cout << "Using prior thickness image." << std::endl; } this->m_CurrentGradientStep = this->m_InitialGradientStep; // Convert all input direction matrices to identities saving the original // directions to put back at the end of filter processing. We do this // because the white and gray matters reside in the same space and the // assumption simplifies the underlying registration code. const bool filterHandlesMemory = false; typename InputImageType::DirectionType identity; identity.SetIdentity(); typedef ImportImageFilter SegmentationImageImporterType; typename SegmentationImageImporterType::Pointer segmentationImageImporter = SegmentationImageImporterType::New(); segmentationImageImporter->SetImportPointer( const_cast( this->GetSegmentationImage()->GetBufferPointer() ), ( this->GetSegmentationImage()->GetBufferedRegion() ).GetNumberOfPixels(), filterHandlesMemory ); segmentationImageImporter->SetRegion( this->GetSegmentationImage()->GetBufferedRegion() ); segmentationImageImporter->SetOrigin( this->GetSegmentationImage()->GetOrigin() ); segmentationImageImporter->SetSpacing( this->GetSegmentationImage()->GetSpacing() ); segmentationImageImporter->SetDirection( identity ); InputImagePointer segmentationImage = segmentationImageImporter->GetOutput(); segmentationImage->Update(); segmentationImage->DisconnectPipeline(); typedef ImportImageFilter ProbablilityImageImporterType; typename ProbablilityImageImporterType::Pointer grayMatterProbabilityImageImporter = ProbablilityImageImporterType::New(); grayMatterProbabilityImageImporter->SetImportPointer( const_cast( this->GetGrayMatterProbabilityImage()->GetBufferPointer() ), ( this->GetGrayMatterProbabilityImage()->GetBufferedRegion() ). GetNumberOfPixels(), filterHandlesMemory ); grayMatterProbabilityImageImporter->SetRegion( this->GetGrayMatterProbabilityImage()->GetBufferedRegion() ); grayMatterProbabilityImageImporter->SetOrigin( this->GetGrayMatterProbabilityImage()->GetOrigin() ); grayMatterProbabilityImageImporter->SetSpacing( this->GetGrayMatterProbabilityImage()->GetSpacing() ); grayMatterProbabilityImageImporter->SetDirection( identity ); RealImagePointer grayMatterProbabilityImage = grayMatterProbabilityImageImporter->GetOutput(); grayMatterProbabilityImage->Update(); grayMatterProbabilityImage->DisconnectPipeline(); typename ProbablilityImageImporterType::Pointer whiteMatterProbabilityImageImporter = ProbablilityImageImporterType::New(); whiteMatterProbabilityImageImporter->SetImportPointer( const_cast( this->GetWhiteMatterProbabilityImage()->GetBufferPointer() ), ( this->GetWhiteMatterProbabilityImage()->GetBufferedRegion() ) .GetNumberOfPixels(), filterHandlesMemory ); whiteMatterProbabilityImageImporter->SetRegion( this->GetWhiteMatterProbabilityImage()->GetBufferedRegion() ); whiteMatterProbabilityImageImporter->SetOrigin( this->GetWhiteMatterProbabilityImage()->GetOrigin() ); whiteMatterProbabilityImageImporter->SetSpacing( this->GetWhiteMatterProbabilityImage()->GetSpacing() ); whiteMatterProbabilityImageImporter->SetDirection( identity ); RealImagePointer whiteMatterProbabilityImage = whiteMatterProbabilityImageImporter->GetOutput(); whiteMatterProbabilityImage->Update(); whiteMatterProbabilityImage->DisconnectPipeline(); // Extract the gray and white matter segmentations and combine to form the // gm/wm region. Dilate the latter region by 1 voxel. InputImagePointer grayMatter = this->ExtractRegion( segmentationImage, this->m_GrayMatterLabel ); InputImagePointer whiteMatter = this->ExtractRegion( segmentationImage, this->m_WhiteMatterLabel ); typedef AddImageFilter AdderType; typename AdderType::Pointer adder = AdderType::New(); adder->SetInput1( grayMatter ); adder->SetInput2( whiteMatter ); adder->Update(); InputImagePointer thresholdedRegion = this->ExtractRegion( const_cast( adder->GetOutput() ), 1 ); // Extract the white and gm/wm matter contours InputImagePointer matterContours = this->ExtractRegionalContours( thresholdedRegion, 1 ); InputImagePointer whiteMatterContoursTmp = this->ExtractRegionalContours( segmentationImage, this->m_WhiteMatterLabel ); typedef CastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput( whiteMatterContoursTmp ); caster->Update(); RealImagePointer whiteMatterContours = caster->GetOutput(); // Initialize fields and images. VectorType zeroVector( 0.0 ); RealImagePointer corticalThicknessImage = RealImageType::New(); corticalThicknessImage->CopyInformation( segmentationImage ); corticalThicknessImage->SetRegions( segmentationImage->GetRequestedRegion() ); corticalThicknessImage->Allocate(); corticalThicknessImage->FillBuffer( 0.0 ); DisplacementFieldPointer forwardIncrementalField = DisplacementFieldType::New(); forwardIncrementalField->CopyInformation( segmentationImage ); forwardIncrementalField->SetRegions( segmentationImage->GetRequestedRegion() ); forwardIncrementalField->Allocate(); RealImagePointer hitImage = RealImageType::New(); hitImage->CopyInformation( segmentationImage ); hitImage->SetRegions( segmentationImage->GetRequestedRegion() ); hitImage->Allocate(); DisplacementFieldPointer integratedField = DisplacementFieldType::New(); integratedField->CopyInformation( segmentationImage ); integratedField->SetRegions( segmentationImage->GetRequestedRegion() ); integratedField->Allocate(); integratedField->FillBuffer( zeroVector ); DisplacementFieldPointer inverseField = DisplacementFieldType::New(); inverseField->CopyInformation( segmentationImage ); inverseField->SetRegions( segmentationImage->GetRequestedRegion() ); inverseField->Allocate(); DisplacementFieldPointer inverseIncrementalField = DisplacementFieldType::New(); inverseIncrementalField->CopyInformation( segmentationImage ); inverseIncrementalField->SetRegions( segmentationImage->GetRequestedRegion() ); inverseIncrementalField->Allocate(); RealImagePointer speedImage = RealImageType::New(); speedImage->CopyInformation( segmentationImage ); speedImage->SetRegions( segmentationImage->GetRequestedRegion() ); speedImage->Allocate(); RealImagePointer thicknessImage = RealImageType::New(); thicknessImage->CopyInformation( segmentationImage ); thicknessImage->SetRegions( segmentationImage->GetRequestedRegion() ); thicknessImage->Allocate(); RealImagePointer totalImage = RealImageType::New(); totalImage->CopyInformation( segmentationImage ); totalImage->SetRegions( segmentationImage->GetRequestedRegion() ); totalImage->Allocate(); DisplacementFieldPointer velocityField = DisplacementFieldType::New(); velocityField->CopyInformation( segmentationImage ); velocityField->SetRegions( segmentationImage->GetRequestedRegion() ); velocityField->Allocate(); velocityField->FillBuffer( zeroVector ); // Instantiate most of the iterators all in one place ImageRegionIterator ItCorticalThicknessImage( corticalThicknessImage, corticalThicknessImage->GetRequestedRegion() ); ImageRegionConstIterator ItGrayMatterProbabilityMap( grayMatterProbabilityImage, grayMatterProbabilityImage->GetRequestedRegion() ); ImageRegionIterator ItHitImage( hitImage, hitImage->GetRequestedRegion() ); ImageRegionIterator ItForwardIncrementalField( forwardIncrementalField, forwardIncrementalField->GetRequestedRegion() ); ImageRegionConstIterator ItMatterContours( matterContours, matterContours->GetRequestedRegion() ); ImageRegionIterator ItInverseIncrementalField( inverseIncrementalField, inverseIncrementalField->GetRequestedRegion() ); ImageRegionConstIteratorWithIndex ItSegmentationImage( segmentationImage, segmentationImage->GetRequestedRegion() ); ImageRegionIterator ItSpeedImage( speedImage, speedImage->GetRequestedRegion() ); ImageRegionIterator ItThicknessImage( thicknessImage, thicknessImage->GetRequestedRegion() ); ImageRegionIterator ItTotalImage( totalImage, totalImage->GetRequestedRegion() ); ImageRegionConstIterator ItWhiteMatterContours( whiteMatterContours, whiteMatterContours->GetRequestedRegion() ); // Monitor the convergence typedef Function::WindowConvergenceMonitoringFunction ConvergenceMonitoringType; ConvergenceMonitoringType::Pointer convergenceMonitoring = ConvergenceMonitoringType::New(); convergenceMonitoring->SetWindowSize( this->m_ConvergenceWindowSize ); // Instantiate the progress reporter IterationReporter reporter( this, 0, 1 ); bool isConverged = false; this->m_CurrentConvergenceMeasurement = NumericTraits::max(); this->m_ElapsedIterations = 0; while( this->m_ElapsedIterations++ < this->m_MaximumNumberOfIterations && isConverged == false ) { this->MakeThicknessImage( hitImage, totalImage, segmentationImage, thicknessImage ); RealType priorEnergy = 0; unsigned long priorEnergyCount = 0; RealType currentEnergy = 0.0; RealType numberOfGrayMatterVoxels = 0.0; forwardIncrementalField->FillBuffer( zeroVector ); inverseField->FillBuffer( zeroVector ); inverseIncrementalField->FillBuffer( zeroVector ); hitImage->FillBuffer( 0.0 ); totalImage->FillBuffer( 0.0 ); ImageRegionIterator ItVelocityField( velocityField, velocityField->GetRequestedRegion() ); unsigned int integrationPoint = 0; while( integrationPoint++ < this->m_NumberOfIntegrationPoints ) { typedef ComposeDisplacementFieldsImageFilter ComposerType; typename ComposerType::Pointer composer = ComposerType::New(); composer->SetDisplacementField( inverseIncrementalField ); composer->SetWarpingField( inverseField ); inverseField = composer->GetOutput(); inverseField->Update(); inverseField->DisconnectPipeline(); RealImagePointer warpedWhiteMatterProbabilityImage = this->WarpImage( whiteMatterProbabilityImage, inverseField ); RealImagePointer warpedWhiteMatterContours = this->WarpImage( whiteMatterContours, inverseField ); RealImagePointer warpedThicknessImage = this->WarpImage( thicknessImage, inverseField ); typedef GradientRecursiveGaussianImageFilter GradientImageFilterType; typename GradientImageFilterType::Pointer gradientFilter = GradientImageFilterType::New(); gradientFilter->SetInput( warpedWhiteMatterProbabilityImage ); gradientFilter->SetSigma( this->m_SmoothingVariance ); gradientFilter->Update(); DisplacementFieldPointer gradientImage = gradientFilter->GetOutput(); // Instantiate the iterators all in one place ImageRegionIterator ItGradientImage( gradientImage, gradientImage->GetRequestedRegion() ); ImageRegionIterator ItWarpedWhiteMatterProbabilityMap( warpedWhiteMatterProbabilityImage, warpedWhiteMatterProbabilityImage->GetRequestedRegion() ); ImageRegionIterator ItWarpedWhiteMatterContours( warpedWhiteMatterContours, warpedWhiteMatterContours->GetRequestedRegion() ); ImageRegionIterator ItInverseField( inverseField, inverseField->GetRequestedRegion() ); ImageRegionIterator ItIntegratedField( integratedField, integratedField->GetRequestedRegion() ); // Generate speed image speedImage->FillBuffer( 0.0 ); ItGradientImage.GoToBegin(); ItGrayMatterProbabilityMap.GoToBegin(); ItSegmentationImage.GoToBegin(); ItSpeedImage.GoToBegin(); ItWarpedWhiteMatterProbabilityMap.GoToBegin(); const typename InputImageType::PixelType grayMatterPixel = static_cast( this->m_GrayMatterLabel ), whiteMatterPixel = static_cast( this->m_WhiteMatterLabel ); while( !ItSegmentationImage.IsAtEnd() ) { if( ItSegmentationImage.Get() == grayMatterPixel ) { RealType norm = ( ItGradientImage.Get() ).GetNorm(); if( norm > 1e-3 && !vnl_math_isnan( norm ) && !vnl_math_isinf( norm ) ) { ItGradientImage.Set( ItGradientImage.Get() / norm ); } else { ItGradientImage.Set( zeroVector ); } RealType delta = ( ItWarpedWhiteMatterProbabilityMap.Get() - ItGrayMatterProbabilityMap.Get() ); currentEnergy += vnl_math_abs( delta ); numberOfGrayMatterVoxels++; RealType speedValue = -1.0 * delta * ItGrayMatterProbabilityMap.Get() * this->m_CurrentGradientStep; if( vnl_math_isnan( speedValue ) || vnl_math_isinf( speedValue ) ) { speedValue = 0.0; } ItSpeedImage.Set( speedValue ); } else { ItSpeedImage.Set( 0.0 ); } ++ItGradientImage; ++ItGrayMatterProbabilityMap; ++ItSegmentationImage; ++ItSpeedImage; ++ItWarpedWhiteMatterProbabilityMap; } // Calculate objective function value ItForwardIncrementalField.GoToBegin(); ItGradientImage.GoToBegin(); ItSegmentationImage.GoToBegin(); ItSpeedImage.GoToBegin(); ItWhiteMatterContours.GoToBegin(); while( !ItSegmentationImage.IsAtEnd() ) { typename InputImageType::IndexType index = ItSegmentationImage.GetIndex(); typename InputImageType::PixelType segmentationValue = ItSegmentationImage.Get(); ItForwardIncrementalField.Set( ItForwardIncrementalField.Get() + ItGradientImage.Get() * ItSpeedImage.Get() ); if( segmentationValue == grayMatterPixel || segmentationValue == whiteMatterPixel ) { if( integrationPoint == 1 ) { typename InputImageType::PixelType whiteMatterContoursValue = static_cast( ItWhiteMatterContours.Get() ); hitImage->SetPixel( index, whiteMatterContoursValue ); VectorType vector = integratedField->GetPixel( index ); RealType weightedNorm = vector.GetNorm() * whiteMatterContoursValue; thicknessImage->SetPixel( index, weightedNorm ); totalImage->SetPixel( index, weightedNorm ); } else if( segmentationValue == grayMatterPixel ) { hitImage->SetPixel( index, hitImage->GetPixel( index ) + warpedWhiteMatterContours->GetPixel( index ) ); totalImage->SetPixel( index, totalImage->GetPixel( index ) + warpedThicknessImage->GetPixel( index ) ); } } ++ItForwardIncrementalField; ++ItGradientImage; ++ItSegmentationImage; ++ItSpeedImage; ++ItWhiteMatterContours; } ItSegmentationImage.GoToBegin(); ItMatterContours.GoToBegin(); ItWhiteMatterContours.GoToBegin(); ItVelocityField.GoToBegin(); ItInverseIncrementalField.GoToBegin(); ItIntegratedField.GoToBegin(); ItInverseField.GoToBegin(); while( !ItSegmentationImage.IsAtEnd() ) { typename InputImageType::PixelType segmentationValue = ItSegmentationImage.Get(); typename InputImageType::PixelType whiteMatterContoursValue = static_cast( ItWhiteMatterContours.Get() ); typename InputImageType::PixelType matterContoursValue = ItMatterContours.Get(); if( segmentationValue == 0 || ( whiteMatterContoursValue == 0 && matterContoursValue == 0 && segmentationValue != this->m_GrayMatterLabel ) ) { ItInverseField.Set( zeroVector ); ItVelocityField.Set( zeroVector ); ItIntegratedField.Set( zeroVector ); } ItInverseIncrementalField.Set( ItVelocityField.Get() ); ++ItSegmentationImage; ++ItMatterContours; ++ItWhiteMatterContours; ++ItVelocityField; ++ItInverseIncrementalField; ++ItInverseField; ++ItIntegratedField; } if( integrationPoint == 1 ) { integratedField->FillBuffer( zeroVector ); } typedef InvertDisplacementFieldImageFilter InverterType; typename InverterType::Pointer inverter1 = InverterType::New(); inverter1->SetInput( inverseField ); inverter1->SetInverseFieldInitialEstimate( integratedField ); inverter1->SetMaximumNumberOfIterations( this->m_MaximumNumberOfInvertDisplacementFieldIterations ); inverter1->SetMeanErrorToleranceThreshold( 0.001 ); inverter1->SetMaxErrorToleranceThreshold( 0.1 ); inverter1->Update(); integratedField = inverter1->GetOutput(); integratedField->DisconnectPipeline(); typename InverterType::Pointer inverter2 = InverterType::New(); inverter2->SetInput( integratedField ); inverter2->SetInverseFieldInitialEstimate( inverseField ); inverter2->SetMaximumNumberOfIterations( this->m_MaximumNumberOfInvertDisplacementFieldIterations ); inverter2->SetMeanErrorToleranceThreshold( 0.001 ); inverter2->SetMaxErrorToleranceThreshold( 0.1 ); inverter2->Update(); inverseField = inverter2->GetOutput(); inverseField->DisconnectPipeline(); } // calculate the size of the solution to allow us to adjust the // gradient step length. // RealType maxNorm = 0.0; // // typename InputImageType::SpacingType spacing = grayMatter->GetSpacing(); // // ImageRegionIterator ItIntegratedField2( // integratedField, // integratedField->GetRequestedRegion() ); // // ItIntegratedField2.GoToBegin(); // for( ItIntegratedField2.GoToBegin(); !ItIntegratedField2.IsAtEnd(); // ++ItIntegratedField2 ) // { // VectorType vector = ItIntegratedField2.Get(); // for( unsigned int d = 0; d < ImageDimension; d++ ) // { // vector[d] = vector[d] / spacing[d]; // } // RealType norm = vector.GetNorm(); // if( norm > maxNorm ) // { // maxNorm = norm; // } // } // itkDebugMacro( " MaxNorm = " << maxNorm ); // // if( this->m_ElapsedIterations == 2 ) // { // this->m_CurrentGradientStep = this->m_CurrentGradientStep * 1.0 / maxNorm; // velocityField->FillBuffer( zeroVector ); // } RealImagePointer smoothHitImage; RealImagePointer smoothTotalImage; if( this->m_SmoothingVariance > 0.0 ) { smoothHitImage = this->SmoothImage( hitImage, this->m_SmoothingVariance ); smoothTotalImage = this->SmoothImage( totalImage, this->m_SmoothingVariance ); } else { smoothHitImage = hitImage; smoothTotalImage = totalImage; } ImageRegionConstIterator ItSmoothHitImage( smoothHitImage, smoothHitImage->GetRequestedRegion() ); ImageRegionConstIterator ItSmoothTotalImage( smoothTotalImage, smoothTotalImage->GetRequestedRegion() ); ItCorticalThicknessImage.GoToBegin(); ItForwardIncrementalField.GoToBegin(); ItSegmentationImage.GoToBegin(); ItSmoothHitImage.GoToBegin(); ItSmoothTotalImage.GoToBegin(); ItVelocityField.GoToBegin(); while( !ItSegmentationImage.IsAtEnd() ) { ItVelocityField.Set( ItVelocityField.Get() + ItForwardIncrementalField.Get() ); const typename InputImageType::PixelType grayMatterPixel = static_cast( this->m_GrayMatterLabel ); if( ItSegmentationImage.Get() == grayMatterPixel ) { RealType thicknessValue = 0.0; if( ItSmoothHitImage.Get() > 0.001 ) { thicknessValue = ItSmoothTotalImage.Get() / ItSmoothHitImage.Get(); if( thicknessValue < 0.0 ) { thicknessValue = 0.0; } if( ! this->m_ThicknessPriorImage && ( thicknessValue > this->m_ThicknessPriorEstimate ) ) { RealType fraction = this->m_ThicknessPriorEstimate / thicknessValue; ItVelocityField.Set( ItVelocityField.Get() * vnl_math_sqr( fraction ) ); } else if( this->m_ThicknessPriorImage ) { typename RealImageType::IndexType index = ItSegmentationImage.GetIndex(); RealType thicknessPrior = this->m_ThicknessPriorImage->GetPixel( index ); if( ( thicknessPrior > NumericTraits::ZeroValue() ) && ( thicknessValue > thicknessPrior ) ) { priorEnergy += vnl_math_abs( thicknessPrior - thicknessValue ); priorEnergyCount++; RealType fraction = thicknessPrior / thicknessValue; ItVelocityField.Set( ItVelocityField.Get() * vnl_math_sqr( fraction ) ); } } } ItCorticalThicknessImage.Set( thicknessValue ); } ++ItCorticalThicknessImage; ++ItForwardIncrementalField; ++ItSmoothHitImage; ++ItSegmentationImage; ++ItSmoothTotalImage; ++ItVelocityField; } if( this->m_UseBSplineSmoothing ) { velocityField = this->BSplineSmoothDisplacementField( velocityField, this->m_BSplineSmoothingIsotropicMeshSpacing ); } else { velocityField = this->GaussianSmoothDisplacementField( velocityField, this->m_SmoothingVelocityFieldVariance ); } // Calculate current energy and current convergence measurement currentEnergy /= numberOfGrayMatterVoxels; priorEnergy /= priorEnergyCount; if( this->m_ThicknessPriorImage ) { itkDebugMacro( " PriorEnergy = " << priorEnergy ); } this->m_CurrentEnergy = currentEnergy; convergenceMonitoring->AddEnergyValue( this->m_CurrentEnergy ); this->m_CurrentConvergenceMeasurement = convergenceMonitoring->GetConvergenceValue(); if( this->m_CurrentConvergenceMeasurement < this->m_ConvergenceThreshold ) { isConverged = true; } reporter.CompletedStep(); } // Replace the identity direction with the original direction in the outputs RealImagePointer warpedWhiteMatterProbabilityImage = this->WarpImage( whiteMatterProbabilityImage, inverseField ); warpedWhiteMatterProbabilityImage->SetDirection( this->GetSegmentationImage()->GetDirection() ); corticalThicknessImage->SetDirection( this->GetSegmentationImage()->GetDirection() ); this->SetNthOutput( 0, corticalThicknessImage ); this->SetNthOutput( 1, warpedWhiteMatterProbabilityImage ); } template typename DiReCTImageFilter::InputImagePointer DiReCTImageFilter ::ExtractRegion( const InputImageType *segmentationImage, typename DiReCTImageFilter::LabelType whichRegion ) { typedef BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( segmentationImage ); thresholder->SetLowerThreshold( whichRegion ); thresholder->SetUpperThreshold( whichRegion ); thresholder->SetInsideValue( 1 ); thresholder->SetOutsideValue( 0 ); thresholder->Update(); InputImagePointer thresholdRegion = thresholder->GetOutput(); thresholdRegion->Update(); thresholdRegion->DisconnectPipeline(); return thresholdRegion; } template typename DiReCTImageFilter::InputImagePointer DiReCTImageFilter ::ExtractRegionalContours( const InputImageType *segmentationImage, typename DiReCTImageFilter::LabelType whichRegion ) { InputImagePointer thresholdedRegion = this->ExtractRegion( segmentationImage, whichRegion ); typedef BinaryContourImageFilter ContourFilterType; typename ContourFilterType::Pointer contourFilter = ContourFilterType::New(); contourFilter->SetInput( thresholdedRegion ); contourFilter->SetFullyConnected( true ); contourFilter->SetBackgroundValue( 0 ); contourFilter->SetForegroundValue( 1 ); InputImagePointer contours = contourFilter->GetOutput(); contours->Update(); contours->DisconnectPipeline(); contours->SetRegions( segmentationImage->GetRequestedRegion() ); return contours; } template void DiReCTImageFilter ::MakeThicknessImage( RealImagePointer hitImage, RealImagePointer totalImage, InputImagePointer segmentationImage, RealImagePointer corticalThicknessImage ) { RealImagePointer smoothHitImage; RealImagePointer smoothTotalImage; if( this->m_SmoothingVariance > 0.0 ) { smoothHitImage = this->SmoothImage( hitImage, this->m_SmoothingVariance ); smoothTotalImage = this->SmoothImage( totalImage, this->m_SmoothingVariance ); } else { smoothHitImage = hitImage; smoothTotalImage = totalImage; } ImageRegionIterator ItCorticalThicknessImage( corticalThicknessImage, corticalThicknessImage->GetRequestedRegion() ); ImageRegionConstIteratorWithIndex ItSegmentationImage( segmentationImage, segmentationImage->GetRequestedRegion() ); ImageRegionConstIterator ItSmoothHitImage( smoothHitImage, smoothHitImage->GetRequestedRegion() ); ImageRegionConstIterator ItSmoothTotalImage( smoothTotalImage, smoothTotalImage->GetRequestedRegion() ); ItCorticalThicknessImage.GoToBegin(); ItSegmentationImage.GoToBegin(); ItSmoothHitImage.GoToBegin(); ItSmoothTotalImage.GoToBegin(); RealType meanThickness = 0; unsigned long count = 0; while( !ItSegmentationImage.IsAtEnd() ) { const typename InputImageType::PixelType grayMatterPixel = static_cast( this->m_GrayMatterLabel ); if( ItSegmentationImage.Get() == grayMatterPixel ) { RealType thicknessValue = 0.0; if( ItSmoothHitImage.Get() > 0.001 ) { thicknessValue = ItSmoothTotalImage.Get() / ItSmoothHitImage.Get(); meanThickness += thicknessValue; count++; if( thicknessValue < 0.0 ) { thicknessValue = 0.0; } } ItCorticalThicknessImage.Set( thicknessValue ); } ++ItCorticalThicknessImage; ++ItSmoothHitImage; ++ItSegmentationImage; ++ItSmoothTotalImage; } } template typename DiReCTImageFilter::RealImagePointer DiReCTImageFilter ::WarpImage( const RealImageType *inputImage, const DisplacementFieldType *displacementField ) { typedef WarpImageFilter WarperType; typename WarperType::Pointer warper = WarperType::New(); warper->SetInput( inputImage ); warper->SetDisplacementField( displacementField ); warper->SetEdgePaddingValue( 0 ); warper->SetOutputSpacing( inputImage->GetSpacing() ); warper->SetOutputOrigin( inputImage->GetOrigin() ); warper->SetOutputDirection( inputImage->GetDirection() ); RealImagePointer warpedImage = warper->GetOutput(); warpedImage->Update(); warpedImage->DisconnectPipeline(); return warpedImage; } template typename DiReCTImageFilter::DisplacementFieldPointer DiReCTImageFilter ::GaussianSmoothDisplacementField( const DisplacementFieldType *inputField, const RealType variance ) { typedef ImageDuplicator DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( inputField ); duplicator->Update(); DisplacementFieldPointer outputField = duplicator->GetModifiableOutput(); typedef VectorNeighborhoodOperatorImageFilter SmootherType; typename SmootherType::Pointer smoother = SmootherType::New(); typedef GaussianOperator GaussianType; GaussianType gaussian; gaussian.SetVariance( variance ); gaussian.SetMaximumError( 0.001 ); for( unsigned int d = 0; d < ImageDimension; d++ ) { gaussian.SetDirection( d ); gaussian.SetMaximumKernelWidth( outputField->GetRequestedRegion().GetSize()[d] ); gaussian.CreateDirectional(); smoother->SetOperator( gaussian ); smoother->SetInput( outputField ); outputField = smoother->GetOutput(); outputField->Update(); outputField->DisconnectPipeline(); } // Ensure zero motion on the boundary RealType weight1 = 1.0; if( variance < 0.5 ) { weight1 = 1.0 - 1.0 * ( variance / 0.5 ); } RealType weight2 = 1.0 - weight1; typedef MultiplyByConstantImageFilter MultiplierType; typename MultiplierType::Pointer multiplier1 = MultiplierType::New(); multiplier1->SetConstant2( weight1 ); multiplier1->SetInput1( outputField ); typename MultiplierType::Pointer multiplier2 = MultiplierType::New(); multiplier2->SetConstant2( weight2 ); multiplier2->SetInput1( inputField ); typedef AddImageFilter AdderType; typename AdderType::Pointer adder = AdderType::New(); adder->SetInput1( multiplier1->GetOutput() ); adder->SetInput2( multiplier2->GetOutput() ); outputField = adder->GetOutput(); outputField->Update(); outputField->DisconnectPipeline(); VectorType zeroVector( 0.0 ); ImageLinearIteratorWithIndex It( outputField, outputField->GetRequestedRegion() ); for( unsigned int d = 0; d < ImageDimension; d++ ) { It.SetDirection( d ); It.GoToBegin(); while( !It.IsAtEnd() ) { It.GoToBeginOfLine(); It.Set( zeroVector ); It.GoToEndOfLine(); --It; It.Set( zeroVector ); It.NextLine(); } } return outputField; } template typename DiReCTImageFilter::DisplacementFieldPointer DiReCTImageFilter ::BSplineSmoothDisplacementField( const DisplacementFieldType *inputField, const RealType isotropicMeshSpacing ) { typedef itk::DisplacementFieldToBSplineImageFilter BSplineFilterType; // calculate the number of control points based on the isotropic mesh spacing typename BSplineFilterType::ArrayType ncps; for( unsigned int d = 0; d < ImageDimension; d++ ) { RealType domain = static_cast( inputField->GetLargestPossibleRegion().GetSize()[d] - 1 ) * inputField->GetSpacing()[d]; ncps[d] = static_cast( std::ceil( domain / isotropicMeshSpacing ) ); } typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); bspliner->SetDisplacementField( inputField ); bspliner->SetNumberOfControlPoints( ncps ); bspliner->SetSplineOrder( 3 ); bspliner->SetNumberOfFittingLevels( 1 ); bspliner->SetEnforceStationaryBoundary( true ); bspliner->SetEstimateInverse( false ); bspliner->Update(); DisplacementFieldPointer outputField = bspliner->GetOutput(); outputField->DisconnectPipeline(); return outputField; } template typename DiReCTImageFilter::RealImagePointer DiReCTImageFilter ::SmoothImage( const RealImageType *inputImage, const RealType variance ) { typedef DiscreteGaussianImageFilter SmootherType; typename SmootherType::Pointer smoother = SmootherType::New(); smoother->SetVariance( variance ); smoother->SetUseImageSpacingOff(); smoother->SetMaximumError( 0.01 ); smoother->SetInput( inputImage ); typename RealImageType::Pointer smoothImage = smoother->GetOutput(); smoothImage->Update(); smoothImage->DisconnectPipeline(); return smoothImage; } /** * Standard "PrintSelf" method */ template void DiReCTImageFilter ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << "Gray matter label = " << this->m_GrayMatterLabel << std::endl; os << indent << "White matter label = " << this->m_WhiteMatterLabel << std::endl; os << indent << "Maximum number of iterations = " << this->m_MaximumNumberOfIterations << std::endl; os << indent << "Thickness prior estimate = " << this->m_ThicknessPriorEstimate << std::endl; os << indent << "Smoothing sigma = " << this->m_SmoothingVariance << std::endl; if( this->m_UseBSplineSmoothing ) { os << indent << "B-spline smoothing isotropic mesh spacing = " << this->m_BSplineSmoothingIsotropicMeshSpacing << std::endl; } else { os << indent << "Smoothing velocity field sigma = " << this->m_SmoothingVelocityFieldVariance << std::endl; } os << indent << "Number of integration points = " << this->m_NumberOfIntegrationPoints << std::endl; os << indent << "Maximum number of invert displacement field iterations = " << this->m_MaximumNumberOfInvertDisplacementFieldIterations << std::endl; os << indent << "Initial gradient step = " << this->m_InitialGradientStep << std::endl; os << indent << "Current gradient step = " << this->m_CurrentGradientStep << std::endl; os << indent << "Convergence threshold = " << this->m_ConvergenceThreshold << std::endl; os << indent << "Convergence window size = " << this->m_ConvergenceWindowSize << std::endl; } } // end namespace itk #endif ants-2.2.0/Utilities/itkDisplacementFieldFromMultiTransformFilter.h000066400000000000000000000124661311104306400255730ustar00rootroot00000000000000#ifndef ITKDEFORMATIONFIELDFROMMULTITRANSFORMFILTER_H_ #define ITKDEFORMATIONFIELDFROMMULTITRANSFORMFILTER_H_ #include "itkWarpImageMultiTransformFilter.h" namespace itk { template < class TOutputImage, class TDisplacementField, class TTransform > class DisplacementFieldFromMultiTransformFilter : public WarpImageMultiTransformFilter { public: /** Standard class typedefs. */ typedef TOutputImage TInputImage; typedef DisplacementFieldFromMultiTransformFilter Self; typedef WarpImageMultiTransformFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods) */ itkTypeMacro( DisplacementFieldFromMultiTransformFilter, WarpImageMultiTransformFilter ); /** Typedef to describe the output image region type. */ typedef typename TOutputImage::RegionType OutputImageRegionType; /** Inherit some types from the superclass. */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::InputImagePointer InputImagePointer; typedef typename Superclass::OutputImageType OutputImageType; typedef typename Superclass::OutputImagePointer OutputImagePointer; typedef typename Superclass::InputImageConstPointer InputImageConstPointer; typedef typename OutputImageType::IndexType IndexType; typedef typename OutputImageType::SizeType SizeType; typedef typename OutputImageType::PixelType PixelType; typedef typename OutputImageType::SpacingType SpacingType; /** Determine the image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension ); itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension ); itkStaticConstMacro(DisplacementFieldDimension, unsigned int, TDisplacementField::ImageDimension ); /** Displacement field typedef support. */ typedef TDisplacementField DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldPointer; typedef typename DisplacementFieldType::PixelType DisplacementType; typedef typename DisplacementType::ValueType DisplacementScalarValueType; typedef typename Superclass::PointType PointType; virtual void GenerateInputRequestedRegion() ITK_OVERRIDE { Superclass::GenerateInputRequestedRegion(); } virtual void BeforeThreadedGenerateData() ITK_OVERRIDE { }; virtual void AfterThreadedGenerateData() ITK_OVERRIDE { }; virtual void GenerateOutputInformation() ITK_OVERRIDE { // call the superclass's implementation Superclass::GenerateOutputInformation(); }; protected: DisplacementFieldFromMultiTransformFilter() : Superclass() { this->SetNumberOfRequiredInputs( 0 ); const DisplacementScalarValueType kMaxDisp = itk::NumericTraits::max(); Superclass::m_EdgePaddingValue.Fill(kMaxDisp); } ~DisplacementFieldFromMultiTransformFilter() { }; void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE { Superclass::PrintSelf(os, indent); }; /** WarpImageMultiTransformFilter is implemented as a multi-threaded filter. * As such, it needs to provide and implementation for * ThreadedGenerateData(). */ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) ITK_OVERRIDE { OutputImagePointer outputPtr = this->GetOutput(); // support progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels() ); // iterator for the output image ImageRegionIteratorWithIndex outputIt(outputPtr, outputRegionForThread); // int cnt = 0; while( !outputIt.IsAtEnd() ) { PointType point1, point2; // get the output image index IndexType index = outputIt.GetIndex(); outputPtr->TransformIndexToPhysicalPoint( index, point1 ); const bool isinside = this->MultiTransformPoint(point1, point2, Superclass::m_bFirstDeformNoInterp, index); if( isinside ) { PixelType value; for( unsigned int ii = 0; ii < OutputImageType::ImageDimension; ii++ ) { value[ii] = point2[ii] - point1[ii]; } outputIt.Set( value ); } else { PixelType value; const DisplacementScalarValueType kMaxDisp = itk::NumericTraits::max(); for( unsigned int ii = 0; ii < OutputImageType::ImageDimension; ii++ ) { value[ii] = kMaxDisp; } outputIt.Set( value ); } ++outputIt; } progress.CompletedPixel(); }; }; } // end namespace itk #endif /*ITKDEFORMATIONFIELDFROMMULTITRANSFORMFILTER_H_*/ ants-2.2.0/Utilities/itkGeneralToBSplineDisplacementFieldFilter.h000066400000000000000000000065151311104306400251140ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkGeneralToBSplineDisplacementFieldFilter_h #define __itkGeneralToBSplineDisplacementFieldFilter_h #include "itkImageToImageFilter.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkPointSet.h" namespace itk { template class GeneralToBSplineDisplacementFieldFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef GeneralToBSplineDisplacementFieldFilter Self; typedef ImageToImageFilter< TInputImage, TOutputImage> Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Extract dimension from input and output image. */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); /** Convenient typedefs for simplifying declarations. */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef typename InputImageType::PixelType InputPixelType; typedef typename OutputImageType::PixelType OutputPixelType; typedef typename InputPixelType::ValueType InputPixelComponentType; typedef InputPixelType VectorType; typedef InputPixelComponentType RealType; typedef Image RealImageType; typedef PointSet PointSetType; typedef BSplineScatteredDataPointSetToImageFilter BSplineFilterType; typedef typename BSplineFilterType::ArrayType ArrayType; // itkSetMacro( ConfidenceImage, RealImageType ); // itkGetConstMacro( ConfidenceImage, RealImageType ); itkSetMacro( NumberOfControlPoints, ArrayType ); itkGetConstMacro( NumberOfControlPoints, ArrayType ); itkSetMacro( NumberOfLevels, unsigned int ); itkGetConstMacro( NumberOfLevels, unsigned int ); itkSetMacro( SplineOrder, unsigned int ); itkGetConstMacro( SplineOrder, unsigned int ); itkSetMacro( IgnorePixelValue, InputPixelType ); itkGetConstMacro( IgnorePixelValue, InputPixelType ); protected: GeneralToBSplineDisplacementFieldFilter(); virtual ~GeneralToBSplineDisplacementFieldFilter(); void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void GenerateData() ITK_OVERRIDE; private: // typename RealImageType::Pointer m_ConfidenceImage; InputPixelType m_IgnorePixelValue; unsigned int m_NumberOfLevels; unsigned int m_SplineOrder; ArrayType m_NumberOfControlPoints; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkGeneralToBSplineDisplacementFieldFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkGeneralToBSplineDisplacementFieldFilter.hxx000066400000000000000000000071011311104306400254640ustar00rootroot00000000000000#ifndef __itkGeneralToBSplineDisplacementFieldFilter_hxx #define __itkGeneralToBSplineDisplacementFieldFilter_hxx #include "itkGeneralToBSplineDisplacementFieldFilter.h" #include "itkBSplineControlPointImageFilter.h" #include "itkImageRegionConstIteratorWithIndex.h" namespace itk { template GeneralToBSplineDisplacementFieldFilter ::GeneralToBSplineDisplacementFieldFilter() { this->m_IgnorePixelValue.Fill( NumericTraits::max() ); this->m_SplineOrder = 3; this->m_NumberOfControlPoints.Fill( this->m_SplineOrder + 1 ); // this->m_ConfidenceImage = NULL; } template GeneralToBSplineDisplacementFieldFilter ::~GeneralToBSplineDisplacementFieldFilter() { } template void GeneralToBSplineDisplacementFieldFilter ::GenerateData() { typename PointSetType::Pointer fieldPoints = PointSetType::New(); fieldPoints->Initialize(); // typename BSplineFilterType::WeightsContainerType confidenceValues; // confidenceValues->Initialize(); ImageRegionConstIteratorWithIndex It( this->GetInput(), this->GetInput()->GetRequestedRegion() ); itkDebugMacro( << "Extracting points from input deformation field. " ) unsigned int N = 0; for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { InputPixelType data = It.Get(); if( data != this->m_IgnorePixelValue ) { typename PointSetType::PointType point; this->GetInput()->TransformIndexToPhysicalPoint( It.GetIndex(), point ); fieldPoints->SetPointData( N, data ); fieldPoints->SetPoint( N, point ); // if ( this->m_ConfidenceImage ) // { // confidenceValues->InsertElement // ( N, this->m_ConfidenceImage->GetPixel( It.GetIndex() ) ); // } N++; } } itkDebugMacro( "Calculating the B-spline deformation field. " ); typename OutputImageType::PointType origin; typename OutputImageType::SpacingType spacing; typename OutputImageType::SizeType size; for( unsigned int i = 0; i < ImageDimension; i++ ) { origin[i] = this->GetInput( 0 )->GetOrigin()[i]; spacing[i] = this->GetInput( 0 )->GetSpacing()[i]; size[i] = this->GetInput( 0 )->GetRequestedRegion().GetSize()[i]; } typename BSplineFilterType::ArrayType close; close.Fill( false ); typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); bspliner->SetOrigin( origin ); bspliner->SetSpacing( spacing ); bspliner->SetSize( size ); bspliner->SetNumberOfLevels( this->m_NumberOfLevels ); bspliner->SetSplineOrder( this->m_SplineOrder ); bspliner->SetNumberOfControlPoints( this->m_NumberOfControlPoints ); bspliner->SetCloseDimension( close ); bspliner->SetInput( fieldPoints ); bspliner->SetGenerateOutputImage( true ); bspliner->Update(); this->SetNthOutput( 0, bspliner->GetOutput() ); } /** * Standard "PrintSelf" method */ template void GeneralToBSplineDisplacementFieldFilter ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << "Ignore pixel value: " << this->m_IgnorePixelValue << std::endl; os << indent << "Number of control points: " << this->m_NumberOfControlPoints << std::endl; os << indent << "Number of levels: " << this->m_NumberOfLevels << std::endl; os << indent << "Spline order: " << this->m_SplineOrder << std::endl; } } // end namespace itk #endif ants-2.2.0/Utilities/itkGeometricJacobianDeterminantImageFilter.h000077500000000000000000000146141311104306400251670ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 __itkGeometricJacobianDeterminantImageFilter_h #define __itkGeometricJacobianDeterminantImageFilter_h #include "itkConstNeighborhoodIterator.h" #include "itkImageToImageFilter.h" #include "itkVector.h" #include "itkVectorLinearInterpolateImageFunction.h" namespace itk { /** \class DeformationFieldGradientTensorImageFilter * */ template > class ITK_EXPORT GeometricJacobianDeterminantImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef GeometricJacobianDeterminantImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods) */ itkTypeMacro( GeometricJacobianDeterminantImageFilter, ImageToImageFilter ); /** Extract some information from the image types. Dimensionality * of the two images is assumed to be the same. */ typedef typename TOutputImage::PixelType OutputPixelType; typedef typename TInputImage::PixelType InputPixelType; /** Image typedef support */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; /** The dimensionality of the input and output images. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension); /** Define the data type and the vector of data type used in calculations. */ typedef TRealType RealType; typedef Image RealImageType; typedef Vector RealVectorType; typedef Image RealVectorImageType; typedef typename RealImageType::PointType PointType; typedef VectorLinearInterpolateImageFunction InterpolatorType; /** Type of the iterator that will be used to move through the image. Also the type which will be passed to the evaluate function */ typedef ConstNeighborhoodIterator ConstNeighborhoodIteratorType; typedef typename ConstNeighborhoodIteratorType::RadiusType RadiusType; /** Superclass typedefs. */ typedef typename Superclass::OutputImageRegionType OutputImageRegionType; /** DeformationFieldGradientTensorImageFilter needs a larger input requested * region than the output requested region (larger by the kernel * size to calculate derivatives). As such, * DeformationFieldGradientTensorImageFilter needs to provide an * implementation for GenerateInputRequestedRegion() in order to inform the * pipeline execution model. * * \sa ImageToImageFilter::GenerateInputRequestedRegion() */ virtual void GenerateInputRequestedRegion() throw( InvalidRequestedRegionError ) ITK_OVERRIDE; /** Get access to the input image casted as real pixel values */ itkGetConstObjectMacro( RealValuedInputImage, RealVectorImageType ); protected: GeometricJacobianDeterminantImageFilter(); virtual ~GeometricJacobianDeterminantImageFilter() {} /** Do any necessary casting/copying of the input data. Input pixel types whose value types are not real number types must be cast to real number types.*/ void BeforeThreadedGenerateData() ITK_OVERRIDE; /** DeformationFieldGradientTensorImageFilter can be implemented as a * multithreaded filter (we're only using vnl_det(), which is trivially * thread safe). Therefore, this implementation provides a * ThreadedGenerateData() routine which is called for each * processing thread. The output image data is allocated * automatically by the superclass prior to calling * ThreadedGenerateData(). ThreadedGenerateData can only write to * the portion of the output image specified by the parameter * "outputRegionForThread" * * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ void ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) ITK_OVERRIDE; void PrintSelf ( std::ostream& os, Indent indent ) const ITK_OVERRIDE; private: RadiusType m_NeighborhoodRadius; typename RealVectorImageType::ConstPointer m_RealValuedInputImage; typename InterpolatorType::Pointer m_Interpolator; RealType m_UndisplacedVolume; RealVectorType m_DeltaTriangularPointA; RealVectorType m_DeltaTriangularPointB; RealVectorType m_DeltaTriangularPointC; RealVectorType m_DeltaTetrahedralPointA; RealVectorType m_DeltaTetrahedralPointB; RealVectorType m_DeltaTetrahedralPointC; RealVectorType m_DeltaTetrahedralPointD; void InitializeTetrahedralDeltaPoints(); void InitializeTriangularDeltaPoints(); RealType CalculateTetrahedralVolume( PointType, PointType, PointType, PointType ); RealType CalculateTriangularArea( PointType, PointType, PointType ); GeometricJacobianDeterminantImageFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkGeometricJacobianDeterminantImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkGeometricJacobianDeterminantImageFilter.hxx000077500000000000000000000315611311104306400255470ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 _itkGeometricJacobianDeterminantImageFilter_hxx #define _itkGeometricJacobianDeterminantImageFilter_hxx #include "itkGeometricJacobianDeterminantImageFilter.h" #include "itkContinuousIndex.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkNeighborhoodAlgorithm.h" #include "itkProgressReporter.h" #include "itkVectorCastImageFilter.h" #include "itkZeroFluxNeumannBoundaryCondition.h" #include "vnl/vnl_cross.h" namespace itk { template GeometricJacobianDeterminantImageFilter ::GeometricJacobianDeterminantImageFilter() { this->m_Interpolator = ITK_NULLPTR; this->m_UndisplacedVolume = 0.0; } template void GeometricJacobianDeterminantImageFilter ::GenerateInputRequestedRegion() throw( InvalidRequestedRegionError ) { // call the superclass' implementation of this method Superclass::GenerateInputRequestedRegion(); // get pointers to the input and output InputImagePointer inputPtr = const_cast< InputImageType * >( this->GetInput() ); OutputImagePointer outputPtr = this->GetOutput(); if ( !inputPtr || !outputPtr ) { return; } // get a copy of the input requested region (should equal the output // requested region) typename TInputImage::RegionType inputRequestedRegion; inputRequestedRegion = inputPtr->GetRequestedRegion(); this->m_NeighborhoodRadius.Fill( 1 ); // pad the input requested region by the operator radius inputRequestedRegion.PadByRadius( this->m_NeighborhoodRadius ); // crop the input requested region at the input's largest possible region if ( inputRequestedRegion.Crop( inputPtr->GetLargestPossibleRegion() ) ) { inputPtr->SetRequestedRegion( inputRequestedRegion ); return; } else { // Couldn't crop the region (requested region is outside the largest // possible region). Throw an exception. // store what we tried to request (prior to trying to crop) inputPtr->SetRequestedRegion( inputRequestedRegion ); // build an exception InvalidRequestedRegionError e( __FILE__, __LINE__ ); e.SetLocation( ITK_LOCATION ); e.SetDescription( "Requested region is outside the largest possible region." ); e.SetDataObject( inputPtr ); throw e; } } template< typename TInputImage, typename TRealType, typename TOutputImage > void GeometricJacobianDeterminantImageFilter ::BeforeThreadedGenerateData() { /** If the input needs casting to a real-valued vector type, create the appropriate image and set the m_RealValuedInputImage pointer to this image. Otherwise just point to the input image. */ if ( typeid( typename InputImageType::PixelType ) != typeid( RealVectorType ) ) { typename VectorCastImageFilter::Pointer caster = VectorCastImageFilter::New(); caster->SetInput( this->GetInput() ); caster->Update(); this->m_RealValuedInputImage = caster->GetOutput(); } else { this->m_RealValuedInputImage = dynamic_cast( this->GetInput() ); } this->m_Interpolator = InterpolatorType::New(); this->m_Interpolator->SetInputImage( this->m_RealValuedInputImage ); PointType origin( 0.0 ); if( ImageDimension == 2 ) { this->InitializeTriangularDeltaPoints(); PointType pointA = origin + this->m_DeltaTriangularPointA; PointType pointB = origin + this->m_DeltaTriangularPointB; PointType pointC = origin + this->m_DeltaTriangularPointC; this->m_UndisplacedVolume = this->CalculateTriangularArea( pointA, pointB, pointC ); } else if( ImageDimension == 3 ) { this->InitializeTetrahedralDeltaPoints(); PointType pointA = origin + this->m_DeltaTetrahedralPointA; PointType pointB = origin + this->m_DeltaTetrahedralPointB; PointType pointC = origin + this->m_DeltaTetrahedralPointC; PointType pointD = origin + this->m_DeltaTetrahedralPointD; this->m_UndisplacedVolume = this->CalculateTetrahedralVolume( pointA, pointB, pointC, pointD ); } else { itkExceptionMacro( "Computations are only valid for ImageDimension = 2 or 3" ); } } template< typename TInputImage, typename TRealType, typename TOutputImage > void GeometricJacobianDeterminantImageFilter ::InitializeTetrahedralDeltaPoints() { // cf http://en.wikipedia.org/wiki/Tetrahedron#Formulas_for_a_regular_tetrahedron typename InputImageType::PointType originPoint; ContinuousIndex cidx; cidx.Fill( 0.0 ); this->m_RealValuedInputImage->TransformContinuousIndexToPhysicalPoint( cidx, originPoint ); typename InputImageType::PointType deltaPoint; cidx[0] = 0.5; cidx[1] = 0.0; cidx[2] = -0.5 / std::sqrt( 2.0 ); this->m_RealValuedInputImage->TransformContinuousIndexToPhysicalPoint( cidx, deltaPoint ); this->m_DeltaTetrahedralPointA = deltaPoint - originPoint; cidx[0] = -0.5; cidx[1] = 0.0; cidx[2] = -0.5 / std::sqrt( 2.0 ); this->m_RealValuedInputImage->TransformContinuousIndexToPhysicalPoint( cidx, deltaPoint ); this->m_DeltaTetrahedralPointB = deltaPoint - originPoint; cidx[0] = 0.0; cidx[1] = 0.5; cidx[2] = 0.5 / std::sqrt( 2.0 ); this->m_RealValuedInputImage->TransformContinuousIndexToPhysicalPoint( cidx, deltaPoint ); this->m_DeltaTetrahedralPointC = deltaPoint - originPoint; cidx[0] = 0.0; cidx[1] = -0.5; cidx[2] = 0.5 / std::sqrt( 2.0 ); this->m_RealValuedInputImage->TransformContinuousIndexToPhysicalPoint( cidx, deltaPoint ); this->m_DeltaTetrahedralPointD = deltaPoint - originPoint; } template< typename TInputImage, typename TRealType, typename TOutputImage > void GeometricJacobianDeterminantImageFilter ::InitializeTriangularDeltaPoints() { typename InputImageType::PointType originPoint; ContinuousIndex cidx; cidx.Fill( 0.0 ); this->m_RealValuedInputImage->TransformContinuousIndexToPhysicalPoint( cidx, originPoint ); typename InputImageType::PointType deltaPoint; cidx[0] = 0.0; cidx[1] = 0.25 * std::sqrt( 3.0 ); this->m_RealValuedInputImage->TransformContinuousIndexToPhysicalPoint( cidx, deltaPoint ); this->m_DeltaTriangularPointA = deltaPoint - originPoint; cidx[0] = -0.5; cidx[1] = -0.25 * std::sqrt( 3.0 ); this->m_RealValuedInputImage->TransformContinuousIndexToPhysicalPoint( cidx, deltaPoint ); this->m_DeltaTriangularPointB = deltaPoint - originPoint; cidx[0] = 0.5; cidx[1] = -0.25 * std::sqrt( 3.0 ); this->m_RealValuedInputImage->TransformContinuousIndexToPhysicalPoint( cidx, deltaPoint ); this->m_DeltaTriangularPointC = deltaPoint - originPoint; } template< typename TInputImage, typename TRealType, typename TOutputImage > void GeometricJacobianDeterminantImageFilter< TInputImage, TRealType, TOutputImage > ::ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) { ZeroFluxNeumannBoundaryCondition nbc; ConstNeighborhoodIteratorType bit; ImageRegionIteratorWithIndex it; typename OutputImageType::Pointer outputPtr = this->GetOutput(); // Find the data-set boundary "faces" typename NeighborhoodAlgorithm:: ImageBoundaryFacesCalculator::FaceListType faceList; NeighborhoodAlgorithm::ImageBoundaryFacesCalculator bC; faceList = bC( dynamic_cast ( this->m_RealValuedInputImage.GetPointer() ), outputRegionForThread, this->m_NeighborhoodRadius ); typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator:: FaceListType::iterator fit; // Support progress methods/callbacks ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); // Process each of the data set faces. The iterator is reinitialized on each // face so that it can determine whether or not to check for boundary // conditions. for ( fit = faceList.begin(); fit != faceList.end(); ++fit ) { bit = ConstNeighborhoodIteratorType( this->m_NeighborhoodRadius, dynamic_cast ( m_RealValuedInputImage.GetPointer() ), *fit ); it = ImageRegionIteratorWithIndex( outputPtr, *fit ); bit.OverrideBoundaryCondition( &nbc ); bit.GoToBegin(); while ( ! bit.IsAtEnd() ) { typename InputImageType::IndexType index = it.GetIndex(); PointType imagePoint; outputPtr->TransformIndexToPhysicalPoint( index, imagePoint ); RealType displacedVolume = 0.0; if( ImageDimension == 2 ) { typename InterpolatorType::PointType pointA; pointA.CastFrom( imagePoint + this->m_DeltaTriangularPointA ); typename InterpolatorType::PointType pointB; pointB.CastFrom( imagePoint + this->m_DeltaTriangularPointB ); typename InterpolatorType::PointType pointC; pointC.CastFrom( imagePoint + this->m_DeltaTriangularPointC ); PointType displacedPointA = pointA + this->m_Interpolator->Evaluate( pointA ); PointType displacedPointB = pointB + this->m_Interpolator->Evaluate( pointB ); PointType displacedPointC = pointC + this->m_Interpolator->Evaluate( pointC ); displacedVolume = this->CalculateTriangularArea( displacedPointA, displacedPointB, displacedPointC ); } else if( ImageDimension == 3 ) { typename InterpolatorType::PointType pointA; pointA.CastFrom( imagePoint + this->m_DeltaTetrahedralPointA ); typename InterpolatorType::PointType pointB; pointB.CastFrom( imagePoint + this->m_DeltaTetrahedralPointB ); typename InterpolatorType::PointType pointC; pointC.CastFrom( imagePoint + this->m_DeltaTetrahedralPointC ); typename InterpolatorType::PointType pointD; pointD.CastFrom( imagePoint + this->m_DeltaTetrahedralPointD ); PointType displacedPointA = pointA + this->m_Interpolator->Evaluate( pointA ); PointType displacedPointB = pointB + this->m_Interpolator->Evaluate( pointB ); PointType displacedPointC = pointC + this->m_Interpolator->Evaluate( pointC ); PointType displacedPointD = pointD + this->m_Interpolator->Evaluate( pointD ); displacedVolume = this->CalculateTetrahedralVolume( displacedPointA, displacedPointB, displacedPointC, displacedPointD ); } RealType volumeDifferential = displacedVolume / ( this->m_UndisplacedVolume ); it.Set( volumeDifferential ); ++bit; ++it; progress.CompletedPixel(); } } } template< typename TInputImage, typename TRealType, typename TOutputImage > typename GeometricJacobianDeterminantImageFilter< TInputImage, TRealType, TOutputImage >::RealType GeometricJacobianDeterminantImageFilter< TInputImage, TRealType, TOutputImage > ::CalculateTetrahedralVolume( PointType a, PointType b, PointType c, PointType d ) { vnl_vector ad = ( a - d ).GetVnlVector(); vnl_vector bd = ( b - d ).GetVnlVector(); vnl_vector cd = ( c - d ).GetVnlVector(); vnl_vector bdxcd = vnl_cross_3d( bd, cd ); RealType volume = vnl_math_abs( ad[0] * bdxcd[0] + ad[1] * bdxcd[1] + ad[2] * bdxcd[2] ) / 6.0; return volume; } template< typename TInputImage, typename TRealType, typename TOutputImage > typename GeometricJacobianDeterminantImageFilter< TInputImage, TRealType, TOutputImage >::RealType GeometricJacobianDeterminantImageFilter< TInputImage, TRealType, TOutputImage > ::CalculateTriangularArea( PointType a, PointType b, PointType c ) { RealVectorType ab = ( a - b ); RealVectorType ac = ( a - c ); RealType area = 0.5 * vnl_math_abs( ab[0] * ac[1] - ac[0] * ab[1] ); return area; } template void GeometricJacobianDeterminantImageFilter ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf(os,indent); os << indent << "m_RealValuedInputImage = " << m_RealValuedInputImage.GetPointer() << std::endl; } } // end namespace itk #endif ants-2.2.0/Utilities/itkImageIntensityAndGradientToPointSetFilter.h000066400000000000000000000116241311104306400255020ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkImageIntensityAndGradientToPointSetFilter_h #define __itkImageIntensityAndGradientToPointSetFilter_h #include "itkCovariantVector.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkImage.h" #include "itkConstNeighborhoodIterator.h" #include "itkPointSet.h" namespace itk { /** \class ImageIntensityAndGradientToPointSetFilter * \brief * Reads a file and creates an ikt point set. * */ template class ImageIntensityAndGradientToPointSetFilter : public MeshSource { public: /** Standard "Self" typedef. */ typedef ImageIntensityAndGradientToPointSetFilter Self; typedef MeshSource Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Extract dimension from the input image. */ itkStaticConstMacro( Dimension, unsigned int, TInputImage::ImageDimension ); /** Run-time type information (and related methods). */ itkTypeMacro( ImageIntensityAndGradientToPointSetFilter, MeshSource ); /** Hold on to the type information specified by the template parameters. */ typedef TInputImage InputImageType; typedef typename InputImageType::PixelType InputImagePixelType; typedef TMaskImage MaskImageType; typedef TOutputMesh OutputMeshType; typedef typename OutputMeshType::MeshTraits MeshTraits; typedef typename OutputMeshType::Superclass PointSetType; typedef typename OutputMeshType::PointType PointType; typedef typename MeshTraits::PixelType PointSetPixelType; typedef CovariantVector GradientPixelType; typedef Image GradientImageType; typedef ConstNeighborhoodIterator ConstNeighborhoodIteratorType; typedef typename ConstNeighborhoodIteratorType::RadiusType NeighborhoodRadiusType; typedef GradientRecursiveGaussianImageFilter GradientFilterType; /** * Set/Get the input image. */ void SetInputImage( const InputImageType *image ) { this->SetNthInput( 0, const_cast( image ) ); } void SetInput1( const InputImageType *image ) { this->SetInputImage( image ); } /** * Get mask image function. */ const InputImageType* GetInputImage() const { return static_cast( this->ProcessObject::GetInput( 0 ) ); } /** * Set mask image function. */ void SetMaskImage( const MaskImageType *mask ) { this->SetNthInput( 1, const_cast( mask ) ); } void SetInput2( const MaskImageType *mask ) { this->SetMaskImage( mask ); } /** * Get mask image function. */ const MaskImageType* GetMaskImage() const { return static_cast( this->ProcessObject::GetInput( 1 ) ); } void Update() ITK_OVERRIDE; /** * Set/Get sigma for the gradient recursive gaussian image filter */ itkSetMacro( Sigma, double ); itkGetConstMacro( Sigma, double ); /** * Set/Get boolean for gradient calculation. */ itkSetMacro( UseCentralDifferenceFunction, bool ); itkGetConstMacro( UseCentralDifferenceFunction, bool ); /** * Set/Get neighborhood radius */ itkSetMacro( NeighborhoodRadius, NeighborhoodRadiusType ); itkGetConstMacro( NeighborhoodRadius, NeighborhoodRadiusType ); protected: ImageIntensityAndGradientToPointSetFilter(); ~ImageIntensityAndGradientToPointSetFilter() { } void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; virtual void GenerateData() ITK_OVERRIDE; double m_Sigma; NeighborhoodRadiusType m_NeighborhoodRadius; bool m_UseCentralDifferenceFunction; private: ImageIntensityAndGradientToPointSetFilter( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented void ReadPoints(); }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkImageIntensityAndGradientToPointSetFilter.hxx" #endif #endif // _itkImageIntensityAndGradientToPointSetFilter_h ants-2.2.0/Utilities/itkImageIntensityAndGradientToPointSetFilter.hxx000066400000000000000000000130461311104306400260620ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkImageIntensityAndGradientToPointSetFilter_hxx #define __itkImageIntensityAndGradientToPointSetFilter_hxx #include "itkImageIntensityAndGradientToPointSetFilter.h" #include "itkCentralDifferenceImageFunction.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkMersenneTwisterRandomVariateGenerator.h" namespace itk { // // Constructor // template ImageIntensityAndGradientToPointSetFilter ::ImageIntensityAndGradientToPointSetFilter(): m_Sigma(1.5), m_NeighborhoodRadius(), m_UseCentralDifferenceFunction(true) { this->m_NeighborhoodRadius.Fill( 1 ), this->ProcessObject::SetNumberOfRequiredInputs( 2 ); // // Create the output // typename TOutputMesh::Pointer output = TOutputMesh::New(); this->ProcessObject::SetNumberOfRequiredOutputs( 1 ); this->ProcessObject::SetNthOutput( 0, output.GetPointer() ); } template void ImageIntensityAndGradientToPointSetFilter ::Update() { this->GenerateData(); } template void ImageIntensityAndGradientToPointSetFilter ::GenerateData() { const InputImageType * inputImage = this->GetInputImage(); const MaskImageType * maskImage = this->GetMaskImage(); typename OutputMeshType::Pointer output = this->GetOutput(); // Calculate gradient image typename GradientImageType::Pointer gradientImage = ITK_NULLPTR; if( this->m_UseCentralDifferenceFunction ) { GradientPixelType zeroVector; zeroVector.Fill( 0 ); gradientImage = GradientImageType::New(); gradientImage->CopyInformation( inputImage ); gradientImage->SetRegions( inputImage->GetRequestedRegion() ); gradientImage->Allocate(); gradientImage->FillBuffer( zeroVector ); typedef CentralDifferenceImageFunction GradientCalculatorType; typename GradientCalculatorType::Pointer gradientCalculator = GradientCalculatorType::New(); gradientCalculator->SetInputImage( inputImage ); gradientCalculator->SetUseImageDirection( true ); ImageRegionIteratorWithIndex It( gradientImage, gradientImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { It.Set( gradientCalculator->EvaluateAtIndex( It.GetIndex() ) ); } } else { typename GradientFilterType::Pointer gradientFilter = GradientFilterType::New(); gradientFilter->SetInput( inputImage ); gradientFilter->SetSigma( this->m_Sigma ); gradientFilter->SetUseImageDirection( true ); gradientImage = gradientFilter->GetOutput(); gradientImage->Update(); gradientImage->DisconnectPipeline(); } // Set up the point set pixel type characteristics // size of pixel type array = intensities + gradientXs + gradientYs + ... // = ( numberOfNeighborhoodVoxels + 1 ) * Dimension SizeValueType numberOfNeighborhoodVoxels = 1; for( SizeValueType d = 0; d < Dimension; d++ ) { numberOfNeighborhoodVoxels *= ( 2 * this->m_NeighborhoodRadius[d] + 1 ); } const SizeValueType sizeOfPixelTypeArray = numberOfNeighborhoodVoxels * ( 1 + Dimension ); // Initialize output point set SizeValueType count = 0; ConstNeighborhoodIteratorType ItN( this->m_NeighborhoodRadius, gradientImage, gradientImage->GetRequestedRegion() ); for( ItN.GoToBegin(); !ItN.IsAtEnd(); ++ItN ) { typename InputImageType::IndexType index = ItN.GetIndex(); if( maskImage->GetPixel( index ) && ItN.InBounds() ) { typename InputImageType::PointType imagePoint; inputImage->TransformIndexToPhysicalPoint( index, imagePoint ); PointType point; point.CastFrom( imagePoint ); PointSetPixelType array( sizeOfPixelTypeArray ); unsigned int arrayIndex = 0; for( SizeValueType n = 0; n < numberOfNeighborhoodVoxels; n++ ) { typename InputImageType::IndexType offsetIndex = ItN.GetIndex( n ); InputImagePixelType intensity = inputImage->GetPixel( offsetIndex ); GradientPixelType gradient = gradientImage->GetPixel( offsetIndex ); array[arrayIndex++] = intensity; for( SizeValueType d = 0; d < Dimension; d++ ) { array[arrayIndex++] = gradient[d]; } } output->SetPoint( count, point ); output->SetPointData( count++, array ); } } } template void ImageIntensityAndGradientToPointSetFilter ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf( os, indent ); os << "Sigma = " << this->m_Sigma << std::endl; os << "Neighborhood radius = " << this->m_NeighborhoodRadius << std::endl; } } // end of namespace itk #endif ants-2.2.0/Utilities/itkLabelOverlapMeasuresImageFilter.h000066400000000000000000000147721311104306400235060ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkLabelOverlapMeasuresImageFilter_h #define __itkLabelOverlapMeasuresImageFilter_h #include "itkImageToImageFilter.h" #include "itkFastMutexLock.h" #include "itkNumericTraits.h" #include "itksys/hash_map.hxx" namespace itk { /** \class LabelOverlapMeasuresImageFilter * \brief Computes overlap measures between the set same set of labels of * pixels of two images. Background is assumed to be 0. * * \sa LabelOverlapMeasuresImageFilter * * \ingroup MultiThreaded */ template class LabelOverlapMeasuresImageFilter : public ImageToImageFilter { public: /** Standard Self typedef */ typedef LabelOverlapMeasuresImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Runtime information support. */ itkTypeMacro( LabelOverlapMeasuresImageFilter, ImageToImageFilter ); /** Image related typedefs. */ typedef TLabelImage LabelImageType; typedef typename TLabelImage::Pointer LabelImagePointer; typedef typename TLabelImage::ConstPointer LabelImageConstPointer; typedef typename TLabelImage::RegionType RegionType; typedef typename TLabelImage::SizeType SizeType; typedef typename TLabelImage::IndexType IndexType; typedef typename TLabelImage::PixelType LabelType; /** Type to use form computations. */ typedef typename NumericTraits::RealType RealType; /** \class LabelLabelOverlapMeasuress * \brief Metrics stored per label */ class LabelSetMeasures { public: // default constructor LabelSetMeasures() { m_Source = 0; m_Target = 0; m_Union = 0; m_Intersection = 0; m_SourceComplement = 0; m_TargetComplement = 0; } // added for completeness LabelSetMeasures & operator=( const LabelSetMeasures& l ) { m_Source = l.m_Source; m_Target = l.m_Target; m_Union = l.m_Union; m_Intersection = l.m_Intersection; m_SourceComplement = l.m_SourceComplement; m_TargetComplement = l.m_TargetComplement; } unsigned long m_Source; unsigned long m_Target; unsigned long m_Union; unsigned long m_Intersection; unsigned long m_SourceComplement; unsigned long m_TargetComplement; }; /** Type of the map used to store data per label */ typedef itksys::hash_map MapType; typedef typename MapType::iterator MapIterator; typedef typename MapType::const_iterator MapConstIterator; /** Image related typedefs. */ itkStaticConstMacro( ImageDimension, unsigned int, TLabelImage::ImageDimension ); /** Set the source image. */ void SetSourceImage( const LabelImageType * image ) { this->SetNthInput( 0, const_cast( image ) ); } /** Set the target image. */ void SetTargetImage( const LabelImageType * image ) { this->SetNthInput( 1, const_cast( image ) ); } /** Get the source image. */ const LabelImageType * GetSourceImage( void ) { return this->GetInput( 0 ); } /** Get the target image. */ const LabelImageType * GetTargetImage( void ) { return this->GetInput( 1 ); } /** Get the label set measures */ MapType GetLabelSetMeasures() { return this->m_LabelSetMeasures; } /** * tric overlap measures */ /** measures over all labels */ RealType GetTotalOverlap(); RealType GetUnionOverlap(); RealType GetMeanOverlap(); RealType GetVolumeSimilarity(); RealType GetFalseNegativeError(); RealType GetFalsePositiveError(); /** measures over individual labels */ RealType GetTargetOverlap( LabelType ); RealType GetUnionOverlap( LabelType ); RealType GetMeanOverlap( LabelType ); RealType GetVolumeSimilarity( LabelType ); RealType GetFalseNegativeError( LabelType ); RealType GetFalsePositiveError( LabelType ); /** alternative names */ RealType GetJaccardCoefficient() { return this->GetUnionOverlap(); } RealType GetJaccardCoefficient( LabelType label ) { return this->GetUnionOverlap( label ); } RealType GetDiceCoefficient() { return this->GetMeanOverlap(); } RealType GetDiceCoefficient( LabelType label ) { return this->GetMeanOverlap( label ); } #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro( Input1HasNumericTraitsCheck, ( Concept::HasNumericTraits ) ); /** End concept checking */ #endif protected: LabelOverlapMeasuresImageFilter(); ~LabelOverlapMeasuresImageFilter() { }; void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; /** * Pass the input through unmodified. Do this by setting the output to the * source this by setting the output to the source image in the * AllocateOutputs() method. */ void AllocateOutputs() ITK_OVERRIDE; void BeforeThreadedGenerateData() ITK_OVERRIDE; void AfterThreadedGenerateData() ITK_OVERRIDE; /** Multi-thread version GenerateData. */ void ThreadedGenerateData( const RegionType &, ThreadIdType ) ITK_OVERRIDE; // Override since the filter needs all the data for the algorithm void GenerateInputRequestedRegion() ITK_OVERRIDE; // Override since the filter produces all of its output void EnlargeOutputRequestedRegion( DataObject *data ) ITK_OVERRIDE; private: LabelOverlapMeasuresImageFilter( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented std::vector m_LabelSetMeasuresPerThread; MapType m_LabelSetMeasures; SimpleFastMutexLock m_Mutex; }; // end of class } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkLabelOverlapMeasuresImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkLabelOverlapMeasuresImageFilter.hxx000066400000000000000000000326551311104306400240660ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkLabelOverlapMeasuresImageFilter_hxx #define _itkLabelOverlapMeasuresImageFilter_hxx #include "itkLabelOverlapMeasuresImageFilter.h" #include "itkImageRegionConstIterator.h" #include "itkProgressReporter.h" namespace itk { #if defined(__GNUC__) && (__GNUC__ <= 2) // NOTE: This class needs a mutex for gnu 2.95 /** Used for mutex locking */ #define LOCK_HASHMAP this->m_Mutex.Lock() #define UNLOCK_HASHMAP this->m_Mutex.Unlock() #else #define LOCK_HASHMAP #define UNLOCK_HASHMAP #endif template LabelOverlapMeasuresImageFilter ::LabelOverlapMeasuresImageFilter() { // this filter requires two input images this->SetNumberOfRequiredInputs( 2 ); } template void LabelOverlapMeasuresImageFilter ::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); if( this->GetSourceImage() ) { LabelImagePointer source = const_cast ( this->GetSourceImage() ); source->SetRequestedRegionToLargestPossibleRegion(); } if( this->GetTargetImage() ) { LabelImagePointer target = const_cast ( this->GetTargetImage() ); target->SetRequestedRegionToLargestPossibleRegion(); } } template void LabelOverlapMeasuresImageFilter ::EnlargeOutputRequestedRegion( DataObject *data ) { Superclass::EnlargeOutputRequestedRegion( data ); data->SetRequestedRegionToLargestPossibleRegion(); } template void LabelOverlapMeasuresImageFilter ::AllocateOutputs() { // Pass the source through as the output LabelImagePointer image = const_cast( this->GetSourceImage() ); this->SetNthOutput( 0, image ); // Nothing that needs to be allocated for the remaining outputs } template void LabelOverlapMeasuresImageFilter ::BeforeThreadedGenerateData() { int numberOfThreads = this->GetNumberOfThreads(); // Resize the thread temporaries this->m_LabelSetMeasuresPerThread.resize( numberOfThreads ); // Initialize the temporaries for( int n = 0; n < numberOfThreads; n++ ) { this->m_LabelSetMeasuresPerThread[n].clear(); } // Initialize the final map this->m_LabelSetMeasures.clear(); } template void LabelOverlapMeasuresImageFilter ::AfterThreadedGenerateData() { // Run through the map for each thread and accumulate the set measures. for( unsigned int n = 0; n < this->GetNumberOfThreads(); n++ ) { // iterate over the map for this thread for( MapConstIterator threadIt = this->m_LabelSetMeasuresPerThread[n].begin(); threadIt != this->m_LabelSetMeasuresPerThread[n].end(); ++threadIt ) { // does this label exist in the cumulative stucture yet? MapIterator mapIt = this->m_LabelSetMeasures.find( ( *threadIt ).first ); if( mapIt == this->m_LabelSetMeasures.end() ) { // create a new entry typedef typename MapType::value_type MapValueType; mapIt = this->m_LabelSetMeasures.insert( MapValueType( (*threadIt).first, LabelSetMeasures() ) ).first; } // accumulate the information from this thread (*mapIt).second.m_Source += (*threadIt).second.m_Source; (*mapIt).second.m_Target += (*threadIt).second.m_Target; (*mapIt).second.m_Union += (*threadIt).second.m_Union; (*mapIt).second.m_Intersection += (*threadIt).second.m_Intersection; (*mapIt).second.m_SourceComplement += (*threadIt).second.m_SourceComplement; (*mapIt).second.m_TargetComplement += (*threadIt).second.m_TargetComplement; } // end of thread map iterator loop } // end of thread loop } template void LabelOverlapMeasuresImageFilter ::ThreadedGenerateData( const RegionType& outputRegionForThread, ThreadIdType threadId ) { ImageRegionConstIterator ItS( this->GetSourceImage(), outputRegionForThread ); ImageRegionConstIterator ItT( this->GetTargetImage(), outputRegionForThread ); // support progress methods/callbacks ProgressReporter progress( this, threadId, 2 * outputRegionForThread.GetNumberOfPixels() ); for( ItS.GoToBegin(), ItT.GoToBegin(); !ItS.IsAtEnd(); ++ItS, ++ItT ) { LabelType sourceLabel = ItS.Get(); LabelType targetLabel = ItT.Get(); // is the label already in this thread? MapIterator mapItS = this->m_LabelSetMeasuresPerThread[threadId].find( sourceLabel ); MapIterator mapItT = this->m_LabelSetMeasuresPerThread[threadId].find( targetLabel ); if( mapItS == this->m_LabelSetMeasuresPerThread[threadId].end() ) { // create a new label set measures object typedef typename MapType::value_type MapValueType; LOCK_HASHMAP; mapItS = this->m_LabelSetMeasuresPerThread[threadId].insert( MapValueType( sourceLabel, LabelSetMeasures() ) ).first; UNLOCK_HASHMAP; } if( mapItT == this->m_LabelSetMeasuresPerThread[threadId].end() ) { // create a new label set measures object typedef typename MapType::value_type MapValueType; LOCK_HASHMAP; mapItT = this->m_LabelSetMeasuresPerThread[threadId].insert( MapValueType( targetLabel, LabelSetMeasures() ) ).first; UNLOCK_HASHMAP; } (*mapItS).second.m_Source++; (*mapItT).second.m_Target++; if( sourceLabel == targetLabel ) { (*mapItS).second.m_Intersection++; (*mapItS).second.m_Union++; } else { (*mapItS).second.m_Union++; (*mapItT).second.m_Union++; (*mapItS).second.m_SourceComplement++; (*mapItT).second.m_TargetComplement++; } progress.CompletedPixel(); } } /** * measures */ template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetTotalOverlap() { RealType numerator = 0.0; RealType denominator = 0.0; for( MapIterator mapIt = this->m_LabelSetMeasures.begin(); mapIt != this->m_LabelSetMeasures.end(); ++mapIt ) { // Do not include the background in the final value. if( (*mapIt).first == NumericTraits::ZeroValue() ) { continue; } numerator += static_cast( (*mapIt).second.m_Intersection ); denominator += static_cast( (*mapIt).second.m_Target ); } return numerator / denominator; } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetTargetOverlap( LabelType label ) { MapIterator mapIt = this->m_LabelSetMeasures.find( label ); if( mapIt == this->m_LabelSetMeasures.end() ) { itkWarningMacro( "Label " << label << " not found." ); return 0.0; } RealType value = static_cast( (*mapIt).second.m_Intersection ) / static_cast( (*mapIt).second.m_Target ); return value; } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetUnionOverlap() { RealType numerator = 0.0; RealType denominator = 0.0; for( MapIterator mapIt = this->m_LabelSetMeasures.begin(); mapIt != this->m_LabelSetMeasures.end(); ++mapIt ) { // Do not include the background in the final value. if( (*mapIt).first == NumericTraits::ZeroValue() ) { continue; } numerator += static_cast( (*mapIt).second.m_Intersection ); denominator += static_cast( (*mapIt).second.m_Union ); } return numerator / denominator; } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetUnionOverlap( LabelType label ) { MapIterator mapIt = this->m_LabelSetMeasures.find( label ); if( mapIt == this->m_LabelSetMeasures.end() ) { itkWarningMacro( "Label " << label << " not found." ); return 0.0; } RealType value = static_cast( (*mapIt).second.m_Intersection ) / static_cast( (*mapIt).second.m_Union ); return value; } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetMeanOverlap() { RealType uo = this->GetUnionOverlap(); return 2.0 * uo / ( 1.0 + uo ); } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetMeanOverlap( LabelType label ) { RealType uo = this->GetUnionOverlap( label ); return 2.0 * uo / ( 1.0 + uo ); } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetVolumeSimilarity() { RealType numerator = 0.0; RealType denominator = 0.0; for( MapIterator mapIt = this->m_LabelSetMeasures.begin(); mapIt != this->m_LabelSetMeasures.end(); ++mapIt ) { // Do not include the background in the final value. if( (*mapIt).first == NumericTraits::ZeroValue() ) { continue; } numerator += ( ( static_cast( (*mapIt).second.m_Source ) - static_cast( (*mapIt).second.m_Target ) ) ); denominator += ( ( static_cast( (*mapIt).second.m_Source ) + static_cast( (*mapIt).second.m_Target ) ) ); } return 2.0 * numerator / denominator; } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetVolumeSimilarity( LabelType label ) { MapIterator mapIt = this->m_LabelSetMeasures.find( label ); if( mapIt == this->m_LabelSetMeasures.end() ) { itkWarningMacro( "Label " << label << " not found." ); return 0.0; } RealType value = 2.0 * ( static_cast( (*mapIt).second.m_Source ) - static_cast( (*mapIt).second.m_Target ) ) / ( static_cast( (*mapIt).second.m_Source ) + static_cast( (*mapIt).second.m_Target ) ); return value; } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetFalseNegativeError() { RealType numerator = 0.0; RealType denominator = 0.0; for( MapIterator mapIt = this->m_LabelSetMeasures.begin(); mapIt != this->m_LabelSetMeasures.end(); ++mapIt ) { // Do not include the background in the final value. if( (*mapIt).first == NumericTraits::ZeroValue() ) { continue; } numerator += static_cast( (*mapIt).second.m_TargetComplement ); denominator += static_cast( (*mapIt).second.m_Target ); } return numerator / denominator; } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetFalseNegativeError( LabelType label ) { MapIterator mapIt = this->m_LabelSetMeasures.find( label ); if( mapIt == this->m_LabelSetMeasures.end() ) { itkWarningMacro( "Label " << label << " not found." ); return 0.0; } RealType value = static_cast( (*mapIt).second.m_TargetComplement ) / static_cast( (*mapIt).second.m_Target ); return value; } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetFalsePositiveError() { RealType numerator = 0.0; RealType denominator = 0.0; for( MapIterator mapIt = this->m_LabelSetMeasures.begin(); mapIt != this->m_LabelSetMeasures.end(); ++mapIt ) { // Do not include the background in the final value. if( (*mapIt).first == NumericTraits::ZeroValue() ) { continue; } numerator += static_cast( (*mapIt).second.m_SourceComplement ); denominator += static_cast( (*mapIt).second.m_Source ); } return numerator / denominator; } template typename LabelOverlapMeasuresImageFilter::RealType LabelOverlapMeasuresImageFilter ::GetFalsePositiveError( LabelType label ) { MapIterator mapIt = this->m_LabelSetMeasures.find( label ); if( mapIt == this->m_LabelSetMeasures.end() ) { itkWarningMacro( "Label " << label << " not found." ); return 0.0; } RealType value = static_cast( (*mapIt).second.m_SourceComplement ) / static_cast( (*mapIt).second.m_Source ); return value; } template void LabelOverlapMeasuresImageFilter ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf( os, indent ); } } // end namespace itk #endif ants-2.2.0/Utilities/itkLabelPerimeterEstimationCalculator.h000066400000000000000000000103211311104306400242450ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: itkLabelPerimeterEstimationCalculator.h Date: $Date$ Version: $Revision$ 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 __itkLabelPerimeterEstimationCalculator_h #define __itkLabelPerimeterEstimationCalculator_h #include "itkObject.h" namespace itk { /** \class LabelPerimeterEstimationCalculator * \brief Estimates the perimeter of a label object. * * The LabelPerimeterEstimationCalculator takes a label object and calculates * an estimated perimeter based on pixels' and their neighbors. * * This implementation was taken from the Insight Journal paper: * http://hdl.handle.net/1926/584 or * http://www.insight-journal.org/browse/publication/176 * * \author Gaetan Lehmann. Biologie du Developpement et de la Reproduction, INRA de Jouy-en-Josas, France. * * \sa */ template< class TInputImage > class ITK_EXPORT LabelPerimeterEstimationCalculator: public Object { public: /** Standard class typedefs. */ typedef LabelPerimeterEstimationCalculator Self; typedef Object Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Some convenient typedefs. */ typedef TInputImage InputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename InputImageType::ConstPointer InputImageConstPointer; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename InputImageType::PixelType InputImagePixelType; typedef typename InputImageType::RegionType RegionType; typedef typename InputImageType::SizeType SizeType; typedef typename InputImageType::IndexType IndexType; typedef typename std::map< InputImagePixelType, double > PerimetersType; /** ImageDimension constants */ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); /** Standard New method. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(LabelPerimeterEstimationCalculator, Object); /** * Set/Get whether the connected components are defined strictly by * face connectivity or by face+edge+vertex connectivity. Default is * FullyConnectedOff. For objects that are 1 pixel wide, use * FullyConnectedOn. */ itkSetMacro(FullyConnected, bool); itkGetConstReferenceMacro(FullyConnected, bool); itkBooleanMacro(FullyConnected); void SetImage(const InputImageType *img) { m_Image = img; } const InputImageType * GetImage() const { return m_Image; } void Compute(); const PerimetersType & GetPerimeters() const { return m_Perimeters; } const double & GetPerimeter(const InputImagePixelType & label) const { if ( m_Perimeters.find(label) != m_Perimeters.end() ) { return m_Perimeters.find(label)->second; } itkExceptionMacro( << "Unknown label: " << static_cast< typename NumericTraits< InputImagePixelType >::PrintType >( label ) ); } bool HasLabel(const InputImagePixelType & label) const { if ( m_Perimeters.find(label) != m_Perimeters.end() ) { return true; } return false; } protected: LabelPerimeterEstimationCalculator(); ~LabelPerimeterEstimationCalculator() {} void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; private: LabelPerimeterEstimationCalculator(const Self &); //purposely not implemented void operator=(const Self &); //purposely not implemented bool m_FullyConnected; const InputImageType *m_Image; PerimetersType m_Perimeters; }; // end of class } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkLabelPerimeterEstimationCalculator.hxx" #endif #endif ants-2.2.0/Utilities/itkLabelPerimeterEstimationCalculator.hxx000066400000000000000000000147501311104306400246370ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: itkLabelPerimeterEstimationCalculator.txx Date: $Date$ Version: $Revision$ 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 __itkLabelPerimeterEstimationCalculator_hxx #define __itkLabelPerimeterEstimationCalculator_hxx #include "itkLabelPerimeterEstimationCalculator.h" #include "itkProgressReporter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkConstShapedNeighborhoodIterator.h" #include "itkConstantBoundaryCondition.h" #include "itkSize.h" #include "itkConnectedComponentAlgorithm.h" #include namespace itk { template< class TInputImage > LabelPerimeterEstimationCalculator< TInputImage > ::LabelPerimeterEstimationCalculator() { m_FullyConnected = false; } template< class TInputImage > void LabelPerimeterEstimationCalculator< TInputImage > ::Compute() { m_Perimeters.clear(); // ProgressReporter progress( this, 0, // this->GetImage()->GetRequestedRegion().GetNumberOfPixels() ); // reduce the region to avoid reading outside RegionType region = this->GetImage()->GetRequestedRegion(); SizeType size = region.GetSize(); for ( unsigned int i = 0; i < ImageDimension; i++ ) { size[i]--; } region.SetSize(size); // the radius which will be used for all the shaped iterators Size< ImageDimension > radius; radius.Fill(1); // set up the iterator typedef ConstShapedNeighborhoodIterator< InputImageType > IteratorType; typename IteratorType::ConstIterator nIt; IteratorType iIt(radius, this->GetImage(), region); // we want to search the neighbors with offset >= 0 // 2D -> 4 neighbors // 3D -> 8 neighbors typename IteratorType::OffsetType offset; unsigned int centerIndex = iIt.GetCenterNeighborhoodIndex(); // store the offsets to reuse them to evaluate the contributions of the // configurations typename std::vector< IndexType > indexes; IndexType idx0; idx0.Fill(0); for ( unsigned int d = centerIndex; d < 2 * centerIndex + 1; d++ ) { offset = iIt.GetOffset(d); bool deactivate = false; for ( unsigned int j = 0; j < ImageDimension && !deactivate; j++ ) { if ( offset[j] < 0 ) { deactivate = true; } } if ( deactivate ) { iIt.DeactivateOffset(offset); } else { iIt.ActivateOffset(offset); indexes.push_back(idx0 + offset); } } // to store the configurations count for all the labels typedef typename std::map< unsigned long, unsigned long > MapType; typedef typename std::map< InputImagePixelType, MapType > LabelMapType; LabelMapType confCount; for ( iIt.GoToBegin(); !iIt.IsAtEnd(); ++iIt ) { // 2 pass - find the labels in the neighborhood // - count the configurations for all the labels typedef typename std::set< InputImagePixelType > LabelSetType; LabelSetType labelSet; for ( nIt = iIt.Begin(); nIt != iIt.End(); nIt++ ) { labelSet.insert( nIt.Get() ); } for ( typename LabelSetType::const_iterator it = labelSet.begin(); it != labelSet.end(); it++ ) { unsigned long conf = 0; int i = 0; for ( nIt = iIt.Begin(); nIt != iIt.End(); nIt++, i++ ) { if ( nIt.Get() == *it ) { conf += 1 << i; } } confCount[*it][conf]++; } // progress.CompletedPixel(); } // compute the participation to the perimeter for all the configurations double physicalSize = 1.0; for ( unsigned int i = 0; i < ImageDimension; i++ ) { physicalSize *= this->GetImage()->GetSpacing()[i]; } typedef typename std::map< unsigned long, double > ContributionMapType; ContributionMapType contributions; const unsigned int numberOfNeighbors = static_cast< unsigned int >( std::pow( 2.0, static_cast< double >( ImageDimension ) ) ); const unsigned int numberOfConfigurations = static_cast< unsigned int >( std::pow( 2.0, static_cast< double >( numberOfNeighbors ) ) ); // create an image to store the neighbors typedef typename itk::Image< bool, ImageDimension > ImageType; typename ImageType::Pointer neighborsImage = ImageType::New(); // typename ImageType::SizeType size; size.Fill(2); neighborsImage->SetRegions(size); neighborsImage->Allocate(); for ( unsigned int i = 0; i < numberOfConfigurations; i++ ) { neighborsImage->FillBuffer(false); for ( unsigned int j = 0; j < numberOfNeighbors; j++ ) { if ( i & 1 << j ) { neighborsImage->SetPixel(indexes[j], true); } } // the image is created - we can now compute the contributions of the pixels // for that configuration contributions[i] = 0; for ( unsigned int j = 0; j < numberOfNeighbors; j++ ) { IndexType currentIdx = indexes[j]; if ( neighborsImage->GetPixel(currentIdx) ) { for ( unsigned int k = 0; k < ImageDimension; k++ ) { IndexType idx = currentIdx; idx[k] = std::abs(idx[k] - 1); if ( !neighborsImage->GetPixel(idx) ) { contributions[i] += physicalSize / this->GetImage()->GetSpacing()[k] / 2.0; } } } } contributions[i] /= ImageDimension; } // and use those contributions to found the perimeter m_Perimeters.clear(); for ( typename LabelMapType::const_iterator it = confCount.begin(); it != confCount.end(); it++ ) { m_Perimeters[it->first] = 0; for ( typename MapType::const_iterator it2 = it->second.begin(); it2 != it->second.end(); it2++ ) { m_Perimeters[it->first] += contributions[it2->first] * it2->second; } } } template< class TInputImage > void LabelPerimeterEstimationCalculator< TInputImage > ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "FullyConnected: " << m_FullyConnected << std::endl; } } // end namespace itk #endif ants-2.2.0/Utilities/itkLabeledPointSetFileReader.h000066400000000000000000000101421311104306400222440ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkLabeledPointSetFileReader_h #define __itkLabeledPointSetFileReader_h #include "itkMesh.h" #include "itkMeshSource.h" #include "itkArray.h" #include "itkImage.h" #include "itkVectorContainer.h" #include namespace itk { /** \class LabeledPointSetFileReader * \brief * Reads a file and creates an itkMesh. * */ template class LabeledPointSetFileReader : public MeshSource { public: /** Standard "Self" typedef. */ typedef LabeledPointSetFileReader Self; typedef MeshSource Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Extract dimension from the output mesh. */ itkStaticConstMacro( Dimension, unsigned int, TOutputMesh::PointType::Dimension ); /** Run-time type information (and related methods). */ itkTypeMacro( LabeledPointSetFileReader, MeshSource ); /** Hold on to the type information specified by the template parameters. */ typedef TOutputMesh OutputMeshType; typedef typename OutputMeshType::MeshTraits MeshTraits; typedef typename OutputMeshType::Superclass PointSetType; typedef typename OutputMeshType::PointType PointType; typedef typename MeshTraits::PixelType PixelType; typedef Array MultiComponentScalarType; typedef Array LineType; typedef VectorContainer MultiComponentScalarSetType; typedef VectorContainer LineSetType; typedef Image LabeledPointSetImageType; typedef std::vector LabelSetType; /** Set/Get the name of the file to be read. */ itkSetStringMacro( FileName ); itkGetStringMacro( FileName ); itkSetMacro( ExtractBoundaryPoints, bool ); itkGetMacro( ExtractBoundaryPoints, bool ); itkBooleanMacro( ExtractBoundaryPoints ); /** * Percentage of points selected randomnly */ itkSetClampMacro( RandomPercentage, double, 0.0, 1.0 ); itkGetConstMacro( RandomPercentage, double ); LabelSetType * GetLabelSet() { return &this->m_LabelSet; } unsigned int GetNumberOfLabels() const { return this->m_LabelSet.size(); } MultiComponentScalarSetType * GetMultiComponentScalars() { return this->m_MultiComponentScalars.GetPointer(); } LineSetType * GetLines() { return this->m_Lines.GetPointer(); } protected: LabeledPointSetFileReader(); ~LabeledPointSetFileReader() { } void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; /** Reads the file */ void GenerateData() ITK_OVERRIDE; bool m_ExtractBoundaryPoints; std::string m_FileName; double m_RandomPercentage; LabelSetType m_LabelSet; typename MultiComponentScalarSetType::Pointer m_MultiComponentScalars; typename LineSetType::Pointer m_Lines; private: LabeledPointSetFileReader( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented void ReadPointsFromImageFile(); void ReadPointsFromAvantsFile(); void ReadVTKFile(); void ReadPointsFromVTKFile(); void ReadScalarsFromVTKFile(); void ReadLinesFromVTKFile(); }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkLabeledPointSetFileReader.hxx" #endif #endif // _itkLabeledPointSetFileReader_h ants-2.2.0/Utilities/itkLabeledPointSetFileReader.hxx000066400000000000000000000370331311104306400226340ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkLabeledPointSetFileReader_hxx #define __itkLabeledPointSetFileReader_hxx #include "itkLabeledPointSetFileReader.h" #include "itkBinaryThresholdImageFilter.h" #include "itkImageFileReader.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkLabelContourImageFilter.h" #include "itkMersenneTwisterRandomVariateGenerator.h" #include "itkByteSwapper.h" #include #include #include namespace itk { // // Constructor // template LabeledPointSetFileReader ::LabeledPointSetFileReader(): m_ExtractBoundaryPoints( false ), m_FileName(), m_RandomPercentage(1.0), m_LabelSet(), m_MultiComponentScalars(ITK_NULLPTR), m_Lines(ITK_NULLPTR) { // // Create the output // typename TOutputMesh::Pointer output = TOutputMesh::New(); this->ProcessObject::SetNumberOfRequiredOutputs( 1 ); this->ProcessObject::SetNthOutput( 0, output.GetPointer() ); } template void LabeledPointSetFileReader ::GenerateData() { if( this->m_FileName == "" ) { itkExceptionMacro( "No input FileName" ); return; } // // Read input file // std::ifstream inputFile( m_FileName.c_str() ); if( !inputFile.is_open() ) { itkExceptionMacro("Unable to open file\n" "inputFilename= " << m_FileName ); return; } else { inputFile.close(); } /** * Get filename extension */ std::string::size_type pos = this->m_FileName.rfind( "." ); std::string extension( this->m_FileName, pos + 1, this->m_FileName.length() - 1 ); if( extension == "txt" ) { this->ReadPointsFromAvantsFile(); } else if( extension == "vtk" ) { this->ReadVTKFile(); } else // try reading the file as an image { this->ReadPointsFromImageFile(); } if( this->m_RandomPercentage < 1.0 ) { typedef Statistics::MersenneTwisterRandomVariateGenerator GeneratorType; typename GeneratorType::Pointer generator = GeneratorType::New(); generator->SetSeed(); typename OutputMeshType::Pointer output = OutputMeshType::New(); output->Initialize(); if( this->GetOutput()->GetNumberOfPoints() > 0 ) { typename OutputMeshType::PointsContainerIterator It = this->GetOutput()->GetPoints()->Begin(); unsigned long count = 0; while( It != this->GetOutput()->GetPoints()->End() ) { if( generator->GetVariateWithClosedRange() <= this->m_RandomPercentage ) { output->SetPoint( count, It.Value() ); PixelType label; bool elementExists = this->GetOutput()->GetPointData() ->GetElementIfIndexExists( It.Index(), &label ); if( elementExists ) { output->SetPointData( count, label ); } count++; } ++It; } } this->GraftOutput( output ); } this->m_LabelSet.clear(); /** * If the number of points does not match the number of * point data, fill the point data with zeros */ if( !this->GetOutput()->GetPointData() || this->GetOutput()->GetPointData()->Size() != this->GetOutput()->GetPoints()->Size() ) { itkWarningMacro( "Number of points does not match number of labels. " << "Filling point data with label zero." ); typename OutputMeshType::PointsContainerIterator It = this->GetOutput()->GetPoints()->Begin(); while( It != this->GetOutput()->GetPoints()->End() ) { this->GetOutput()->SetPointData( It.Index(), NumericTraits::ZeroValue() ); ++It; } } if( this->GetOutput()->GetNumberOfPoints() > 0 ) { typename OutputMeshType::PointDataContainerIterator ItD = this->GetOutput()->GetPointData()->Begin(); while( ItD != this->GetOutput()->GetPointData()->End() ) { if( find( this->m_LabelSet.begin(), this->m_LabelSet.end(), ItD.Value() ) == this->m_LabelSet.end() ) { this->m_LabelSet.push_back( ItD.Value() ); } ++ItD; } } } template void LabeledPointSetFileReader ::ReadPointsFromAvantsFile() { typename OutputMeshType::Pointer outputMesh = this->GetOutput(); std::ifstream inputFile( m_FileName.c_str() ); unsigned long count = 0; while( !inputFile.eof() ) { PointType point; PixelType label; if( Dimension == 2 ) { float trash; inputFile >> point >> trash >> label; } else // Dimension == 3 { inputFile >> point >> label; } if( ( point.GetVectorFromOrigin() ).GetSquaredNorm() > 0.0 || label != 0 ) { outputMesh->SetPointData( count, label ); outputMesh->SetPoint( count, point ); count++; } } inputFile.close(); } template void LabeledPointSetFileReader ::ReadVTKFile() { this->ReadPointsFromVTKFile(); this->ReadScalarsFromVTKFile(); this->ReadLinesFromVTKFile(); } template void LabeledPointSetFileReader ::ReadPointsFromVTKFile() { typename OutputMeshType::Pointer outputMesh = this->GetOutput(); std::ifstream inputFile( this->m_FileName.c_str() ); std::string line; bool isBinary = false; while( !inputFile.eof() ) { std::getline( inputFile, line ); if( line.find( "BINARY" ) != std::string::npos ) { isBinary = true; } if( line.find( "POINTS" ) != std::string::npos ) { break; } } itkDebugMacro( "POINTS line" << line ); std::string pointLine( line, strlen( "POINTS " ), line.length() ); itkDebugMacro( "pointLine " << pointLine ); int numberOfPoints = -1; if( sscanf( pointLine.c_str(), "%d", &numberOfPoints ) != 1 ) { itkExceptionMacro( "ERROR: Failed to read numberOfPoints\n" " pointLine = " << pointLine ); return; } itkDebugMacro( "numberOfPoints = " << numberOfPoints ); if( numberOfPoints < 1 ) { itkExceptionMacro( "numberOfPoints < 1" << " numberOfPoints = " << numberOfPoints ); return; } outputMesh->GetPoints()->Reserve( numberOfPoints ); // // Load the point coordinates into the itk::Mesh // PointType point; if( isBinary ) { itkDebugMacro( "Data is binary" ); float * ptData = new float[numberOfPoints * 3]; inputFile.read( reinterpret_cast( ptData ), 3 * numberOfPoints * sizeof(float) ); ByteSwapper::SwapRangeFromSystemToBigEndian(ptData, numberOfPoints * 3); for( long i = 0; i < numberOfPoints; i++ ) { for( long j = 0; j < Dimension; j++ ) { point[j] = ptData[i * 3 + j]; } outputMesh->SetPoint( i, point ); } delete [] ptData; } else { for( long i = 0; i < numberOfPoints; i++ ) { if( Dimension == 2 ) { float trash; inputFile >> point >> trash; } else // Dimension = 3 { inputFile >> point; } outputMesh->SetPoint( i, point ); } } inputFile.close(); } template void LabeledPointSetFileReader ::ReadScalarsFromVTKFile() { typename OutputMeshType::Pointer outputMesh = this->GetOutput(); std::ifstream inputFile( this->m_FileName.c_str() ); std::string line; bool isBinary = false; // // Find the labels associated with each pixel // while( !inputFile.eof() ) { std::getline( inputFile, line ); if( line.find( "BINARY" ) != std::string::npos ) { isBinary = true; } if( line.find( "SCALARS" ) != std::string::npos ) { break; } } if( inputFile.eof() ) { inputFile.close(); return; } std::string::size_type pos = line.rfind( " " ); std::string temp = std::string( line, pos + 1, line.length() - 1 ); unsigned int numberOfComponents = std::atoi( temp.c_str() ); std::getline( inputFile, line ); if( isBinary ) { int numberOfValues = outputMesh->GetNumberOfPoints() * numberOfComponents; float p; int * scalarData = new int[numberOfValues]; inputFile.read( reinterpret_cast( scalarData ), numberOfComponents * sizeof(p) ); ByteSwapper::SwapRangeFromSystemToBigEndian(scalarData, numberOfValues); if( numberOfComponents == 1 ) { // PixelType label; for( unsigned long i = 0; i < outputMesh->GetNumberOfPoints(); i++ ) { outputMesh->SetPointData( i, scalarData[i] ); } // itkExceptionMacro( "Only single label components are readable" ); } else { this->m_MultiComponentScalars = MultiComponentScalarSetType::New(); this->m_MultiComponentScalars->Initialize(); for( unsigned long i = 0; i < outputMesh->GetNumberOfPoints(); i++ ) { MultiComponentScalarType scalar; scalar.SetSize( numberOfComponents ); for( unsigned int d = 0; d < numberOfComponents; d++ ) { scalar[d] = scalarData[i * numberOfComponents + d]; } this->m_MultiComponentScalars->InsertElement( i, scalar ); } } delete [] scalarData; } else { if( numberOfComponents == 1 ) { PixelType label; for( unsigned long i = 0; i < outputMesh->GetNumberOfPoints(); i++ ) { inputFile >> label; outputMesh->SetPointData( i, label ); } // itkExceptionMacro( "Only single label components are readable" ); } else { this->m_MultiComponentScalars = MultiComponentScalarSetType::New(); this->m_MultiComponentScalars->Initialize(); for( unsigned long i = 0; i < outputMesh->GetNumberOfPoints(); i++ ) { MultiComponentScalarType scalar; scalar.SetSize( numberOfComponents ); for( unsigned int d = 0; d < numberOfComponents; d++ ) { inputFile >> scalar[d]; } this->m_MultiComponentScalars->InsertElement( i, scalar ); } } } inputFile.close(); } template void LabeledPointSetFileReader ::ReadLinesFromVTKFile() { typename OutputMeshType::Pointer outputMesh = this->GetOutput(); std::ifstream inputFile( this->m_FileName.c_str() ); std::string line; bool isBinary = false; // // Find the labels associated with each pixel // while( !inputFile.eof() ) { std::getline( inputFile, line ); if( line.find( "BINARY" ) != std::string::npos ) { isBinary = true; } if( line.find( "LINES" ) != std::string::npos ) { break; } } if( inputFile.eof() ) { inputFile.close(); return; } std::string::size_type pos = line.rfind( " " ); std::string temp = std::string( line, 6, pos - 1 ); unsigned int numberOfLines = std::atoi( temp.c_str() ); temp = std::string(line, pos, line.length() - 1 ); unsigned int numberOfValues = std::atoi( temp.c_str() ); this->m_Lines = LineSetType::New(); this->m_Lines->Initialize(); if( isBinary ) { int p; int * lineData = new int[numberOfValues]; inputFile.read( reinterpret_cast( lineData ), numberOfValues * sizeof(p) ); ByteSwapper::SwapRangeFromSystemToBigEndian(lineData, numberOfValues); unsigned long valueId = 0; unsigned long lineId = 0; while( valueId < numberOfValues ) { int lineLength = lineData[valueId]; ++valueId; LineType polyLine; polyLine.SetSize( lineLength ); for( long i = 0; i < lineLength; i++ ) { polyLine[i] = lineData[valueId]; ++valueId; } this->m_Lines->InsertElement( lineId, polyLine ); ++lineId; } delete [] lineData; } else { for( unsigned int i = 0; i < numberOfLines; i++ ) { LineType line_loc; unsigned int numberOfPoints; inputFile >> numberOfPoints; line_loc.SetSize( numberOfPoints ); for( unsigned int d = 0; d < numberOfPoints; d++ ) { inputFile >> line_loc[d]; } this->m_Lines->InsertElement( i, line_loc ); } } inputFile.close(); } template void LabeledPointSetFileReader ::ReadPointsFromImageFile() { typename OutputMeshType::Pointer outputMesh = this->GetOutput(); typedef ImageFileReader ImageReaderType; typename ImageReaderType::Pointer imageReader = ImageReaderType::New(); imageReader->SetFileName( this->m_FileName.c_str() ); imageReader->Update(); if( !this->m_ExtractBoundaryPoints ) { ImageRegionIteratorWithIndex It( imageReader->GetOutput(), imageReader->GetOutput()->GetLargestPossibleRegion() ); unsigned long count = 0; for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( label != NumericTraits::ZeroValue() ) { typename LabeledPointSetImageType::PointType imagePoint; imageReader->GetOutput()->TransformIndexToPhysicalPoint( It.GetIndex(), imagePoint ); PointType point; point.CastFrom( imagePoint ); outputMesh->SetPoint( count, point ); outputMesh->SetPointData( count, label ); count++; } } } else { typedef LabelContourImageFilter ContourFilterType; typename ContourFilterType::Pointer contourFilter = ContourFilterType::New(); contourFilter->SetInput( imageReader->GetOutput() ); contourFilter->SetFullyConnected( true ); contourFilter->SetBackgroundValue( 0 ); contourFilter->Update(); this->m_LabelSet.clear(); ImageRegionIteratorWithIndex It( contourFilter->GetOutput(), contourFilter->GetOutput()->GetLargestPossibleRegion() ); unsigned long count = 0; for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PixelType label = It.Get(); if( It.Get() > 0 ) { typename LabeledPointSetImageType::PointType imagePoint; contourFilter->GetOutput()->TransformIndexToPhysicalPoint( It.GetIndex(), imagePoint ); PointType point; point.CastFrom( imagePoint ); outputMesh->SetPoint( count, point ); outputMesh->SetPointData( count, It.Get() ); count++; if( find( this->m_LabelSet.begin(), this->m_LabelSet.end(), label ) == this->m_LabelSet.end() ) { this->m_LabelSet.push_back( label ); } } } } } template void LabeledPointSetFileReader ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf(os, indent); os << indent << "FileName: " << this->m_FileName << std::endl; os << indent << "RandomPercentage: " << this->m_RandomPercentage << std::endl; } } // end of namespace itk #endif ants-2.2.0/Utilities/itkLabeledPointSetFileWriter.h000066400000000000000000000112521311104306400223210ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkLabeledPointSetFileWriter_h #define __itkLabeledPointSetFileWriter_h #include "itkMesh.h" #include "itkArray.h" #include "itkImage.h" #include "itkVectorContainer.h" namespace itk { /** \class LabeledPointSetFileWriter * \brief * Writes an itkMesh to a file in various txt file formats. * */ template class LabeledPointSetFileWriter : public Object { public: /** Standard "Self" typedef. */ typedef LabeledPointSetFileWriter Self; typedef Object Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory */ itkNewMacro( Self ); /** Write the Input mesh to the Output file. * Use either Update() or Write(). */ void Update(); void Write(); /** Extract dimension from the output mesh. */ itkStaticConstMacro( Dimension, unsigned int, TInputMesh::PointType::Dimension ); /** Run-time type information (and related methods). */ itkTypeMacro( LabeledPointSetFileWriter, Object ); /** Hold on to the type information specified by the template parameters. */ typedef TInputMesh InputMeshType; typedef typename TInputMesh::Pointer InputMeshPointer; typedef typename InputMeshType::MeshTraits MeshTraits; typedef typename InputMeshType::Superclass PointSetType; typedef typename InputMeshType::PointType PointType; typedef typename MeshTraits::PixelType PixelType; typedef Array MultiComponentScalarType; typedef Array LineType; typedef VectorContainer MultiComponentScalarSetType; typedef VectorContainer LineSetType; typedef Image LabeledPointSetImageType; typedef typename LabeledPointSetImageType::SizeType ImageSizeType; typedef typename LabeledPointSetImageType::PointType ImageOriginType; typedef typename LabeledPointSetImageType::SpacingType ImageSpacingType; typedef typename LabeledPointSetImageType::DirectionType ImageDirectionType; /** Set the Input */ void SetInput( InputMeshType * input ); /** Set/Get the name of the file where data are written. */ itkSetStringMacro( FileName ); itkGetStringMacro( FileName ); /** Specify other attributes */ itkSetMacro( Lines, typename LineSetType::Pointer ); itkSetMacro( MultiComponentScalars, typename MultiComponentScalarSetType::Pointer ); /** Specify image attributes if output is an image. */ itkSetMacro( ImageSize, ImageSizeType ); itkGetConstMacro( ImageSize, ImageSizeType ); itkSetMacro( ImageOrigin, ImageOriginType ); itkGetConstMacro( ImageOrigin, ImageOriginType ); itkSetMacro( ImageSpacing, ImageSpacingType ); itkGetConstMacro( ImageSpacing, ImageSpacingType ); itkSetMacro( ImageDirection, ImageDirectionType ); itkGetConstMacro( ImageDirection, ImageDirectionType ); protected: LabeledPointSetFileWriter(); virtual ~LabeledPointSetFileWriter(); virtual void GenerateData(); std::string m_FileName; InputMeshPointer m_Input; typename MultiComponentScalarSetType::Pointer m_MultiComponentScalars; typename LineSetType::Pointer m_Lines; /** * If output is an image type, the attributes must be specified. */ ImageSizeType m_ImageSize; ImageSpacingType m_ImageSpacing; ImageOriginType m_ImageOrigin; ImageDirectionType m_ImageDirection; void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; private: LabeledPointSetFileWriter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented void WritePointsToAvantsFile(); void WritePointsToImageFile(); void WriteVTKFile(); void WritePointsToVTKFile(); void WriteScalarsToVTKFile(); void WriteLinesToVTKFile(); }; } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkLabeledPointSetFileWriter.hxx" #endif #endif ants-2.2.0/Utilities/itkLabeledPointSetFileWriter.hxx000066400000000000000000000245311311104306400227050ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkLabeledPointSetFileWriter_hxx #define __itkLabeledPointSetFileWriter_hxx #include "itkLabeledPointSetFileWriter.h" #include "itkBoundingBox.h" #include "itkImageFileWriter.h" #include namespace itk { // // Constructor // template LabeledPointSetFileWriter ::LabeledPointSetFileWriter() { this->m_Input = ITK_NULLPTR; this->m_FileName = ""; this->m_MultiComponentScalars = ITK_NULLPTR; this->m_Lines = ITK_NULLPTR; this->m_ImageSize.Fill( 0 ); } // // Destructor // template LabeledPointSetFileWriter ::~LabeledPointSetFileWriter() { } // // Set the input mesh // template void LabeledPointSetFileWriter ::SetInput(InputMeshType * input) { this->m_Input = input; } // // Write the input mesh to the output file // template void LabeledPointSetFileWriter ::Update() { this->GenerateData(); } // // Write the input mesh to the output file // template void LabeledPointSetFileWriter ::Write() { this->GenerateData(); } template void LabeledPointSetFileWriter ::GenerateData() { if( this->m_FileName == "" ) { itkExceptionMacro( "No FileName" ); return; } if( this->m_ImageSize[0] == 0 ) { typedef BoundingBox BoundingBoxType; typename BoundingBoxType::Pointer bbox = BoundingBoxType::New(); bbox->SetPoints( this->m_Input->GetPoints() ); bbox->ComputeBoundingBox(); for( unsigned int d = 0; d < Dimension; d++ ) { this->m_ImageSpacing[d] = ( bbox->GetMaximum()[d] - bbox->GetMinimum()[d] ) / static_cast( this->m_ImageSize[d] + 1 ); } this->m_ImageDirection.SetIdentity(); } // // Read output file // std::ofstream outputFile( m_FileName.c_str() ); if( !outputFile.is_open() ) { itkExceptionMacro( "Unable to open file\n" "outputFilename= " << m_FileName ); return; } else { outputFile.close(); } /** * Get filename extension */ std::string::size_type pos = this->m_FileName.rfind( "." ); std::string extension( this->m_FileName, pos + 1, this->m_FileName.length() - 1 ); if( extension == "txt" ) { this->WritePointsToAvantsFile(); } else if( extension == "vtk" ) { this->WriteVTKFile(); } else { try { this->WritePointsToImageFile(); } catch( ... ) { itkExceptionMacro( "Unknown extension: " << extension ); } } } template void LabeledPointSetFileWriter ::WriteVTKFile() { this->WritePointsToVTKFile(); this->WriteLinesToVTKFile(); this->WriteScalarsToVTKFile(); } template void LabeledPointSetFileWriter ::WritePointsToVTKFile() { // // Write to output file // std::ofstream outputFile( this->m_FileName.c_str() ); outputFile << "#vtk DataFile Version 2.0" << std::endl; outputFile << "File written by itkLabeledPointSetFileWriter" << std::endl; outputFile << "ASCII" << std::endl; outputFile << "DATASET POLYDATA" << std::endl; // POINTS go first unsigned int numberOfPoints = this->m_Input->GetNumberOfPoints(); outputFile << "POINTS " << numberOfPoints << " float" << std::endl; typename InputMeshType::PointsContainerIterator pointIterator = this->m_Input->GetPoints()->Begin(); typename InputMeshType::PointsContainerIterator pointEnd = this->m_Input->GetPoints()->End(); while( pointIterator != pointEnd ) { PointType point = pointIterator.Value(); outputFile << point[0] << " " << point[1]; if( Dimension == 2 ) { outputFile << " 0 " << std::endl; } else if( Dimension == 3 ) { outputFile << " " << point[2] << " " << std::endl; } pointIterator++; } outputFile.close(); } template void LabeledPointSetFileWriter ::WriteScalarsToVTKFile() { // // Write to output file // std::ofstream outputFile( this->m_FileName.c_str(), std::ios::app ); // No point data conditions if( !this->m_Input->GetPointData() ) { return; } if( this->m_Input->GetPointData()->Size() == 0 ) { return; } unsigned int numberOfPoints = this->m_Input->GetNumberOfPoints(); outputFile << std::endl; outputFile << "POINT_DATA " << numberOfPoints << std::endl; std::string type = std::string( "float" ); if( !this->m_MultiComponentScalars ) { outputFile << "SCALARS pointLabels " << type << " 1" << std::endl; outputFile << "LOOKUP_TABLE default" << std::endl; typename InputMeshType::PointDataContainerIterator pointDataIterator = this->m_Input->GetPointData()->Begin(); typename InputMeshType::PointDataContainerIterator pointDataEnd = this->m_Input->GetPointData()->End(); while( pointDataIterator != pointDataEnd ) { outputFile << pointDataIterator.Value() << " "; pointDataIterator++; } outputFile << std::endl; } else { MultiComponentScalarType scalar = this->m_MultiComponentScalars->GetElement( 0 ); unsigned int numberOfComponents = scalar.GetSize(); outputFile << "SCALARS scalars " << type << numberOfComponents << std::endl; outputFile << "LOOKUP_TABLE default" << std::endl; typename MultiComponentScalarSetType::Iterator It = this->m_MultiComponentScalars->Begin(); typename MultiComponentScalarSetType::Iterator ItEnd = this->m_MultiComponentScalars->End(); while( It != ItEnd ) { outputFile << It.Value() << " "; ++It; } outputFile << std::endl; } outputFile.close(); } template void LabeledPointSetFileWriter ::WriteLinesToVTKFile() { if( this->m_Lines ) { std::ofstream outputFile( this->m_FileName.c_str(), std::ios::app ); unsigned int numberOfLines = this->m_Lines->Size(); unsigned int totalSize = 0; typename LineSetType::Iterator It = this->m_Lines->Begin(); typename LineSetType::Iterator ItEnd = this->m_Lines->End(); while( It != ItEnd ) { totalSize += ( It.Value() ).Size(); totalSize++; ++It; } outputFile << "LINES " << numberOfLines << " " << totalSize << std::endl; It = this->m_Lines->Begin(); while( It != ItEnd ) { unsigned int numberOfPoints = ( It.Value() ).Size(); outputFile << numberOfPoints << " "; for( unsigned int d = 0; d < numberOfPoints; d++ ) { outputFile << ( It.Value() )[d] << " "; } outputFile << std::endl; ++It; } outputFile << std::endl; outputFile.close(); } } template void LabeledPointSetFileWriter ::WritePointsToAvantsFile() { // // Write to output file // std::ofstream outputFile( this->m_FileName.c_str() ); outputFile << "0 0 0 0" << std::endl; if( this->m_Input->GetNumberOfPoints() > 0 ) { typename InputMeshType::PointsContainerIterator pointIterator = this->m_Input->GetPoints()->Begin(); typename InputMeshType::PointsContainerIterator pointEnd = this->m_Input->GetPoints()->End(); typename InputMeshType::PointDataContainerIterator pointDataIterator = this->m_Input->GetPointData()->Begin(); while( pointIterator != pointEnd ) { PointType point = pointIterator.Value(); outputFile << point[0] << " " << point[1]; if( Dimension == 2 ) { outputFile << " 0 "; } else if( Dimension == 3 ) { outputFile << " " << point[2] << " "; } outputFile << pointDataIterator.Value() << std::endl; pointIterator++; pointDataIterator++; } } outputFile << "0 0 0 0" << std::endl; outputFile.close(); } template void LabeledPointSetFileWriter ::WritePointsToImageFile() { typename LabeledPointSetImageType::Pointer outputImage = LabeledPointSetImageType::New(); outputImage->SetDirection( this->m_ImageDirection ); outputImage->SetRegions( this->m_ImageSize ); outputImage->SetOrigin( this->m_ImageOrigin ); outputImage->SetSpacing( this->m_ImageSpacing ); outputImage->Allocate(); outputImage->FillBuffer( NumericTraits::ZeroValue() ); if( this->m_Input->GetNumberOfPoints() > 0 ) { typename InputMeshType::PointsContainerIterator pointIterator = this->m_Input->GetPoints()->Begin(); typename InputMeshType::PointsContainerIterator pointEnd = this->m_Input->GetPoints()->End(); typename InputMeshType::PointDataContainerIterator pointDataIterator = this->m_Input->GetPointData()->Begin(); while( pointIterator != pointEnd ) { PointType point = pointIterator.Value(); PixelType label = pointDataIterator.Value(); typename LabeledPointSetImageType::IndexType index; typename LabeledPointSetImageType::PointType ipoint; ipoint.CastFrom( point ); if( outputImage->TransformPhysicalPointToIndex( ipoint, index ) ) { outputImage->SetPixel( index, label ); } pointIterator++; pointDataIterator++; } } typedef ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName( this->m_FileName.c_str() ); writer->SetInput( outputImage ); writer->Update(); } template void LabeledPointSetFileWriter ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf(os, indent); os << indent << "FileName: " << this->m_FileName << std::endl; } } // end of namespace itk #endif ants-2.2.0/Utilities/itkManifoldParzenWindowsPointSetFunction.h000066400000000000000000000133221311104306400247660ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkManifoldParzenWindowsPointSetFunction_h #define __itkManifoldParzenWindowsPointSetFunction_h #include "itkPointSetFunction.h" #include "itkGaussianMembershipFunction.h" #include "itkKdTreeGenerator.h" #include "itkListSample.h" #include "itkMatrix.h" #include "itkMersenneTwisterRandomVariateGenerator.h" #include "itkMeshSource.h" #include "itkPointSet.h" #include "itkVector.h" #include "itkWeightedCentroidKdTreeGenerator.h" #include namespace itk { /** \class ManifoldParzenWindowsPointSetFunction.h * \brief point set filter. */ template class ManifoldParzenWindowsPointSetFunction : public PointSetFunction { public: typedef ManifoldParzenWindowsPointSetFunction Self; typedef PointSetFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Extract dimension from output image. */ itkStaticConstMacro( Dimension, unsigned int, TPointSet::PointDimension ); typedef typename Superclass::InputPointSetType InputPointSetType; typedef typename Superclass::InputPointType InputPointType; /** Point set typedef support. */ typedef TPointSet PointSetType; typedef typename PointSetType::PointType PointType; typedef typename PointSetType ::PointsContainerConstIterator PointsContainerConstIterator; typedef Vector MeasurementVectorType; typedef typename Statistics::ListSample SampleType; typedef typename Statistics ::WeightedCentroidKdTreeGenerator TreeGeneratorType; typedef typename TreeGeneratorType::KdTreeType KdTreeType; typedef typename KdTreeType ::InstanceIdentifierVectorType NeighborhoodIdentifierType; /** Other typedef */ typedef TOutput RealType; typedef TOutput OutputType; typedef typename Statistics ::MersenneTwisterRandomVariateGenerator RandomizerType; typedef typename Statistics:: GaussianMembershipFunction GaussianType; typedef std::vector GaussianContainerType; typedef typename GaussianType::MatrixType CovarianceMatrixType; /** Helper functions */ itkSetMacro( CovarianceKNeighborhood, unsigned int ); itkGetConstMacro( CovarianceKNeighborhood, unsigned int ); itkSetMacro( EvaluationKNeighborhood, unsigned int ); itkGetConstMacro( EvaluationKNeighborhood, unsigned int ); itkSetMacro( RegularizationSigma, RealType ); itkGetConstMacro( RegularizationSigma, RealType ); itkSetMacro( KernelSigma, RealType ); itkGetConstMacro( KernelSigma, RealType ); itkSetMacro( BucketSize, unsigned int ); itkGetConstMacro( BucketSize, unsigned int ); itkSetMacro( Normalize, bool ); itkGetConstMacro( Normalize, bool ); itkBooleanMacro( Normalize ); itkSetMacro( UseAnisotropicCovariances, bool ); itkGetConstMacro( UseAnisotropicCovariances, bool ); itkBooleanMacro( UseAnisotropicCovariances ); virtual void SetInputPointSet( const InputPointSetType * ptr ); virtual TOutput Evaluate( const InputPointType& point ) const; PointType GenerateRandomSample(); typename GaussianType::Pointer GetGaussian( unsigned int i ) { if( i < this->m_Gaussians.size() ) { return this->m_Gaussians[i].GetPointer(); } else { return NULL; } this->Modified(); } void SetGaussian( unsigned int i, typename GaussianType::Pointer gaussian ) { if( i >= this->m_Gaussians.size() ) { this->m_Gaussians.resize( i + 1 ); } this->m_Gaussians[i] = gaussian; this->Modified(); } void GenerateKdTree(); NeighborhoodIdentifierType GetNeighborhoodIdentifiers( MeasurementVectorType, unsigned int ); NeighborhoodIdentifierType GetNeighborhoodIdentifiers( InputPointType, unsigned int ); protected: ManifoldParzenWindowsPointSetFunction(); virtual ~ManifoldParzenWindowsPointSetFunction(); void PrintSelf( std::ostream& os, Indent indent ) const; void GenerateData(); private: // purposely not implemented ManifoldParzenWindowsPointSetFunction( const Self & ); void operator=( const Self & ); unsigned int m_CovarianceKNeighborhood; unsigned int m_EvaluationKNeighborhood; unsigned int m_BucketSize; RealType m_RegularizationSigma; RealType m_KernelSigma; typename TreeGeneratorType::Pointer m_KdTreeGenerator; typename SampleType::Pointer m_SamplePoints; typename RandomizerType::Pointer m_Randomizer; GaussianContainerType m_Gaussians; bool m_Normalize; bool m_UseAnisotropicCovariances; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkManifoldParzenWindowsPointSetFunction.hxx" #endif #endif ants-2.2.0/Utilities/itkManifoldParzenWindowsPointSetFunction.hxx000066400000000000000000000256571311104306400253640ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkManifoldParzenWindowsPointSetFunction_hxx #define __itkManifoldParzenWindowsPointSetFunction_hxx #include "itkManifoldParzenWindowsPointSetFunction.h" #include "vnl/vnl_vector.h" #include "vnl/vnl_math.h" namespace itk { template ManifoldParzenWindowsPointSetFunction ::ManifoldParzenWindowsPointSetFunction() { this->m_BucketSize = 4; this->m_CovarianceKNeighborhood = 5; this->m_EvaluationKNeighborhood = 50; this->m_SamplePoints = NULL; this->m_KdTreeGenerator = NULL; this->m_RegularizationSigma = 1.0; this->m_KernelSigma = 1.0; this->m_Normalize = true; this->m_UseAnisotropicCovariances = true; this->m_Randomizer = RandomizerType::New(); this->m_Randomizer->SetSeed(); } template ManifoldParzenWindowsPointSetFunction ::~ManifoldParzenWindowsPointSetFunction() { } template void ManifoldParzenWindowsPointSetFunction ::SetInputPointSet( const InputPointSetType * ptr ) { this->m_PointSet = ptr; /** * Generate KdTree and create set of gaussians from input point set */ this->m_SamplePoints = SampleType::New(); this->m_SamplePoints->SetMeasurementVectorSize( Dimension ); std::vector inputGaussians; inputGaussians.resize( this->GetInputPointSet()->GetNumberOfPoints() ); this->m_Gaussians.resize( this->GetInputPointSet()->GetNumberOfPoints() ); MeasurementVectorType mv; unsigned long count = 0; PointsContainerConstIterator It = this->GetInputPointSet()->GetPoints()->Begin(); while( It != this->GetInputPointSet()->GetPoints()->End() ) { PointType point = It.Value(); typename GaussianType::MeanType mean( Dimension ); for( unsigned int d = 0; d < Dimension; d++ ) { mv[d] = point[d]; mean[d] = point[d]; } this->m_SamplePoints->PushBack( mv ); inputGaussians[count] = GaussianType::New(); inputGaussians[count]->SetGenerateRandomSamples( false ); inputGaussians[count]->SetSigma( this->m_KernelSigma ); inputGaussians[count]->SetMean( mean ); count++; ++It; } this->m_KdTreeGenerator = TreeGeneratorType::New(); m_KdTreeGenerator->SetSample( this->m_SamplePoints ); m_KdTreeGenerator->SetBucketSize( this->m_BucketSize ); m_KdTreeGenerator->Update(); /** * Calculate covariance matrices */ It = this->GetInputPointSet()->GetPoints()->Begin(); while( It != this->GetInputPointSet()->GetPoints()->End() ) { PointType point = It.Value(); unsigned long index = It.Index(); this->m_Gaussians[index] = GaussianType::New(); this->m_Gaussians[index]->SetGenerateRandomSamples( true ); this->m_Gaussians[index]->SetMean( inputGaussians[index]->GetMean() ); if( this->m_CovarianceKNeighborhood > 0 && this->m_UseAnisotropicCovariances ) { CovarianceMatrixType Cout( Dimension, Dimension ); Cout.Fill( 0 ); MeasurementVectorType queryPoint; for( unsigned int d = 0; d < Dimension; d++ ) { queryPoint[d] = point[d]; } typename TreeGeneratorType::KdTreeType ::InstanceIdentifierVectorType neighbors; this->m_KdTreeGenerator->GetOutput()->Search( queryPoint, this->m_CovarianceKNeighborhood, neighbors ); RealType denominator = 0.0; for( unsigned int j = 0; j < this->m_CovarianceKNeighborhood; j++ ) { if( neighbors[j] != index && neighbors[j] < this->GetInputPointSet()->GetNumberOfPoints() ) { MeasurementVectorType neighbor = this->m_KdTreeGenerator->GetOutput()->GetMeasurementVector( neighbors[j] ); RealType kernelValue = inputGaussians[index]->Evaluate( neighbor ); denominator += kernelValue; if( kernelValue > 0.0 ) { for( unsigned int m = 0; m < Dimension; m++ ) { for( unsigned int n = m; n < Dimension; n++ ) { RealType covariance = kernelValue * ( neighbor[m] - queryPoint[m] ) * ( neighbor[n] - queryPoint[n] ); Cout( m, n ) += covariance; Cout( n, m ) += covariance; } } } } } if( this->m_Normalize && denominator > 0.0 ) { Cout /= denominator; } else { Cout /= static_cast( this->m_CovarianceKNeighborhood ); } for( unsigned int m = 0; m < Dimension; m++ ) { Cout( m, m ) += ( this->m_RegularizationSigma * this->m_RegularizationSigma ); } this->m_Gaussians[index]->SetCovariance( Cout ); } else { this->m_Gaussians[index]->SetSigma( this->m_RegularizationSigma ); } ++It; } } template void ManifoldParzenWindowsPointSetFunction ::GenerateKdTree() { /** * Generate KdTree and create set of gaussians from input point set */ this->m_SamplePoints = SampleType::New(); this->m_SamplePoints->SetMeasurementVectorSize( Dimension ); MeasurementVectorType mv; typename GaussianContainerType::const_iterator it; for( it = this->m_Gaussians.begin(); it != this->m_Gaussians.end(); ++it ) { typename GaussianType::MeanType mean = (*it)->GetMean(); for( unsigned int d = 0; d < Dimension; d++ ) { mv[d] = mean[d]; } this->m_SamplePoints->PushBack( mv ); } this->m_KdTreeGenerator = TreeGeneratorType::New(); this->m_KdTreeGenerator->SetSample( this->m_SamplePoints ); this->m_KdTreeGenerator->SetBucketSize( this->m_BucketSize ); this->m_KdTreeGenerator->Update(); } template TOutput ManifoldParzenWindowsPointSetFunction ::Evaluate( const InputPointType & point ) const { if( !this->m_KdTreeGenerator ) { MeasurementVectorType measurement; for( unsigned int d = 0; d < Dimension; d++ ) { measurement[d] = point[d]; } OutputType sum = 0.0; typename GaussianContainerType::const_iterator it; for( it = this->m_Gaussians.begin(); it != this->m_Gaussians.end(); ++it ) { sum += static_cast( (*it)->Evaluate( measurement ) ); } return static_cast( sum / static_cast( this->m_Gaussians.size() ) ); } else { MeasurementVectorType queryPoint; for( unsigned int d = 0; d < Dimension; d++ ) { queryPoint[d] = point[d]; } unsigned int numberOfNeighbors = vnl_math_min( this->m_EvaluationKNeighborhood, static_cast( this->m_Gaussians.size() ) ); OutputType sum = 0.0; if( numberOfNeighbors == this->m_Gaussians.size() ) { for( unsigned int j = 0; j < this->m_Gaussians.size(); j++ ) { sum += static_cast( this->m_Gaussians[j]->Evaluate( queryPoint ) ); } } else { typename TreeGeneratorType::KdTreeType ::InstanceIdentifierVectorType neighbors; this->m_KdTreeGenerator->GetOutput()->Search( queryPoint, static_cast( numberOfNeighbors ), neighbors ); for( unsigned int j = 0; j < numberOfNeighbors; j++ ) { sum += static_cast( this->m_Gaussians[neighbors[j]]->Evaluate( queryPoint ) ); } } return static_cast( sum / static_cast( this->m_Gaussians.size() ) ); } } template typename ManifoldParzenWindowsPointSetFunction ::NeighborhoodIdentifierType ManifoldParzenWindowsPointSetFunction ::GetNeighborhoodIdentifiers( MeasurementVectorType point, unsigned int numberOfNeighbors ) { if( numberOfNeighbors > this->m_KdTreeGenerator->GetOutput()->Size() ) { numberOfNeighbors = this->m_KdTreeGenerator->GetOutput()->Size(); } NeighborhoodIdentifierType neighbors; this->m_KdTreeGenerator->GetOutput()->Search( point, numberOfNeighbors, neighbors ); return neighbors; } template typename ManifoldParzenWindowsPointSetFunction ::NeighborhoodIdentifierType ManifoldParzenWindowsPointSetFunction ::GetNeighborhoodIdentifiers( InputPointType point, unsigned int numberOfNeighbors ) { MeasurementVectorType queryPoint( Dimension ); for( unsigned int d = 0; d < Dimension; d++ ) { queryPoint[d] = point[d]; } return this->GetNeighborhoodIdentifiers( queryPoint, numberOfNeighbors ); } template typename ManifoldParzenWindowsPointSetFunction ::PointType ManifoldParzenWindowsPointSetFunction ::GenerateRandomSample() { typename GaussianType::MeasurementVectorType gaussianSample; gaussianSample = this->m_Gaussians[this->m_Randomizer->GetIntegerVariate( this->GetInputPointSet()->GetNumberOfPoints() - 1 ) ]->GenerateRandomSample(); PointType sample; for( unsigned int d = 0; d < Dimension; d++ ) { sample[d] = gaussianSample[d]; } return sample; } /** * Standard "PrintSelf" method */ template void ManifoldParzenWindowsPointSetFunction ::PrintSelf( std::ostream& os, Indent indent) const { os << indent << "Covariance: " << this->m_CovarianceKNeighborhood << std::endl; os << indent << "Evaluation: " << this->m_EvaluationKNeighborhood << std::endl; os << indent << "Regularization sigma: " << this->m_RegularizationSigma << std::endl; os << indent << "Kernel sigma: " << this->m_KernelSigma << std::endl; os << indent << "Bucket size: " << this->m_BucketSize << std::endl; os << indent << "Normalize: " << this->m_Normalize << std::endl; os << indent << "Use anisotropic covariances: " << this->m_UseAnisotropicCovariances << std::endl; } } // end namespace itk #endif ants-2.2.0/Utilities/itkMultiScaleLaplacianBlobDetectorImageFilter.h000066400000000000000000000206751311104306400255700ustar00rootroot00000000000000// =========================================================================== // // PUBLIC DOMAIN NOTICE // Office of High Performance Computing and Communications // // This software/database is a "United States Government Work" under the // terms of the United States Copyright Act. It was written as part of // the author's official duties as a United States Government employee and // thus cannot be copyrighted. This software is freely available // to the public for use. The National Library of Medicine and the U.S. // Government have not placed any restriction on its use or reproduction. // // Although all reasonable efforts have been taken to ensure the accuracy // and reliability of the software and data, the NLM and the U.S. // Government do not and cannot warrant the performance or results that // may be obtained by using this software or data. The NLM and the U.S. // Government disclaim all warranties, express or implied, including // warranties of performance, merchantability or fitness for any particular // purpose. // // Please cite the author in any work or product based on this material. // // =========================================================================== // #ifndef __itkMultiScaleLaplacianBlobDetectorImageFilter_h #define __itkMultiScaleLaplacianBlobDetectorImageFilter_h #include "itkImageToImageFilter.h" #include "itkGaussianSpatialObject.h" namespace itk { /** \class ScaleSpaceBlobSpatialObject * \brief a spatial object to represent a gaussian blob with size * * The superclass parameter "Sigma" is the size of the blob if it was * a gaussian. * \ingroup ITKImageScaleSpace **/ template class ScaleSpaceBlobSpatialObject : public GaussianSpatialObject { public: typedef ScaleSpaceBlobSpatialObject Self; typedef double ScalarType; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef GaussianSpatialObject Superclass; typedef SmartPointer SuperclassPointer; typedef typename Superclass::PointType PointType; typedef float RealPixelType; typedef Image RealImageType; typedef typename RealImageType::IndexType CenterType; itkStaticConstMacro(NumberOfDimensions, unsigned int, TDimension); itkNewMacro(Self); itkTypeMacro(ScaleSpaceBlobSpatialObject, GaussianSpatialObject); /** Set/Get the normalized laplacian value of the extrema */ itkGetMacro( ScaleSpaceValue, double ); itkSetMacro( ScaleSpaceValue, double ); /** The radius of the object if it is a solid hyper-sphere */ double GetObjectRadius( void ) const { return this->GetSigma() * vnl_math::sqrt2; } /** The sigma of the laplacian where the extrema occoured */ double GetScaleSpaceSigma( void ) const { return this->GetSigma() / ( std::sqrt( TDimension / 2.0 ) ); } /** The location where the extrema occoured */ itkGetMacro( Center, CenterType ); itkSetMacro( Center, CenterType ); private: double m_ScaleSpaceValue; double m_ObjectRadius; CenterType m_Center; }; /** \class MultiScaleLaplacianBlobDetectorImageFilter * \brief Performs detection of "blob" by searching for local extrema * of the Laplacian of the Gassian (LoG) in pixel-space and * scale-space. * * Define a blob. * * Explain notation of scale space used * * * * \sa LaplacianRecursiveGaussianImageFilter * * \ingroup ITKImageScaleSpace * * \author Bradley Lowekamp */ template class MultiScaleLaplacianBlobDetectorImageFilter : public ImageToImageFilter { public: // todo figure out a better base class /** Standard class typedefs. */ typedef MultiScaleLaplacianBlobDetectorImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** label image typedef */ typedef float BlobRadiusType; typedef itk::Image BlobRadiusImageType; typedef typename BlobRadiusImageType::Pointer BlobRadiusImagePointer; /** Blob typedef */ typedef ScaleSpaceBlobSpatialObject BlobType; typedef typename BlobType::Pointer BlobPointer; typedef std::vector BlobsListType; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(MultiScaleLaplacianBlobDetectorImageFilter, ImageToImageFilter); /** Typedef to images */ typedef TInputImage InputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename InputImageType::ConstPointer InputImageConstPointer; typedef typename Superclass::OutputImageRegionType OutputImageRegionType; /** Set/Get the starting value to search scale-space. * * T is equivalent to varinace or sigma squared. **/ itkSetMacro( StartT, double ); itkGetMacro( StartT, double ); /** Set/Get the ending value to search scale-space. */ itkSetMacro( EndT, double ); itkGetMacro( EndT, double ); /** Set/Get the number of steps per doubling of sigma sampled. */ itkSetMacro( StepsPerOctave, double ); itkGetMacro( StepsPerOctave, double ); /** Set/Get the number of blobs to find */ itkSetMacro( NumberOfBlobs, size_t ); itkGetMacro( NumberOfBlobs, size_t ); /** Set/Get the label image */ itkSetMacro( BlobRadiusImage, BlobRadiusImagePointer ); itkGetMacro( BlobRadiusImage, BlobRadiusImagePointer ); /** Pseudo-output * Get the list of circles. This recomputes the circles */ BlobsListType & GetBlobs( void ) { return this->m_BlobList; } #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ // / \todo need to concepts checked /** End concept checking */ #endif protected: /** Internal Real ImageType */ typedef float RealPixelType; typedef Image RealImageType; typedef typename RealImageType::Pointer RealImagePointer; typedef typename RealImageType::ConstPointer RealImageConstPointer; MultiScaleLaplacianBlobDetectorImageFilter(); // not defined or implemented as default works // virtual ~MultiScaleLaplacianBlobDetectorImageFilter( void ) {} void GenerateData() ITK_OVERRIDE; void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId ) ITK_OVERRIDE; private: MultiScaleLaplacianBlobDetectorImageFilter( const Self &); // purposely not implemented void operator=( const Self &); // purposely not implemented class Blob { public: Blob( void ) { } Blob( typename RealImageType::IndexType center, double sigma, RealPixelType value ) : m_Center( center ), m_Sigma( sigma ), m_Value( value ) { } Blob( const Blob & b ) : m_Center( b.m_Center ), m_Sigma( b.m_Sigma ), m_Value( b.m_Value ) { } Blob & operator=( const Blob & b ) { this->m_Center = b.m_Center; this->m_Sigma = b.m_Sigma; this->m_Value = b.m_Value; return *this; } typename RealImageType::IndexType m_Center; double m_Sigma; RealPixelType m_Value; }; static bool BlobValueGreaterCompare(Blob & a, Blob & b) { return a.m_Value > b.m_Value; } static bool BlobValueLesserCompare(Blob & a, Blob & b) { return a.m_Value < b.m_Value; } typedef std::vector BlobHeapType; // private member variable go here RealImageConstPointer m_LaplacianImage[3]; std::vector m_BlobHeapPerThread; RealPixelType m_GlobalMinimalBestBlobValue; double m_CurrentSigma; float m_CurrentProgress; // private IVAR size_t m_NumberOfBlobs; BlobsListType m_BlobList; unsigned int m_StepsPerOctave; double m_StartT; double m_EndT; BlobRadiusImagePointer m_BlobRadiusImage; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkMultiScaleLaplacianBlobDetectorImageFilter.hxx" #endif #endif // __itkMultiScaleLaplacianBlobDetectorImageFilter_h ants-2.2.0/Utilities/itkMultiScaleLaplacianBlobDetectorImageFilter.hxx000066400000000000000000000267331311104306400261510ustar00rootroot00000000000000// =========================================================================== // // PUBLIC DOMAIN NOTICE // Office of High Performance Computing and Communications // // This software/database is a "United States Government Work" under the // terms of the United States Copyright Act. It was written as part of // the author's official duties as a United States Government employee and // thus cannot be copyrighted. This software is freely available // to the public for use. The National Library of Medicine and the U.S. // Government have not placed any restriction on its use or reproduction. // // Although all reasonable efforts have been taken to ensure the accuracy // and reliability of the software and data, the NLM and the U.S. // Government do not and cannot warrant the performance or results that // may be obtained by using this software or data. The NLM and the U.S. // Government disclaim all warranties, express or implied, including // warranties of performance, merchantability or fitness for any particular // purpose. // // Please cite the author in any work or product based on this material. // // =========================================================================== // #ifndef __itkMultiScaleLaplacianBlobDetectorImageFilter_hxx #define __itkMultiScaleLaplacianBlobDetectorImageFilter_hxx #include "itkMultiScaleLaplacianBlobDetectorImageFilter.h" #include "itkEllipseSpatialObject.h" #include "itkCastImageFilter.h" #include "itkLaplacianRecursiveGaussianImageFilter.h" #include "itkNeighborhoodAlgorithm.h" #include "itkProgressAccumulator.h" #include namespace itk { template MultiScaleLaplacianBlobDetectorImageFilter ::MultiScaleLaplacianBlobDetectorImageFilter( void ) { m_NumberOfBlobs = 1000; m_StepsPerOctave = 15; m_StartT = 8; m_EndT = 128; } template void MultiScaleLaplacianBlobDetectorImageFilter ::GenerateData( void ) { itkDebugMacro(<< " generating data "); const InputImageConstPointer inputImage( this->GetInput() ); const InputImagePointer outputImage( this->GetOutput() ); this->m_BlobRadiusImage = BlobRadiusImageType::New(); this->m_BlobRadiusImage->CopyInformation( inputImage ); this->m_BlobRadiusImage->SetRegions( inputImage->GetRequestedRegion() ); this->m_BlobRadiusImage->Allocate(); this->m_BlobRadiusImage->FillBuffer( 0 ); typedef itk::CastImageFilter CasterFilterType; typename CasterFilterType::Pointer caster = CasterFilterType::New(); caster->SetNumberOfThreads( this->GetNumberOfThreads() ); caster->InPlaceOff(); caster->SetInput( inputImage ); caster->GraftOutput( outputImage ); caster->Update(); this->GraftOutput( caster->GetOutput() ); // Create a process accumulator for tracking the progress of minipipeline ProgressAccumulator::Pointer progress = ProgressAccumulator::New(); progress->SetMiniPipelineFilter(this); // prepare one time threaded data this->m_BlobHeapPerThread.resize( this->GetNumberOfThreads() ); // we wish to add an additional laplacian before and after the user // defined range to check for maximums const double k = std::pow( 2.0, 1.0 / m_StepsPerOctave ); const double initial_sigma = std::sqrt(m_StartT) * 1.0 / k; const unsigned int numberOfScales = std::ceil( std::log( std::sqrt( m_EndT ) / initial_sigma ) / std::log( k ) ) + 1.0; typedef itk::LaplacianRecursiveGaussianImageFilter LaplacianFilterType; typename LaplacianFilterType::Pointer laplacianFilter[3]; for( unsigned int i = 0; i < 3; ++i ) { laplacianFilter[i] = LaplacianFilterType::New(); laplacianFilter[i]->SetNumberOfThreads( this->GetNumberOfThreads() ); laplacianFilter[i]->SetInput( inputImage ); laplacianFilter[i]->SetNormalizeAcrossScale( true ); progress->RegisterInternalFilter( laplacianFilter[i], 1.0 / numberOfScales ); } BlobHeapType blobs; m_GlobalMinimalBestBlobValue = itk::NumericTraits::NonpositiveMin(); for( unsigned int i = 0; i < numberOfScales; ++i ) { // simga' = k^i * initial_sigma // t = sigma^2 const double sigma = initial_sigma * std::pow( k, double( numberOfScales - i - 1 ) ); // const double t = vnl_math_sqr( initial_sigma * std::pow( k, double( numberOfScales - i - 1 ) ) ); itkDebugMacro( << "i: " << i << " sigma: " << sigma << " k: " << k ); // update largest index with new largest sigma laplacianFilter[2]->SetSigma( sigma ); laplacianFilter[2]->Update(); // wait until all three laplacian filters have run if( i > 1 ) { // prepare for threaded execution this->m_LaplacianImage[0] = laplacianFilter[0]->GetOutput(); this->m_LaplacianImage[1] = laplacianFilter[1]->GetOutput(); this->m_LaplacianImage[2] = laplacianFilter[2]->GetOutput(); this->m_CurrentProgress = this->GetProgress(); // Set up the multithreaded processing of the outputs typename Superclass::ThreadStruct str; str.Filter = this; this->GetMultiThreader()->SetNumberOfThreads( this->GetNumberOfThreads() ); this->GetMultiThreader()->SetSingleMethod( Self::ThreaderCallback, &str ); // multithread the execution this->GetMultiThreader()->SingleMethodExecute(); // combine all the maximally sorted lists into a maximally // sorted one for( unsigned int tt = 0; tt < m_BlobHeapPerThread.size(); ++tt ) { // after threaded execution the heap will be sorted BlobHeapType & threadBlobs = m_BlobHeapPerThread[tt]; if( threadBlobs.empty() ) { continue; } BlobHeapType temp; std::swap(temp, blobs); blobs.resize( threadBlobs.size() + temp.size() ); std::merge( temp.begin(), temp.end(), threadBlobs.begin(), threadBlobs.end(), blobs.begin(), BlobValueGreaterCompare ); if( blobs.size() > m_NumberOfBlobs ) { blobs.resize( m_NumberOfBlobs ); } threadBlobs.clear(); } // set the minimal value if( !blobs.empty() ) { m_GlobalMinimalBestBlobValue = blobs.back().m_Value; } } // circularly rotate laplacian filters down in scale std::swap( laplacianFilter[2], laplacianFilter[1] ); std::swap( laplacianFilter[2], laplacianFilter[0] ); // Current Sigma refers to the center filter, hence next it'll be // the center m_CurrentSigma = sigma; // after each pass reset progress to accumulate next iteration progress->ResetFilterProgressAndKeepAccumulatedProgress(); } // clean up member variables this->m_LaplacianImage[0] = ITK_NULLPTR; this->m_LaplacianImage[1] = ITK_NULLPTR; this->m_LaplacianImage[2] = ITK_NULLPTR; this->m_BlobHeapPerThread.clear(); m_BlobList.clear(); // typename InputImageType::SpacingType spacing = inputImage->GetSpacing(); typename BlobType::PointType zeroPoint; zeroPoint.Fill(0); // Convert to SpatialObject blob for( typename BlobHeapType::const_iterator i = blobs.begin(); i != blobs.end(); ++i ) { const double sigma = std::sqrt( InputImageType::ImageDimension / 2.0 ) * i->m_Sigma; // transform the center index into offset vector typename BlobType::PointType centerPoint; inputImage->TransformIndexToPhysicalPoint( i->m_Center, centerPoint ); BlobPointer blob = BlobType::New(); blob->SetSigma( sigma ); blob->SetScaleSpaceValue( i->m_Value ); blob->SetCenter( i->m_Center ); this->m_BlobRadiusImage->SetPixel( i->m_Center, ( int ) ( 0.5 + i->m_Sigma ) ); const typename BlobType::VectorType centerVector = centerPoint - zeroPoint; blob->GetObjectToParentTransform()->SetOffset(centerVector); blob->ComputeBoundingBox(); m_BlobList.push_back( blob ); } } template void MultiScaleLaplacianBlobDetectorImageFilter ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId ) { // unsigned int m_Number = 20; // const unsigned int numberOfScales = m_Number+2; // const unsigned int numberOfFilters = numberOfScales + m_Number; // ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels(), 100, m_CurrentProgress, // 1.0/numberOfFilters); BlobHeapType & blobHeap = this->m_BlobHeapPerThread[threadId]; blobHeap.reserve( m_NumberOfBlobs ); RealPixelType localMinimalBestBlobValue = m_GlobalMinimalBestBlobValue; // center laplacian image typename RealImageType::ConstPointer laplacianImage = this->m_LaplacianImage[1]; InputImagePointer outputImage( this->GetOutput() ); typedef itk::ConstNeighborhoodIterator NeighborhoodIteratorType; typename NeighborhoodIteratorType::RadiusType radius; radius.Fill(1); typedef itk::NeighborhoodAlgorithm::ImageBoundaryFacesCalculator FaceCalculatorType; FaceCalculatorType faceCalculator; typename FaceCalculatorType::FaceListType faceList; typename FaceCalculatorType::FaceListType::iterator fit; faceList = faceCalculator( laplacianImage, outputRegionForThread, radius ); Point zeroPoint; zeroPoint.Fill(0); for( fit = faceList.begin(); fit != faceList.end(); ++fit ) { NeighborhoodIteratorType n0( radius, this->m_LaplacianImage[0], *fit ); NeighborhoodIteratorType n1( radius, this->m_LaplacianImage[1], *fit ); NeighborhoodIteratorType n2( radius, this->m_LaplacianImage[2], *fit ); const size_t neighborhoodSize = n0.Size(); while( !n0.IsAtEnd() && !n1.IsAtEnd() && !n2.IsAtEnd() ) { const RealPixelType center = n1.GetCenterPixel(); bool isUsable = true; if( center < localMinimalBestBlobValue ) { isUsable = false; } else { for( size_t h = 0; h < neighborhoodSize && isUsable; ++h ) { if( center < n0.GetPixel( h ) ) { isUsable = false; } } for( size_t h = 0; h < neighborhoodSize && isUsable; ++h ) { if( center < n1.GetPixel( h ) ) { isUsable = false; } } for( size_t h = 0; h < neighborhoodSize && isUsable; ++h ) { if( center < n2.GetPixel( h ) ) { isUsable = false; } } } if( isUsable ) { // add blob to thread list Blob blob( n1.GetIndex(), m_CurrentSigma, center ); // maintain a minimum heap ( first element is less than all // others) no greater then the target number of blobs if( blobHeap.size() < m_NumberOfBlobs ) { blobHeap.push_back( blob ); std::push_heap( blobHeap.begin(), blobHeap.end(), BlobValueGreaterCompare ); } else if( blob.m_Value > localMinimalBestBlobValue ) { std::pop_heap( blobHeap.begin(), blobHeap.end(), BlobValueGreaterCompare ); blobHeap.back() = blob; std::push_heap( blobHeap.begin(), blobHeap.end(), BlobValueGreaterCompare ); localMinimalBestBlobValue = blobHeap.front().m_Value; } } ++n0, ++n1, ++n2; // progress.CompletedPixel(); } } // end face list // sort the heap, the first element will now be maximal std::sort_heap( blobHeap.begin(), blobHeap.end(), BlobValueGreaterCompare ); } } #endif // __itkMultiScaleLaplacianBlobDetectorImageFilter_hxx ants-2.2.0/Utilities/itkMultiplyByConstantVectorImageFilter.h000066400000000000000000000100401311104306400244200ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkMultiplyByConstantVectorImageFilter_h #define __itkMultiplyByConstantVectorImageFilter_h #include "itkUnaryFunctorImageFilter.h" namespace itk { /** \class MultiplyByConstantVectorImageFilter * * \brief Multiply input pixels by a constant. * * This filter is templated over the input image type * and the output image type. * * \ingroup IntensityImageFilters Multithreaded * \sa UnaryFunctorImageFilter */ namespace Functor { template class MultiplyByConstantVector { public: MultiplyByConstantVector() { } ~MultiplyByConstantVector() { } bool operator!=(const MultiplyByConstantVector & other) const { return !( *this == other ); } bool operator==(const MultiplyByConstantVector & other) const { return other.m_ConstantVector == m_ConstantVector; } inline TOutput operator()(const TInput & A) const { // Because the user has to specify the constant we don't // check if the cte is not 0; TConstantVector value; for( unsigned int i = 0; i < m_ConstantVector.GetVectorDimension(); i++ ) { value[i] = A[i] * m_ConstantVector[i]; } return value; } void SetConstantVector(TConstantVector ct) { this->m_ConstantVector = ct; } const TConstantVector & GetConstantVector() const { return m_ConstantVector; } TConstantVector m_ConstantVector; }; } template class MultiplyByConstantVectorImageFilter : public UnaryFunctorImageFilter > { public: /** Standard class typedefs. */ typedef MultiplyByConstantVectorImageFilter Self; typedef UnaryFunctorImageFilter< TInputImage, TOutputImage, Functor::MultiplyByConstantVector< typename TInputImage::PixelType, TConstantVector, typename TOutputImage::PixelType> > Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods). */ itkTypeMacro( MultiplyByConstantVectorImageFilter, UnaryFunctorImageFilter ); /** Set the constant that will be used to multiply all the image pixels */ void SetConstantVector( TConstantVector ct ) { if( ct != this->GetFunctor().GetConstantVector() ) { this->GetFunctor().SetConstantVector( ct ); this->Modified(); } } const TConstantVector & GetConstantVector() const { return this->GetFunctor().GetConstantVector(); } protected: MultiplyByConstantVectorImageFilter() { } virtual ~MultiplyByConstantVectorImageFilter() { } void PrintSelf(std::ostream & os, Indent indent ) const { Superclass::PrintSelf( os, indent ); os << indent << "Constant: " << this->GetConstantVector() << std::endl; } private: MultiplyByConstantVectorImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #endif ants-2.2.0/Utilities/itkN3MRIBiasFieldCorrectionImageFilter.h000066400000000000000000000272431311104306400240510ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkN3MRIBiasFieldCorrectionImageFilter_h #define __itkN3MRIBiasFieldCorrectionImageFilter_h #include "itkImageToImageFilter.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkPointSet.h" #include "itkSingleValuedCostFunction.h" #include "itkVector.h" #include "vnl/vnl_vector.h" namespace itk { /** \class N3MRIBiasFieldCorrectionImageFilter.h * \brief Implementation of the N3 MRI bias field correction algorithm. * * The nonparametric nonuniform intensity normalization (N3) algorithm * is a method for correcting nonuniformity associated with MR images. * The algorithm assumes a simple parametric model (Gaussian) for the bias field * but does not require tissue class segmentation. In addition, there are * only a couple of parameters to tune with the default values performing * quite well. * * N3 has been publicly available as a set of perl scripts * (http://www.bic.mni.mcgill.ca/software/N3/) but, with this class, has been * reimplemented for the ITK library with only one minor variation involving * the b-spline fitting routine. We replaced the original fitting approach * with the itkBSplineScatteredDataPointSetToImageFilter which is not * susceptible to ill-conditioned matrix calculation as is the original proposed * fitting component. * * Notes for the user: * 0. Based on our experience with the filter, sometimes the scale of the bias * field is too small particularly for large mesh element sizes. Therefore, * we added an option (which is true by default) for finding the optimal * scaling of the bias field which is based on minimizing the coefficient * of variation of the corrected image. This cost function is given below * in N3BiasFieldScaleCostFunction. * 1. Since much of the image manipulation is done in the log space of the * intensities, input images with negative and small values (< 1) are * discouraged. * 2. The original authors recommend performing the bias field correction * on a downsampled version of the original image. * 3. A mask and/or confidence image can be supplied. * 4. The filter returns the corrected image. If the bias field is wanted, one * can reconstruct it using the class itkBSplineControlPointImageFilter. * See the IJ article and the test file for an example. * 5. The 'Z' parameter in Sled's 1998 paper is the square root * of the class variable 'm_WeinerFilterNoise'. * * \author Nicholas J. Tustison * * Contributed by Nicholas J. Tustison, James C. Gee * in the Insight Journal paper: * * \par REFERENCE * J.G. Sled, P. Zijdenbos and A.C. Evans, "A comparison of retrospective * intensity non-uniformity correction methods for MRI". Information * Processing Medical Imaging Proc, 15th Int. Conf. IMPI'97, vol.1230, * pp 459-464,1997. * * J.G. Sled, A.P. Zijdenbos and A.C. Evans. "A Nonparametric Method for * Automatic Correction of Intensity Nonuniformity in MRI Data" * IEEE Transactions on Medical Imaging, Vol 17, No 1. Feb 1998. * */ /** * Class definition for N3BiasFieldScaleCostFunction */ template class N3BiasFieldScaleCostFunction : public SingleValuedCostFunction { public: typedef N3BiasFieldScaleCostFunction Self; typedef SingleValuedCostFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro( N3BiasFieldScaleCostFunction, SingleValuedCostFunction ); /** Method for creation through the object factory. */ itkNewMacro( Self ); typedef Superclass::MeasureType MeasureType; typedef Superclass::DerivativeType DerivativeType; typedef Superclass::ParametersType ParametersType; itkSetObjectMacro( InputImage, TInputImage ); itkSetObjectMacro( BiasFieldImage, TBiasFieldImage ); itkSetObjectMacro( MaskImage, TMaskImage ); itkSetObjectMacro( ConfidenceImage, TConfidenceImage ); virtual MeasureType GetValue( const ParametersType & parameters ) const ITK_OVERRIDE; virtual void GetDerivative( const ParametersType & parameters, DerivativeType & derivative ) const ITK_OVERRIDE; virtual unsigned int GetNumberOfParameters() const ITK_OVERRIDE; protected: N3BiasFieldScaleCostFunction(); virtual ~N3BiasFieldScaleCostFunction(); private: N3BiasFieldScaleCostFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented typename TInputImage::Pointer m_InputImage; typename TBiasFieldImage::Pointer m_BiasFieldImage; typename TMaskImage::Pointer m_MaskImage; typename TConfidenceImage::Pointer m_ConfidenceImage; }; /** * Class definition for N3MRIBiasFieldCorrectionImageFilter */ template , class TOutputImage = TInputImage> class N3MRIBiasFieldCorrectionImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef N3MRIBiasFieldCorrectionImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Runtime information support. */ itkTypeMacro( N3MRIBiasFieldCorrectionImageFilter, ImageToImageFilter ); /** Standard New method. */ itkNewMacro( Self ); /** ImageDimension constants */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); /** Some convenient typedefs. */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef TMaskImage MaskImageType; typedef typename MaskImageType::PixelType MaskPixelType; typedef float RealType; typedef Image RealImageType; /** B-spline smoothing filter typedefs */ typedef Vector ScalarType; typedef PointSet PointSetType; typedef Image ScalarImageType; typedef BSplineScatteredDataPointSetToImageFilter BSplineFilterType; typedef typename BSplineFilterType::PointDataImageType BiasFieldControlPointLatticeType; typedef typename BSplineFilterType::ArrayType ArrayType; void SetMaskImage( const MaskImageType *mask ) { this->SetNthInput( 1, const_cast( mask ) ); } const MaskImageType * GetMaskImage() const { return static_cast( const_cast ( this->ProcessObject::GetInput( 1 ) ) ); } void SetConfidenceImage( const RealImageType *image ) { this->SetNthInput( 2, const_cast( image ) ); } const RealImageType * GetConfidenceImage() const { return static_cast( const_cast ( this->ProcessObject::GetInput( 2 ) ) ); } void SetInput1( const TInputImage *input ) { this->SetInput( input ); } void SetInput2( const TMaskImage *mask ) { this->SetMaskImage( mask ); } void SetInput3( const RealImageType *image ) { this->SetConfidenceImage( image ); } itkSetMacro( NumberOfHistogramBins, unsigned int ); itkGetConstMacro( NumberOfHistogramBins, unsigned int ); itkSetMacro( WeinerFilterNoise, RealType ); itkGetConstMacro( WeinerFilterNoise, RealType ); itkSetMacro( BiasFieldFullWidthAtHalfMaximum, RealType ); itkGetConstMacro( BiasFieldFullWidthAtHalfMaximum, RealType ); itkSetMacro( MaximumNumberOfIterations, unsigned int ); itkGetConstMacro( MaximumNumberOfIterations, unsigned int ); itkSetMacro( ConvergenceThreshold, RealType ); itkGetConstMacro( ConvergenceThreshold, RealType ); itkSetMacro( SplineOrder, unsigned int ); itkGetConstMacro( SplineOrder, unsigned int ); itkSetMacro( NumberOfFittingLevels, ArrayType ); itkGetConstMacro( NumberOfFittingLevels, ArrayType ); void SetNumberOfFittingLevels( unsigned int n ) { ArrayType nlevels; nlevels.Fill( n ); this->SetNumberOfFittingLevels( nlevels ); } itkSetMacro( NumberOfControlPoints, ArrayType ); itkGetConstMacro( NumberOfControlPoints, ArrayType ); itkGetConstMacro( LogBiasFieldControlPointLattice, typename BiasFieldControlPointLatticeType::Pointer ); itkSetMacro( UseOptimalBiasFieldScaling, bool ); itkGetConstMacro( UseOptimalBiasFieldScaling, bool ); itkBooleanMacro( UseOptimalBiasFieldScaling ); itkGetConstMacro( BiasFieldScaling, RealType ); itkGetConstMacro( ElapsedIterations, unsigned int ); itkGetConstMacro( CurrentConvergenceMeasurement, RealType ); protected: N3MRIBiasFieldCorrectionImageFilter(); ~N3MRIBiasFieldCorrectionImageFilter() { }; void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; void GenerateData() ITK_OVERRIDE; private: N3MRIBiasFieldCorrectionImageFilter( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented typename N3MRIBiasFieldCorrectionImageFilter::RealImageType::Pointer SharpenImage( typename N3MRIBiasFieldCorrectionImageFilter::RealImageType::Pointer ); typename N3MRIBiasFieldCorrectionImageFilter::RealImageType::Pointer SmoothField( typename N3MRIBiasFieldCorrectionImageFilter::RealImageType::Pointer); RealType CalculateConvergenceMeasurement( typename RealImageType::Pointer, typename RealImageType::Pointer ); RealType CalculateOptimalBiasFieldScaling( typename RealImageType::Pointer ); /** * Parameters for deconvolution with Weiner filter */ unsigned int m_NumberOfHistogramBins; RealType m_WeinerFilterNoise; RealType m_BiasFieldFullWidthAtHalfMaximum; /** * Convergence parameters */ unsigned int m_MaximumNumberOfIterations; unsigned int m_ElapsedIterations; RealType m_ConvergenceThreshold; RealType m_CurrentConvergenceMeasurement; /** * B-spline fitting parameters */ typename BiasFieldControlPointLatticeType::Pointer m_LogBiasFieldControlPointLattice; unsigned int m_SplineOrder; ArrayType m_NumberOfControlPoints; ArrayType m_NumberOfFittingLevels; /** * other parameters */ RealType m_BiasFieldScaling; bool m_UseOptimalBiasFieldScaling; }; // end of class } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkN3MRIBiasFieldCorrectionImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkN3MRIBiasFieldCorrectionImageFilter.hxx000066400000000000000000000674251311104306400244370ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkN3MRIBiasFieldCorrectionImageFilter_hxx #define __itkN3MRIBiasFieldCorrectionImageFilter_hxx #include "itkN3MRIBiasFieldCorrectionImageFilter.h" #include "itkDivideImageFilter.h" #include "itkExpImageFilter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkIterationReporter.h" #include "itkLBFGSBOptimizer.h" #include "itkLogImageFilter.h" #include "itkSubtractImageFilter.h" #include "vnl/algo/vnl_fft_1d.h" #include "vnl/vnl_complex_traits.h" namespace itk { /** * N3BiasFieldScaleCostFunction class definitions */ template N3BiasFieldScaleCostFunction ::N3BiasFieldScaleCostFunction() { this->m_InputImage = ITK_NULLPTR; this->m_BiasFieldImage = ITK_NULLPTR; this->m_MaskImage = ITK_NULLPTR; this->m_ConfidenceImage = ITK_NULLPTR; } template N3BiasFieldScaleCostFunction ::~N3BiasFieldScaleCostFunction() { } template typename N3BiasFieldScaleCostFunction::MeasureType N3BiasFieldScaleCostFunction ::GetValue( const ParametersType & parameters ) const { ImageRegionConstIterator ItI( this->m_InputImage, this->m_InputImage->GetRequestedRegion() ); ImageRegionConstIterator ItB( this->m_BiasFieldImage, this->m_BiasFieldImage->GetRequestedRegion() ); MeasureType mu = 0.0; MeasureType N = 0.0; for( ItI.GoToBegin(), ItB.GoToBegin(); !ItI.IsAtEnd(); ++ItI, ++ItB ) { if( ( !this->m_MaskImage || this->m_MaskImage->GetPixel( ItI.GetIndex() ) != NumericTraits::ZeroValue() ) && ( !this->m_ConfidenceImage || this->m_ConfidenceImage->GetPixel( ItI.GetIndex() ) > 0.0 ) ) { mu += static_cast( ItI.Get() ) / ( parameters[0] * ( static_cast( ItB.Get() ) - 1.0 ) + 1.0 ); N += 1.0; } } mu /= N; MeasureType value = 0.0; for( ItI.GoToBegin(), ItB.GoToBegin(); !ItI.IsAtEnd(); ++ItI, ++ItB ) { if( ( !this->m_MaskImage || this->m_MaskImage->GetPixel( ItI.GetIndex() ) != NumericTraits::ZeroValue() ) && ( !this->m_ConfidenceImage || this->m_ConfidenceImage->GetPixel( ItI.GetIndex() ) > 0.0 ) ) { value += vnl_math_sqr( ( ItI.Get() / ( parameters[0] * ( static_cast( ItB.Get() ) - 1.0 ) + 1.0 ) ) / mu - 1.0 ); } } value /= ( N - 1.0 ); return value; } template void N3BiasFieldScaleCostFunction ::GetDerivative( const ParametersType & parameters, DerivativeType & derivative ) const { ImageRegionConstIterator ItI( this->m_InputImage, this->m_InputImage->GetRequestedRegion() ); ImageRegionConstIterator ItB( this->m_BiasFieldImage, this->m_BiasFieldImage->GetRequestedRegion() ); MeasureType mu = 0.0; MeasureType dmu = 0.0; MeasureType N = 0.0; for( ItI.GoToBegin(), ItB.GoToBegin(); !ItI.IsAtEnd(); ++ItI, ++ItB ) { if( ( !this->m_MaskImage || this->m_MaskImage->GetPixel( ItI.GetIndex() ) != NumericTraits::ZeroValue() ) && ( !this->m_ConfidenceImage || this->m_ConfidenceImage->GetPixel( ItI.GetIndex() ) > 0.0 ) ) { MeasureType d = parameters[0] * ( static_cast( ItB.Get() ) - 1.0 ) + 1.0; mu += ( static_cast( ItI.Get() ) / d ); dmu += -static_cast( ItI.Get() ) * ( static_cast( ItB.Get() ) - 1.0 ) / d; N += 1.0; } } mu /= N; dmu /= N; MeasureType value = 0.0; for( ItI.GoToBegin(), ItB.GoToBegin(); !ItI.IsAtEnd(); ++ItI, ++ItB ) { if( ( !this->m_MaskImage || this->m_MaskImage->GetPixel( ItI.GetIndex() ) != NumericTraits::ZeroValue() ) && ( !this->m_ConfidenceImage || this->m_ConfidenceImage->GetPixel( ItI.GetIndex() ) > 0.0 ) ) { MeasureType d = parameters[0] * ( static_cast( ItB.Get() ) - 1.0 ) + 1.0; MeasureType t = static_cast( ItI.Get() ) / d; MeasureType dt = -t * ( static_cast( ItB.Get() ) - 1.0 ); value += ( ( t / mu - 1.0 ) * ( dt / mu - dmu * t / ( vnl_math_sqr( mu ) ) ) ); } } derivative.SetSize( 1 ); derivative( 0 ) = 2.0 * value / ( N - 1 ); } template unsigned int N3BiasFieldScaleCostFunction ::GetNumberOfParameters() const { return NumericTraits::OneValue(); } /** * N3MRIBiasFieldCorrectionImageFilter class definitions */ template N3MRIBiasFieldCorrectionImageFilter ::N3MRIBiasFieldCorrectionImageFilter() { this->SetNumberOfRequiredInputs( 1 ); this->m_NumberOfHistogramBins = 200; this->m_WeinerFilterNoise = 0.01; this->m_BiasFieldFullWidthAtHalfMaximum = 0.15; this->m_MaximumNumberOfIterations = 50; this->m_ConvergenceThreshold = 0.001; this->m_SplineOrder = 3; this->m_NumberOfFittingLevels.Fill( 4 ); this->m_NumberOfControlPoints.Fill( 4 ); this->m_UseOptimalBiasFieldScaling = true; this->m_BiasFieldScaling = 1.0; } template void N3MRIBiasFieldCorrectionImageFilter ::GenerateData() { /** * Calculate the log of the input image. */ typename RealImageType::Pointer logInputImage = RealImageType::New(); typedef ExpImageFilter ExpImageFilterType; typedef LogImageFilter LogFilterType; typename LogFilterType::Pointer logFilter1 = LogFilterType::New(); logFilter1->SetInput( this->GetInput() ); logFilter1->Update(); logInputImage = logFilter1->GetOutput(); /** * Remove possible nans/infs from the log input image. */ ImageRegionIteratorWithIndex It( logInputImage, logInputImage->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { if( ( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( It.GetIndex() ) != NumericTraits::ZeroValue() ) && ( !this->GetConfidenceImage() || this->GetConfidenceImage()->GetPixel( It.GetIndex() ) > 0.0 ) ) { if( vnl_math_isnan( It.Get() ) || vnl_math_isinf( It.Get() ) || It.Get() < 0.0 ) { It.Set( 0.0 ); } } } /** * Provide an initial log bias field of zeros */ typename RealImageType::Pointer logBiasField = AllocImage(this->GetInput()->GetRequestedRegion(), 0.0); logBiasField->SetOrigin( this->GetInput()->GetOrigin() ); logBiasField->SetSpacing( this->GetInput()->GetSpacing() ); logBiasField->SetDirection( this->GetInput()->GetDirection() ); /** * Iterate until convergence or iterative exhaustion. */ IterationReporter reporter( this, 0, 1 ); bool isConverged = false; this->m_ElapsedIterations = 0; while( !isConverged && this->m_ElapsedIterations++ < this->m_MaximumNumberOfIterations ) { typedef SubtractImageFilter SubtracterType; typename SubtracterType::Pointer subtracter1 = SubtracterType::New(); subtracter1->SetInput1( logInputImage ); subtracter1->SetInput2( logBiasField ); subtracter1->Update(); typename RealImageType::Pointer logSharpenedImage = this->SharpenImage( subtracter1->GetOutput() ); typename SubtracterType::Pointer subtracter2 = SubtracterType::New(); subtracter2->SetInput1( logInputImage ); subtracter2->SetInput2( logSharpenedImage ); subtracter2->Update(); typename RealImageType::Pointer newLogBiasField = this->SmoothField( subtracter2->GetOutput() ); this->m_CurrentConvergenceMeasurement = this->CalculateConvergenceMeasurement( logBiasField, newLogBiasField ); isConverged = ( this->m_CurrentConvergenceMeasurement < this->m_ConvergenceThreshold ); logBiasField = newLogBiasField; reporter.CompletedStep(); } typedef ExpImageFilter ExpImageFilterType; typename ExpImageFilterType::Pointer expFilter = ExpImageFilterType::New(); expFilter->SetInput( logBiasField ); expFilter->Update(); /** * Calculate the optimal scaling */ if( this->m_UseOptimalBiasFieldScaling ) { this->m_BiasFieldScaling = this->CalculateOptimalBiasFieldScaling( expFilter->GetOutput() ); ImageRegionIterator ItE( expFilter->GetOutput(), expFilter->GetOutput()->GetRequestedRegion() ); for( ItE.GoToBegin(); !ItE.IsAtEnd(); ++ItE ) { ItE.Set( this->m_BiasFieldScaling * ( ItE.Get() - 1.0 ) + 1.0 ); } } /** * Divide the input image by the bias field to get the final image. */ typedef DivideImageFilter DividerType; typename DividerType::Pointer divider = DividerType::New(); divider->SetInput1( this->GetInput() ); divider->SetInput2( expFilter->GetOutput() ); divider->Update(); this->SetNthOutput( 0, divider->GetOutput() ); } template typename N3MRIBiasFieldCorrectionImageFilter::RealImageType::Pointer N3MRIBiasFieldCorrectionImageFilter ::SharpenImage( typename N3MRIBiasFieldCorrectionImageFilter::RealImageType::Pointer unsharpenedImage ) { /** * Build the histogram for the uncorrected image. Store copy * in a vnl_vector to utilize vnl FFT routines. Note that variables * in real space are denoted by a single uppercase letter whereas their * frequency counterparts are indicated by a trailing lowercase 'f'. */ RealType binMaximum = NumericTraits::NonpositiveMin(); RealType binMinimum = NumericTraits::max(); ImageRegionIterator ItU( unsharpenedImage, unsharpenedImage->GetLargestPossibleRegion() ); for( ItU.GoToBegin(); !ItU.IsAtEnd(); ++ItU ) { if( ( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItU.GetIndex() ) != NumericTraits::ZeroValue() ) && ( !this->GetConfidenceImage() || this->GetConfidenceImage()->GetPixel( ItU.GetIndex() ) > 0.0 ) ) { RealType pixel = ItU.Get(); if( pixel > binMaximum ) { binMaximum = pixel; } else if( pixel < binMinimum ) { binMinimum = pixel; } } } RealType histogramSlope = ( binMaximum - binMinimum ) / static_cast( this->m_NumberOfHistogramBins - 1 ); /** * Create the intensity profile (within the masked region, if applicable) * using a triangular parzen windowing scheme. */ vnl_vector H( this->m_NumberOfHistogramBins, 0.0 ); for( ItU.GoToBegin(); !ItU.IsAtEnd(); ++ItU ) { if( ( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItU.GetIndex() ) != NumericTraits::ZeroValue() ) && ( !this->GetConfidenceImage() || this->GetConfidenceImage()->GetPixel( ItU.GetIndex() ) > 0.0 ) ) { RealType pixel = ItU.Get(); float cidx = ( static_cast( pixel ) - binMinimum ) / histogramSlope; unsigned int idx = vnl_math_floor( cidx ); RealType offset = cidx - static_cast( idx ); if( offset == 0.0 ) { H[idx] += 1.0; } else if( idx < this->m_NumberOfHistogramBins - 1 ) { H[idx] += 1.0 - offset; H[idx + 1] += offset; } } } /** * Determine information about the intensity histogram and zero-pad * histogram to a power of 2. */ RealType exponent = std::ceil( std::log( static_cast( this->m_NumberOfHistogramBins ) ) / std::log( 2.0 ) ) + 1; unsigned int paddedHistogramSize = static_cast( std::pow( static_cast( 2.0 ), exponent ) + 0.5 ); unsigned int histogramOffset = static_cast( 0.5 * ( paddedHistogramSize - this->m_NumberOfHistogramBins ) ); vnl_vector > V( paddedHistogramSize, std::complex( 0.0, 0.0 ) ); for( unsigned int n = 0; n < this->m_NumberOfHistogramBins; n++ ) { V[n + histogramOffset] = H[n]; } /** * Instantiate the 1-d vnl fft routine */ vnl_fft_1d fft( paddedHistogramSize ); vnl_vector > Vf( V ); fft.fwd_transform( Vf ); /** * Create the Gaussian filter. */ RealType scaledFWHM = this->m_BiasFieldFullWidthAtHalfMaximum / histogramSlope; RealType expFactor = 4.0 * std::log( 2.0 ) / vnl_math_sqr( scaledFWHM ); RealType scaleFactor = 2.0 * std::sqrt( std::log( 2.0 ) / vnl_math::pi ) / scaledFWHM; vnl_vector > F( paddedHistogramSize, std::complex( 0.0, 0.0 ) ); F[0] = std::complex( scaleFactor, 0.0 ); unsigned int halfSize = static_cast( 0.5 * paddedHistogramSize ); for( unsigned int n = 1; n <= halfSize; n++ ) { F[n] = F[paddedHistogramSize - n] = std::complex( scaleFactor * std::exp( -vnl_math_sqr( static_cast( n ) ) * expFactor ), 0.0 ); } if( paddedHistogramSize % 2 == 0 ) { F[halfSize] = std::complex( scaleFactor * std::exp( 0.25 * -vnl_math_sqr( static_cast( paddedHistogramSize ) ) * expFactor ), 0.0 ); } vnl_vector > Ff( F ); fft.fwd_transform( Ff ); /** * Create the Weiner deconvolution filter. */ vnl_vector > Gf( paddedHistogramSize ); for( unsigned int n = 0; n < paddedHistogramSize; n++ ) { std::complex c = vnl_complex_traits >::conjugate( Ff[n] ); Gf[n] = c / ( c * Ff[n] + this->m_WeinerFilterNoise ); } vnl_vector > Uf( paddedHistogramSize ); for( unsigned int n = 0; n < paddedHistogramSize; n++ ) { Uf[n] = Vf[n] * Gf[n].real(); } vnl_vector > U( Uf ); fft.bwd_transform( U ); for( unsigned int n = 0; n < paddedHistogramSize; n++ ) { U[n] = std::complex( vnl_math_max( U[n].real(), static_cast( 0.0 ) ), 0.0 ); } /** * Compute mapping E(u|v) */ vnl_vector > numerator( paddedHistogramSize ); for( unsigned int n = 0; n < paddedHistogramSize; n++ ) { numerator[n] = std::complex( ( binMinimum + ( static_cast( n ) - histogramOffset ) * histogramSlope ) * U[n].real(), 0.0 ); } fft.fwd_transform( numerator ); for( unsigned int n = 0; n < paddedHistogramSize; n++ ) { numerator[n] *= Ff[n]; } fft.bwd_transform( numerator ); vnl_vector > denominator( U ); fft.fwd_transform( denominator ); for( unsigned int n = 0; n < paddedHistogramSize; n++ ) { denominator[n] *= Ff[n]; } fft.bwd_transform( denominator ); vnl_vector E( paddedHistogramSize ); for( unsigned int n = 0; n < paddedHistogramSize; n++ ) { E[n] = numerator[n].real() / denominator[n].real(); if( vnl_math_isinf( E[n] ) || vnl_math_isnan( E[n] ) ) { E[n] = 0.0; } } /** * Remove the zero-padding from the mapping */ E = E.extract( this->m_NumberOfHistogramBins, histogramOffset ); /** * Sharpen the image with the new mapping, E(u|v) */ typename RealImageType::Pointer sharpenedImage = AllocImage(unsharpenedImage, 0.0); ImageRegionIterator ItC( sharpenedImage, sharpenedImage->GetLargestPossibleRegion() ); for( ItU.GoToBegin(), ItC.GoToBegin(); !ItU.IsAtEnd(); ++ItU, ++ItC ) { if( ( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( ItU.GetIndex() ) != NumericTraits::ZeroValue() ) && ( !this->GetConfidenceImage() || this->GetConfidenceImage()->GetPixel( ItU.GetIndex() ) > 0.0 ) ) { float cidx = ( ItU.Get() - binMinimum ) / histogramSlope; unsigned int idx = vnl_math_floor( cidx ); RealType correctedPixel = 0; if( idx < E.size() - 1 ) { correctedPixel = E[idx] + ( E[idx + 1] - E[idx] ) * ( cidx - static_cast( idx ) ); } else { correctedPixel = E[E.size() - 1]; } ItC.Set( correctedPixel ); } } return sharpenedImage; } template typename N3MRIBiasFieldCorrectionImageFilter ::RealImageType::Pointer N3MRIBiasFieldCorrectionImageFilter ::SmoothField( typename N3MRIBiasFieldCorrectionImageFilter::RealImageType::Pointer fieldEstimate ) { /** * Get original direction and change to identity temporarily for the * b-spline fitting. */ typename RealImageType::DirectionType direction = fieldEstimate->GetDirection(); typename RealImageType::DirectionType identity; identity.SetIdentity(); fieldEstimate->SetDirection( identity ); typename PointSetType::Pointer fieldPoints = PointSetType::New(); fieldPoints->Initialize(); typename BSplineFilterType::WeightsContainerType::Pointer weights = BSplineFilterType::WeightsContainerType::New(); weights->Initialize(); ImageRegionConstIteratorWithIndex It( fieldEstimate, fieldEstimate->GetRequestedRegion() ); unsigned int N = 0; for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { if( ( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( It.GetIndex() ) != NumericTraits::ZeroValue() ) && ( !this->GetConfidenceImage() || this->GetConfidenceImage()->GetPixel( It.GetIndex() ) > 0.0 ) ) { typename PointSetType::PointType point; fieldEstimate->TransformIndexToPhysicalPoint( It.GetIndex(), point ); ScalarType scalar; scalar[0] = It.Get(); fieldPoints->SetPointData( N, scalar ); fieldPoints->SetPoint( N, point ); if( this->GetConfidenceImage() ) { weights->InsertElement( N, this->GetConfidenceImage()->GetPixel( It.GetIndex() ) ); } else { weights->InsertElement( N, 1.0 ); } N++; } } fieldEstimate->SetDirection( direction ); typename BSplineFilterType::Pointer bspliner = BSplineFilterType::New(); bspliner->SetOrigin( fieldEstimate->GetOrigin() ); bspliner->SetSpacing( fieldEstimate->GetSpacing() ); bspliner->SetSize( fieldEstimate->GetLargestPossibleRegion().GetSize() ); bspliner->SetDirection( fieldEstimate->GetDirection() ); bspliner->SetGenerateOutputImage( true ); bspliner->SetNumberOfLevels( this->m_NumberOfFittingLevels ); bspliner->SetSplineOrder( this->m_SplineOrder ); bspliner->SetNumberOfControlPoints( this->m_NumberOfControlPoints ); bspliner->SetInput( fieldPoints ); bspliner->SetPointWeights( weights ); bspliner->Update(); /** * Save the bias field control points in case the user wants to * reconstruct the bias field. */ this->m_LogBiasFieldControlPointLattice = bspliner->GetPhiLattice(); typename RealImageType::Pointer smoothField = AllocImage(fieldEstimate->GetLargestPossibleRegion() ); smoothField->SetOrigin( fieldEstimate->GetOrigin() ); smoothField->SetSpacing( fieldEstimate->GetSpacing() ); smoothField->SetDirection( direction ); ImageRegionIterator ItB( bspliner->GetOutput(), bspliner->GetOutput()->GetLargestPossibleRegion() ); ImageRegionIterator ItF( smoothField, smoothField->GetLargestPossibleRegion() ); for( ItB.GoToBegin(), ItF.GoToBegin(); !ItB.IsAtEnd(); ++ItB, ++ItF ) { ItF.Set( ItB.Get()[0] ); } return smoothField; } template typename N3MRIBiasFieldCorrectionImageFilter ::RealType N3MRIBiasFieldCorrectionImageFilter ::CalculateConvergenceMeasurement( typename RealImageType::Pointer fieldEstimate1, typename RealImageType::Pointer fieldEstimate2 ) { typedef SubtractImageFilter SubtracterType; typename SubtracterType::Pointer subtracter = SubtracterType::New(); subtracter->SetInput1( fieldEstimate1 ); subtracter->SetInput2( fieldEstimate2 ); subtracter->Update(); /** * Calculate statistics over the mask region */ RealType mu = 0.0; RealType sigma = 0.0; RealType N = 0.0; ImageRegionConstIteratorWithIndex It( subtracter->GetOutput(), subtracter->GetOutput()->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { if( !this->GetMaskImage() || this->GetMaskImage()->GetPixel( It.GetIndex() ) != NumericTraits::ZeroValue() ) { RealType pixel = It.Get(); N += 1.0; if( N > 1.0 ) { sigma = sigma + vnl_math_sqr( pixel - mu ) * ( N - 1.0 ) / N; } mu = mu * ( 1.0 - 1.0 / N ) + pixel / N; } } sigma = std::sqrt( sigma / ( N - 1.0 ) ); /** * Although Sled's paper proposes convergence determination via * the coefficient of variation, the actual mnc implementation * utilizes the standard deviation as the convergence measurement. */ return sigma; } template typename N3MRIBiasFieldCorrectionImageFilter ::RealType N3MRIBiasFieldCorrectionImageFilter ::CalculateOptimalBiasFieldScaling( typename RealImageType::Pointer biasField ) { /** * This section is not described in Sled's paper but rather stems from * our own experience with N4ITK and the resulting innovation. For an initial * B-spline mesh of large resolution and a low number of fitting levels, * although the shape of of the bias field appears correect, the scale is too * small. This section finds an optimal scaling by minimizing the coefficient * of variation over masked region. */ typedef N3BiasFieldScaleCostFunction ScaleCostFunctionType; typename ScaleCostFunctionType::Pointer scaleCostFunction = ScaleCostFunctionType::New(); scaleCostFunction->SetInputImage( const_cast( this->GetInput() ) ); scaleCostFunction->SetBiasFieldImage( biasField ); scaleCostFunction->SetMaskImage( const_cast( this->GetMaskImage() ) ); scaleCostFunction->SetConfidenceImage( const_cast( this->GetConfidenceImage() ) ); typename LBFGSBOptimizer::BoundSelectionType boundSelection; boundSelection.SetSize( 1 ); boundSelection.Fill( 1 ); // only set a lower bound on the scale factor typename LBFGSBOptimizer::BoundValueType lowerBound; lowerBound.SetSize( 1 ); lowerBound.Fill( 1.0 ); typename LBFGSBOptimizer::BoundValueType upperBound; upperBound.SetSize( 1 ); upperBound.Fill( NumericTraits::max() ); typename LBFGSBOptimizer::ParametersType initialParameters; initialParameters.SetSize( 1 ); initialParameters.Fill( 1.0 ); typename LBFGSBOptimizer::Pointer optimizer = LBFGSBOptimizer::New(); optimizer->SetMinimize( true ); optimizer->SetCostFunction( scaleCostFunction ); optimizer->SetInitialPosition( initialParameters ); optimizer->SetCostFunctionConvergenceFactor( 1e1 ); optimizer->SetLowerBound( lowerBound ); optimizer->SetUpperBound( upperBound ); optimizer->SetBoundSelection( boundSelection ); optimizer->SetProjectedGradientTolerance( 1e-10 ); optimizer->StartOptimization(); typename LBFGSBOptimizer::ParametersType finalParameters = optimizer->GetCurrentPosition(); return finalParameters[0]; } template void N3MRIBiasFieldCorrectionImageFilter ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << "Number of histogram bins: " << this->m_NumberOfHistogramBins << std::endl; os << indent << "Weiner filter noise: " << this->m_WeinerFilterNoise << std::endl; os << indent << "Bias field FWHM: " << this->m_BiasFieldFullWidthAtHalfMaximum << std::endl; os << indent << "Maximum number of iterations: " << this->m_MaximumNumberOfIterations << std::endl; os << indent << "Convergence threshold: " << this->m_ConvergenceThreshold << std::endl; os << indent << "Spline order: " << this->m_SplineOrder << std::endl; os << indent << "Number of fitting levels: " << this->m_NumberOfFittingLevels << std::endl; os << indent << "Number of control points: " << this->m_NumberOfControlPoints << std::endl; if( this->m_UseOptimalBiasFieldScaling ) { os << indent << "Optimal bias field scaling: " << this->m_BiasFieldScaling << std::endl; } } } // end namespace itk #endif ants-2.2.0/Utilities/itkNeighborhoodFirstOrderStatisticsImageFilter.h000066400000000000000000000076721311104306400261200ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkNeighborhoodFirstOrderStatisticsImageFilter_h #define __itkNeighborhoodFirstOrderStatisticsImageFilter_h #include "itkMovingHistogramImageFilter.h" #include "itkTextureHistogram.h" namespace itk { /** * \class NeighborhoodFirstOrderStatisticsImageFilter * \brief Compute first order statistics in a neighborhood at each pixel * * \ingroup ITK-TextureAnalysis */ template< class TInputImage, class TOutputImage, class TKernel > class ITK_EXPORT NeighborhoodFirstOrderStatisticsImageFilter: public MovingHistogramImageFilter< TInputImage, TOutputImage, TKernel, typename Function::TextureHistogram< typename TInputImage::PixelType, typename TOutputImage::PixelType > > { public: /** Standard class typedefs. */ typedef NeighborhoodFirstOrderStatisticsImageFilter Self; typedef MovingHistogramImageFilter< TInputImage, TOutputImage, TKernel, typename Function::TextureHistogram< typename TInputImage::PixelType, typename TOutputImage::PixelType> > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Standard New method. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro( NeighborhoodFirstOrderStatisticsImageFilter, MovingHistogramImageFilter ); /** Image related typedefs. */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef typename TInputImage::RegionType RegionType; typedef typename TInputImage::SizeType SizeType; typedef typename TInputImage::IndexType IndexType; typedef typename TInputImage::PixelType PixelType; typedef typename TInputImage::OffsetType OffsetType; typedef typename Superclass::OutputImageRegionType OutputImageRegionType; typedef typename TOutputImage::PixelType OutputPixelType; /** Image related typedefs. */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); protected: unsigned int GetNumberOfOutputComponents() { return 8; } NeighborhoodFirstOrderStatisticsImageFilter() { //this->m_Boundary = NumericTraits< PixelType >::max(); } void GenerateOutputInformation() ITK_OVERRIDE { // this methods is overloaded so that if the output image is a // VectorImage then the correct number of components are set. Superclass::GenerateOutputInformation(); OutputImageType* output = this->GetOutput(); if ( !output ) { return; } if ( output->GetNumberOfComponentsPerPixel() != this->GetNumberOfOutputComponents() ) { output->SetNumberOfComponentsPerPixel( this->GetNumberOfOutputComponents() ); } } ~NeighborhoodFirstOrderStatisticsImageFilter() {} private: NeighborhoodFirstOrderStatisticsImageFilter(const Self &); //purposely not implemented void operator=(const Self &); //purposely not implemented }; // end of class } // end namespace itk #endif ants-2.2.0/Utilities/itkNonLocalPatchBasedImageFilter.h000066400000000000000000000153341311104306400230500ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkNonLocalPatchBasedImageFilter_h #define itkNonLocalPatchBasedImageFilter_h #include "itkImageToImageFilter.h" #include "itkConstNeighborhoodIterator.h" namespace itk { /** * \class NonLocalPatchBasedImageFilter * \brief Implementation of a non-local upsampling (i.e., superresolution) image filter. * * \ingroup ITKFiltering */ template class NonLocalPatchBasedImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef NonLocalPatchBasedImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Runtime information support. */ itkTypeMacro( NonLocalPatchBasedImageFilter, ImageToImageFilter ); /** Standard New method. */ itkNewMacro( Self ); /** ImageDimension constants */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); /** Some convenient typedefs. */ typedef TInputImage InputImageType; typedef typename InputImageType::PixelType InputPixelType; typedef typename InputImageType::Pointer InputImagePointer; typedef std::vector InputImageList; typedef std::vector InputImageSetList; typedef typename InputImageType::RegionType RegionType; typedef TOutputImage OutputImageType; typedef typename OutputImageType::PixelType OutputPixelType; typedef std::vector InputImagePixelVectorType; typedef float RealType; typedef Image RealImageType; typedef typename RealImageType::Pointer RealImagePointer; typedef typename RealImageType::IndexType IndexType; typedef Neighborhood NeighborhoodType; typedef SizeValueType NeighborhoodSizeType; typedef ConstNeighborhoodIterator ConstNeighborhoodIteratorType; typedef typename ConstNeighborhoodIteratorType::RadiusType NeighborhoodRadiusType; typedef typename ConstNeighborhoodIteratorType::OffsetType NeighborhoodOffsetType; typedef std::vector NeighborhoodOffsetListType; /** * Neighborhood patch similarity metric enumerated type */ enum SimilarityMetricType { PEARSON_CORRELATION, MEAN_SQUARES }; /** * Get/set neighborhood search radius. * Default = 3x3x... */ itkSetMacro( NeighborhoodSearchRadius, NeighborhoodRadiusType ); itkGetConstMacro( NeighborhoodSearchRadius, NeighborhoodRadiusType ); /** * Get/set neighborhood search size. */ itkSetMacro( NeighborhoodSearchSize, NeighborhoodSizeType ); itkGetConstMacro( NeighborhoodSearchSize, NeighborhoodSizeType ); /** * Get/set neighborhood search offset list. */ virtual void SetNeighborhoodSearchOffsetList( const NeighborhoodOffsetListType list ) { this->m_NeighborhoodSearchOffsetList = list; this->Modified(); } itkGetConstMacro( NeighborhoodSearchOffsetList, NeighborhoodOffsetListType ); /** * Get/set neighborhood patch radius. * Default = 1x1x... */ itkSetMacro( NeighborhoodPatchRadius, NeighborhoodRadiusType ); itkGetConstMacro( NeighborhoodPatchRadius, NeighborhoodRadiusType ); /** * Get/set neighborhood patch size. */ itkSetMacro( NeighborhoodPatchSize, NeighborhoodSizeType ); itkGetConstMacro( NeighborhoodPatchSize, NeighborhoodSizeType ); /** * Get/set neighborhood patch offset list. */ virtual void SetNeighborhoodPatchOffsetList( const NeighborhoodOffsetListType list ) { this->m_NeighborhoodPatchOffsetList = list; this->Modified(); } itkGetConstMacro( NeighborhoodPatchOffsetList, NeighborhoodOffsetListType ); /** * Enumerated type for neighborhood similarity. Default = MEAN_SQUARES */ itkSetMacro( SimilarityMetric, SimilarityMetricType ); itkGetConstMacro( SimilarityMetric, SimilarityMetricType ); protected: NonLocalPatchBasedImageFilter(); ~NonLocalPatchBasedImageFilter() {} void BeforeThreadedGenerateData() ITK_OVERRIDE; void PrintSelf( std::ostream & os, Indent indent ) const ITK_OVERRIDE; RealType ComputeNeighborhoodPatchSimilarity( const InputImageList &, const IndexType, const InputImagePixelVectorType &, const bool ); InputImagePixelVectorType VectorizeImageListPatch( const InputImageList &, const IndexType, const bool ); InputImagePixelVectorType VectorizeImagePatch( const InputImagePointer, const IndexType, const bool ); void GetMeanAndStandardDeviationOfVectorizedImagePatch( const InputImagePixelVectorType &, RealType &, RealType & ); itkSetMacro( TargetImageRegion, RegionType ); itkGetConstMacro( TargetImageRegion, RegionType ); SimilarityMetricType m_SimilarityMetric; SizeValueType m_NeighborhoodSearchSize; NeighborhoodRadiusType m_NeighborhoodSearchRadius; NeighborhoodOffsetListType m_NeighborhoodSearchOffsetList; SizeValueType m_NeighborhoodPatchSize; NeighborhoodRadiusType m_NeighborhoodPatchRadius; NeighborhoodOffsetListType m_NeighborhoodPatchOffsetList; RegionType m_TargetImageRegion; private: NonLocalPatchBasedImageFilter( const Self& ) ITK_DELETE_FUNCTION; void operator=( const Self& ) ITK_DELETE_FUNCTION; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkNonLocalPatchBasedImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkNonLocalPatchBasedImageFilter.hxx000066400000000000000000000201031311104306400234160ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkNonLocalPatchBasedImageFilter_hxx #define itkNonLocalPatchBasedImageFilter_hxx #include "itkNonLocalPatchBasedImageFilter.h" #include "itkNeighborhood.h" namespace itk { template NonLocalPatchBasedImageFilter ::NonLocalPatchBasedImageFilter() { this->m_SimilarityMetric = MEAN_SQUARES; this->m_NeighborhoodPatchRadius.Fill( 1 ); this->m_NeighborhoodPatchOffsetList.clear(); this->m_NeighborhoodSearchRadius.Fill( 3 ); this->m_NeighborhoodSearchOffsetList.clear(); } template void NonLocalPatchBasedImageFilter ::BeforeThreadedGenerateData() { // Set up the search neighborhood parameters this->m_NeighborhoodSearchOffsetList.clear(); NeighborhoodType searchNeighborhood; searchNeighborhood.SetRadius( this->m_NeighborhoodSearchRadius ); this->m_NeighborhoodSearchSize = searchNeighborhood.Size(); for( unsigned int n = 0; n < this->m_NeighborhoodSearchSize; n++ ) { this->m_NeighborhoodSearchOffsetList.push_back( searchNeighborhood.GetOffset( n ) ); } // Set up the patch neighborhood parameters this->m_NeighborhoodPatchOffsetList.clear(); NeighborhoodType patchNeighborhood; patchNeighborhood.SetRadius( this->m_NeighborhoodPatchRadius ); this->m_NeighborhoodPatchSize = patchNeighborhood.Size(); for( unsigned int n = 0; n < this->m_NeighborhoodPatchSize; n++ ) { this->m_NeighborhoodPatchOffsetList.push_back( patchNeighborhood.GetOffset( n ) ); } this->m_TargetImageRegion = this->GetInput()->GetRequestedRegion(); } template typename NonLocalPatchBasedImageFilter::InputImagePixelVectorType NonLocalPatchBasedImageFilter ::VectorizeImageListPatch( const InputImageList &imageList, const IndexType index, const bool normalize ) { InputImagePixelVectorType patchVector( this->m_NeighborhoodPatchSize * imageList.size() ); for( unsigned int i = 0; i < imageList.size(); i++ ) { InputImagePixelVectorType patchVectorPerModality = this->VectorizeImagePatch( imageList[i], index, normalize ); for( unsigned int j = 0; j < this->m_NeighborhoodPatchSize; j++ ) { patchVector[i * this->m_NeighborhoodPatchSize + j] = patchVectorPerModality[j]; } } return patchVector; } template typename NonLocalPatchBasedImageFilter::InputImagePixelVectorType NonLocalPatchBasedImageFilter ::VectorizeImagePatch( const InputImagePointer image, const IndexType index, const bool normalize ) { InputImagePixelVectorType patchVector( this->m_NeighborhoodPatchSize ); for( SizeValueType i = 0; i < this->m_NeighborhoodPatchSize; i++ ) { IndexType neighborhoodIndex = index + this->m_NeighborhoodPatchOffsetList[i]; bool isInBounds = this->m_TargetImageRegion.IsInside( neighborhoodIndex ); if( isInBounds ) { InputPixelType pixel = image->GetPixel( neighborhoodIndex ); patchVector[i] = pixel; } else { patchVector[i] = std::numeric_limits::quiet_NaN(); } } if( normalize ) { RealType mean = 0.0; RealType standardDeviation = 0.0; this->GetMeanAndStandardDeviationOfVectorizedImagePatch( patchVector, mean, standardDeviation ); standardDeviation = std::max( standardDeviation, NumericTraits::OneValue() ); typename InputImagePixelVectorType::iterator it; for( it = patchVector.begin(); it != patchVector.end(); ++it ) { *it = ( *it - mean ) / standardDeviation; } } return patchVector; } template void NonLocalPatchBasedImageFilter ::GetMeanAndStandardDeviationOfVectorizedImagePatch( const InputImagePixelVectorType &patchVector, RealType &mean, RealType &standardDeviation ) { RealType sum = 0.0; RealType sumOfSquares = 0.0; RealType count = 0.0; typename InputImagePixelVectorType::const_iterator it; for( it = patchVector.begin(); it != patchVector.end(); ++it ) { if( std::isfinite( *it ) ) { sum += *it; sumOfSquares += vnl_math_sqr( *it ); count += 1.0; } } mean = sum / count; standardDeviation = std::sqrt( ( sumOfSquares - count * vnl_math_sqr( mean ) ) / ( count - 1.0 ) ); } template typename NonLocalPatchBasedImageFilter::RealType NonLocalPatchBasedImageFilter ::ComputeNeighborhoodPatchSimilarity( const InputImageList &imageList, const IndexType index, const InputImagePixelVectorType &patchVectorY, const bool useOnlyFirstImage ) { unsigned int numberOfImagesToUse = imageList.size(); if( useOnlyFirstImage ) { numberOfImagesToUse = 1; } RealType sumX = 0.0; RealType sumOfSquaresX = 0.0; RealType sumOfSquaredDifferencesXY = 0.0; RealType sumXY = 0.0; RealType N = 0.0; SizeValueType count = 0; for( SizeValueType i = 0; i < numberOfImagesToUse; i++ ) { for( SizeValueType j = 0; j < this->m_NeighborhoodPatchSize; j++ ) { IndexType neighborhoodIndex = index + this->m_NeighborhoodPatchOffsetList[j]; bool isInBounds = this->m_TargetImageRegion.IsInside( neighborhoodIndex ); if( isInBounds && std::isfinite( patchVectorY[count] ) ) { RealType x = static_cast( imageList[i]->GetPixel( neighborhoodIndex ) ); RealType y = static_cast( patchVectorY[count] ); sumX += x; sumOfSquaresX += vnl_math_sqr( x ); sumXY += ( x * y ); sumOfSquaredDifferencesXY += vnl_math_sqr( y - x ); N += 1.0; } ++count; } } // If we are on the boundary, a neighborhood patch might not overlap // with the image. If we have 2 voxels or less for a neighborhood patch // we don't consider it to be a suitable match. if( N < 3.0 ) { return NumericTraits::max(); } if( this->m_SimilarityMetric == PEARSON_CORRELATION ) { RealType varianceX = sumOfSquaresX - vnl_math_sqr( sumX ) / N; varianceX = std::max( varianceX, static_cast( 1.0e-6 ) ); RealType measure = vnl_math_sqr( sumXY ) / varianceX; if( sumXY > 0 ) { return -measure; } else { return measure; } } else if( this->m_SimilarityMetric == MEAN_SQUARES ) { return ( sumOfSquaredDifferencesXY / N ); } else { itkExceptionMacro( "Unrecognized similarity metric." ); } } template void NonLocalPatchBasedImageFilter ::PrintSelf( std::ostream &os, Indent indent ) const { Superclass::PrintSelf( os, indent ); if( this->m_SimilarityMetric == PEARSON_CORRELATION ) { os << "Using Pearson correlation to measure the patch similarity." << std::endl; } else if( this->m_SimilarityMetric == MEAN_SQUARES ) { os << "Using mean squares to measure the patch similarity." << std::endl; } os << indent << "Neighborhood search radius = " << this->m_NeighborhoodSearchRadius << std::endl; os << indent << "Neighborhood patch radius = " << this->m_NeighborhoodPatchRadius << std::endl; } } // end namespace itk #endif ants-2.2.0/Utilities/itkNonLocalSuperresolutionImageFilter.h000066400000000000000000000203031311104306400242640ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkNonLocalSuperresolutionImageFilter_h #define itkNonLocalSuperresolutionImageFilter_h #include "itkNonLocalPatchBasedImageFilter.h" #include "itkConstNeighborhoodIterator.h" namespace itk { /** * \class NonLocalSuperresolutionImageFilter * \brief Implementation of a non-local upsampling (i.e., superresolution) image filter. * * \author Jose V. Manjon with ITK porting by Nick Tustison * * Contributed by * * \par REFERENCE * * José V. Manjón, Pierrick Coupe, Antonio Buades, Vladimir Fonov, Louis Collins and * Montserrat Robles. * "Non-local MRI Upsampling", * Medical Image Analysis, 14:784-792, 2010. * * José V. Manjón, Pierrick Coupe, Antonio Buades, Louis Collins and Montserrat Robles. * "MRI Superresolution Using Self-Similarity and Image Priors", * International Journal of Biomedical Imaging, 2010. * * \ingroup ITKFiltering */ template class NonLocalSuperresolutionImageFilter : public NonLocalPatchBasedImageFilter { public: /** Standard class typedefs. */ typedef NonLocalSuperresolutionImageFilter Self; typedef NonLocalPatchBasedImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Runtime information support. */ itkTypeMacro( NonLocalSuperresolutionImageFilter, NonLocalPatchBasedImageFilter ); /** Standard New method. */ itkNewMacro( Self ); /** ImageDimension constants */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); /** Some convenient typedefs. */ typedef TInputImage InputImageType; typedef typename InputImageType::PixelType InputPixelType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename Superclass::InputImageList InputImageList; typedef typename Superclass::InputImageSetList InputImageSetList; typedef typename Superclass::RegionType RegionType; typedef typename Superclass::IndexType IndexType; typedef TOutputImage OutputImageType; typedef typename OutputImageType::PixelType OutputPixelType; typedef typename Superclass::InputImagePixelVectorType InputImagePixelVectorType; typedef typename Superclass::RealType RealType; typedef typename Superclass::RealImageType RealImageType; typedef typename Superclass::RealImagePointer RealImagePointer; typedef typename Superclass::ConstNeighborhoodIteratorType ConstNeighborhoodIteratorType; typedef typename Superclass::NeighborhoodOffsetListType NeighborhoodOffsetListType; typedef std::vector ScaleLevelsArrayType; /** * Set the low resolution input image to be refined. */ void SetLowResolutionInputImage( const InputImageType *image ) { this->SetInput( const_cast( image ) ); } void SetInput1( const InputImageType *image ) { this->SetLowResolutionInputImage( image ); } /** * Get the low resolution input image to be upsampled. */ const InputImageType* GetLowResolutionInputImage() const { return static_cast( this->ProcessObject::GetInput( 0 ) ); } /** * Set the high resolution input image to be used as the reference input. */ void SetHighResolutionReferenceImage( const InputImageType *image ) { this->SetNthInput( 1, const_cast( image ) ); } void SetInput2( const InputImageType *image ) { this->SetHighResolutionReferenceImage( image ); } /** * Get the high resolution input image to be used as the reference input. */ const InputImageType* GetHighResolutionReferenceImage() const { return static_cast( this->ProcessObject::GetInput( 1 ) ); } /** * Type of the Interpolator Base class */ typedef InterpolateImageFunction InterpolatorType; /** * Set the interpolator. */ itkSetObjectMacro( Interpolator, InterpolatorType ); /** * Get the interpolator. */ itkGetModifiableObjectMacro( Interpolator, InterpolatorType ); /** * Set/get the interpolator. */ itkSetMacro( IntensityDifferenceSigma, RealType ); itkGetConstMacro( IntensityDifferenceSigma, RealType ); /** * Get the current convergence measurement. This is a helper function for * reporting observations. */ itkGetConstMacro( CurrentEpsilon, RealType ); /** * Set/Get the convergence threshold. Default = 0.1. */ itkSetMacro( EpsilonThreshold, RealType ); itkGetConstMacro( EpsilonThreshold, RealType ); /** * Set/get perform initial mean correction. "True" if doing single modality with * interpolated image as the high resolution reference image. "False" otherwise. */ itkSetMacro( PerformInitialMeanCorrection, bool ); itkGetConstMacro( PerformInitialMeanCorrection, bool ); itkBooleanMacro( PerformInitialMeanCorrection ); /** * Get the interpolator. */ itkSetMacro( PatchSimilaritySigma, RealType ); itkGetConstMacro( PatchSimilaritySigma, RealType ); /** * Scale levels for . */ virtual void SetScaleLevels( const ScaleLevelsArrayType list ) { this->m_ScaleLevels = list; this->Modified(); } itkGetConstMacro( ScaleLevels, ScaleLevelsArrayType ); /** * Get the number of elapsed iterations. This is a helper function for * reporting observations. */ itkGetConstMacro( CurrentIteration, SizeValueType ); protected: NonLocalSuperresolutionImageFilter(); ~NonLocalSuperresolutionImageFilter() {} void PrintSelf( std::ostream & os, Indent indent ) const ITK_OVERRIDE; void GenerateData() ITK_OVERRIDE; void ThreadedGenerateData( const RegionType &, ThreadIdType ) ITK_OVERRIDE; void BeforeThreadedGenerateData() ITK_OVERRIDE; void AfterThreadedGenerateData() ITK_OVERRIDE; void AllocateOutputs() ITK_OVERRIDE; void VerifyInputInformation() ITK_OVERRIDE; void GenerateOutputInformation() ITK_OVERRIDE; void GenerateInputRequestedRegion() ITK_OVERRIDE; private: NonLocalSuperresolutionImageFilter( const Self& ) ITK_DELETE_FUNCTION; void operator=( const Self& ) ITK_DELETE_FUNCTION; InputImagePointer PerformMeanCorrection( InputImageType * ); RealType m_EpsilonThreshold; RealType m_CurrentEpsilon; RealImagePointer m_WeightSumImage; RegionType m_TargetImageRequestedRegion; typename InputImageType::Pointer m_InterpolatedLowResolutionInputImage; typename InterpolatorType::Pointer m_Interpolator; RealType m_PatchSimilaritySigma; RealType m_IntensityDifferenceSigma; bool m_PerformInitialMeanCorrection; ScaleLevelsArrayType m_ScaleLevels; SizeValueType m_CurrentIteration; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkNonLocalSuperresolutionImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkNonLocalSuperresolutionImageFilter.hxx000066400000000000000000000372421311104306400246560ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkNonLocalSuperresolutionImageFilter_hxx #define itkNonLocalSuperresolutionImageFilter_hxx #include "itkNonLocalSuperresolutionImageFilter.h" #include "itkAbsoluteValueDifferenceImageFilter.h" #include "itkArray.h" #include "itkBoxMeanImageFilter.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkCastImageFilter.h" #include "itkDivideImageFilter.h" #include "itkImageDuplicator.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkIterationReporter.h" #include "itkLinearInterpolateImageFunction.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkMath.h" #include "itkNeighborhoodIterator.h" #include "itkResampleImageFilter.h" #include "itkStatisticsImageFilter.h" #include "itkSubtractImageFilter.h" #include "itkProgressReporter.h" #include #include namespace itk { template NonLocalSuperresolutionImageFilter ::NonLocalSuperresolutionImageFilter() : m_EpsilonThreshold( 0.1 ), m_PatchSimilaritySigma( 1.0 ), m_IntensityDifferenceSigma( 1.0 ), m_PerformInitialMeanCorrection( false ), m_CurrentIteration( 0 ) { this->SetNumberOfRequiredInputs( 2 ); this->m_WeightSumImage = ITK_NULLPTR; this->m_InterpolatedLowResolutionInputImage = ITK_NULLPTR; // Interpolator --- default to linear typedef LinearInterpolateImageFunction LinearInterpolatorType; this->m_Interpolator = LinearInterpolatorType::New(); this->SetSimilarityMetric( Superclass::MEAN_SQUARES ); } template void NonLocalSuperresolutionImageFilter ::VerifyInputInformation() { } template void NonLocalSuperresolutionImageFilter ::AllocateOutputs() { typename OutputImageType::Pointer outputImage = this->GetOutput(); outputImage->CopyInformation( this->GetHighResolutionReferenceImage() ); outputImage->SetRegions( this->GetHighResolutionReferenceImage()->GetBufferedRegion() ); outputImage->Allocate(); outputImage->FillBuffer( 0 ); } template void NonLocalSuperresolutionImageFilter ::GenerateOutputInformation() { // Call the superclass' implementation of this method Superclass::GenerateOutputInformation(); // Get pointers to the input and output OutputImageType *outputImage = this->GetOutput(); if( !outputImage ) { return; } const InputImageType *referenceImage = this->GetHighResolutionReferenceImage(); // Set the size of the output region if( referenceImage ) { outputImage->SetLargestPossibleRegion( referenceImage->GetLargestPossibleRegion() ); outputImage->SetSpacing( referenceImage->GetSpacing() ); outputImage->SetOrigin( referenceImage->GetOrigin() ); outputImage->SetDirection( referenceImage->GetDirection() ); } } template void NonLocalSuperresolutionImageFilter ::GenerateInputRequestedRegion() { // Call the superclass' implementation of this method Superclass::GenerateInputRequestedRegion(); if ( !this->GetInput() ) { return; } // Get pointers to the input InputImagePointer inputPtr = const_cast< TInputImage * >( this->GetInput() ); // Determining the actual input region is non-trivial, especially // when we cannot assume anything about the transform being used. // So we do the easy thing and request the entire input image. // inputPtr->SetRequestedRegionToLargestPossibleRegion(); } template void NonLocalSuperresolutionImageFilter ::GenerateData() { if( this->m_ScaleLevels.size() == 0 ) { itkExceptionMacro( "There are no scale levels." ); } IterationReporter reporter( this, 0, 1 ); bool isConverged = false; this->m_CurrentIteration = 0; this->m_CurrentEpsilon = NumericTraits::max(); while( this->m_CurrentIteration < this->m_ScaleLevels.size() && isConverged == false ) { reporter.CompletedStep(); this->BeforeThreadedGenerateData(); typename ImageSource::ThreadStruct str1; str1.Filter = this; this->GetMultiThreader()->SetNumberOfThreads( this->GetNumberOfThreads() ); this->GetMultiThreader()->SetSingleMethod( this->ThreaderCallback, &str1 ); this->GetMultiThreader()->SingleMethodExecute(); this->AfterThreadedGenerateData(); OutputImageType * outputImage = this->GetOutput(); typedef DivideImageFilter DividerType; typename DividerType::Pointer divider = DividerType::New(); divider->SetInput1( outputImage ); divider->SetInput2( this->m_WeightSumImage ); divider->Update(); InputImagePointer meanCorrectedImage = this->PerformMeanCorrection( divider->GetOutput() ); typedef AbsoluteValueDifferenceImageFilter AbsoluterType; typename AbsoluterType::Pointer absoluter = AbsoluterType::New(); absoluter->SetInput1( outputImage ); absoluter->SetInput2( meanCorrectedImage ); typedef StatisticsImageFilter StatsFilterType; typename StatsFilterType::Pointer stats = StatsFilterType::New(); stats->SetInput( absoluter->GetOutput() ); stats->Update(); this->m_CurrentEpsilon = stats->GetMean(); if( this->m_CurrentEpsilon < this->m_EpsilonThreshold ) { isConverged = true; } typedef CastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput( meanCorrectedImage ); caster->Update(); this->SetNthOutput( 0, caster->GetOutput() ); this->m_CurrentIteration++; } } template void NonLocalSuperresolutionImageFilter ::BeforeThreadedGenerateData() { if( this->m_CurrentIteration == 0 ) { Superclass::BeforeThreadedGenerateData(); this->m_Interpolator->SetInputImage( this->GetLowResolutionInputImage() ); typedef IdentityTransform IdentityTransformType; typename IdentityTransformType::Pointer identityTransform = IdentityTransformType::New(); identityTransform->SetIdentity(); typedef ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); resampler->SetInterpolator( this->m_Interpolator ); resampler->SetInput( this->GetLowResolutionInputImage() ); resampler->SetTransform( identityTransform ); resampler->SetOutputParametersFromImage( this->GetHighResolutionReferenceImage() ); resampler->Update(); this->m_InterpolatedLowResolutionInputImage = resampler->GetOutput(); // Initialize the weight sum image this->m_WeightSumImage = RealImageType::New(); this->m_WeightSumImage->CopyInformation( this->GetHighResolutionReferenceImage() ); this->m_WeightSumImage->SetRegions( this->GetHighResolutionReferenceImage()->GetBufferedRegion() ); this->m_WeightSumImage->SetLargestPossibleRegion( this->GetHighResolutionReferenceImage()->GetLargestPossibleRegion() ); this->m_WeightSumImage->Allocate(); this->m_WeightSumImage->FillBuffer( 1.0 ); Superclass::SetTargetImageRegion( this->GetHighResolutionReferenceImage()->GetBufferedRegion() ); if( this->m_PerformInitialMeanCorrection ) { InputImageType * referenceImage = const_cast( this->GetHighResolutionReferenceImage() ); InputImagePointer meanCorrectedImage = this->PerformMeanCorrection( referenceImage ); this->SetHighResolutionReferenceImage( meanCorrectedImage ); } this->AllocateOutputs(); } else { typedef CastImageFilter CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput( this->GetOutput() ); caster->Update(); this->m_InterpolatedLowResolutionInputImage = caster->GetOutput(); this->m_WeightSumImage->FillBuffer( 1.0 ); } } template void NonLocalSuperresolutionImageFilter ::ThreadedGenerateData( const RegionType ®ion, ThreadIdType threadId ) { ProgressReporter progress( this, threadId, region.GetNumberOfPixels(), 100 ); const InputImageType *highResolutionInputImage = this->GetInput( 1 ); OutputImageType *outputImage = this->GetOutput(); NeighborhoodOffsetListType searchNeighborhoodOffsetList = this->GetNeighborhoodSearchOffsetList(); SizeValueType searchNeighborhoodSize = searchNeighborhoodOffsetList.size(); // This is used for future extensions to include multiple high reference images InputImageList highResolutionInputImageList; highResolutionInputImageList.push_back( const_cast( highResolutionInputImage ) ); ConstNeighborhoodIteratorType It( this->GetNeighborhoodPatchRadius(), highResolutionInputImage, region ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { progress.CompletedPixel(); IndexType currentCenterIndex = It.GetIndex(); InputImagePixelVectorType highResolutionPatch = this->VectorizeImageListPatch( highResolutionInputImageList, currentCenterIndex, true ); for( SizeValueType i = 0; i < searchNeighborhoodSize; i++ ) { IndexType searchIndex = currentCenterIndex + searchNeighborhoodOffsetList[i]; if( searchIndex == currentCenterIndex ) { continue; } if( !outputImage->GetBufferedRegion().IsInside( searchIndex ) ) { continue; } RealType intensityDifference = It.GetCenterPixel() - highResolutionInputImage->GetPixel( searchIndex ); if( std::fabs( intensityDifference ) > 3.0 * this->m_IntensityDifferenceSigma * vnl_math_sqr( this->m_ScaleLevels[this->m_CurrentIteration] ) ) { continue; } RealType patchSimilarity = this->ComputeNeighborhoodPatchSimilarity( highResolutionInputImageList, searchIndex, highResolutionPatch, true ); RealType intensityWeight = vnl_math_sqr( intensityDifference / ( this->m_IntensityDifferenceSigma * this->m_ScaleLevels[this->m_CurrentIteration] ) ); RealType patchWeight = vnl_math_sqr( patchSimilarity / ( this->m_PatchSimilaritySigma * this->m_ScaleLevels[this->m_CurrentIteration] ) ); RealType weight = std::exp( -( intensityWeight + patchWeight ) ); outputImage->SetPixel( currentCenterIndex, outputImage->GetPixel( currentCenterIndex ) + weight * this->m_InterpolatedLowResolutionInputImage->GetPixel( searchIndex ) ); this->m_WeightSumImage->SetPixel( currentCenterIndex, this->m_WeightSumImage->GetPixel( currentCenterIndex ) + weight ); } } } template void NonLocalSuperresolutionImageFilter ::AfterThreadedGenerateData() { } template typename NonLocalSuperresolutionImageFilter::InputImagePointer NonLocalSuperresolutionImageFilter ::PerformMeanCorrection( InputImageType * image ) { typedef BoxMeanImageFilter BoxMeanFilterType; typename BoxMeanFilterType::Pointer boxMeanFilter = BoxMeanFilterType::New(); boxMeanFilter->SetInput( image ); typename InputImageType::SpacingType lowResolutionSpacing = this->GetLowResolutionInputImage()->GetSpacing(); typename InputImageType::SpacingType highResolutionSpacing = this->GetHighResolutionReferenceImage()->GetSpacing(); typename BoxMeanFilterType::RadiusType boxRadius; for( SizeValueType d = 0; d < ImageDimension; d++ ) { boxRadius[d] = static_cast( std::ceil( highResolutionSpacing[d] / lowResolutionSpacing[d] ) ) - 1; } boxMeanFilter->SetRadius( boxRadius ); boxMeanFilter->Update(); typedef NearestNeighborInterpolateImageFunction NearestNeighborInterpolatorType; typename NearestNeighborInterpolatorType::Pointer nearestNeighborInterpolator = NearestNeighborInterpolatorType::New(); nearestNeighborInterpolator->SetInputImage( boxMeanFilter->GetOutput() ); typedef IdentityTransform IdentityTransformType; typename IdentityTransformType::Pointer identityTransform = IdentityTransformType::New(); identityTransform->SetIdentity(); typedef ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); resampler->SetInterpolator( nearestNeighborInterpolator ); resampler->SetInput( boxMeanFilter->GetOutput() ); resampler->SetTransform( identityTransform ); resampler->SetOutputParametersFromImage( this->GetLowResolutionInputImage() ); typedef SubtractImageFilter SubtracterType; typename SubtracterType::Pointer subtracter = SubtracterType::New(); subtracter->SetInput1( resampler->GetOutput() ); subtracter->SetInput2( this->GetLowResolutionInputImage() ); subtracter->Update(); nearestNeighborInterpolator->SetInputImage( subtracter->GetOutput() ); typedef ResampleImageFilter ResamplerType2; typename ResamplerType2::Pointer resampler2 = ResamplerType2::New(); resampler2->SetInterpolator( nearestNeighborInterpolator ); resampler2->SetInput( subtracter->GetOutput() ); resampler2->SetTransform( identityTransform ); resampler2->SetOutputParametersFromImage( this->GetHighResolutionReferenceImage() ); typedef SubtractImageFilter SubtracterType2; typename SubtracterType2::Pointer subtracter2 = SubtracterType2::New(); subtracter2->SetInput1( image ); subtracter2->SetInput2( resampler2->GetOutput() ); subtracter2->Update(); ImageRegionIteratorWithIndex It( subtracter2->GetOutput(), subtracter2->GetOutput()->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { if( It.Get() < NumericTraits::ZeroValue() ) { It.Set( this->m_InterpolatedLowResolutionInputImage->GetPixel( It.GetIndex() ) ); } } InputImagePointer meanCorrectedImage = subtracter2->GetOutput(); meanCorrectedImage->DisconnectPipeline(); return meanCorrectedImage; } template void NonLocalSuperresolutionImageFilter ::PrintSelf( std::ostream &os, Indent indent ) const { Superclass::PrintSelf( os, indent ); os << "Interpolator: " << std::endl; this->m_Interpolator->Print( os, indent ); os << indent << "Intensity difference sigma = " << this->m_IntensityDifferenceSigma << std::endl; os << indent << "Patch similarity sigma = " << this->m_PatchSimilaritySigma << std::endl; } } // end namespace itk #endif ants-2.2.0/Utilities/itkOptimalSharpeningImageFilter.h000066400000000000000000000116321311104306400230450ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkOptimalSharpeningImageFilter_h #define __itkOptimalSharpeningImageFilter_h #include "itkNumericTraits.h" #include "itkImageToImageFilter.h" #include "itkImage.h" namespace itk { /** * \class OptimalSharpeningImageFilter * * This filter sharpens an image using a Laplacian. OptimalSharpening * highlights regions of rapid intensity change and therefore * highlights or enhances the edges. The result is an image that * appears more in focus. * * \par The OptimalSharpening at each pixel location is computed by * convolution with the itk::LaplacianOperator. * * \par Inputs and Outputs * The input to this filter is a scalar-valued itk::Image of arbitrary * dimension. The output is a scalar-valued itk::Image. * \sa Image * \sa Neighborhood * \sa NeighborhoodOperator * \sa NeighborhoodIterator * \sa LaplacianOperator * * \ingroup ImageFeatureExtraction */ template class OptimalSharpeningImageFilter : public ImageToImageFilter { public: /** Standard "Self" & Superclass typedef. */ typedef OptimalSharpeningImageFilter Self; typedef ImageToImageFilter Superclass; /** Extract some information from the image types. Dimensionality * of the two images is assumed to be the same. */ typedef typename TOutputImage::PixelType OutputPixelType; typedef typename TOutputImage::InternalPixelType OutputInternalPixelType; typedef typename NumericTraits::RealType RealType; typedef typename TInputImage::PixelType InputPixelType; typedef typename TInputImage::InternalPixelType InputInternalPixelType; itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension); /** Image typedef support. */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef typename InputImageType::Pointer InputImagePointer; /** Smart pointer typedef support. */ typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods) */ itkTypeMacro(OptimalSharpeningImageFilter, ImageToImageFilter); /** Method for creation through the object factory. */ itkNewMacro(Self); /** OptimalSharpeningImageFilter needs a larger input requested * region than the output requested region (larger in the direction * of the derivative). As such, OptimalSharpeningImageFilter * needs to provide an implementation for * GenerateInputRequestedRegion() in order to inform the pipeline * execution model. * * \sa ImageToImageFilter::GenerateInputRequestedRegion() */ virtual void GenerateInputRequestedRegion() throw (InvalidRequestedRegionError); void SetSValue(float s) { this->m_SValue = s; } /** Use the image spacing information in calculations. Use this option if you * want derivatives in physical space. Default is UseImageSpacingOn. */ void SetUseImageSpacingOn() { this->SetUseImageSpacing(true); } /** Ignore the image spacing. Use this option if you want derivatives in isotropic pixel space. Default is UseImageSpacingOn. */ void SetUseImageSpacingOff() { this->SetUseImageSpacing(false); } /** Set/Get whether or not the filter will use the spacing of the input image in its calculations */ itkSetMacro(UseImageSpacing, bool); itkGetMacro(UseImageSpacing, bool); protected: OptimalSharpeningImageFilter() { m_UseImageSpacing = true; m_SValue = 0.5; } virtual ~OptimalSharpeningImageFilter() { } /** Standard pipeline method. While this class does not implement a * ThreadedGenerateData(), its GenerateData() delegates all * calculations to an NeighborhoodOperatorImageFilter. Since the * NeighborhoodOperatorImageFilter is multithreaded, this filter is * multithreaded by default. */ void GenerateData(); void PrintSelf(std::ostream &, Indent) const; private: OptimalSharpeningImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented bool m_UseImageSpacing; float m_SValue; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkOptimalSharpeningImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkOptimalSharpeningImageFilter.hxx000066400000000000000000000200601311104306400234200ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkOptimalSharpeningImageFilter_hxx #define __itkOptimalSharpeningImageFilter_hxx #include "itkOptimalSharpeningImageFilter.h" #include "itkNeighborhoodOperatorImageFilter.h" #include "itkLaplacianOperator.h" #include "itkZeroFluxNeumannBoundaryCondition.h" #include "itkProgressAccumulator.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkImageRegionIterator.h" namespace itk { template void OptimalSharpeningImageFilter ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "UseImageSpacing = " << m_UseImageSpacing << std::endl; } template void OptimalSharpeningImageFilter ::GenerateInputRequestedRegion() throw (InvalidRequestedRegionError) { // call the superclass' implementation of this method. this should // copy the output requested region to the input requested region Superclass::GenerateInputRequestedRegion(); // get pointers to the input and output InputImagePointer inputPtr = const_cast( this->GetInput() ); if( !inputPtr ) { return; } // Build an operator so that we can determine the kernel size LaplacianOperator oper; oper.CreateOperator(); // get a copy of the input requested region (should equal the output // requested region) typename TInputImage::RegionType inputRequestedRegion; inputRequestedRegion = inputPtr->GetRequestedRegion(); // pad the input requested region by the operator radius inputRequestedRegion.PadByRadius( oper.GetRadius() ); // crop the input requested region at the input's largest possible region if( inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion() ) ) { inputPtr->SetRequestedRegion( inputRequestedRegion ); return; } else { // Couldn't crop the region (requested region is outside the largest // possible region). Throw an exception. // store what we tried to request (prior to trying to crop) inputPtr->SetRequestedRegion( inputRequestedRegion ); // build an exception InvalidRequestedRegionError e(__FILE__, __LINE__); e.SetLocation(ITK_LOCATION); e.SetDescription("Requested region is (at least partially) outside the largest possible region."); e.SetDataObject(inputPtr); throw e; } } template void OptimalSharpeningImageFilter ::GenerateData() { // Create the Laplacian operator LaplacianOperator oper; double s[ImageDimension]; for( unsigned i = 0; i < ImageDimension; i++ ) { if( this->GetInput()->GetSpacing()[i] == 0.0 ) { itkExceptionMacro( << "Image spacing cannot be zero" ); } else { s[i] = 1.0 / this->GetInput()->GetSpacing()[i]; } } oper.SetDerivativeScalings( s ); oper.CreateOperator(); // do calculations in floating point typedef Image RealImageType; typedef NeighborhoodOperatorImageFilter NOIF; ZeroFluxNeumannBoundaryCondition nbc; typename NOIF::Pointer filter = NOIF::New(); filter->OverrideBoundaryCondition(static_cast(&nbc) ); // Create a process accumulator for tracking the progress of this minipipeline ProgressAccumulator::Pointer progress = ProgressAccumulator::New(); progress->SetMiniPipelineFilter(this); // Register the filter with the with progress accumulator using // equal weight proportion progress->RegisterInternalFilter(filter, 0.8f); // // set up the mini-pipline // filter->SetOperator(oper); filter->SetInput(this->GetInput() ); filter->GetOutput() ->SetRequestedRegion( this->GetOutput()->GetRequestedRegion() ); // execute the mini-pipeline filter->Update(); // determine how the data will need to scaled to be properly combined typename MinimumMaximumImageCalculator::Pointer inputCalculator = MinimumMaximumImageCalculator::New(); typename MinimumMaximumImageCalculator::Pointer filteredCalculator = MinimumMaximumImageCalculator::New(); inputCalculator->SetImage(this->GetInput() ); inputCalculator->SetRegion(this->GetOutput()->GetRequestedRegion() ); inputCalculator->Compute(); filteredCalculator->SetImage(filter->GetOutput() ); filteredCalculator->SetRegion(this->GetOutput()->GetRequestedRegion() ); filteredCalculator->Compute(); RealType inputShift, inputScale, filteredShift, filteredScale; inputShift = static_cast(inputCalculator->GetMinimum() ); inputScale = static_cast(inputCalculator->GetMaximum() ) - static_cast(inputCalculator->GetMinimum() ); filteredShift = filteredCalculator->GetMinimum(); // no need to cast filteredScale = filteredCalculator->GetMaximum() - filteredCalculator->GetMinimum(); ImageRegionIterator it(filter->GetOutput(), filter->GetOutput()->GetRequestedRegion() ); ImageRegionConstIterator inIt(this->GetInput(), this->GetOutput()->GetRequestedRegion() ); // combine the input and laplacian images RealType value, invalue; RealType inputSum = 0.0; RealType enhancedSum = 0.0; while( !it.IsAtEnd() ) { value = it.Get(); // laplacian value // rescale to [0,1] value = (value - filteredShift) / filteredScale; // rescale to the input dynamic range value = value * inputScale + inputShift; // combine the input and laplacian image (note that we subtract // the laplacian due to the signs in our laplacian kernel). invalue = static_cast(inIt.Get() ); value = invalue - value * this->m_SValue; it.Set( value ); inputSum += invalue; enhancedSum += value; ++it; ++inIt; } RealType inputMean = inputSum / static_cast(this->GetOutput()->GetRequestedRegion() .GetNumberOfPixels() ); RealType enhancedMean = enhancedSum / static_cast(this->GetOutput()->GetRequestedRegion() .GetNumberOfPixels() ); // update progress this->UpdateProgress( 0.9 ); // copy and cast the output typename TOutputImage::Pointer output = this->GetOutput(); output->SetBufferedRegion(output->GetRequestedRegion() ); output->Allocate(); RealType inputMinimum = inputCalculator->GetMinimum(); RealType inputMaximum = inputCalculator->GetMaximum(); OutputPixelType castInputMinimum = static_cast(inputMinimum); OutputPixelType castInputMaximum = static_cast(inputMaximum); ImageRegionIterator outIt = ImageRegionIterator(output, output->GetRequestedRegion() ); outIt.GoToBegin(); it.GoToBegin(); while( !outIt.IsAtEnd() ) { value = it.Get(); // adjust value to make the mean intensities before and after match value = value - enhancedMean + inputMean; if( value < inputMinimum ) { outIt.Set( castInputMinimum ); } else if( value > inputMaximum ) { outIt.Set( castInputMaximum ); } else { outIt.Set( static_cast(value) ); } ++outIt; ++it; } // update progress this->UpdateProgress( 1.0 ); } } // end namespace itk #endif ants-2.2.0/Utilities/itkPointSetFunction.h000066400000000000000000000102251311104306400205600ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkPointSetFunction_h #define __itkPointSetFunction_h #include "itkFunctionBase.h" #include "itkPoint.h" #include "itkIndex.h" #include "itkContinuousIndex.h" #include "itkImageBase.h" namespace itk { /** \class PointSetFunction * \brief Evaluates a function of an image at specified position. * * PointSetFunction is a baseclass for all objects that evaluates * a function of an image at index, continuous index or point. * This class is templated over the input image type, the type * of the function output and the coordinate representation type * (e.g. float or double). * * The input image is set via method SetInputPointSet(). * Methods Evaluate, EvaluateAtIndex and EvaluateAtContinuousIndex * respectively evaluates the function at an geometric point, * image index and continuous image index. * * \warning Image BufferedRegion information is cached during * in SetInputPointSet( image ). If the image BufferedRegion has changed * one must call SetInputPointSet( image ) again to update the cache * to the current values. * * \sa Point * \sa Index * \sa ContinuousIndex * * \ingroup PointSetFunctions */ template < class TInputPointSet, class TOutput, class TCoordRep = float > class PointSetFunction : public FunctionBase { public: /** Dimension underlying input point set. */ itkStaticConstMacro(Dimension, unsigned int, TInputPointSet::PointDimension); /** Standard class typedefs. */ typedef PointSetFunction Self; typedef FunctionBase Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro( PointSetFunction, FunctionBase ); /** InputPointSetType typedef support. */ typedef TInputPointSet InputPointSetType; /** InputPixel typedef support */ typedef typename InputPointSetType::PointType InputPointType; typedef typename InputPointSetType::PixelType InputPixelType; /** InputPointSetPointer typedef support */ typedef typename InputPointSetType::ConstPointer InputPointSetConstPointer; /** OutputType typedef support. */ typedef TOutput OutputType; /** CoordRepType typedef support. */ typedef TCoordRep CoordRepType; /** Set the input point set. * \warning this method caches BufferedRegion information. * If the BufferedRegion has changed, user must call * SetInputPointSet again to update cached values. */ virtual void SetInputPointSet( const InputPointSetType * ptr ); /** Get the input image. */ const InputPointSetType * GetInputPointSet() const { return m_PointSet.GetPointer(); } /** Evaluate the function at specified Point position. * Subclasses must provide this method. */ virtual TOutput Evaluate( const InputPointType& point ) const = 0; protected: PointSetFunction(); ~PointSetFunction() { } void PrintSelf(std::ostream& os, Indent indent) const; /** Const pointer to the input image. */ InputPointSetConstPointer m_PointSet; private: PointSetFunction(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk // Define instantiation macro for this template. #define ITK_TEMPLATE_PointSetFunction(_, EXPORT, x, y) namespace itk { \ _(3 (class EXPORT PointSetFunction ) ) \ namespace Templates { typedef PointSetFunction PointSetFunction##y; } \ } #ifndef ITK_MANUAL_INSTANTIATION #include "itkPointSetFunction.hxx" #endif #endif ants-2.2.0/Utilities/itkPointSetFunction.hxx000066400000000000000000000027671311104306400211540ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkPointSetFunction_hxx #define __itkPointSetFunction_hxx #include "itkPointSetFunction.h" namespace itk { /** * Constructor */ template PointSetFunction ::PointSetFunction() { m_PointSet = NULL; } /** * Standard "PrintSelf" method */ template void PointSetFunction ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << "InputPointSet: " << m_PointSet.GetPointer() << std::endl; } /** * Initialize by setting the input point set */ template void PointSetFunction ::SetInputPointSet( const InputPointSetType * ptr ) { // set the input image m_PointSet = ptr; } } // end namespace itk #endif ants-2.2.0/Utilities/itkPseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter.h000066400000000000000000000135561311104306400320150ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkPseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter_h #define __itkPseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter_h #include "itkImageToImageFilter.h" namespace itk { /** \class PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter * \brief Join N-D images into an (N+1)-D image * * This filter is templated over the input image type and the output image * type. The pixel type of them must be the same and the input dimension * must be less than the output dimension. * When the input images are N-dimensinal, they are joined in order and * the size of the N+1'th dimension of the output is same as the number of * the inputs. The spacing and the origin (where the first input is placed) * for the N+1'th dimension is specified in this filter. The output image * informations for the first N dimensions are taken from the first input. * Note that all the inputs should have the same information. * * \ingroup GeometricTransform * \ingroup MultiThreaded * \ingroup Streamed * * \author Hideaki Hiraki * * Contributed in the users list * http://public.kitware.com/pipermail/insight-users/2004-February/006542.html * * \ingroup ITKImageCompose */ template class PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter, ImageToImageFilter); /** Compiler can't inherit typedef? */ typedef TInputImage InputImageType; typedef typename Superclass::OutputImageType OutputImageType; typedef TReferenceImage ReferenceImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename ReferenceImageType::Pointer ReferenceImagePointer; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::RegionType OutputImageRegionType; typedef typename ReferenceImageType::RegionType ReferenceImageRegionType; /** Compiler can't inherit ImageDimension enumeration? */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); itkStaticConstMacro(ReferenceImageDimension, unsigned int, TReferenceImage::ImageDimension); itkGetMacro(TI1, float); itkSetMacro(TI1, float); itkGetMacro(TI2, float); itkSetMacro(TI2, float); itkGetMacro(T1blood, float); itkSetMacro(T1blood, float); itkGetMacro(Alpha, float); itkSetMacro(Alpha, float); itkGetMacro(Lambda, float); itkSetMacro(Lambda, float); itkGetMacro(SliceDelay, float); itkSetMacro(SliceDelay, float); void SetDifferenceImage( const InputImageType * image ); void SetReferenceImage( const ReferenceImageType * image ); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro( InputConvertibleToOutputCheck, ( Concept::Convertible ) ); /** End concept checking */ #endif protected: PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter(); ~PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter() { } typename TInputImage::ConstPointer GetDifferenceImage(); typename TReferenceImage::ConstPointer GetReferenceImage(); void PrintSelf(std::ostream & os, Indent indent) const; /** PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter can be implemented as a multithreaded filter. * \sa ImageSource::ThreadedGenerateData(), * ImageSource::GenerateData() */ virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId); private: PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** IndexValueType is used to switch among the inputs and * is used as the index value of the new dimension */ typedef unsigned int IndexValueType; float m_TI1; float m_TI2; float m_T1blood; float m_Lambda; float m_Alpha; float m_SliceDelay; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkPseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkPseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter.hxx000066400000000000000000000121551311104306400323670ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkPseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter_hxx #define __itkPseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter_hxx #include "itkPseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter.h" #include "itkProgressReporter.h" #include "itkImageRegionIterator.h" namespace itk { template PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter ::PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter() { this->m_TI1 = 700; this->m_TI2 = 1700; this->m_T1blood = 1664; this->m_Lambda = 0.9; this->m_Alpha = 0.85; //0.95; this->m_SliceDelay = 45; this->SetNumberOfRequiredInputs( 2 ); } template void PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); } template void PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter ::SetDifferenceImage(const TInputImage* img) { SetNthInput(0, const_cast(img)); } template typename TInputImage::ConstPointer PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter ::GetDifferenceImage() { return static_cast< const TInputImage * > ( this->ProcessObject::GetInput(0) ); } template void PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter ::SetReferenceImage(const TReferenceImage* ref) { SetNthInput(1, const_cast(ref)); } template typename TReferenceImage::ConstPointer PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter ::GetReferenceImage() { return static_cast< const TReferenceImage * > ( this->ProcessObject::GetInput(1) ); } template void PseudoContinuousArterialSpinLabeledCerebralBloodFlowImageFilter ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { itkDebugMacro(<< "Actually executing"); ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); OutputImageRegionType outputRegion = outputRegionForThread; InputImageRegionType inputRegion; this->CallCopyOutputRegionToInputRegion(inputRegion, outputRegionForThread); ImageRegionIterator outIt( this->GetOutput(), outputRegion); ImageRegionConstIterator inIt( this->GetInput(), inputRegion ); while( !outIt.IsAtEnd() ) { // account for delay between acquisition of slices // const float TI = ( this->m_TI2 - this->m_TI1) + this->m_SliceDelay * (outIt.GetIndex()[2] - 1); typename ReferenceImageType::IndexType idx; for ( unsigned int i=0; iGetReferenceImage()->GetPixel( idx ); //1.06 * M0W * exp( 1 / 40.0 - 1 / 80.0) * TE; //RealType deltaM = itIt.Value(); // control - tagged if control is odd //RealType T_1a = 1650; // 3T from ASL_whyNhow.pdf //RealType T_1t = 1300; // 3T // from "Impact of equilibrium magnetization of blood on ASL quantification" //RealType tau = 2100; // FIXME milliseconds from PMC3049525 //RealType w = 700; // FIXME milliseconds from PMC3049525 // Label width: Not in dicom, but sequence-specific -- magic parameter. Reference values: pCASL 1.5, CASL 1.6, // PASL 0.7. // from PMC3049525 //const RealType scaling = 4 * this->Alpha * M_0 * T_1t // * ( exp( -1.0 * ( tau + w ) / T_1a ) - exp( -1.0 * w / T_1t ) ); //float cbf = this->Lambda * inIt.Value() * ( -1.0 ) / scaling; float cbf = 0; outIt.Set( cbf ); ++outIt; ++inIt; } } } // end namespace itk #endif ants-2.2.0/Utilities/itkPulsedArterialSpinLabeledCerebralBloodFlowImageFilter.h000066400000000000000000000134321311104306400277140ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkPulsedArterialSpinLabeledCerebralBloodFlowImageFilter_h #define __itkPulsedArterialSpinLabeledCerebralBloodFlowImageFilter_h #include "itkImageToImageFilter.h" namespace itk { /** \class PulsedArterialSpinLabeledCerebralBloodFlowImageFilter * \brief Join N-D images into an (N+1)-D image * * This filter is templated over the input image type and the output image * type. The pixel type of them must be the same and the input dimension * must be less than the output dimension. * When the input images are N-dimensinal, they are joined in order and * the size of the N+1'th dimension of the output is same as the number of * the inputs. The spacing and the origin (where the first input is placed) * for the N+1'th dimension is specified in this filter. The output image * informations for the first N dimensions are taken from the first input. * Note that all the inputs should have the same information. * * \ingroup GeometricTransform * \ingroup MultiThreaded * \ingroup Streamed * * \author Hideaki Hiraki * * Contributed in the users list * http://public.kitware.com/pipermail/insight-users/2004-February/006542.html * * \ingroup ITKImageCompose */ template class PulsedArterialSpinLabeledCerebralBloodFlowImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef PulsedArterialSpinLabeledCerebralBloodFlowImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(PulsedArterialSpinLabeledCerebralBloodFlowImageFilter, ImageToImageFilter); /** Compiler can't inherit typedef? */ typedef TInputImage InputImageType; typedef typename Superclass::OutputImageType OutputImageType; typedef TReferenceImage ReferenceImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename ReferenceImageType::Pointer ReferenceImagePointer; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::RegionType OutputImageRegionType; typedef typename ReferenceImageType::RegionType ReferenceImageRegionType; /** Compiler can't inherit ImageDimension enumeration? */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); itkStaticConstMacro(ReferenceImageDimension, unsigned int, TReferenceImage::ImageDimension); itkGetMacro(TI1, float); itkSetMacro(TI1, float); itkGetMacro(TI2, float); itkSetMacro(TI2, float); itkGetMacro(T1blood, float); itkSetMacro(T1blood, float); itkGetMacro(Alpha, float); itkSetMacro(Alpha, float); itkGetMacro(Lambda, float); itkSetMacro(Lambda, float); itkGetMacro(SliceDelay, float); itkSetMacro(SliceDelay, float); void SetDifferenceImage( const InputImageType * image ); void SetReferenceImage( const ReferenceImageType * image ); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro( InputConvertibleToOutputCheck, ( Concept::Convertible ) ); /** End concept checking */ #endif protected: PulsedArterialSpinLabeledCerebralBloodFlowImageFilter(); ~PulsedArterialSpinLabeledCerebralBloodFlowImageFilter() { } typename TInputImage::ConstPointer GetDifferenceImage(); typename TReferenceImage::ConstPointer GetReferenceImage(); void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; /** PulsedArterialSpinLabeledCerebralBloodFlowImageFilter can be implemented as a multithreaded filter. * \sa ImageSource::ThreadedGenerateData(), * ImageSource::GenerateData() */ virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) ITK_OVERRIDE; private: PulsedArterialSpinLabeledCerebralBloodFlowImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** IndexValueType is used to switch among the inputs and * is used as the index value of the new dimension */ typedef unsigned int IndexValueType; float m_TI1; float m_TI2; float m_T1blood; float m_Lambda; float m_Alpha; float m_SliceDelay; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkPulsedArterialSpinLabeledCerebralBloodFlowImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkPulsedArterialSpinLabeledCerebralBloodFlowImageFilter.hxx000066400000000000000000000105651311104306400303000ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkPulsedArterialSpinLabeledCerebralBloodFlowImageFilter_hxx #define __itkPulsedArterialSpinLabeledCerebralBloodFlowImageFilter_hxx #include "itkPulsedArterialSpinLabeledCerebralBloodFlowImageFilter.h" #include "itkProgressReporter.h" #include "itkImageRegionIterator.h" namespace itk { template PulsedArterialSpinLabeledCerebralBloodFlowImageFilter ::PulsedArterialSpinLabeledCerebralBloodFlowImageFilter() { this->m_TI1 = 700; this->m_TI2 = 1700; this->m_T1blood = 1664; this->m_Lambda = 0.9; this->m_Alpha = 0.95; this->m_SliceDelay = 45; this->SetNumberOfRequiredInputs( 2 ); } template void PulsedArterialSpinLabeledCerebralBloodFlowImageFilter ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); } template void PulsedArterialSpinLabeledCerebralBloodFlowImageFilter ::SetDifferenceImage(const TInputImage* img) { this->SetNthInput(0, const_cast(img)); } template typename TInputImage::ConstPointer PulsedArterialSpinLabeledCerebralBloodFlowImageFilter ::GetDifferenceImage() { return static_cast< const TInputImage * > ( this->ProcessObject::GetInput(0) ); } template void PulsedArterialSpinLabeledCerebralBloodFlowImageFilter ::SetReferenceImage(const TReferenceImage* ref) { this->SetNthInput(1, const_cast(ref)); } template typename TReferenceImage::ConstPointer PulsedArterialSpinLabeledCerebralBloodFlowImageFilter ::GetReferenceImage() { return static_cast< const TReferenceImage * > ( this->ProcessObject::GetInput(1) ); } template void PulsedArterialSpinLabeledCerebralBloodFlowImageFilter ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { itkDebugMacro(<< "Actually executing"); ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); OutputImageRegionType outputRegion = outputRegionForThread; InputImageRegionType inputRegion; this->CallCopyOutputRegionToInputRegion(inputRegion, outputRegionForThread); ImageRegionIterator outIt( this->GetOutput(), outputRegion); ImageRegionConstIterator inIt( this->GetInput(), inputRegion ); while( !outIt.IsAtEnd() ) { // account for delay between acquisition of slices float TI = ( this->m_TI2 - this->m_TI1) + this->m_SliceDelay * (outIt.GetIndex()[2] - 1); typename ReferenceImageType::IndexType idx; for ( unsigned int i=0; iGetReferenceImage()->GetPixel( idx ); // 540,000 is a unit conversion to give ml/100g/min float cbf = ratio * 5400000.0 * this->m_Lambda / ( 2.0 * this->m_Alpha * this->m_TI1 * exp( -TI / this->m_T1blood ) ); outIt.Set( cbf ); ++outIt; ++inIt; } } } // end namespace itk #endif ants-2.2.0/Utilities/itkSliceTimingCorrectionImageFilter.h000066400000000000000000000160261311104306400236620ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkSliceTimingCorrectionImageFilter_h #define __itkSliceTimingCorrectionImageFilter_h #include "itkImageToImageFilter.h" #include "itkExtrapolateImageFunction.h" #include "itkInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" namespace itk { /** \class SliceTimingCorrectionImageFilter * \brief Finds difference signal from alternating signal * * This filter is templated over the input image type and the output image * type. Each signal is interpolated over the entire range of the * subtraction dimension. The output image is the difference between * the two intepolated signals. * * \ingroup GeometricTransform * \ingroup MultiThreaded * \ingroup Streamed * * \author Jeffrey Duda * */ template class SliceTimingCorrectionImageFilter : public ImageToImageFilter { public: /** Standard class typedefs. */ typedef SliceTimingCorrectionImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(SliceTimingCorrectionImageFilter, ImageToImageFilter); /** Compiler can't inherit typedef? */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::OutputImageType OutputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::RegionType OutputImageRegionType; typedef LinearInterpolateImageFunction DefaultInterpolatorType; typedef typename DefaultInterpolatorType::Pointer DefaultInterpolatorPointerType; typedef InterpolateImageFunction< InputImageType, double > InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointerType; //typedef typename InputImageType::SpacingType::ValueType TimingType; typedef double TimingType; /** Compiler can't inherit ImageDimension enumeration? */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); itkGetMacro( TimeDimension, unsigned int ); itkSetMacro( TimeDimension, unsigned int ); itkSetMacro( SliceDimension, unsigned int ); itkGetMacro( SliceDimension, unsigned int ); itkGetMacro( IndexPadding, unsigned int ); itkSetMacro( IndexPadding, unsigned int ); itkSetMacro( SliceTiming, TimingType ); itkGetMacro( SliceTiming, TimingType ); itkSetMacro( ExtrapolateEdges, bool ); itkGetMacro( ExtrapolateEdges, bool ); /** Set the interpolator function. The default is * LinearInterpolateImageFunction. WindowedSincInterpolateImageFunction * is often suggested in the literature. Some * other options are NearestNeighborInterpolateImageFunction * (useful for binary masks and other images with a small number of * possible pixel values), and BSplineInterpolateImageFunction * (which provides a higher order of interpolation). */ itkSetObjectMacro(Interpolator, InterpolatorType); /** Get a pointer to the interpolator function. */ itkGetConstObjectMacro(Interpolator, InterpolatorType); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro( InputConvertibleToOutputCheck, ( Concept::Convertible ) ); itkConceptMacro( DimensionCheck, ( Concept::SameDimension ) ); /** End concept checking */ #endif protected: SliceTimingCorrectionImageFilter(); ~SliceTimingCorrectionImageFilter() { } void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; /** Override VeriyInputInformation() to add the additional check * that all inputs have the same number of components. * * \sa ProcessObject::VerifyInputInformation */ virtual void VerifyInputInformation() ITK_OVERRIDE; /** Overrides GenerateOutputInformation() in order to produce * an image which has a different information than the first input. * \sa ProcessObject::GenerateOutputInformaton() */ virtual void GenerateOutputInformation() ITK_OVERRIDE; /** Overrides GenerateInputRequestedRegion() in order to inform * the pipeline execution model of different input requested regions * than the output requested region. * \sa ImageToImageFilter::GenerateInputRequestedRegion() */ //virtual void GenerateInputRequestedRegion(); /** This method is used to set the state of the filter before * multi-threading. */ virtual void BeforeThreadedGenerateData() ITK_OVERRIDE; /** SliceTimingCorrectionImageFilter can be implemented as a multithreaded filter. * \sa ImageSource::ThreadedGenerateData(), * ImageSource::GenerateData() */ virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) ITK_OVERRIDE; private: SliceTimingCorrectionImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** IndexValueType is used to switch among the inputs and * is used as the index value of the new dimension */ typedef unsigned int IndexValueType; unsigned int m_TimeDimension; unsigned int m_SliceDimension; unsigned int m_IndexPadding; TimingType m_SliceTiming; InterpolatorPointerType m_Interpolator; // Image function for // interpolation bool m_ExtrapolateEdges; // ExtrapolatorPointerType m_Extrapolator; // Image function for // extrapolation }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkSliceTimingCorrectionImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkSliceTimingCorrectionImageFilter.hxx000066400000000000000000000242741311104306400242460ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkSliceTimingCorrectionImageFilter_hxx #define __itkSliceTimingCorrectionImageFilter_hxx #include "itkSliceTimingCorrectionImageFilter.h" #include "itkProgressReporter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIterator.h" namespace itk { template SliceTimingCorrectionImageFilter ::SliceTimingCorrectionImageFilter() { m_Interpolator = dynamic_cast< InterpolatorType * > ( DefaultInterpolatorType::New().GetPointer() ); m_IndexPadding = 1; m_TimeDimension = InputImageDimension-1; m_SliceDimension = InputImageDimension-2; m_SliceTiming = 0.0; m_ExtrapolateEdges = true; } template void SliceTimingCorrectionImageFilter ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "TimeDimension: " << m_TimeDimension << std::endl; } template void SliceTimingCorrectionImageFilter ::VerifyInputInformation() { Superclass::VerifyInputInformation(); itkDebugMacro(<< "Verify Input Information"); // Verify that all input have the same number of components typename InputImageType::ConstPointer image = this->GetInput(); if( image.IsNull() ) { itkExceptionMacro( << "Input not set as expected!" ); } const unsigned int numComponents = image->GetNumberOfComponentsPerPixel(); for( IndexValueType idx = 1; idx < this->GetNumberOfInputs(); ++idx ) { image = this->GetInput(idx); // if the input was not set it could still be null if( image.IsNull() ) { // an invalid requested region exception will be generated later. continue; } if( numComponents != image->GetNumberOfComponentsPerPixel() ) { itkExceptionMacro( << "Primary input has " << numComponents << " numberOfComponents " << "but input " << idx << " has " << image->GetNumberOfComponentsPerPixel() << "!" ); } } } /** * \sa UnaryFunctorImageFilter::GenerateOutputInformation() */ template void SliceTimingCorrectionImageFilter ::GenerateOutputInformation() { //std::cout << "GenerateOutputInformation" << std::endl; itkDebugMacro(<< "GenerateOutputInformation"); // do not call the superclass' implementation of this method since // this filter allows the input and output to be of different size // get pointers to the input and output typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); typename Superclass::InputImageConstPointer inputPtr = this->GetInput(); if( !outputPtr || !inputPtr ) { return; } // Check for valid time dimension if ( !(this->m_TimeDimension < InputImageDimension) ) { itkExceptionMacro( << "Time dimension must be within dimensions of the input image" ); } // Set the output image largest possible region. Use a RegionCopier // so that the input and output images can be different dimensions. OutputImageRegionType outputLargestPossibleRegion; this->CallCopyInputRegionToOutputRegion( outputLargestPossibleRegion, inputPtr->GetLargestPossibleRegion() ); TimingType timingCoverage = inputPtr->GetLargestPossibleRegion().GetSize()[m_SliceDimension] * m_SliceTiming; itkDebugMacro( "Timing coverage = " << timingCoverage ); double timingDiff = inputPtr->GetSpacing()[m_TimeDimension] - timingCoverage; if ( timingDiff < 0 ) { std::cout << "Timing diff = " << timingDiff << std::endl; std::cout << "Timing coverage = " << timingCoverage << std::endl; std::cout << "Volume timing = " << inputPtr->GetSpacing()[m_TimeDimension] << std::endl; std::cout << "Slice dimension = " << m_SliceDimension << std::endl; std::cout << "Slice Dimension Size = " << inputPtr->GetLargestPossibleRegion().GetSize()[m_SliceDimension] << std::endl; itkExceptionMacro( << "SliceTiming * Dim[SliceDimension] should not be greater than Spacing[TimeDimension]" ); } // for the time dimension if ( (2 * m_IndexPadding) >= this->GetInput()->GetLargestPossibleRegion().GetSize()[m_TimeDimension] ) { itkExceptionMacro( << "IndexPadding is too large for this input image" ); } //unsigned int nTimePointsOut = this->GetInput()->GetLargestPossibleRegion().GetSize()[m_TimeDimension] // - 2 * m_IndexPadding; unsigned int nTimePointsOut = this->GetInput()->GetLargestPossibleRegion().GetSize()[m_TimeDimension]; outputLargestPossibleRegion.SetSize( m_TimeDimension, nTimePointsOut ); outputPtr->SetLargestPossibleRegion(outputLargestPossibleRegion); // Set the output spacing and origin const ImageBase *phyData; phyData = dynamic_cast *>( this->GetInput() ); if( phyData ) { // Copy what we can from the image from spacing and origin of the input // This logic needs to be augmented with logic that select which // dimensions to copy unsigned int ii; const typename InputImageType::SpacingType & inputSpacing = inputPtr->GetSpacing(); const typename InputImageType::PointType & inputOrigin = inputPtr->GetOrigin(); typename OutputImageType::SpacingType outputSpacing; typename OutputImageType::PointType outputOrigin; // copy the input to the output and fill the rest of the // output with zeros. for( ii = 0; ii < OutputImageDimension; ++ii ) { outputSpacing[ii] = inputSpacing[ii]; outputOrigin[ii] = inputOrigin[ii]; } //outputOrigin[ m_TimeDimension ] += outputSpacing[ m_TimeDimension ]*m_IndexPadding; // set the spacing and origin outputPtr->SetSpacing(outputSpacing); outputPtr->SetOrigin(outputOrigin); // // Copy the direction cosines from the input to the output. // On join, the output dim is always >= input dim typedef typename InputImageType::DirectionType InputDirectionType; typedef typename OutputImageType::DirectionType OutputDirectionType; InputDirectionType inputDir = inputPtr->GetDirection(); unsigned int outputdim = OutputImageType::GetImageDimension(); OutputDirectionType outputDir = outputPtr->GetDirection(); for( unsigned int i = 0; i < outputdim; i++ ) { for( unsigned int j = 0; j < outputdim; j++ ) { outputDir[i][j] = inputDir[i][j]; } } outputPtr->SetDirection(outputDir); } else { // pointer could not be cast back down itkExceptionMacro( << "itk::SliceTimingCorrectionImageFilter::GenerateOutputInformation " << "cannot cast input to " << typeid( ImageBase * ).name() ); } // Support VectorImages by setting number of components on output. const unsigned int numComponents = inputPtr->GetNumberOfComponentsPerPixel(); if( numComponents != outputPtr->GetNumberOfComponentsPerPixel() ) { outputPtr->SetNumberOfComponentsPerPixel( numComponents ); } } /** * Set up state of filter before multi-threading. * InterpolatorType::SetInputImage is not thread-safe and hence * has to be set up before ThreadedGenerateData */ template void SliceTimingCorrectionImageFilter ::BeforeThreadedGenerateData() { itkDebugMacro(<< "Before Threaded Generate Data"); if( !m_Interpolator ) { itkExceptionMacro(<< "Interpolator not set"); } // Connect input image to interpolator m_Interpolator->SetInputImage( this->GetInput() ); // Connect input image to extrapolator // if( !m_Extrapolator.IsNull() ) // { // m_Extrapolator->SetInputImage( this->GetInput() ); // } } template void SliceTimingCorrectionImageFilter ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { itkDebugMacro(<< "Actually executing"); //std::cout << "Actually executing" << std::endl; ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); OutputImageRegionType outputRegion = outputRegionForThread; InputImageRegionType inputRegion; this->CallCopyOutputRegionToInputRegion(inputRegion, outputRegionForThread); ImageRegionIteratorWithIndex outIt( this->GetOutput(), outputRegion); typename InputImageType::PointType pt; while( !outIt.IsAtEnd() ) { typename InputImageType::IndexType idx = outIt.GetIndex(); unsigned int maxDim = this->GetOutput()->GetLargestPossibleRegion().GetSize()[m_TimeDimension] - 1; //std::cout << pt << std::endl; if ( this->m_ExtrapolateEdges ) { if ( idx[m_TimeDimension] < m_IndexPadding ) { idx[m_TimeDimension] = m_IndexPadding; } else if ( idx[m_TimeDimension] > ( maxDim - m_IndexPadding ) ) { idx[m_TimeDimension] = maxDim - m_IndexPadding; } } this->GetOutput()->TransformIndexToPhysicalPoint( idx, pt ); pt[m_TimeDimension] -= idx[m_SliceDimension] * m_SliceTiming; if ( this->m_Interpolator->IsInsideBuffer( pt ) ) { float oValue = this->m_Interpolator->Evaluate( pt ); outIt.Set( oValue ); } else { outIt.Set(0.0); } ++outIt; } } } // end namespace itk #endif ants-2.2.0/Utilities/itkSplitAlternatingTimeSeriesImageFilter.h000066400000000000000000000115061311104306400246770ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkSplitAlternatingTimeSeriesImageFilter_h #define __itkSplitAlternatingTimeSeriesImageFilter_h #include "itkImageToImageFilter.h" namespace itk { /** \class SplitAlternatingTimeSeriesImageFilter * \brief Join N-D images into an (N+1)-D image * * This filter is templated over the input image type and the output image * type. The pixel type of them must be the same and the input dimension * must be less than the output dimension. * When the input images are N-dimensinal, they are joined in order and * the size of the N+1'th dimension of the output is same as the number of * the inputs. The spacing and the origin (where the first input is placed) * for the N+1'th dimension is specified in this filter. The output image * informations for the first N dimensions are taken from the first input. * Note that all the inputs should have the same information. * * \ingroup GeometricTransform * \ingroup MultiThreaded * \ingroup Streamed * * \author Hideaki Hiraki * * Contributed in the users list * http://public.kitware.com/pipermail/insight-users/2004-February/006542.html * * \ingroup ITKImageCompose */ template< class TInputImage, class TOutputImage > class SplitAlternatingTimeSeriesImageFilter: public ImageToImageFilter< TInputImage, TOutputImage > { public: /** Standard class typedefs. */ typedef SplitAlternatingTimeSeriesImageFilter Self; typedef ImageToImageFilter< TInputImage, TOutputImage > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(SplitAlternatingTimeSeriesImageFilter, ImageToImageFilter); /** Compiler can't inherit typedef? */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::OutputImageType OutputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::RegionType OutputImageRegionType; /** Compiler can't inherit ImageDimension enumeration? */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro( InputConvertibleToOutputCheck, ( Concept::Convertible< typename TInputImage::PixelType, typename TOutputImage::PixelType > ) ); /** End concept checking */ #endif protected: SplitAlternatingTimeSeriesImageFilter(); ~SplitAlternatingTimeSeriesImageFilter() {} void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; /** Overrides GenerateOutputInformation() in order to produce * an image which has a different information than the first input. * \sa ProcessObject::GenerateOutputInformaton() */ virtual void GenerateOutputInformation() ITK_OVERRIDE; /** SplitAlternatingTimeSeriesImageFilter can be implemented as a multithreaded filter. * \sa ImageSource::ThreadedGenerateData(), * ImageSource::GenerateData() */ virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) ITK_OVERRIDE; private: SplitAlternatingTimeSeriesImageFilter(const Self &); //purposely not implemented void operator=(const Self &); //purposely not implemented /** IndexValueType is used to switch among the inputs and * is used as the index value of the new dimension */ typedef unsigned int IndexValueType; OutputImagePointer m_PrimaryImage; OutputImagePointer m_SecondaryImage; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkSplitAlternatingTimeSeriesImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkSplitAlternatingTimeSeriesImageFilter.hxx000066400000000000000000000113711311104306400252570ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 __itkSplitAlternatingTimeSeriesImageFilter_hxx #define __itkSplitAlternatingTimeSeriesImageFilter_hxx #include "itkSplitAlternatingTimeSeriesImageFilter.h" #include "itkProgressReporter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIterator.h" namespace itk { template< class TInputImage, class TOutputImage > SplitAlternatingTimeSeriesImageFilter< TInputImage, TOutputImage > ::SplitAlternatingTimeSeriesImageFilter() { this->SetNumberOfRequiredOutputs(2); this->SetNthOutput( 0, this->MakeOutput(0) ); this->SetNthOutput( 1, this->MakeOutput(1) ); } template< class TInputImage, class TOutputImage > void SplitAlternatingTimeSeriesImageFilter< TInputImage, TOutputImage > ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); } /** * \sa UnaryFunctorImageFilter::GenerateOutputInformation() */ template< class TInputImage, class TOutputImage > void SplitAlternatingTimeSeriesImageFilter< TInputImage, TOutputImage > ::GenerateOutputInformation() { // do not call the superclass' implementation of this method since // this filter allows the input the output to be of different dimensions // get pointers to the input and output typename Superclass::InputImageConstPointer inputPtr = this->GetInput(); typename Superclass::OutputImagePointer outputPtr0 = this->GetOutput(0); typename Superclass::OutputImagePointer outputPtr1 = this->GetOutput(1); if ( !outputPtr0 || !outputPtr1 || !inputPtr ) { return; } // Set the output image largest possible region. Use a RegionCopier // so that the input and output images can be different dimensions. OutputImageRegionType outputLargestPossibleRegion; this->CallCopyInputRegionToOutputRegion( outputLargestPossibleRegion, inputPtr->GetLargestPossibleRegion() ); // for the last dimension, assumed to be time unsigned int halfTime = inputPtr->GetLargestPossibleRegion().GetSize()[InputImageDimension-1] / 2; outputLargestPossibleRegion.SetSize( InputImageDimension-1, halfTime ); outputPtr0->SetLargestPossibleRegion(outputLargestPossibleRegion); outputPtr1->SetLargestPossibleRegion(outputLargestPossibleRegion); outputPtr0->SetSpacing( inputPtr->GetSpacing() ); outputPtr1->SetSpacing( inputPtr->GetSpacing() ); outputPtr0->SetDirection( inputPtr->GetDirection() ); outputPtr1->SetDirection( inputPtr->GetDirection() ); typename InputImageType::PointType origin = inputPtr->GetOrigin(); outputPtr0->SetOrigin( origin ); origin[InputImageDimension-1] += inputPtr->GetSpacing()[InputImageDimension-1]; outputPtr1->SetOrigin( origin ); // Support VectorImages by setting number of components on output. const unsigned int numComponents = inputPtr->GetNumberOfComponentsPerPixel(); if ( numComponents != outputPtr0->GetNumberOfComponentsPerPixel() ) { outputPtr0->SetNumberOfComponentsPerPixel( numComponents ); } if ( numComponents != outputPtr1->GetNumberOfComponentsPerPixel() ) { outputPtr1->SetNumberOfComponentsPerPixel( numComponents ); } } template< class TInputImage, class TOutputImage > void SplitAlternatingTimeSeriesImageFilter< TInputImage, TOutputImage > ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { itkDebugMacro(<< "Actually executing"); ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); OutputImageRegionType outputRegion = outputRegionForThread; ImageRegionIterator< OutputImageType > outIt0( this->GetOutput(0), outputRegion); ImageRegionIterator< OutputImageType > outIt1( this->GetOutput(1), outputRegion); while ( !outIt0.IsAtEnd() ) { typename InputImageType::IndexType idx = outIt0.GetIndex(); idx[InputImageDimension-1] *= 2; outIt0.Set( this->GetInput()->GetPixel( idx ) ); idx[InputImageDimension-1] += 1; outIt1.Set( this->GetInput()->GetPixel( idx ) ); ++outIt0; ++outIt1; } } } // end namespace itk #endif ants-2.2.0/Utilities/itkSurfaceCurvatureBase.h000066400000000000000000000202061311104306400213710ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _SurfaceCurvatureBase_h #define _SurfaceCurvatureBase_h #include #include #include #include // #include #include "itkObject.h" #include "itkProcessObject.h" #include "itkVectorContainer.h" #include "itkCastImageFilter.h" namespace itk { /** \class SurfaceCurvatureBase * * This class takes a surface as input and creates a local * geometric frame for each surface point. * * \note The origin of a neighborhood is always taken to be * the first point entered into and the * last point stored in the list. */ template class SurfaceCurvatureBase : public ProcessObject { public: /** Standard class typedefs. */ typedef SurfaceCurvatureBase Self; typedef ProcessObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(SurfaceCurvatureBase, ProcessObject); /** Method for creation through the object factory. */ itkNewMacro(Self); /** Image types. */ typedef TSurface SurfaceType; /** Image dimension. */ // itkStaticConstMacro(ImageDimension, unsigned int, TImage::ImageDimension); itkStaticConstMacro(ImageDimension, unsigned int, TDimension); itkStaticConstMacro(SurfaceDimension, unsigned int, TDimension); typedef float RealType; typedef vnl_vector VectorType; typedef vnl_vector_fixed FixedVectorType; typedef vnl_vector_fixed PointType; typedef vnl_matrix MatrixType; typedef std::vector PointContainerType; typedef std::vector FunctionContainerType; /** Set input parameter file */ itkSetStringMacro( ParameterFileName ); /** Set input parameter file */ itkGetStringMacro( ParameterFileName ); /** Fill the point list with the points neighboring the current origin */ virtual void FindNeighborhood(unsigned int temp = 0); /** A Euclidean relative of L.D. Griffin's compactness.*/ RealType ComputeMeanEuclideanDistance(); /** */ void ComputeAveragePoint(); void ProjectToTangentPlane(PointType); void EigenDecomposition(MatrixType D); /** Estimate the plane tangent to a point using the neighborhood * of the point. This is, in general, an over-constrained least * squares fitting problem. */ void EstimateTangentPlane(PointType); void WeightedEstimateTangentPlane(PointType); void TestEstimateTangentPlane(PointType); /** This function sets the reference tangent arbitrarily. * It can be overridden in case there is a better practical approach. */ void ChooseReferenceTangent(); /** This function fills the weight and angle vectors for * a given neighborhood. */ virtual void ComputeWeightsAndDirectionalKappaAndAngles(PointType origin); /** */ void ComputeFrame(PointType origin); /** */ void ComputeFrameAndKappa(PointType origin); void ShimshoniFrame(PointType origin); /** */ void ComputeJoshiFrame(PointType origin); /** */ void EstimateCurvature(RealType w1 = 3. / 8., RealType w2 = 1. / 8., RealType w3 = 1. / 8., RealType w4 = 3. / 8.); /** Use the Besl and Jain analytical curvature computation. * We use a least square polynomial fit to the local neighborhood * to estimate the mean and gaussian curvature. */ void JainMeanAndGaussianCurvature(PointType); /** This function returns a weight given a distance * It may be the identity function, a normalization * or a gaussianization of the input distance. */ virtual RealType GetWeight(PointType, PointType); /** This function returns the angle between the reference tangent and the projection onto the tangent plane of the vector between the neighborhood focus and its neighbor. */ virtual RealType GetTheta(PointType Neighbor, PointType origin); /** Estimate the directional curvature using Shimshoni's method (eq 6).*/ virtual void EstimateDirectionalCurvature(PointType, PointType); void PrintFrame(); virtual void ComputeFrameOverDomain(unsigned int /* which */ = 3) { }; RealType ErrorEstimate(PointType, RealType sign = 1.0 ); unsigned int CharacterizeSurface(); itkSetMacro(Origin, PointType); itkGetMacro(Origin, PointType); itkSetMacro(AveragePoint, PointType); itkGetMacro(AveragePoint, PointType); itkGetMacro(Normal, FixedVectorType); itkGetMacro(Sigma, RealType); itkGetMacro(MeanKappa, RealType); itkSetMacro(Sigma, RealType); itkGetMacro(UseGeodesicNeighborhood, bool); itkSetMacro(UseGeodesicNeighborhood, bool); /** Set normal estimates a 3D frame from a given normal */ void SetFrameFromNormal(FixedVectorType); /** We use the cross-product of the tangents times the image spacing to get the local area. */ RealType ComputeLocalArea( FixedVectorType ); /** We estimate the integral as a sum, assuming the local area (from compute local area) scales the value of the function at the pixel. See http://mathworld.wolfram.com/SurfaceIntegral.html*/ virtual RealType IntegrateFunctionOverNeighborhood(bool /* norm */ = false) { return 0; } void SwitchNormalSign() { m_Normal *= (-1.0); } // for conjugate harmonic function float dstarUestimate(); // get this from the local frame - 1st order vs 2nd order shape operator void EstimateMetricTensor(); protected: SurfaceCurvatureBase(); virtual ~SurfaceCurvatureBase() { }; /** Holds the value of Pi. */ double m_Pi; bool m_Debug; /** Data structures to contain points. */ PointContainerType m_PointList; /** Data structures to contain function values associated with points. */ FunctionContainerType m_FunctionValueList; /** This list contains the projection of the vectors onto the tangent plane (T_i Shimshoni). */ PointContainerType m_TangentProjectionList; /** The point that is the origin of the current neighborhood. */ PointType m_Origin; PointType m_AveragePoint; PointType m_PlaneVector; /** Data that represents single vectors */ FixedVectorType m_ArbitraryTangent; FixedVectorType m_Normal; FixedVectorType m_Tangent1; FixedVectorType m_Tangent2; RealType m_dX; // size in local x dir RealType m_dY; // size in local y dir FixedVectorType m_MetricTensor; VectorType m_ThetaVector; VectorType m_WeightVector; VectorType m_DirectionalKappaVector; /** Data for representing neighborhoods and derived from the vector frames*/ /** Approximate directional curvature */ RealType m_DirectionalKappa; RealType m_MetricTensorDeterminant; /** Approximate principal curvature 1*/ RealType m_Kappa1; /** Approximate principal curvature 2*/ RealType m_Kappa2; RealType m_GaussianKappa; RealType m_MeanKappa; /** Solution weights eq. (7) (8) Shimshoni */ RealType m_A; RealType m_B; RealType m_C; /** True Eigenvector weights */ RealType m_W1; RealType m_W2; /** Eigenvalues */ RealType m_Eval0; RealType m_Eval1; RealType m_Eval2; unsigned int m_CurrentNeighborhoodPointIndex; std::string m_ParameterFileName; /** We use this to avoid computing the frame in degenerate cases. */ RealType m_TotalDKap; RealType m_TotalArea; RealType m_Sigma; bool m_UseGeodesicNeighborhood; private: }; } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkSurfaceCurvatureBase.hxx" #endif #endif ants-2.2.0/Utilities/itkSurfaceCurvatureBase.hxx000066400000000000000000001044771311104306400217660ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _SurfaceCurvatureBase_hxx #define _SurfaceCurvatureBase_hxx #include #include #include #include #include // #include #include #include #include #include "itkSurfaceCurvatureBase.h" namespace itk { template SurfaceCurvatureBase ::SurfaceCurvatureBase() { m_Origin.fill(0.0); m_ArbitraryTangent.fill(0.); m_Normal.fill(0.); m_Tangent1.fill(0.); m_Tangent2.fill(0.); m_DirectionalKappa = 0.0; m_Kappa1 = 0.0; m_Kappa2 = 0.0; m_GaussianKappa = 0.0; m_A = 0.0; m_B = 0.0; m_C = 0.0; m_W1 = 3. / 8.; m_W2 = 1. / 8.; m_Eval0 = 0.0; m_Eval1 = 0.0; m_Eval2 = 0.0; m_CurrentNeighborhoodPointIndex = 0; m_ParameterFileName = ""; m_Pi = 3.14159265358979323846; m_Debug = true; m_Debug = false; m_UseGeodesicNeighborhood = false; m_TotalArea = 0.0; } template void SurfaceCurvatureBase ::ComputeAveragePoint() { m_AveragePoint.fill(0.0); unsigned int npts = m_PointList.size(); unsigned int dim = SurfaceDimension; for( unsigned int i = 0; i < npts; i++ ) { for( unsigned int j = 0; j < dim; j++ ) { m_AveragePoint[j] += m_PointList[i][j]; } } m_AveragePoint /= (RealType)npts; } template void SurfaceCurvatureBase ::ProjectToTangentPlane(PointType dif) { MatrixType id(ImageDimension, ImageDimension); id.fill(0.0); unsigned int i = 0, j = 0; MatrixType NN(ImageDimension, ImageDimension); for( i = 0; i < ImageDimension; i++ ) { id(i, i) = 1.0; m_PlaneVector[i] = 0.0; for( j = 0; j < ImageDimension; j++ ) { NN(i, j) = id(i, j) - m_Normal(i) * m_Normal(j); m_PlaneVector[i] += NN(i, j) * dif(j); } } } template void SurfaceCurvatureBase ::SetFrameFromNormal(FixedVectorType N) { // WORKS ONLY FOR 3D! RealType mag = N.magnitude(); if( mag != 0.0 ) { m_Normal = N / mag; } else { m_Tangent1.fill(0.0); m_Tangent2.fill(0.0); return; } // now estimate the tangent plane if( fabs(m_Normal[0]) > 1.e-1 ) { float norm = 1. / m_Normal[0]; m_Tangent1[1] = norm * N[0]; m_Tangent1[0] = -1. * norm * (N[1] + N[2]); m_Tangent1[2] = norm * N[0]; } else if( fabs(m_Normal[1]) > 1.e-1 ) { float norm = 1. / m_Normal[1]; m_Tangent1[0] = norm * N[1]; m_Tangent1[1] = -1. * norm * (N[0] + N[2]); m_Tangent1[2] = norm * N[1]; } else if( fabs(m_Normal[2]) > 1.e-1 ) { float norm = 1. / m_Normal[2]; m_Tangent1[0] = norm * N[2]; m_Tangent1[2] = -1. * norm * (N[0] + N[1]); m_Tangent1[1] = norm * N[2]; } m_Tangent1 /= m_Tangent1.magnitude(); m_Tangent2 = vnl_cross_3d(m_Normal, m_Tangent1); m_Tangent2 /= m_Tangent2.magnitude(); this->ChooseReferenceTangent(); } template void SurfaceCurvatureBase ::EigenDecomposition(MatrixType D) { // Compute estimated frame using eigensystem of D'*D { vnl_symmetric_eigensystem eig(D.transpose() * D); for( unsigned int j = 0; j < SurfaceDimension; j++ ) { m_Normal[j] = eig.get_eigenvector(0)[j]; m_Tangent1[j] = eig.get_eigenvector(1)[j]; m_Tangent2[j] = eig.get_eigenvector(2)[j]; } // this->SetFrameFromNormal(m_Normal); m_Eval0 = eig.get_eigenvalue(0); m_Eval1 = eig.get_eigenvalue(1); m_Eval2 = eig.get_eigenvalue(2); if( m_Debug ) { vnl_vector a = eig.get_eigenvector(0); std::cout << "Eig residual = " << (D * a).magnitude() << std::endl; a = eig.get_eigenvector(1); std::cout << "Eig residual = " << (D * a).magnitude() << std::endl; a = eig.get_eigenvector(2); std::cout << "Eig residual = " << (D * a).magnitude() << std::endl; } } } template void SurfaceCurvatureBase::EstimateTangentPlane(PointType origin) { // Build cov matrix D unsigned int npts = m_PointList.size() - 1; unsigned int dim = SurfaceDimension; MatrixType D(npts, dim); for( unsigned int i = 0; i < npts; i++ ) { for( unsigned int j = 0; j < dim; j++ ) { D(i, j) = (m_PointList[i][j] - origin(j) ) * (m_PointList[i][j] - origin(j) ); } } this->EigenDecomposition(D); m_dX = m_Tangent1.magnitude(); m_dY = m_Tangent2.magnitude(); m_Tangent1 /= m_dX; m_Tangent2 /= m_dY; this->ChooseReferenceTangent(); } template void SurfaceCurvatureBase::WeightedEstimateTangentPlane(PointType origin) { // Build cov matrix D RealType twi = 0.0; unsigned int npts = m_PointList.size() - 1; unsigned int dim = SurfaceDimension; MatrixType D(npts, dim); for( unsigned int i = 0; i < npts; i++ ) { PointType t = m_PointList[i] - origin; RealType wi = t.magnitude(); if( wi != 0.0 ) { wi = 1. / wi; } twi += wi; for( unsigned int j = 0; j < dim; j++ ) { D(i, j) = wi * (m_PointList[i][j] - origin(j) ) * (m_PointList[i][j] - origin(j) ); } } this->EigenDecomposition(D / twi); this->ChooseReferenceTangent(); } template void SurfaceCurvatureBase::ComputeFrame(PointType /* origin */) { // Build cov matrix D unsigned int npts = m_PointList.size() - 1; unsigned int dim = SurfaceDimension; RealType twi = 1., wi = 0.; MatrixType D; bool method1 = true; if( method1 ) { twi = 0.0; D.set_size(dim, dim); D.fill(0.0); for( unsigned int i = 0; i < npts; i++ ) { PointType p = m_PointList[i] - m_Origin; wi = 1. / p.magnitude(); twi += wi; for( unsigned int j = 0; j < dim; j++ ) { for( unsigned int k = 0; k < dim; k++ ) { D(j, k) += m_TangentProjectionList[i][j] * m_TangentProjectionList[i][k]; // *wi; } } } } else { D.set_size(npts, dim); for( unsigned int i = 0; i < npts; i++ ) { for( unsigned int j = 0; j < dim; j++ ) { D(i, j) = m_TangentProjectionList[i][j] * m_TangentProjectionList[i][j] * m_DirectionalKappaVector[i] * m_WeightVector[i]; } } } this->EigenDecomposition(D / twi); this->ChooseReferenceTangent(); } template void SurfaceCurvatureBase::ComputeFrameAndKappa(PointType /* origin */) { // Build cov matrix D unsigned int npts = m_PointList.size() - 1; unsigned int dim = SurfaceDimension; /* if (m_TotalDKap/(float)m_PointList.size() < 0.05) { m_Kappa1=0.0; m_Kappa2=0.0; m_A=0.; m_B=0.; m_C=0.0; m_GaussianKappa=0.0; m_TotalDKap=0.0; return; } */ MatrixType D; // dim=2; D.set_size(dim, dim); D.fill(0.0); for( unsigned int i = 0; i < npts; i++ ) { for( unsigned int j = 0; j < dim; j++ ) { for( unsigned int k = 0; k < dim; k++ ) { D(j, k) += m_TangentProjectionList[i][j] * m_TangentProjectionList[i][k] * m_WeightVector[i] * (m_DirectionalKappaVector[i]); // } } } vnl_symmetric_eigensystem eig(D.transpose() * D); m_Eval0 = eig.get_eigenvalue(0); m_Eval1 = eig.get_eigenvalue(1); m_Eval2 = eig.get_eigenvalue(2); } template void SurfaceCurvatureBase ::ShimshoniFrame(PointType origin) { this->ComputeWeightsAndDirectionalKappaAndAngles(origin); this->ComputeFrameAndKappa(origin); this->EstimateCurvature(m_A, m_B, m_B, m_C); // this->EstimateCurvature(); } template void SurfaceCurvatureBase ::JainMeanAndGaussianCurvature(PointType origin) { // Build cov matrix D unsigned int npts = m_PointList.size() - 1; if( npts < 7 ) { m_MeanKappa = 0; m_GaussianKappa = 0; m_Kappa1 = 0; m_Kappa2 = 0; return; } vnl_vector dists(npts); vnl_vector wts(npts); MatrixType D; D.set_size(npts, 6); // each row contains [u^2 , uv, v^2, u, v, 1] for point p D.fill(0.0); float totwt = 0.0; float wt = 0.0; bool robust = true; robust = false; unsigned int i = 0; for( i = 0; i < npts; i++ ) { PointType Q = m_PointList[i]; PointType Dif = Q - origin; float u1 = 0.0; float u2 = 0.0; wt = 1. / Dif.magnitude(); wts[i] = wt; // totwt+=wt; for( unsigned int pp = 0; pp < SurfaceDimension; pp++ ) { u1 += Dif[pp] * m_Tangent1[pp]; u2 += Dif[pp] * m_Tangent2[pp]; } // co-ordinates in tangent plane // RealType sign=1.0; // if (u1 < 0.) sign=-1.0; // u1=sqrt(fabs(u1))*sign; // if (u2 < 0.) sign=-1.0; else sign=1.0; // u2=sqrt(fabs(u2))*sign; dists[i] = 0.0; PointType tanproj; for( unsigned int jj = 0; jj < SurfaceDimension; jj++ ) { m_PlaneVector[jj] = u1 * m_Tangent1[jj] + u2 * m_Tangent2[jj]; // tangent projection of Q tanproj[jj] = origin[jj] + m_PlaneVector[jj]; } PointType temp = Q - tanproj; dists[i] = temp.magnitude(); // sqrt(dists[i]); // if (robust) dists[i]/=wt; D(i, 0) = u1 * u1; D(i, 1) = 2. * u1 * u2; D(i, 2) = u2 * u2; D(i, 3) = u1; D(i, 4) = u2; D(i, 5) = 1.0; } if( robust ) { totwt = 0.0; float sigma = m_Sigma; for( unsigned int tt = 0; tt < npts; tt++ ) { wt = exp(-1. * wts[tt] / sigma); dists[tt] *= wt; totwt += wt; // std::cout << " wt " << wt << std::endl; } if( totwt > 0 ) { dists /= totwt; } } vnl_svd svd(D); vnl_vector a = svd.solve(dists); // std::cout << " a vec " << a << std::endl; // for (int tt=0; tt<6; tt++) // if (a[tt] < 1.e-6) a[tt]=0.0; /* double fu = a(0); //a(3); double fv = a(2); //a(4); double fuu = a(3); //2.*a(0); double fuv = a(1); //a(1); double fvv = a(4); //2.*a(2); */ double fu = a(3); double fv = a(4); double fuu = 2. * a(0); double fuv = a(1); double fvv = 2. * a(2); m_MeanKappa = (1.0 + fv * fv) * fuu - 2.0 * fu * fv * fuv + (1.0 + fu * fu) * fvv; m_MeanKappa /= (2.0 * pow( 1.0 + fu * fu + fv * fv, 1.5) ); m_GaussianKappa = (fuu * fvv - fuv * fuv) / ( ( 1.0 + fu * fu + fv * fv ) * ( 1.0 + fu * fu + fv * fv ) ); m_Kappa1 = m_MeanKappa + sqrt( m_MeanKappa * m_MeanKappa - m_GaussianKappa ); m_Kappa2 = m_MeanKappa - sqrt( m_MeanKappa * m_MeanKappa - m_GaussianKappa ); if( origin[0] == 20 && origin[1] == 65 && origin[2] == 39 ) { std::cout << " surf params " << a << std::endl; std::cout << " k1 " << m_Kappa1 << " k2 " << m_Kappa2 << std::endl; this->PrintFrame(); for( unsigned int jj = 0; jj < npts; jj++ ) { std::cout << " point " << m_PointList[jj]; std::cout << " dist " << dists[jj] << std::endl; } } // NOTE: THIS IS THE ERROR ESTIMATE // std::cout << "SVD residual = " << (D * a).magnitude() << std::endl; // std::cout << " a " << a << std::endl; // MatrixType C(2,2); // C(0,0)=a(0); C(1,0)=a(1); C(0,1)=a(1); C(1,1)=a(2); // vnl_symmetric_eigensystem eig(C); // m_Kappa1=eig.get_eigenvalue(0); // m_Kappa2=eig.get_eigenvalue(1); // // m_MeanKappa=0.5*(m_Kappa1+m_Kappa2); // m_GaussianKappa = m_Kappa1*m_Kappa2; // // std::cout << " eval 0 " << m_Kappa1 << " eval 1 " << m_Kappa2 << std::endl; return; } // end jain frame template void SurfaceCurvatureBase ::ComputeJoshiFrame(PointType origin) { // Build cov matrix D unsigned int npts = m_PointList.size() - 1; if( npts < 5 ) { m_MeanKappa = 0; m_GaussianKappa = 0; m_Kappa1 = 0; m_Kappa2 = 0; return; } vnl_vector dists(npts); MatrixType D; D.set_size(npts, 3); D.fill(0.0); float totw = 0; for( unsigned int i = 0; i < npts; i++ ) { PointType Q = m_PointList[i]; float u1 = 0.0; float u2 = 0.0; float sqdif = 0; for( unsigned int ijj = 0; ijj < SurfaceDimension; ijj++ ) { u1 += (Q[ijj] - origin[ijj]) * m_Tangent1[ijj]; u2 += (Q[ijj] - origin[ijj]) * m_Tangent2[ijj]; sqdif += ( (Q[ijj] - origin[ijj]) * (Q[ijj] - origin[ijj]) ); } totw += 1. / sqdif; // // co-ordinates in tangent plane // RealType sign=1.0; // if (u1 < 0.) sign=-1.0; // u1=sqrt(fabs(u1))*sign; // if (u2 < 0.) sign=-1.0; else sign=1.0; // u2=sqrt(fabs(u2))*sign; // // // RealType pvMag=0; // for (unsigned int i=0; i svd(D); vnl_vector c = svd.solve(dists); // NOTE: THIS IS THE ERROR ESTIMATE // std::cout << "SVD residual = " << (D * c).magnitude() << std::endl; // std::cout << " C " << c << std::endl; MatrixType C(2, 2); C(0, 0) = c(0); C(1, 0) = c(1); C(0, 1) = c(1); C(1, 1) = c(2); vnl_symmetric_eigensystem eig(C); m_Kappa1 = eig.get_eigenvalue(0); m_Kappa2 = eig.get_eigenvalue(1); m_MeanKappa = 0.5 * (m_Kappa1 + m_Kappa2); m_GaussianKappa = m_Kappa1 * m_Kappa2; // std::cout << " eval 0 " << m_Kappa1 << " eval 1 " << m_Kappa2 << std::endl; } template typename SurfaceCurvatureBase::RealType SurfaceCurvatureBase ::ComputeLocalArea( FixedVectorType spacing ) { PointType t1; PointType t2; for( unsigned int i = 0; i < SurfaceDimension; i++ ) { t1[i] = m_Tangent1[i] * spacing[i]; t2[i] = m_Tangent2[i] * spacing[i]; } PointType temp = vnl_cross_3d( t1, t2); return temp.magnitude(); } template unsigned int SurfaceCurvatureBase ::CharacterizeSurface() { float th = 1.e-6; if( fabs(m_GaussianKappa) < th * th ) { m_GaussianKappa = 0.0; } if( fabs(m_MeanKappa) < th ) { m_MeanKappa = 0.0; } if( m_MeanKappa > 0 && m_GaussianKappa > 0 ) { return 1; // bowl } else if( m_MeanKappa < 0 && m_GaussianKappa > 0 ) { return 2; // inv bowl } else if( m_MeanKappa > 0 && m_GaussianKappa < 0 ) { return 3; // pos saddle } else if( m_MeanKappa < 0 && m_GaussianKappa < 0 ) { return 4; // neg saddle } else if( m_MeanKappa > 0 && m_GaussianKappa == 0 ) { return 5; // pos U } else if( m_MeanKappa < 0 && m_GaussianKappa == 0 ) { return 6; // neg U } else if( m_MeanKappa == 0 && m_GaussianKappa == 0 ) { return 7; // flat } else if( m_MeanKappa == 0 && m_GaussianKappa < 0 ) { return 8; // perfect saddle } else { return 0; } // old below if( m_MeanKappa > 0 && m_GaussianKappa > 0 ) { return 8; } else if( m_MeanKappa > 0 && m_GaussianKappa == 0 ) { return 7; } else if( m_MeanKappa > 0 && m_GaussianKappa < 0 ) { return 6; } else if( m_MeanKappa == 0 && m_GaussianKappa == 0 ) { return 5; } else if( m_MeanKappa == 0 && m_GaussianKappa < 0 ) { return 4; } else if( m_MeanKappa < 0 && m_GaussianKappa > 0 ) { return 3; } else if( m_MeanKappa < 0 && m_GaussianKappa == 0 ) { return 2; } else if( m_MeanKappa < 0 && m_GaussianKappa < 0 ) { return 1; } else { return 0; } } template typename SurfaceCurvatureBase::RealType SurfaceCurvatureBase ::ComputeMeanEuclideanDistance() { double totdist = 0.0; unsigned int ct = 0; unsigned int pls = m_PointList.size(); for( unsigned int i = 0; i < pls; i++ ) { for( unsigned int j = 0; j < pls; j++ ) { PointType p = m_PointList[j] - m_PointList[i]; totdist += p.magnitude(); ct++; } } RealType meandist = totdist / (float)ct; // 4 pi r^2 = a, r^2 = a/(4pi) RealType spheremeandist = 1.0; // sqrt((float)ct/(4.0*3.1418)); RealType compactness = meandist / spheremeandist; return 10. - compactness; } template void SurfaceCurvatureBase::TestEstimateTangentPlane(PointType origin) { // Read points from stdin MatrixType pts; std::cout << " input points " << std::endl; std::cin >> pts; // Build cov matrix D int npts = pts.rows(); int dim = pts.columns(); MatrixType D(npts, dim); // get average of points RealType count = 0.0; PointType avgpt; avgpt.fill(0); for( int i = 0; i < npts; i++ ) { for( int j = 0; j < dim; j++ ) { avgpt(j) += pts(i, j); count += 1.0; } } avgpt /= count; if( m_Debug ) { std::cout << " avg " << avgpt << std::endl; } origin = avgpt; for( int i = 0; i < npts; i++ ) { for( int j = 0; j < dim; j++ ) { D(i, j) = (pts(i, j) - origin(j) ) * (pts(i, j) - origin(j) ); } // D(i,dim) = 1; } // 1. Compute using SVD { vnl_svd svd(D); vnl_vector a = svd.nullvector(); std::cout << "SVD residual = " << (D * a).magnitude() << std::endl; std::cout << "SVD normal " << a << std::endl; } // 2. Compute using eigensystem of D'*D { vnl_symmetric_eigensystem eig(D.transpose() * D); vnl_vector a = eig.get_eigenvector(0); std::cout << "Eig residual = " << (D * a).magnitude() << std::endl; std::cout << " normal " << eig.get_eigenvector(0) << std::endl; std::cout << "Eigvec 1 " << eig.get_eigenvector(1) << std::endl; std::cout << "Eigvec 2 " << eig.get_eigenvector(2) << std::endl; std::cout << "Eigval normal " << eig.get_eigenvalue(0) << std::endl; std::cout << "Eigval 1 " << eig.get_eigenvalue(1) << std::endl; std::cout << "Eigval 2 " << eig.get_eigenvalue(2) << std::endl; } } template void SurfaceCurvatureBase::FindNeighborhood(unsigned int /* temp */) { // Read points from stdin MatrixType pts; std::cout << " input points " << std::endl; std::cin >> pts; // Build cov matrix D unsigned int npts = pts.rows(); unsigned int dim = pts.columns(); m_AveragePoint.fill(0.0); for( unsigned int i = 0; i < npts; i++ ) { PointType pt; for( unsigned int j = 0; j < dim; j++ ) { pt(j) = pts(i, j); m_AveragePoint[j] += pts(i, j); } if( i == 0 ) { m_Origin = pt; } m_PointList.insert(m_PointList.begin(), pt); } m_AveragePoint /= (RealType)npts; if( m_Debug ) { std::cout << " point list size " << m_PointList.size() << std::endl; for( unsigned int i = 0; i < m_PointList.size(); i++ ) { std::cout << " point " << m_PointList[i]; } std::cout << std::endl; } } /** This function sets the reference tangent arbitrarily. * It can be overridden in case there is a better practical approach. */ template void SurfaceCurvatureBase::ChooseReferenceTangent() { float w = 1.; float w2 = (1.0 - w); // m_ArbitraryTangent[0]=w; m_ArbitraryTangent[1]=1.-w; m_ArbitraryTangent[2]=0.; m_ArbitraryTangent = w * m_Tangent2 + (w2 * m_Tangent1); m_ArbitraryTangent /= m_ArbitraryTangent.magnitude(); if( m_Debug ) { std::cout << " arb tan " << m_ArbitraryTangent << std::endl; } } template void SurfaceCurvatureBase ::ComputeWeightsAndDirectionalKappaAndAngles(PointType origin) { unsigned int sz = m_PointList.size(); if( sz > 1 ) { sz = sz - 1; } m_TangentProjectionList.clear(); m_DirectionalKappaVector.set_size(sz); m_DirectionalKappaVector.fill(0.0); m_WeightVector.set_size(sz); m_WeightVector.fill(0.0); m_ThetaVector.set_size(sz); m_ThetaVector.fill(0.0); RealType w1 = 0, w2 = 0, totalWeight = 0.0; RealType difMag = 0, sqdifMag = 0; m_TotalDKap = 0.0; m_A = 0.0; m_B = 0.0; m_C = 0.0; for( unsigned int ii = 0; ii < sz; ii++ ) { PointType Q = m_PointList[ii]; PointType Dif = Q - origin; w1 = 0.0; w2 = 0.0; sqdifMag = 0.0; m_DirectionalKappa = 0.0; for( unsigned int i = 0; i < SurfaceDimension; i++ ) { m_DirectionalKappa += (m_Normal[i] * Dif[i]); w1 += Dif[i] * m_Tangent1[i]; w2 += Dif[i] * m_Tangent2[i]; sqdifMag += Dif[i] * Dif[i]; } if( sqdifMag != 0.0 && Q != origin ) { // m_PlaneVector[0]=w1; m_PlaneVector[1]=w2; difMag = sqrt(sqdifMag); m_ThetaVector[ii] = GetTheta(Q, origin); m_TangentProjectionList.insert(m_TangentProjectionList.begin(), m_PlaneVector); totalWeight += 1. / difMag; m_WeightVector[ii] = 1. / difMag; m_TotalDKap += m_DirectionalKappa; m_DirectionalKappaVector[ii] = m_DirectionalKappa; } else { m_WeightVector[ii] = 0.0; m_DirectionalKappaVector[ii] = 0.0; PointType pp; pp.fill(0.0); m_TangentProjectionList.insert(m_TangentProjectionList.begin(), pp); } if( m_Origin[0] == 95 && m_Origin[1] == 94 && m_Origin[2] == 63 ) { std::cout << " tdk " << m_TotalDKap << " nor " << m_Normal << " dk " << m_DirectionalKappa << " dif " << Dif << " mpv " << m_PlaneVector << std::endl; // m_Debug=true; } double costheta = cos(m_ThetaVector[ii]); double sintheta = sin(m_ThetaVector[ii]); double weight = m_WeightVector[ii]; m_A += weight * costheta * costheta * costheta * costheta; m_B += weight * sintheta * sintheta * costheta * costheta; m_C += weight * sintheta * sintheta * sintheta * sintheta; } if( m_Origin[0] == 54 && m_Origin[1] == 54 && m_Origin[2] == 63 ) { std::cout << m_Origin << " tdk " << m_TotalDKap << " nor " << m_Normal << std::endl; // m_Debug=true; } // Now compute A, B, C m_A /= totalWeight; m_B /= totalWeight; m_C /= totalWeight; m_WeightVector /= totalWeight; if( m_Debug ) { std::cout << " weight " << m_WeightVector << std::endl; std::cout << " theta " << m_ThetaVector << std::endl; std::cout << " dkap " << m_DirectionalKappaVector << std::endl; std::cout << " A " << m_A << " B " << m_B << " C " << m_C << std::endl; } } /** This function returns a weight given a distance * It may be the identity function, a normalization * or a gaussianization of the input distance. */ template typename SurfaceCurvatureBase::RealType SurfaceCurvatureBase::GetWeight(PointType p1, PointType p2) { PointType d = p1 - p2; return d.magnitude(); } /** This function returns the angle between the reference tangent and the projection onto the tangent plane of the vector between the neighborhood focus and its neighbor. */ template typename SurfaceCurvatureBase::RealType SurfaceCurvatureBase ::GetTheta(PointType Q, PointType origin) { RealType theta = 0.0; PointType Dif = Q - origin; // float dm=Dif.magnitude(); // if (dm==0) dm=1.0; // Dif/=dm; float u1 = 0.0; float u2 = 0.0; for( unsigned int i = 0; i < SurfaceDimension; i++ ) { u1 += Dif[i] * m_Tangent1[i]; u2 += Dif[i] * m_Tangent2[i]; } // co-ordinates in tangent plane // RealType sign=1.0; // if (u1 < 0.) sign=-1.0; // u1=sqrt(fabs(u1))*sign; // if (u2 < 0.) sign=-1.0; else sign=1.0; // u2=sqrt(fabs(u2))*sign; RealType tot = 1; // fabs(u1)+fabs(u2); // if (tot ==0) { // std::cout << " tan1 " << m_Tangent1 << " tan2 " << m_Tangent2 << std::endl; // std::cout << " norm " << m_Normal << " u1 " << u1 << " u2 " << u2 << std::endl; // std::cout << " Origin " << m_Origin << " Q " << Q << std::endl; // tot=1.; } RealType pvMag = 0; RealType ip = 0.0; RealType arbMag = 0.0; for( unsigned int i = 0; i < SurfaceDimension; i++ ) { m_PlaneVector[i] = u1 / (tot) * m_Tangent1[i] + u2 / (tot) * m_Tangent2[i]; ip += m_PlaneVector[i] * m_ArbitraryTangent[i]; pvMag += m_PlaneVector[i] * m_PlaneVector[i]; arbMag += m_ArbitraryTangent[i] * m_ArbitraryTangent[i]; } // ProjectToTangentPlane(Dif); // m_PlaneVector/=m_PlaneVector.magnitude(); // if (pvMag!=0)m_PlaneVector/=sqrt(pvMag); if( m_Debug ) { std::cout << " m_PlaneVector " << m_PlaneVector << " dif " << Dif << std::endl; } float temp = (sqrt(pvMag) * sqrt(arbMag) ); if( temp != 0.0 ) { theta = acos(ip / temp); } else { theta = 0.0; // FIXME? } return theta; } /** */ template void SurfaceCurvatureBase ::EstimateDirectionalCurvature(PointType Q, PointType P) { PointType Dif = Q - P; m_DirectionalKappa = 0.0; for( unsigned int i = 0; i < SurfaceDimension; i++ ) { m_DirectionalKappa += (m_Normal[i] * Dif[i]); } m_DirectionalKappa = 2.0 * m_DirectionalKappa / Dif.magnitude(); } /** */ template void SurfaceCurvatureBase ::EstimateCurvature(RealType w1, RealType w2, RealType w3, RealType w4) { /* if (m_TotalDKap/(float)m_PointList.size() < 0.05) { m_Kappa1=0.0; m_Kappa2=0.0; m_A=0.; m_B=0.; m_C=0.0; m_GaussianKappa=0.0; m_TotalDKap=0.0; return; }*/ RealType denom = (w4 - w2 * w3 / w1); if( fabs(denom) > 1.e-12 ) { m_Kappa2 = (m_Eval2 - m_Eval1 * w3 / w1) / denom; } else { w1 = m_W1; w2 = m_W2; w3 = m_W2; w4 = m_W1; denom = (w4 - w2 * w3 / w1); m_Kappa2 = (m_Eval2 - m_Eval1 * w3 / w1) / denom; } m_Kappa1 = (m_Eval1 - w2 * m_Kappa1) / w1; m_MeanKappa = 0.5 * (m_Kappa1 + m_Kappa2); m_GaussianKappa = m_Kappa1 * m_Kappa2; // std::cout << " eval test " << w1*m_Kappa1 + w2*m_Kappa2 << " e1 " << m_Eval1 << std::endl; // std::cout << " eval test " << w3*m_Kappa1 + w4*m_Kappa2 << " e2 " << m_Eval2 << std::endl; } /** */ template void SurfaceCurvatureBase ::PrintFrame() { std::cout << " normal " << m_Normal << std::endl; std::cout << " t1 " << m_Tangent1 << std::endl; std::cout << " t2 " << m_Tangent2 << std::endl; std::cout << " k1 " << m_Kappa1 << " k2 " << m_Kappa2 << std::endl; std::cout << " e0 " << m_Eval0 << " e1 " << m_Eval1 << " e2 " << m_Eval2 << std::endl; std::cout << " A " << m_A << " B " << m_B << " C " << m_C << std::endl; std::cout << std::endl; } /** */ template typename SurfaceCurvatureBase::RealType SurfaceCurvatureBase ::ErrorEstimate(PointType origin, RealType sign) { unsigned int npts = m_PointList.size() - 1; unsigned int dim = SurfaceDimension; // float error=0.0; float toterror = 0.0; double qpt = 0.0; double qpnkpt = 0.0; double costheta; double sintheta; double kp; RealType theta; PointType Q, est; for( unsigned int pp = 0; pp < npts; pp++ ) { Q = m_PointList[pp]; if( Q != origin ) { theta = this->GetTheta(Q, origin); costheta = cos(theta); sintheta = sin(theta); kp = m_Kappa1 * costheta * costheta + m_Kappa2 * sintheta * sintheta; if( kp != 0.0 ) { PointType qp = Q - origin; FixedVectorType normal = m_Normal * sign; float w1 = 0, w2 = 0; for( unsigned int j = 0; j < dim; j++ ) { qpt += qp[j] * normal[j] * kp; w1 += qp[j] * m_Tangent1[j]; w2 += qp[j] * m_Tangent2[j]; qpnkpt += qp[j] * m_PlaneVector[j]; } vnl_vector f2(4); f2(0) = 0.5 * kp * kp; f2(1) = 0.0; f2(2) = 1. - qpnkpt; f2(3) = -1. * qpt; /** commenting out until we can get vnl_rpoly_roots working vnl_rpoly_roots roots(f2); // Evaluate results //vnl_real_polynomial p(f2); //for(unsigned int i = 0; i < p.degree(); i++) // vnl_test_assert("Root residual", std::abs(p.evaluate(roots[i])) < 1e-12); float minrel=9.e9; float mins=0.0; float s=0.0; vnl_vector so=roots.real(); // for (s=0.1; s<=2.0; s=s+.01) for (unsigned int ind=0; ind void SurfaceCurvatureBase ::EstimateMetricTensor() { float a = 0; for( unsigned int i = 0; i < 3; i++ ) { a += m_Tangent1[i] * m_Tangent1[i]; } m_MetricTensor[0] = a; float b = 0; for( unsigned int i = 0; i < 3; i++ ) { b += m_Tangent2[i] * m_Tangent1[i]; } m_MetricTensor[1] = b; float c = 0; for( unsigned int i = 0; i < 3; i++ ) { c += m_Tangent2[i] * m_Tangent2[i]; } m_MetricTensor[2] = c; m_MetricTensorDeterminant = sqrt(a * c - b * b); if( m_MetricTensorDeterminant < 0.0 ) { std::cout << "bad metric tensor "; } } template float SurfaceCurvatureBase ::dstarUestimate() { // this->FindNeighborhood(); make sure this also fills the function values functionvaluelist this->EstimateTangentPlane(m_Origin); // below, basically replace the heights with the function values // Build cov matrix D int npts = m_PointList.size(); if( npts < 4 ) { return 0.0; } vnl_vector dists(npts); vnl_vector surfx(npts); vnl_vector surfy(npts); vnl_vector surfz(npts); vnl_vector funcvals(npts); vnl_vector wts(npts); MatrixType D; D.set_size(npts, 3); // each row contains [ u, v, 1] for point p D.fill(0.0); float totwt = 0.0; float totfunc = 0.0; float wt = 0.0; float meanu1 = 0; float meanu2 = 0; for( int i = 0; i < npts; i++ ) { PointType Q = m_PointList[i]; PointType Dif = Q - m_Origin; surfx[i] = Dif[0]; surfy[i] = Dif[1]; surfz[i] = Dif[2]; float u1 = 0.0; float u2 = 0.0; wt = Dif.magnitude(); wts[i] = wt; totwt += wt; for( unsigned int pp = 0; pp < SurfaceDimension; pp++ ) { u1 += Dif[pp] * m_Tangent1[pp]; u2 += Dif[pp] * m_Tangent2[pp]; } dists[i] = 0.0; PointType tanproj; for( unsigned int jj = 0; jj < SurfaceDimension; jj++ ) { m_PlaneVector[jj] = u1 * m_Tangent1[jj] + u2 * m_Tangent2[jj]; // tangent projection of Q tanproj[jj] = m_Origin[jj] + m_PlaneVector[jj]; } PointType temp = Q - tanproj; dists[i] = temp.magnitude(); funcvals[i] = m_FunctionValueList[i]; totfunc += m_FunctionValueList[i]; D(i, 0) = u1; D(i, 1) = u2; D(i, 2) = 1.0; meanu1 += fabs(u1); meanu2 += fabs(u2); } meanu1 /= (float)npts; meanu2 /= (float)npts; float mx; if( meanu1 > meanu2 ) { mx = meanu1; } else { mx = meanu2; } meanu1 /= mx; meanu2 /= mx; vnl_svd svd(D); float aa, bb, cc; vnl_vector a = svd.solve(surfx); vnl_vector b = svd.solve(surfy); vnl_vector c = svd.solve(surfz); aa = a(0) * a(0) + b(0) * b(0) + c(0) * c(0); bb = a(1) * a(0) + b(1) * b(0) + c(1) * c(0); cc = a(1) * a(1) + b(1) * b(1) + c(1) * c(1); vnl_vector func = svd.solve(funcvals); double Ux = func(0); double Uy = func(1); float dx = meanu1; // m_dX;//m_Eval1;//m_dX; float dy = meanu2; // m_dY;//m_Eval2;//m_dY; m_MetricTensor[0] = aa; m_MetricTensor[1] = bb; m_MetricTensor[2] = cc; float denom = sqrt(aa * cc - bb * bb); // denom=1.0; // std::cout << " denom " << denom << " dx " << dx << " dy " << dy << std::endl; // std::cout << " a " << aa << " b " << bb << " c " << cc << std::endl; float dstarU = 1.0 / denom * ( (bb * Ux - aa * Uy) * dx + (cc * Ux - bb * Uy) * dy ); // std::cout << " dstarU " << dstarU << std::endl; return dstarU; } } // namespace itk #endif ants-2.2.0/Utilities/itkSurfaceImageCurvature.h000066400000000000000000000202161311104306400215420ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _SurfaceImageCurvature_h #define _SurfaceImageCurvature_h #include "itkNeighborhoodIterator.h" #include "itkSurfaceCurvatureBase.h" #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkGradientImageFilter.h" #include "itkVectorLinearInterpolateImageFunction.h" namespace itk { /** \class SurfaceImageCurvature * * This class takes a surface as input and creates a local * geometric frame for each surface point. * * */ template class SurfaceImageCurvature : public SurfaceCurvatureBase { public: /** Standard class typedefs. */ typedef SurfaceImageCurvature Self; typedef SurfaceCurvatureBase Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(SurfaceImageCurvature, SurfaceCurvatureBase); /** Method for creation through the object factory. */ itkNewMacro(Self); /** Image related types. */ typedef typename TSurface::PixelType PixelType; enum { ImageDimension = TSurface::ImageDimension }; typedef Image ImageType; typedef typename ImageType::IndexType IndexType; typedef typename ImageType::SizeType SizeType; typedef ImageRegionIteratorWithIndex ImageIteratorType; /** Image dimension. */ itkStaticConstMacro(SurfaceDimension, unsigned int, TSurface::ImageDimension); typedef typename Superclass::RealType RealType; typedef typename Superclass::PointType VectorType; typedef typename Superclass::PointType FixedVectorType; typedef typename Superclass::PointType PointType; typedef typename Superclass::MatrixType MatrixType; typedef typename ImageType::PointType ImagePointType; typedef Image OutputImageType; typedef ImageRegionIteratorWithIndex OutputImageIteratorType; typedef typename OutputImageType::Pointer OutputImagePointer; typedef Image FrameImageType; /** Gradient filtering */ typedef CovariantVector GradientPixelType; typedef Image GradientImageType; typedef itk::VectorLinearInterpolateImageFunction VectorInterpolatorType; typedef SmartPointer GradientImagePointer; typedef GradientRecursiveGaussianImageFilter GradientImageFilterType; typedef GradientImageFilter GradientImageFilterType2; typedef typename GradientImageFilterType::Pointer GradientImageFilterPointer; typedef NeighborhoodIterator NeighborhoodIteratorType; /** Find all points within some distance of the origin. * The argument gives the number of times to apply the * mean shift algorithm to find the best neighborhood. */ void FindNeighborhood(unsigned int numMeanShifts = 0) ITK_OVERRIDE; void FindEuclideanNeighborhood(PointType p); void FindGeodesicNeighborhood(); /** This applies one of the algorithms for finding the local curvature and frame. The default is joshi. */ void ComputeFrameOverDomain(unsigned int which = 0) ITK_OVERRIDE; ImageType * GetInput(); virtual void SetInputImage(typename ImageType::Pointer & input); OutputImageType * GetOutput(); /** Apply the level set curvature equation over the whole image */ void LevelSetMeanCurvature(); /** Use the gradient of the image to estimate the normals everywhere. * Also compute area, if boolean is set. */ void EstimateNormalsFromGradient(); /** Use the Weingarten map to estimate the curvature.*/ void WeingartenMap(); /** Use the Weingarten map to estimate the curvature.*/ void WeingartenMapGradients(); /** Computes a neighborhood surface area function everywhere*/ void ComputeSurfaceArea(); /** Use the gradient estimated normal to get the local frame. Requires call to SetNormal to find tangents. */ void EstimateFrameFromGradient(IndexType); void EstimateFrameFromGradient(ImagePointType); /** Implemented version of virtual function from parent class. Here, we just sum the computed function, held in CurvatureImage, over a neighborhood */ RealType IntegrateFunctionOverNeighborhood(bool norm = false) ITK_OVERRIDE; /** Get the neighborhood integral for every surface point.*/ RealType IntegrateFunctionOverSurface(bool norm = false); /** Postprocess the curvature function by, e.g., gaussian smoothing of the curvature (and perhaps frame) in the local neighbhorhood. */ void PostProcessGeometry(); itkSetMacro(NeighborhoodRadius, RealType); itkGetMacro(NeighborhoodRadius, RealType); itkSetMacro(UseLabel, bool); itkGetMacro(UseLabel, bool); itkSetMacro(kSign, float); itkSetMacro(Sigma, float); itkSetMacro(Threshold, float); itkGetMacro(FunctionImage, OutputImagePointer); itkSetMacro(FunctionImage, OutputImagePointer); void ProcessLabelImage(); float CurvatureAtIndex(IndexType index) { PointType p; for( unsigned int k = 0; k < ImageDimension; k++ ) { p[k] = (RealType) index[k]; } this->SetOrigin(p); this->EstimateFrameFromGradient(index); this->FindNeighborhood(); this->WeingartenMap(); float fval = this->m_GaussianKappa; float kpix; fval = this->m_MeanKappa; if( fabs(fval) > 1 ) { fval /= fval; } kpix = kpix + m_kSign * fval; return kpix; } inline RealType innerProduct( PointType v1, PointType v2 ) { return ( v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2] ); } inline bool IsValidIndex( IndexType ind ) { for( unsigned int i = 0; i < ImageDimension; i++ ) { float shifted = ind[i]; if( shifted < 1 || shifted > ( this->m_ImageSize[i] - 1 ) ) { return false; } } return true; } inline bool IsValidSurface(PixelType pix, IndexType ind ) { for( unsigned int i = 0; i < ImageDimension; i++ ) { float shifted = ind[i]; if( shifted < 1 || shifted > ( this->m_ImageSize[i] - 1 ) ) { return false; } } if( this->m_UseLabel ) { if( pix == this->m_SurfaceLabel ) { return true; } else { return false; } } else { if( pix > this->m_Threshold ) { return true; } else { return false; } } } protected: SurfaceImageCurvature(); ~SurfaceImageCurvature() { }; void CopyImageToFunctionImage( OutputImagePointer, OutputImagePointer); /** This function changes the values of the label image for use with the fast marching image filter. */ private: PixelType m_SurfaceLabel; OutputImagePointer m_FunctionImage; RealType m_NeighborhoodRadius; SizeType m_ImageSize; GradientImagePointer m_GradientImage; NeighborhoodIteratorType m_ti; NeighborhoodIteratorType m_ti2; bool m_UseLabel; float m_kSign; float m_Sigma; float m_Threshold; float m_Area; RealType m_MinSpacing; typename VectorInterpolatorType::Pointer m_Vinterp; }; } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkSurfaceImageCurvature.hxx" #endif #endif ants-2.2.0/Utilities/itkSurfaceImageCurvature.hxx000066400000000000000000001022241311104306400221220ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _SurfaceImageCurvature_hxx #define _SurfaceImageCurvature_hxx #include "antsAllocImage.h" #include #include "itkSurfaceImageCurvature.h" // #include "itkLevelSetCurvatureFunction.h" #include "itkCastImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" #include #include #include namespace itk { /** Quick node class to help get the geodesic neighborhood */ template class GeodesicNode { public: /** Image related types. */ typedef TSurface ImageType; typedef typename ImageType::IndexType IndexType; unsigned long neighborhoodindex; float distance; bool connected; IndexType imageindex; GeodesicNode() { distance = 0.0; connected = false; neighborhoodindex = 0; } GeodesicNode(unsigned long i, float d, bool t, IndexType ind) { distance = d; connected = t; neighborhoodindex = i; imageindex = ind; } ~GeodesicNode() { } }; template class GeodesicNodePriority /* defines the comparison operator for the prioritiy queue */ { public: bool operator()( pclass N1, pclass N2) { return N1.distance > N2.distance; } }; template SurfaceImageCurvature ::SurfaceImageCurvature() { // outputs are curvature image and normal image this->ProcessObject::SetNumberOfRequiredOutputs( 2 ); m_SurfaceLabel = 1; m_GradientImage = ITK_NULLPTR; m_UseLabel = false; m_kSign = -1.0; m_FunctionImage = ITK_NULLPTR; m_Sigma = 1.0; this->m_Vinterp = ITK_NULLPTR; this->m_MinSpacing = itk::NumericTraits::max() ; } template void SurfaceImageCurvature::ProcessLabelImage() { ImageType* image = GetInput(); if( !image ) { return; } IndexType index; typename ImageType::RegionType requestedRegion; m_ImageSize = image->GetLargestPossibleRegion().GetSize(); ImageIteratorType ti( image, image->GetLargestPossibleRegion() ); ti.GoToBegin(); while( !ti.IsAtEnd() ) { PixelType pix = ti.Get(); index = ti.GetIndex(); if( ti.Get() == 2 ) { ti.Set(m_SurfaceLabel); } else { ti.Set(0); } ti.Set(0); // if (index[0] == 20 && index[1] < 100 && index[2] < 100) ti.Set(m_SurfaceLabel); float rad = 23, d = 0; IndexType cind = {{120, 120, 80}}; for( unsigned int j = 0; j < ImageDimension; j++ ) { d += (cind[j] - index[j]) * (cind[j] - index[j]); } d = sqrt(d); if( fabs(d - rad) <= 0.5 ) { ti.Set(m_SurfaceLabel); } rad = 12, d = 0; IndexType cind2 = {{20, 170, 45}}; for( unsigned int j = 0; j < ImageDimension; j++ ) { d += (cind2[j] - index[j]) * (cind2[j] - index[j]); } d = sqrt(d); // if (fabs(d-rad) <= 0.5) ti.Set(m_SurfaceLabel); ++ti; } } template void SurfaceImageCurvature::FindEuclideanNeighborhood (typename SurfaceImageCurvature::PointType rootpoint) { this->m_AveragePoint = this->m_Origin; this->m_PointList.insert(this->m_PointList.begin(), this->m_Origin); typename ImageType::SizeType rad; IndexType oindex, index; typename ImageType::PointType tempp; tempp[0] = rootpoint[0]; tempp[1] = rootpoint[1]; tempp[2] = rootpoint[2]; this->m_FunctionImage->TransformPhysicalPointToIndex( tempp, oindex ); for( unsigned int i = 0; i < ImageDimension; i++ ) { rad[i] = (long)(m_NeighborhoodRadius); } m_ti.SetLocation(oindex); unsigned int temp = 0; for( temp = 0; temp < m_ti.Size(); temp++ ) { index = m_ti.GetIndex(temp); if( this->IsValidIndex( index) ) { float pix = m_ti.GetPixel( temp ); if( this->IsValidSurface( pix, index) ) { PointType p; typename ImageType::PointType ipt; this->m_FunctionImage->TransformIndexToPhysicalPoint( index, ipt ); float dist = 0.0; bool isorigin = true; for( unsigned int k = 0; k < ImageDimension; k++ ) { if( index[k] != oindex[k] ) { isorigin = false; } p[k] = ipt[k]; RealType delt = static_cast(oindex[k]) - static_cast(index[k]); dist += delt*delt; } dist = sqrt(dist); if( !isorigin && dist <= (m_NeighborhoodRadius) ) { this->m_AveragePoint = this->m_AveragePoint + p; this->m_PointList.insert(this->m_PointList.begin(), p); } } } // test valid index and valid surface } unsigned int npts = this->m_PointList.size(); if( npts > 0 ) { this->m_AveragePoint /= (RealType)npts; } else { this->m_AveragePoint = this->m_Origin; } if( this->m_Debug ) { std::cout << " point list size " << this->m_PointList.size() << std::endl; for( unsigned int i = 0; i < this->m_PointList.size(); i++ ) { std:: cout << " point " << this->m_PointList[i]; } std::cout << std::endl; } } template void SurfaceImageCurvature::FindGeodesicNeighborhood() { typedef std::priority_queue , std::vector >, GeodesicNodePriority > > QType; QType nodeq; std::map > nodes; this->m_AveragePoint = this->m_Origin; this->m_PointList.insert(this->m_PointList.begin(), this->m_Origin); typename ImageType::SizeType rad; IndexType oindex, index; float dist = 0.0; unsigned int k = 0; unsigned long longindex = 0; for( unsigned int i = 0; i < ImageDimension; i++ ) { rad[i] = (long)(m_NeighborhoodRadius); oindex[i] = (long) (this->m_Origin[i] + 0.5); } index = oindex; for( k = 0; k < ImageDimension; k++ ) { if( k == 0 ) { longindex = index[0]; } if( k == 1 ) { longindex = index[1] + longindex + index[0] * m_ImageSize[0]; } if( k == 2 ) { longindex = index[2] + longindex + index[2] * m_ImageSize[0] * m_ImageSize[1]; } } GeodesicNode gnode(longindex, 0.0, true, oindex); nodes[longindex] = gnode; nodeq.push(gnode); float lastdist = 0.0; // if ( this->m_Origin[1]==146 && this->m_Origin[0] > 167 ) // if ( this->m_Origin[1]==146 && this->m_Origin[0] == 168 && this->m_Origin[2]==215) // { // std::cout << " origin " << this->m_Origin << std::endl; // } while( !nodeq.empty() && lastdist <= m_NeighborhoodRadius ) { GeodesicNode g = nodeq.top(); lastdist = g.distance; PointType q; // if ( g.distance < 2.0) // { // this->m_PointList.insert(this->m_PointList.begin(),q); // nodes[g.neighborhoodindex].connected=true; // } // else if( lastdist <= m_NeighborhoodRadius ) { m_ti2.SetLocation(g.imageindex); for( unsigned int jj = 0; jj < m_ti2.Size(); jj++ ) { index = m_ti2.GetIndex(jj); if( // m_ti2.GetPixel(jj) == m_SurfaceLabel && this->IsValidSurface( m_ti2.GetPixel(jj), index) && index[0] < m_ImageSize[0] - m_NeighborhoodRadius && index[0] > m_NeighborhoodRadius && index[1] < m_ImageSize[1] - m_NeighborhoodRadius && index[1] > m_NeighborhoodRadius && index[2] < m_ImageSize[2] - m_NeighborhoodRadius && index[2] > m_NeighborhoodRadius ) { longindex = 0; dist = 0; for( k = 0; k < ImageDimension; k++ ) { if( k == 0 ) { longindex = index[0]; } if( k == 1 ) { longindex = index[1] + longindex + index[0] * m_ImageSize[0]; } if( k == 2 ) { longindex = index[2] + longindex + index[2] * m_ImageSize[0] * m_ImageSize[1]; } q[k] = (RealType) index[k]; // dist+=(g.imageindex[k]-oindex[k])*(g.imageindex[k]-oindex[k]); // dist+=(index[k]-oindex[k])*(index[k]-oindex[k]); dist += (float)(g.imageindex[k] - index[k]) * (g.imageindex[k] - index[k]); } dist = sqrt(dist); // if ( this->m_Origin[1]==146 && this->m_Origin[0] == 168 && this->m_Origin[2]==215) // { // std::cout << " testing point " << index << " longind " << longindex << " dist " << dist << // " bool " << nodes[longindex].connected << std::endl; // } // if (!nodes[longindex].connected ) //&& !nodes[g.neighborhoodindex].connected) if( !nodes[longindex].connected && (dist + lastdist) <= m_NeighborhoodRadius ) { GeodesicNode _gnode(longindex, dist + lastdist, true, index); // GeodesicNode _gnode(longindex,dist,true,index); nodes[longindex] = _gnode; nodeq.push(_gnode); // if ( this->m_Origin[1]==146 && this->m_Origin[0] == 168 && this->m_Origin[2]==215) // /{ // std::cout << " inserting point " << index << std::endl; // } this->m_PointList.insert(this->m_PointList.begin(), q); this->m_AveragePoint = this->m_AveragePoint + q; } // else if ( dist > 0 && dist+lastdist < nodes[longindex].distance ) // { // nodes[longindex].distance=dist+lastdist; // } } } } nodeq.pop(); } this->m_AveragePoint = this->m_AveragePoint / ( (float)this->m_PointList.size() ); } template void SurfaceImageCurvature::FindNeighborhood(unsigned int numMeanShifts) { if( this->m_UseGeodesicNeighborhood ) { this->FindGeodesicNeighborhood(); } else { this->FindEuclideanNeighborhood( this->GetOrigin() ); for( unsigned int dd = 0; dd < numMeanShifts; dd++ ) { this->m_PointList.clear(); this->FindEuclideanNeighborhood(this->GetAveragePoint() ); } } } template void SurfaceImageCurvature ::LevelSetMeanCurvature() { /* ImageType* image=GetInput(); if (!image) return; IndexType index; typename ImageType::RegionType requestedRegion; this->m_ImageSize=image->GetLargestPossibleRegion().GetSize(); ImageIteratorType ti( image, image->GetLargestPossibleRegion() ); // Define a level set curvature calculator typedef LevelSetCurvatureFunction CurvatureType; typename CurvatureType::Pointer inCurvature = CurvatureType::New(); inCurvature->SetInputImage( image ); ti.GoToBegin(); while(!ti.IsAtEnd() ) { index=ti.GetIndex(); //if (ti.Get() == this->m_SurfaceLabel) if(this->IsValidSurface(ti.Get(),index)) { double curvature = inCurvature->EvaluateAtIndex( index ); this->m_FunctionImage->SetPixel(index,fabs(curvature)); } ++ti; } */ } template void SurfaceImageCurvature ::EstimateNormalsFromGradient() { typename ImageType::Pointer image = GetInput(); if( !image ) { return; } typename ImageType::SizeType rad; typename ImageType::SizeType rad2; for( unsigned int t = 0; t < ImageDimension; t++ ) { rad[t] = (unsigned long) (this->m_NeighborhoodRadius); rad2[t] = 1; } this->m_ti.Initialize( rad, image, image->GetLargestPossibleRegion() ); this->m_ti2.Initialize( rad2, image, image->GetLargestPossibleRegion() ); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType Iterator( image, image->GetLargestPossibleRegion().GetSize() ); bool wmgmcurv = true; Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { float pix = Iterator.Get(); if( pix != 0 && pix != 1 && pix != 2 ) { wmgmcurv = false; } ++Iterator; } if( wmgmcurv ) { typename OutputImageType::Pointer laplacian = AllocImage( image->GetLargestPossibleRegion() ); laplacian->SetSpacing(image->GetSpacing() ); laplacian->SetDirection(image->GetDirection() ); laplacian->SetOrigin(image->GetOrigin() ); Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { IndexType ind = Iterator.GetIndex(); if( image->GetPixel(ind) == 2 ) { laplacian->SetPixel(ind, 1); } else if( image->GetPixel(ind) == 1 ) { laplacian->SetPixel(ind, 0.); } else { laplacian->SetPixel(ind, 0.); } ++Iterator; } // smooth and then reset the values unsigned int totit = 50; for( unsigned int iterations = 0; iterations < totit; iterations++ ) { while( !Iterator.IsAtEnd() ) { IndexType ind = Iterator.GetIndex(); if( image->GetPixel(ind) == 2 ) { laplacian->SetPixel(ind, 1); } else if( image->GetPixel(ind) == 0 ) { laplacian->SetPixel(ind, 0.); } ++Iterator; } typedef itk::DiscreteGaussianImageFilter dgf; typename dgf::Pointer filter = dgf::New(); filter->SetVariance(0.5); filter->SetUseImageSpacingOn(); filter->SetMaximumError(.01f); filter->SetInput(laplacian); filter->Update(); laplacian = filter->GetOutput(); Iterator.GoToBegin(); } // WriteImage(laplacian,"lap.hdr"); GradientImageFilterPointer filter = GradientImageFilterType::New(); filter->SetInput( laplacian ); RealType sigma = this->m_Sigma; filter->SetSigma( sigma ); // Execute the filter filter->Update(); this->m_GradientImage = filter->GetOutput(); GradientPixelType zero; zero.Fill(0); Iterator.GoToBegin(); while( !Iterator.IsAtEnd() ) { IndexType ind = Iterator.GetIndex(); if( image->GetPixel(ind) != 1 ) { this->m_GradientImage->SetPixel(ind, zero); } ++Iterator; } } else { GradientImageFilterPointer filter = GradientImageFilterType::New(); filter->SetInput( image ); RealType sigma = this->m_Sigma; filter->SetSigma( sigma ); // Execute the filter filter->Update(); this->m_GradientImage = filter->GetOutput(); } this->m_Vinterp = VectorInterpolatorType::New(); this->m_Vinterp->SetInputImage( this->m_GradientImage ); } template void SurfaceImageCurvature ::WeingartenMap() { typename ImageType::Pointer image = this->GetInput(); if( !image ) { return; } MatrixType D; unsigned int j = 0; unsigned int i = 0; unsigned int npts = this->m_PointList.size(); unsigned int vars = 3; if( npts < (vars+1) ) { this->m_MeanKappa = 0; this->m_GaussianKappa = 0; this->m_Kappa1 = 0; this->m_Kappa2 = 0; this->m_Area = 0; return; } D.set_size(npts, vars); // each row contains [u^2 , uv, v^2, u, v, 1] for point p D.fill(0.0); MatrixType W(2, 2); W.fill(0.0); vnl_vector xdists(npts); xdists.fill(0.0); vnl_vector ydists(npts); ydists.fill(0.0); vnl_vector zdists(npts); zdists.fill(0.0); vnl_vector f_uvs(npts); f_uvs.fill(0.0); // go through all the points // compute weight // compute dist of unit dif and tangents // compute dif of normal with grad at point this->m_Area = 0; PointType Q = this->m_Origin; RealType areaelt = 1000.0 / static_cast(npts); for( j = 0; j < npts; j++ ) { typename ImageType::PointType pt; pt[0] = this->m_PointList[j][0]; pt[1] = this->m_PointList[j][1]; pt[2] = this->m_PointList[j][2]; GradientPixelType norm = this->m_Vinterp->Evaluate( pt ); PointType Grad; for( i = 0; i < SurfaceDimension; i++ ) { Grad[i] = norm[i]; } PointType PN = Grad / ( Grad.magnitude() ); // get the surface parameterization ... float u1 = 0.0; float u2 = 0.0; float f_uv = 0.0; // PointType Dif = Q + this->m_Normal - this->m_PointList[j] - PN; PointType Dif = Q - this->m_PointList[j]; // this is the predefined u_i parameter u1 = this->innerProduct( Dif, this->m_Tangent1 ); // this is the predefined v_i parameter u2 = this->innerProduct( Dif, this->m_Tangent2 ); // now the inner product of PN and the normal is f_uv ... f_uv = this->innerProduct( PN, this->m_Normal ); // the point is therefore defined as: // PointType surfacePoint = this->m_Tangent1 * u1 + this->m_Tangent2 * u2 + PN; xdists[j] = (PN[0]); ydists[j] = (PN[1]); zdists[j] = (PN[2]); f_uvs[j] = f_uv; if( vars == 6 ) { // each row contains [u^2 , uv, v^2, u, v, 1] for point p D(j, 5) = u2 * u2; // (0 , 2*u2) D(j, 4) = u1 * u1; // (2*u1, 0) D(j, 3) = u1 * u2; // (u2 , u1) } // each row contains [ u, v, 1] for point p D(j, 2) = u2; // (1 , 0) D(j, 1) = u1; // (0 , 1) D(j, 0) = 1.0; RealType dfuv_u = 0; RealType dfuv_v = 0; if ( ( vnl_math_abs(u1) > 0 ) && ( vnl_math_abs(u2) < 1.e-6 ) ) dfuv_u = vnl_math_abs( f_uv - 1.0 ) / vnl_math_abs(u1) * 100.0; if ( ( vnl_math_abs(u2) > 0 ) && ( vnl_math_abs(u1) < 1.e-6 ) ) dfuv_v = vnl_math_abs( f_uv - 1.0 ) / vnl_math_abs(u2) * 100.0; // this->m_Area += sqrt( 1.0 + dfuv_u*dfuv_u + dfuv_v*dfuv_v ); this->m_Area += vnl_math_abs( f_uv - 1.0 ); } this->m_Area *= areaelt; vnl_svd svd(D); vnl_vector ax = svd.solve(xdists); // /totwt); vnl_vector ay = svd.solve(ydists); // /totwt); vnl_vector az = svd.solve(zdists); // /totwt); vnl_vector df_uvs = svd.solve( f_uvs ); // /totwt); // now get the first partials of each of these terms w.r.t. u and v // dN/du = (dN/du \dot T_1) T_1+ (dNdu dot T_2) T_2 PointType dNdu; dNdu[0] = ax[1]; dNdu[1] = ay[1]; dNdu[2] = az[1]; PointType dNdv; dNdv[0] = ax[2]; dNdv[1] = ay[2]; dNdv[2] = az[2]; // df_uvs = df_uvs * 1.e5; // scale up // this->m_Area = sqrt( 1.0 + df_uvs[1] * df_uvs[1] + df_uvs[2] * df_uvs[2] ); float a = 0; float b = 0; float c = 0; float d = 0; for( i = 0; i < SurfaceDimension; i++ ) { a += dNdu[i] * this->m_Tangent1[i]; b += dNdv[i] * this->m_Tangent1[i]; c += dNdu[i] * this->m_Tangent2[i]; d += dNdv[i] * this->m_Tangent2[i]; } W(0, 0) = a; W(0, 1) = b; W(1, 0) = c; W(1, 1) = d; // Compute estimated frame using eigensystem of D'*D { vnl_real_eigensystem eig(W); vnl_diag_matrix > DD(eig.D.rows() ); // this->m_Kappa1 = std::real(eig.D(1, 1) ); this->m_Kappa2 = std::real(eig.D(0, 0) ); this->m_MeanKappa = (this->m_Kappa1 + this->m_Kappa2) * 0.5; this->m_GaussianKappa = (this->m_Kappa1 * this->m_Kappa2); } } template void SurfaceImageCurvature ::WeingartenMapGradients() { typename ImageType::Pointer image = this->GetInput(); if( !image ) { return; } // rebuild the point list from gradients defined at u and v this->m_PointList.clear(); PointType p; RealType paramdelt = this->m_MinSpacing*0.5; RealType eps=1.e-6; for ( RealType zi = -1.0 * paramdelt; zi <= paramdelt+eps; zi=zi+paramdelt) for ( RealType ui = -1.0 * paramdelt; ui <= paramdelt+eps; ui=ui+paramdelt) { for ( RealType vi = -1.0 * paramdelt; vi <= paramdelt+eps; vi=vi+paramdelt) { p = this->m_Origin + this->m_Tangent1 * ui + this->m_Tangent2 * vi + this->m_Normal * zi; this->m_PointList.insert( this->m_PointList.begin(), p ); } } this->WeingartenMap(); return; } template void SurfaceImageCurvature ::ComputeSurfaceArea() { ImageType* image = GetInput(); typename ImageType::SpacingType ispacing = image->GetSpacing(); if( !image ) { return; } FixedVectorType spacing; spacing[0] = ispacing[0]; spacing[1] = ispacing[1]; spacing[2] = ispacing[2]; // BUG FIXME if( !this->m_GradientImage ) { this->EstimateNormalsFromGradient(); } IndexType index; typename ImageType::RegionType requestedRegion; this->m_ImageSize = image->GetLargestPossibleRegion().GetSize(); ImageIteratorType ti( image, image->GetLargestPossibleRegion() ); RealType area = 0.0; this->m_TotalArea = 0.0; ti.GoToBegin(); unsigned int ct = 0; while( !ti.IsAtEnd() ) { index = ti.GetIndex(); if( // ti.Get() == this->m_SurfaceLabel && (this->IsValidSurface(ti.Get(), index) ) && index[0] < this->m_ImageSize[0] - this->m_NeighborhoodRadius && index[0] > this->m_NeighborhoodRadius && index[1] < this->m_ImageSize[1] - this->m_NeighborhoodRadius && index[1] > this->m_NeighborhoodRadius && index[2] < this->m_ImageSize[2] - this->m_NeighborhoodRadius && index[2] > this->m_NeighborhoodRadius ) { ct++; // BUG FIXME area = this->ComputeLocalArea( spacing ); // area = 1.0; this->m_FunctionImage->SetPixel(index, area); this->m_TotalArea += area; this->m_PointList.clear(); if( ct % 1000 == 0 ) { // std::cout << " ind " << index << " area " << area << std::endl; } } ++ti; } // std::cout << " surface area " << this->m_TotalArea << std::endl; return; } template void SurfaceImageCurvature ::EstimateFrameFromGradient( IndexType index ) { GradientPixelType g = this->m_GradientImage->GetPixel( index ); RealType mag = 0.0; for( int i = 0; i < ImageDimension; i++ ) { this->m_Normal(i) = (RealType) g[i]; mag += g[i] * g[i]; } mag = sqrt(mag); if( mag <= 1.e-9 ) { this->m_Normal.fill(0.); } else { this->m_Normal /= sqrt(mag); } this->SetFrameFromNormal(this->m_Normal); } template void SurfaceImageCurvature ::EstimateFrameFromGradient( ImagePointType ipt ) { GradientPixelType g = this->m_Vinterp->Evaluate( ipt ); RealType mag = 0.0; for( int i = 0; i < ImageDimension; i++ ) { this->m_Normal(i) = (RealType) g[i]; mag += g[i] * g[i]; } mag = sqrt(mag); if( mag <= 1.e-9 ) { this->m_Normal.fill(0.); } else { this->m_Normal /= sqrt(mag); } this->SetFrameFromNormal(this->m_Normal); } template typename SurfaceImageCurvature::RealType SurfaceImageCurvature ::IntegrateFunctionOverSurface(bool norm) { typename OutputImageType::Pointer image = this->m_FunctionImage; typename OutputImageType::Pointer tempimage = OutputImageType::New(); tempimage->SetLargestPossibleRegion( image->GetLargestPossibleRegion() ); tempimage->SetBufferedRegion( image->GetLargestPossibleRegion() ); tempimage->Allocate(); typename ImageType::SizeType rad; typename ImageType::SizeType rad2; for( unsigned int t = 0; t < ImageDimension; t++ ) { rad[t] = (unsigned long) (this->m_NeighborhoodRadius); rad2[t] = 1; } this->m_ti.Initialize( rad, this->GetInput(), image->GetLargestPossibleRegion() ); this->m_ti2.Initialize( rad2, this->GetInput(), image->GetLargestPossibleRegion() ); IndexType index; typename ImageType::RegionType requestedRegion; ImageIteratorType ti( this->GetInput(), this->GetInput()->GetLargestPossibleRegion() ); // std::cout << " begin integrate "; ti.GoToBegin(); unsigned int ct = 0; // std::cout << " begin while " << std::endl; while( !ti.IsAtEnd() ) { index = ti.GetIndex(); tempimage->SetPixel(index, 0); if( // ti.Get() == this->m_SurfaceLabel && (this->IsValidSurface(ti.Get(), index) ) && index[0] < this->m_ImageSize[0] - this->m_NeighborhoodRadius && index[0] > this->m_NeighborhoodRadius && index[1] < this->m_ImageSize[1] - this->m_NeighborhoodRadius && index[1] > this->m_NeighborhoodRadius && index[2] < this->m_ImageSize[2] - this->m_NeighborhoodRadius && index[2] > this->m_NeighborhoodRadius ) { PointType p; ct++; for( unsigned int k = 0; k < ImageDimension; k++ ) { p[k] = (RealType) index[k]; } this->SetOrigin(p); // std::cout << " find nhood "; this->FindNeighborhood(); RealType area = this->IntegrateFunctionOverNeighborhood(norm); tempimage->SetPixel(index, area); } ++ti; } this->CopyImageToFunctionImage(tempimage, this->m_FunctionImage); return 0; } template void SurfaceImageCurvature ::CopyImageToFunctionImage(OutputImagePointer i1, OutputImagePointer i2) { if( !i1 || !i2 ) { return; } typename ImageType::RegionType requestedRegion; OutputImageIteratorType ti1( i1, i1->GetLargestPossibleRegion() ); OutputImageIteratorType ti2( i2, i2->GetLargestPossibleRegion() ); ti1.GoToBegin(); ti2.GoToBegin(); while( !ti1.IsAtEnd() ) { ti2.Set(ti1.Get() ); ++ti1; ++ti2; } } template typename SurfaceImageCurvature::RealType SurfaceImageCurvature ::IntegrateFunctionOverNeighborhood(bool norm) { unsigned int npts = this->m_PointList.size(); double curvature = 0.0, tw = 0; for( unsigned int pp = 0; pp < npts; pp++ ) { IndexType localindex; for( unsigned int k = 0; k < ImageDimension; k++ ) { localindex[k] = (long) this->m_PointList[pp][k]; } PointType dd = this->m_Origin - this->m_PointList[pp]; double wi = dd.magnitude(); if( wi != 0.0 ) { wi = 1. / wi; } tw += wi; RealType func = this->m_FunctionImage->GetPixel( localindex ); if( norm ) { curvature += wi * func; } else { curvature += func; } // curvature*=this->ComputeLocalArea(spacing); } // if (norm ) curvature/=tw; // SD sometimes tw is zero making curvature = NaN if( norm && tw != 0 ) { curvature /= tw; } this->m_PointList.clear(); return curvature; } template void SurfaceImageCurvature ::PostProcessGeometry() { typename ImageType::Pointer image = GetInput(); if( !image ) { return; } IndexType index; typename ImageType::RegionType requestedRegion; this->m_ImageSize = image->GetLargestPossibleRegion().GetSize(); ImageIteratorType ti( image, image->GetLargestPossibleRegion() ); std::vector kvec; ti.GoToBegin(); while( !ti.IsAtEnd() ) { PixelType pix = ti.Get(); index = ti.GetIndex(); if( // ti.Get() == this->m_SurfaceLabel && (this->IsValidSurface(ti.Get(), index) ) && index[0] < this->m_ImageSize[0] - this->m_NeighborhoodRadius && index[0] > this->m_NeighborhoodRadius && index[1] < this->m_ImageSize[1] - this->m_NeighborhoodRadius && index[1] > this->m_NeighborhoodRadius && index[2] < this->m_ImageSize[2] - this->m_NeighborhoodRadius && index[2] > this->m_NeighborhoodRadius ) // { PointType p; for( unsigned int k = 0; k < ImageDimension; k++ ) { p[k] = (RealType) index[k]; } this->SetOrigin(p); this->FindNeighborhood(); int npts = this->m_PointList.size() - 1; double curvature = 0.0; for( int pp = 0; pp < npts; pp++ ) { IndexType localindex; for( unsigned int k = 0; k < ImageDimension; k++ ) { localindex[k] = (long) this->m_PointList[pp][k]; } // PointType dd=this->m_Origin-this->m_PointList[pp]; // double wi=dd.magnitude(); // if (wi!=0.0) wi=1./wi; // tw+=wi;vector vec; curvature = this->m_FunctionImage->GetPixel( localindex ); kvec.push_back(curvature); } std::sort(kvec.begin(), kvec.end() ); // Sort the vector this->m_PointList.clear(); // curvature/=tw; this->m_FunctionImage->SetPixel(index, kvec[kvec.size() / 2]); kvec.clear(); } ++ti; } } template void SurfaceImageCurvature ::ComputeFrameOverDomain(unsigned int which) { ImageType* image = this->GetInput(); if( !image ) { return; } for ( unsigned int d = 0; d < ImageDimension; d++ ) if ( image->GetSpacing()[d] < this->m_MinSpacing ) this->m_MinSpacing = image->GetSpacing()[d]; IndexType index; typename ImageType::RegionType requestedRegion; this->m_ImageSize = image->GetLargestPossibleRegion().GetSize(); ImageIteratorType ti( image, image->GetLargestPossibleRegion() ); // std::exception(); // Get Normals First! this->EstimateNormalsFromGradient(); unsigned int ct = 1; unsigned long ct2 = 0; RealType kpix = 0; double thresh = 0.0; ti.GoToBegin(); while( !ti.IsAtEnd() ) { index = ti.GetIndex(); typename ImageType::PointType pt; image->TransformIndexToPhysicalPoint( index, pt ); kpix = 0.0; if( // ti.Get() == this->m_SurfaceLabel && this->IsValidSurface(ti.Get(), index) && index[0] < this->m_ImageSize[0] - 2 * this->m_NeighborhoodRadius && index[0] > 2 * this->m_NeighborhoodRadius && index[1] < this->m_ImageSize[1] - 2 * this->m_NeighborhoodRadius && index[1] > 2 * this->m_NeighborhoodRadius && index[2] < this->m_ImageSize[2] - 2 * this->m_NeighborhoodRadius && index[2] > 2 * this->m_NeighborhoodRadius ) // { PointType p; for( unsigned int k = 0; k < ImageDimension; k++ ) { p[k] = pt[k]; } this->SetOrigin(p); this->EstimateFrameFromGradient( pt ); this->FindNeighborhood(); switch( which ) { case ( 0 ): { this->ComputeJoshiFrame( this->m_Origin); } break; case ( 1 ): { this->JainMeanAndGaussianCurvature( this->m_Origin); } break; case ( 2 ): { this->ShimshoniFrame(this->m_Origin); } break; case ( 3 ): { this->WeingartenMapGradients(); } break; case ( 4 ): { kpix = this->ComputeMeanEuclideanDistance(); } break; default: { this->WeingartenMapGradients(); } } // this->PrintFrame(); bool geterror = false; if( geterror ) { float error = 0.0; float temp1 = this->ErrorEstimate(this->GetOrigin() ); float temp2 = this->ErrorEstimate(this->GetOrigin(), -1); if( temp1 < temp2 ) { error = temp1; } else { error = temp2; this->SwitchNormalSign(); // this->ComputeWeightsAndDirectionalKappaAndAngles(this->GetOrigin()); // this->EstimateCurvature(this->m_A,this->m_B,this->m_B,this->m_C); // this->EstimateCurvature(); } // std::cout << " best error " << error << std::endl; } kpix = 0; float fval = this->m_GaussianKappa; fval = this->m_MeanKappa; // if( fabs(fval) > 1 ) // { // fval = 0; // } kpix = this->m_kSign * fval; // sulci if( vnl_math_isnan(kpix) || vnl_math_isinf(kpix) ) { this->m_Kappa1 = 0.0; this->m_Kappa2 = 0.0; this->m_MeanKappa = 0.0; this->m_GaussianKappa = 0.0; this->m_Area = 0.0; kpix = 0.0; } if( which == 5 ) { kpix = this->CharacterizeSurface(); } if( which == 6 ) { kpix = this->m_GaussianKappa; } if( which == 7 ) { kpix = this->m_Area; } ct++; this->m_PointList.clear(); } thresh += kpix; float offset = 0; if( which == 5 ) { offset = 0; } // if ( () && ( ) ) kpix=0; this->m_FunctionImage->SetPixel( index, offset + kpix ); ct2++; ++ti; } } template typename SurfaceImageCurvature::ImageType * SurfaceImageCurvature ::GetInput(void) { if( this->GetNumberOfInputs() < 1 ) { return ITK_NULLPTR; } return static_cast (this->ProcessObject::GetInput(0) ); } /** * */ template typename SurfaceImageCurvature::OutputImageType * SurfaceImageCurvature ::GetOutput() { return static_cast(this->ProcessObject::GetOutput(0) ); } template void SurfaceImageCurvature ::SetInputImage(typename ImageType::Pointer & input) { this->ProcessObject::SetNthInput(0, input); this->m_ImageSize = input->GetLargestPossibleRegion().GetSize(); typename OutputImageType::RegionType region = input->GetLargestPossibleRegion(); if( !this->m_FunctionImage ) { this->m_FunctionImage = OutputImageType::New(); this->m_FunctionImage->CopyInformation( input ); this->m_FunctionImage->SetRegions( region ); this->m_FunctionImage->Allocate(); this->m_FunctionImage->FillBuffer( 0 ); } } } // namespace itk #endif ants-2.2.0/Utilities/itkSurfaceMeshCurvature.h000066400000000000000000000103301311104306400214100ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _SurfaceMeshCurvature_h #define _SurfaceMeshCurvature_h #include "itkSurfaceCurvatureBase.h" namespace itk { /** \class SurfaceMeshCurvature * * This class takes a surface as input and creates a local * geometric frame for each surface point. * * */ template class SurfaceMeshCurvature : public SurfaceCurvatureBase { public: /** Standard class typedefs. */ typedef SurfaceMeshCurvature Self; typedef SurfaceCurvatureBase Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(SurfaceMeshCurvature, SurfaceCurvatureBase); /** Method for creation through the object factory. */ itkNewMacro(Self); typedef typename Superclass::RealType RealType; typedef typename Superclass::PointType VectorType; typedef typename Superclass::PointType FixedVectorType; typedef typename Superclass::PointType PointType; typedef typename Superclass::MatrixType MatrixType; typedef TSurfacePatch SurfacePatch; typedef typename TSurfacePatch::Pointer SurfacePatchPointer; typedef typename TSurfacePatch::NodeLocationType VertexType; typedef typename TSurfacePatch::PixelType FunctionValueType; typedef TSurface * SurfacePointer; // probably vtkmesh /** Find all points within some distance of the origin. * The argument gives the number of times to apply the * mean shift algorithm to find the best neighborhood. */ virtual void FindNeighborhood(unsigned int numMeanShifts = 0) { this->m_PointList.clear(); this->m_FunctionValueList.clear(); VertexType origin = m_SurfacePatch->GetLocation(); PointType pt; for( int j = 0; j < 3; j++ ) { pt(j) = origin[j]; } this->m_Origin = pt; this->m_PointList.insert(this->m_PointList.begin(), pt); this->m_FunctionValueList.insert(this->m_FunctionValueList.begin(), m_SurfacePatch->GetValue() ); for( unsigned int i = 0; i < m_SurfacePatch->m_Neighbors.size(); i++ ) { VertexType neigh = m_SurfacePatch->m_Neighbors[i]->GetLocation(); PointType pti; for( int j = 0; j < 3; j++ ) { pti(j) = neigh[j]; } this->m_PointList.insert(this->m_PointList.begin(), pti); this->m_FunctionValueList.insert( this->m_FunctionValueList.begin(), this->m_SurfacePatch->m_Neighbors[i]->GetValue(0) ); } } void SetSurface(SurfacePointer s) { m_Surface = s; } void SetSurfacePatch( SurfacePatchPointer s) { m_SurfacePatch = s; } SurfaceMeshCurvature() { this->m_Origin.fill(0.0); this->m_ArbitraryTangent.fill(0.); this->m_Normal.fill(0.); this->m_Tangent1.fill(0.); this->m_Tangent2.fill(0.); this->m_DirectionalKappa = 0.0; this->m_Kappa1 = 0.0; this->m_Kappa2 = 0.0; this->m_GaussianKappa = 0.0; this->m_A = 0.0; this->m_B = 0.0; this->m_C = 0.0; this->m_W1 = 3. / 8.; this->m_W2 = 1. / 8.; this->m_Eval0 = 0.0; this->m_Eval1 = 0.0; this->m_Eval2 = 0.0; this->m_CurrentNeighborhoodPointIndex = 0; this->m_ParameterFileName = ""; this->m_Pi = 3.14159265358979323846; this->m_Debug = true; this->m_Debug = false; this->m_UseGeodesicNeighborhood = false; this->m_TotalArea = 0.0; } ~SurfaceMeshCurvature() { }; protected: private: SurfacePointer m_Surface; SurfacePatchPointer m_SurfacePatch; }; } // namespace itk #ifndef ITK_MANUAL_INSTANTIATION // #include "itkSurfaceMeshCurvature.hxx" #endif #endif ants-2.2.0/Utilities/itkTextureHistogram.h000066400000000000000000000103401311104306400206210ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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. * *=========================================================================*/ // histogram from the moving histogram operations #ifndef __itkTextureHistogram_h #define __itkTextureHistogram_h #include "itkNumericTraits.h" namespace itk { namespace Function { /* * * */ template< class TInputPixel, class TOutputPixel > class TextureHistogram { public: TextureHistogram() { m_Count = 0; } // ~TextureHistogram() {} default is ok void AddPixel( const TInputPixel & p ) { m_Map[p]++; ++m_Count; } void RemovePixel(const TInputPixel & p) { // insert new item if one doesn't exist typename MapType::iterator it = m_Map.find( p ); assert( it != m_Map.end() ); if ( --(it->second) == 0 ) { m_Map.erase( it ); } --m_Count; } TOutputPixel GetValue(const TInputPixel &) { TOutputPixel out; NumericTraits::SetLength( out, 8 ); double sum = 0.0; double sum2 = 0.0; double sum3 = 0.0; double sum4 = 0.0; const size_t count = m_Count; //double median = 0.0; double entropy = 0.0; size_t curCount = 0; for ( typename MapType::iterator i = m_Map.begin(); i != m_Map.end(); ++i ) { double t = double(i->first)*double(i->second); sum += t; sum2 += ( t *= double(i->first) ); sum3 += ( t *= double(i->first) ); sum4 += ( t *= double(i->first) ); curCount += i->second; const double p_x = double( i->second ) / count; entropy += -p_x*std::log( p_x ); // // this is wrong! // if ( curCount == count / 2 ) // { // median += i->first; // medianIt = il // // we have an even number so take the average // if ( !(count % 2) ) // { // median *= 0.5; // } // } } // curCount = 0; // typename MapType::iterator fmedianIt = medianIt; // typename MapType::iterator rmedianIt = medianIt; // double mad = 0.0; // while (curCount < count/2 ) // { // if ( std::fabs( fmedianIt->first - median ) < std::fabs( rmedianIt->first - median ) ) // { // curCount += fmedianIt->second; // ++fmedianIt; // } // else // { // curCount += rmedianIt->second; // --rmedianIt; // } // } const double icount = 1.0 / count; const double mean = sum * icount; // unbiased estimate const double variance = ( sum2 - ( sum * sum * icount ) ) / ( count - 1 ); const double sigma = std::sqrt(variance); double skewness = 0.0; double kurtosis = 0.0; if(std::abs(variance * sigma) > itk::NumericTraits::min()) { skewness = ( ( sum3 - 3.0 * mean * sum2 ) * icount + 2.0 * mean * mean*mean ) / ( variance * sigma ); } if(std::abs(variance) > itk::NumericTraits::min()) { kurtosis = ( sum4 * icount + mean *( -4.0 * sum3 * icount + mean * ( 6.0 *sum2 * icount - 3.0 * mean * mean ))) / ( variance * variance ) - 3.0; } unsigned int i = 0; out[i++] = mean; out[i++] = m_Map.begin()->first; out[i++] = m_Map.rbegin()->first; out[i++] = variance; out[i++] = sigma; out[i++] = skewness; out[i++] = kurtosis; out[i++] = entropy; return out; } void AddBoundary(){} void RemoveBoundary(){} private: typedef typename std::map< TInputPixel, size_t > MapType; MapType m_Map; size_t m_Count; }; } // end namespace Function } // end namespace itk #endif ants-2.2.0/Utilities/itkVarianceImageFilter.h000066400000000000000000000102231311104306400211440ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkVarianceImageFilter_h #define itkVarianceImageFilter_h #include "itkBoxImageFilter.h" #include "itkImage.h" #include "itkNumericTraits.h" namespace itk { /** \class VarianceImageFilter * \brief Applies an averaging filter to an image * * Computes an image where a given pixel is the variance value of the * the pixels in a neighborhood about the corresponding input pixel. * * A variacne filter is one of the family of linear filters. * * \sa Image * \sa Neighborhood * \sa NeighborhoodOperator * \sa NeighborhoodIterator * * \ingroup IntensityImageFilters * \ingroup ITKSmoothing * * \wiki * \wikiexample{Smoothing/VarianceImageFilter,Variance filter an image} * \endwiki */ template< typename TInputImage, typename TOutputImage > class VarianceImageFilter: public BoxImageFilter< TInputImage, TOutputImage > { public: /** Extract dimension from input and output image. */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TOutputImage::ImageDimension); /** Convenient typedefs for simplifying declarations. */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; /** Standard class typedefs. */ typedef VarianceImageFilter Self; typedef BoxImageFilter< InputImageType, OutputImageType > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(VarianceImageFilter, BoxImageFilter); /** Image typedef support. */ typedef typename InputImageType::PixelType InputPixelType; typedef typename OutputImageType::PixelType OutputPixelType; typedef typename NumericTraits< InputPixelType >::RealType InputRealType; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::RegionType OutputImageRegionType; typedef typename InputImageType::SizeType InputSizeType; #ifdef ITK_USE_CONCEPT_CHECKING // Begin concept checking itkConceptMacro( InputHasNumericTraitsCheck, ( Concept::HasNumericTraits< InputPixelType > ) ); // End concept checking #endif protected: VarianceImageFilter(); virtual ~VarianceImageFilter() {} /** VarianceImageFilter can be implemented as a multithreaded filter. * Therefore, this implementation provides a ThreadedGenerateData() * routine which is called for each processing thread. The output * image data is allocated automatically by the superclass prior to * calling ThreadedGenerateData(). ThreadedGenerateData can only * write to the portion of the output image specified by the * parameter "outputRegionForThread" * * \sa BoxImageFilter::ThreadedGenerateData(), * BoxImageFilter::GenerateData() */ void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) ITK_OVERRIDE; private: VarianceImageFilter(const Self &); //purposely not implemented void operator=(const Self &); //purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkVarianceImageFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkVarianceImageFilter.hxx000066400000000000000000000070101311104306400215240ustar00rootroot00000000000000/*========================================================================= * * Copyright Insight Software Consortium * * 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 itkVarianceImageFilter_hxx #define itkVarianceImageFilter_hxx #include "itkVarianceImageFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkNeighborhoodInnerProduct.h" #include "itkImageRegionIterator.h" #include "itkNeighborhoodAlgorithm.h" #include "itkOffset.h" #include "itkProgressReporter.h" namespace itk { template< typename TInputImage, typename TOutputImage > VarianceImageFilter< TInputImage, TOutputImage > ::VarianceImageFilter() {} template< typename TInputImage, typename TOutputImage > void VarianceImageFilter< TInputImage, TOutputImage > ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { unsigned int i; ZeroFluxNeumannBoundaryCondition< InputImageType > nbc; ConstNeighborhoodIterator< InputImageType > bit; ImageRegionIterator< OutputImageType > it; // Allocate output typename OutputImageType::Pointer output = this->GetOutput(); typename InputImageType::ConstPointer input = this->GetInput(); // Find the data-set boundary "faces" typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator< InputImageType >::FaceListType faceList; NeighborhoodAlgorithm::ImageBoundaryFacesCalculator< InputImageType > bC; faceList = bC( input, outputRegionForThread, this->GetRadius() ); typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator< InputImageType >::FaceListType::iterator fit; // support progress methods/callbacks ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); InputRealType sum; InputRealType sumOfSquares; // Process each of the boundary faces. These are N-d regions which border // the edge of the buffer. for ( fit = faceList.begin(); fit != faceList.end(); ++fit ) { bit = ConstNeighborhoodIterator< InputImageType >(this->GetRadius(), input, *fit); unsigned int neighborhoodSize = bit.Size(); it = ImageRegionIterator< OutputImageType >(output, *fit); bit.OverrideBoundaryCondition(&nbc); bit.GoToBegin(); while ( !bit.IsAtEnd() ) { sum = NumericTraits< InputRealType >::ZeroValue(); sumOfSquares = NumericTraits< InputRealType >::ZeroValue(); for ( i = 0; i < neighborhoodSize; ++i ) { sum += static_cast< InputRealType >( bit.GetPixel(i) ); sumOfSquares += vnl_math_sqr( static_cast< InputRealType >( bit.GetPixel(i) ) ); } // get the variance value const double num = static_cast< double >( neighborhoodSize ); OutputPixelType var = ( sumOfSquares - ( vnl_math_sqr( sum ) / num ) ) / ( num - 1.0 ); it.Set( var ); ++bit; ++it; progress.CompletedPixel(); } } } } // end namespace itk #endif ants-2.2.0/Utilities/itkVectorFieldGradientImageFunction.h000066400000000000000000000214071311104306400236460ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkVectorFieldGradientImageFunction_h #define __itkVectorFieldGradientImageFunction_h #include "itkImageFunction.h" #include "itkVariableSizeMatrix.h" namespace itk { /** \class VectorFieldGradientImageFunction * */ template > class VectorFieldGradientImageFunction : public ImageFunction { public: /** Standard class typedefs. */ typedef VectorFieldGradientImageFunction Self; typedef ImageFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Run-time type information (and related methods) */ itkTypeMacro( VectorFieldGradientImageFunction, ImageFunction ); /** Extract some information from the image types. Dimensionality * of the two images is assumed to be the same. */ typedef TInputImage InputImageType; typedef typename InputImageType::PixelType VectorType; typedef TOutput MatrixType; /** Declare typedefs of superclass */ typedef typename Superclass::PointType PointType; typedef typename Superclass::IndexType IndexType; typedef typename Superclass::ContinuousIndexType ContinuousIndexType; /** The dimensionality of the input and output images. */ itkStaticConstMacro( ImageDimension, unsigned int, TInputImage::ImageDimension ); /** Length of the vector pixel type of the input image. */ itkStaticConstMacro( VectorDimension, unsigned int, VectorType::Dimension ); /** Define the data type and the vector of data type used in calculations. */ typedef TRealType RealType; /** * Evaluate deformation gradient tensor */ MatrixType EvaluateDeformationGradientTensor( const PointType & ) const; MatrixType EvaluateDeformationGradientTensorAtIndex( const IndexType & idx ) const; MatrixType EvaluateDeformationGradientTensorAtContinuousIndex ( const ContinuousIndexType & idx ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateDeformationGradientTensor( point ); } /** * Evaluate Jacobian */ virtual MatrixType Evaluate( const PointType & point ) const ITK_OVERRIDE { return this->EvaluateJacobian( point ); } virtual MatrixType EvaluateAtIndex( const IndexType & idx ) const ITK_OVERRIDE { return this->EvaluateJacobianAtIndex( idx ); } virtual MatrixType EvaluateAtContinuousIndex( const ContinuousIndexType & idx ) const ITK_OVERRIDE { return this->EvaluateJacobianAtContinuousIndex( idx ); } MatrixType EvaluateJacobian( const PointType & ) const; MatrixType EvaluateJacobianAtIndex( const IndexType & idx ) const; MatrixType EvaluateJacobianAtContinuousIndex( const ContinuousIndexType & idx ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateJacobian( point ); } /** * Evaluate Jacobian determinant */ RealType EvaluateJacobianDeterminant( const PointType & ) const; RealType EvaluateJacobianDeterminantAtIndex( const IndexType & idx ) const; RealType EvaluateJacobianDeterminantAtContinuousIndex( const ContinuousIndexType & idx ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateJacobianDeterminant( point ); } /** * Evaluate Lagrangian strain tensor */ MatrixType EvaluateLagrangianStrainTensor( const PointType & ) const; MatrixType EvaluateLagrangianStrainTensorAtIndex( const IndexType & idx ) const; MatrixType EvaluateLagrangianStrainTensorAtContinuousIndex( const ContinuousIndexType & idx ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateLagrangianStrainTensor( point ); } /** * Evaluate Lagrangian directional strain */ RealType EvaluateLagrangianDirectionalStrain( const PointType &, const VectorType & ) const; RealType EvaluateLagrangianDirectionalStrainAtIndex( const IndexType & idx, const VectorType & V ) const; RealType EvaluateLagrangianDirectionalStrainAtContinuousIndex( const ContinuousIndexType & idx, const VectorType & V ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateLagrangianDirectionalStrain( point, V ); } /** * Evaluate Eulerian strain tensor */ MatrixType EvaluateEulerianStrainTensor( const PointType & ) const; MatrixType EvaluateEulerianStrainTensorAtIndex( const IndexType & idx ) const; MatrixType EvaluateEulerianStrainTensorAtContinuousIndex( const ContinuousIndexType & idx ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateEulerianStrainTensor( point ); } /** * Evaluate Eulerian directional strain */ RealType EvaluateEulerianDirectionalStrain( const PointType &, const VectorType & ) const; RealType EvaluateEulerianDirectionalStrainAtIndex( const IndexType & idx, const VectorType & V ) const; RealType EvaluateEulerianDirectionalStrainAtContinuousIndex( const ContinuousIndexType & idx, const VectorType & V ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateEulerianDirectionalStrain( point, V ); } /** * Evaluate Right Cauchy-Green strain tensor */ MatrixType EvaluateRightCauchyGreenDeformationTensor( const PointType & ) const; MatrixType EvaluateRightCauchyGreenDeformationTensorAtIndex( const IndexType & idx ) const; MatrixType EvaluateRightCauchyGreenDeformationTensorAtContinuousIndex( const ContinuousIndexType & idx ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateRightCauchyGreenDeformationTensor( point ); } /** * Evaluate Left Cauchy-Green strain tensor */ MatrixType EvaluateLeftCauchyGreenDeformationTensor( const PointType & ) const; MatrixType EvaluateLeftCauchyGreenDeformationTensorAtIndex( const IndexType & idx ) const; MatrixType EvaluateLeftCauchyGreenDeformationTensorAtContinuousIndex( const ContinuousIndexType & idx ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateLeftCauchyGreenDeformationTensor( point ); } /** * Evaluate left stretch tensor */ MatrixType EvaluateLeftStretchTensor( const PointType & ) const; MatrixType EvaluateLeftStretchTensorAtIndex( const IndexType & idx ) const; MatrixType EvaluateLeftStretchTensorAtContinuousIndex( const ContinuousIndexType & idx ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateLeftStretchTensor( point ); } /** * Evaluate right stretch tensor */ MatrixType EvaluateRightStretchTensor( const PointType & ) const; MatrixType EvaluateRightStretchTensorAtIndex( const IndexType & idx ) const; MatrixType EvaluateRightStretchTensorAtContinuousIndex( const ContinuousIndexType & idx ) const { PointType point; this->GetInputImage()->TransformContinuousIndexToPhysicalPoint( idx, point ); return this->EvaluateRightStretchTensor( point ); } protected: VectorFieldGradientImageFunction(); virtual ~VectorFieldGradientImageFunction() { } void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE; private: VectorFieldGradientImageFunction(const Self &); // purposely not implemented MatrixType operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkVectorFieldGradientImageFunction.hxx" #endif #endif ants-2.2.0/Utilities/itkVectorFieldGradientImageFunction.hxx000066400000000000000000000510441311104306400242260ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit 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 _itkVectorFieldGradientImageFunction_hxx #define _itkVectorFieldGradientImageFunction_hxx #include "itkVectorFieldGradientImageFunction.h" #include "itkDecomposeTensorFunction.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "vnl/algo/vnl_matrix_inverse.h" #include "vnl/vnl_vector.h" namespace itk { template VectorFieldGradientImageFunction ::VectorFieldGradientImageFunction() { } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateDeformationGradientTensor( const PointType & point ) const { if( !this->IsInsideBuffer( point ) ) { unsigned int minDimension = ImageDimension; MatrixType F; F.SetSize( ImageDimension, VectorDimension ); F.Fill( 0.0 ); for( unsigned int i = 0; i < minDimension; i++ ) { F[i][i] = 1.0; } itkWarningMacro( "The specified point, " << point << ", is outside the image boundaries." ); return F; } typename InputImageType::SpacingType spacing = this->GetInputImage()->GetSpacing(); typedef VectorLinearInterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); interpolator->SetInputImage( this->GetInputImage() ); MatrixType F; F.SetSize( ImageDimension, VectorDimension ); typename InterpolatorType::OutputType x; typename InterpolatorType::PointType ipoint; ipoint.CastFrom( point ); x = interpolator->Evaluate( ipoint ); for( unsigned int i = 0; i < ImageDimension; i++ ) { typename PointType::VectorType delta; delta.Fill( 0.0 ); delta[i] = spacing[i]; typename InterpolatorType::PointType::VectorType idelta; idelta.Fill( 0.0 ); idelta[i] = spacing[i]; typename InterpolatorType::OutputType xp1; typename InterpolatorType::OutputType xp2; typename InterpolatorType::OutputType xm1; typename InterpolatorType::OutputType xm2; if( !this->IsInsideBuffer( point + delta ) ) { xp2 = xp1 = x; } else { xp1 = interpolator->Evaluate( ipoint + idelta ); if( this->IsInsideBuffer( point + delta * 2.0 ) ) { xp2 = interpolator->Evaluate( ipoint + idelta * 2.0 ); } else { xp2 = xp1; } } if( !this->IsInsideBuffer( point - delta ) ) { xm2 = xm1 = x; } else { xm1 = interpolator->Evaluate( ipoint - idelta ); if( this->IsInsideBuffer( point - delta * 2.0 ) ) { xm2 = interpolator->Evaluate( ipoint - idelta * 2.0 ); } else { xm2 = xm1; } } RealType weight = 1.0 / ( 12.0 * delta[i] ); for( unsigned int j = 0; j < VectorDimension; j++ ) { F[i][j] = weight * ( -xp2[j] + 8.0 * xp1[j] - 8.0 * xm1[j] + xm2[j] ); } } unsigned int minDimension = ImageDimension; if( static_cast( VectorDimension ) < static_cast( ImageDimension ) ) { minDimension = VectorDimension; } for( unsigned int i = 0; i < minDimension; i++ ) { F[i][i] += 1.0; } return F; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateDeformationGradientTensorAtIndex( const IndexType & index ) const { if( !this->IsInsideBuffer( index ) ) { unsigned int minDimension = ImageDimension; MatrixType F; F.SetSize( ImageDimension, VectorDimension ); F.Fill( 0.0 ); for( unsigned int i = 0; i < minDimension; i++ ) { F[i][i] = 1.0; } itkWarningMacro( "The specified index, " << index << ", is outside the image boundaries." ); return F; } MatrixType F; F.SetSize( ImageDimension, VectorDimension ); typename InputImageType::PixelType x; x = this->GetInputImage()->GetPixel( index ); typename InputImageType::SpacingType spacing = this->GetInputImage()->GetSpacing(); for( unsigned int i = 0; i < ImageDimension; i++ ) { typename InputImageType::OffsetType offset1; offset1.Fill( 0 ); offset1[i] = 1; typename InputImageType::OffsetType offset2; offset2.Fill( 0 ); offset2[i] = 2; typename InputImageType::PixelType xp1; typename InputImageType::PixelType xp2; typename InputImageType::PixelType xm1; typename InputImageType::PixelType xm2; if( !this->IsInsideBuffer( index + offset1 ) ) { xp2 = xp1 = x; } else { xp1 = this->GetInputImage()->GetPixel( index + offset1 ); if( this->IsInsideBuffer( index + offset2 ) ) { xp2 = this->GetInputImage()->GetPixel( index + offset2 ); } else { xp2 = xp1; } } if( !this->IsInsideBuffer( index - offset1 ) ) { xm2 = xm1 = x; } else { xm1 = this->GetInputImage()->GetPixel( index - offset1 ); if( this->IsInsideBuffer( index - offset2 ) ) { xm2 = this->GetInputImage()->GetPixel( index - offset2 ); } else { xm2 = xm1; } } RealType weight = 1.0 / ( 12.0 * static_cast( offset1[i] ) * spacing[i] ); for( unsigned int j = 0; j < VectorDimension; j++ ) { F[i][j] = weight * ( -xp2[j] + 8.0 * xp1[j] - 8.0 * xm1[j] + xm2[j] ); } } unsigned int minDimension = ImageDimension; if( static_cast( VectorDimension ) < static_cast( ImageDimension ) ) { minDimension = VectorDimension; } for( unsigned int i = 0; i < minDimension; i++ ) { F[i][i] += 1.0; } return F; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateJacobian( const PointType & point ) const { return this->EvaluateDeformationGradientTensor( point ); } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateJacobianAtIndex( const IndexType & index ) const { return this->EvaluateDeformationGradientTensorAtIndex( index ); } template typename VectorFieldGradientImageFunction ::RealType VectorFieldGradientImageFunction ::EvaluateJacobianDeterminant( const PointType & point ) const { MatrixType J = this->EvaluateJacobian( point ); typedef DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); return decomposer->EvaluateDeterminant( J ); } template typename VectorFieldGradientImageFunction ::RealType VectorFieldGradientImageFunction ::EvaluateJacobianDeterminantAtIndex( const IndexType & index ) const { MatrixType J = this->EvaluateJacobianAtIndex( index ); typedef DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); return decomposer->EvaluateDeterminant( J ); } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateLagrangianStrainTensor( const PointType & point ) const { MatrixType F = this->EvaluateDeformationGradientTensor( point ); MatrixType E(ImageDimension, ImageDimension); typename MatrixType::InternalMatrixType ff = F.GetTranspose() * F.GetVnlMatrix(); for( unsigned int i = 0; i < ff.rows(); i++ ) { for( unsigned int j = 0; j < ff.columns(); j++ ) { E[i][j] = ff.get( i, j ); if( i == j ) { E[i][j] -= 1.0; } E[i][j] *= 0.5; } } return E; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateLagrangianStrainTensorAtIndex( const IndexType & index ) const { MatrixType F = this->EvaluateDeformationGradientTensorAtIndex( index ); MatrixType E(ImageDimension, ImageDimension); typename MatrixType::InternalMatrixType ff = F.GetTranspose() * F.GetVnlMatrix(); for( unsigned int i = 0; i < ff.rows(); i++ ) { for( unsigned int j = 0; j < ff.columns(); j++ ) { E[i][j] = ff.get( i, j ); if( i == j ) { E[i][j] -= 1.0; } E[i][j] *= 0.5; } } return E; } template typename VectorFieldGradientImageFunction ::RealType VectorFieldGradientImageFunction ::EvaluateLagrangianDirectionalStrain( const PointType & point, const VectorType & V ) const { MatrixType E = this->EvaluateLagrangianStrainTensor( point ); vnl_vector v = E * V.GetVnlVector(); RealType s = 0.0; for( unsigned int i = 0; i < v.size(); i++ ) { s += v[i] * V[i]; } return s; } template typename VectorFieldGradientImageFunction ::RealType VectorFieldGradientImageFunction ::EvaluateLagrangianDirectionalStrainAtIndex ( const IndexType & index, const VectorType & V ) const { MatrixType E = this->EvaluateLagrangianStrainTensorAtIndex( index ); vnl_vector v = E * V.GetVnlVector(); RealType s = 0.0; for( unsigned int i = 0; i < v.size(); i++ ) { s += v[i] * V[i]; } return s; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateEulerianStrainTensor( const PointType & point ) const { MatrixType F = this->EvaluateDeformationGradientTensor( point ); MatrixType E(ImageDimension, ImageDimension); typename MatrixType::InternalMatrixType ff = vnl_matrix_inverse( F.GetVnlMatrix() * F.GetTranspose() ); for( unsigned int i = 0; i < ff.rows(); i++ ) { for( unsigned int j = 0; j < ff.columns(); j++ ) { E[i][j] = -ff.get( i, j ); if( i == j ) { E[i][j] += 1.0; } E[i][j] *= 0.5; } } return E; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateEulerianStrainTensorAtIndex( const IndexType & index ) const { MatrixType F = this->EvaluateDeformationGradientTensorAtIndex( index ); MatrixType E(ImageDimension, ImageDimension); typename MatrixType::InternalMatrixType ff = vnl_matrix_inverse( F.GetVnlMatrix() * F.GetTranspose() ); for( unsigned int i = 0; i < ff.rows(); i++ ) { for( unsigned int j = 0; j < ff.columns(); j++ ) { E[i][j] = -ff.get( i, j ); if( i == j ) { E[i][j] += 1.0; } E[i][j] *= 0.5; } } return E; } template typename VectorFieldGradientImageFunction ::RealType VectorFieldGradientImageFunction ::EvaluateEulerianDirectionalStrain( const PointType & point, const VectorType & V ) const { MatrixType E = this->EvaluateEulerianStrainTensor( point ); vnl_vector v = E * V.GetVnlVector(); RealType s = 0.0; for( unsigned int i = 0; i < v.size(); i++ ) { s += v[i] * V[i]; } return s; } template typename VectorFieldGradientImageFunction ::RealType VectorFieldGradientImageFunction ::EvaluateEulerianDirectionalStrainAtIndex( const IndexType & index, const VectorType & V ) const { MatrixType E = this->EvaluateEulerianStrainTensorAtIndex( index ); vnl_vector v = E * V.GetVnlVector(); RealType s = 0.0; for( unsigned int i = 0; i < v.size(); i++ ) { s += v[i] * V[i]; } return s; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateLeftCauchyGreenDeformationTensor( const PointType & point ) const { MatrixType F = this->EvaluateDeformationGradientTensor( point ); typename MatrixType::InternalMatrixType b = F.GetVnlMatrix() * F.GetTranspose(); MatrixType B; B.SetSize( F.Rows(), F.Cols() ); for( unsigned int i = 0; i < F.Rows(); i++ ) { for( unsigned int j = 0; j < F.Cols(); j++ ) { B[i][j] = b[i][j]; } } return B; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateLeftCauchyGreenDeformationTensorAtIndex( const IndexType & index ) const { MatrixType F = this->EvaluateDeformationGradientTensorAtIndex( index ); typename MatrixType::InternalMatrixType b = F.GetVnlMatrix() * F.GetTranspose(); MatrixType B; B.SetSize( F.Rows(), F.Cols() ); for( unsigned int i = 0; i < F.Rows(); i++ ) { for( unsigned int j = 0; j < F.Cols(); j++ ) { B[i][j] = b[i][j]; } } return B; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateRightCauchyGreenDeformationTensor( const PointType & point ) const { MatrixType F = this->EvaluateDeformationGradientTensor( point ); typename MatrixType::InternalMatrixType c = F.GetVnlMatrix() * F.GetTranspose(); MatrixType C; C.SetSize( F.Rows(), F.Cols() ); for( unsigned int i = 0; i < F.Rows(); i++ ) { for( unsigned int j = 0; j < F.Cols(); j++ ) { C[i][j] = c[i][j]; } } return C; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateRightCauchyGreenDeformationTensorAtIndex( const IndexType & index ) const { MatrixType F = this->EvaluateDeformationGradientTensorAtIndex( index ); typename MatrixType::InternalMatrixType c = F.GetVnlMatrix() * F.GetTranspose(); MatrixType C; C.SetSize( F.Rows(), F.Cols() ); for( unsigned int i = 0; i < F.Rows(); i++ ) { for( unsigned int j = 0; j < F.Cols(); j++ ) { C[i][j] = c[i][j]; } } return C; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateRightStretchTensor( const PointType & point ) const { MatrixType C = this->EvaluateRightCauchyGreenDeformationTensor( point ); MatrixType D; MatrixType V; typedef DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); decomposer->EvaluateSymmetricEigenDecomposition( C, D, V ); MatrixType U; U.SetSize( C.Rows(), C.Cols() ); U.Fill( 0 ); for( unsigned int d = 0; d < C.Rows(); d++ ) { RealType lambda = sqrt( D[d][d] ); for( unsigned int i = 0; i < C.Rows(); i++ ) { for( unsigned int j = 0; j < C.Cols(); j++ ) { U[i][j] += lambda * V[i][d] * V[j][d]; } } } return U; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateRightStretchTensorAtIndex( const IndexType & index ) const { MatrixType C = this->EvaluateRightCauchyGreenDeformationTensorAtIndex( index ); MatrixType D; MatrixType V; typedef DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); decomposer->EvaluateSymmetricEigenDecomposition( C, D, V ); MatrixType U; U.SetSize( C.Rows(), C.Cols() ); U.Fill( 0 ); for( unsigned int d = 0; d < C.Rows(); d++ ) { RealType lambda = sqrt( D[d][d] ); for( unsigned int i = 0; i < C.Rows(); i++ ) { for( unsigned int j = 0; j < C.Cols(); j++ ) { U[i][j] += lambda * V[i][d] * V[j][d]; } } } return U; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateLeftStretchTensor( const PointType & point ) const { MatrixType B = this->EvaluateLeftCauchyGreenDeformationTensor( point ); MatrixType D; MatrixType V; typedef DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); decomposer->EvaluateSymmetricEigenDecomposition( B, D, V ); MatrixType U; U.SetSize( B.Rows(), B.Cols() ); U.Fill( 0 ); for( unsigned int d = 0; d < B.Rows(); d++ ) { RealType lambda = sqrt( D[d][d] ); for( unsigned int i = 0; i < B.Rows(); i++ ) { for( unsigned int j = 0; j < B.Cols(); j++ ) { U[i][j] += lambda * V[i][d] * V[j][d]; } } } return U; } template typename VectorFieldGradientImageFunction ::MatrixType VectorFieldGradientImageFunction ::EvaluateLeftStretchTensorAtIndex( const IndexType & index ) const { MatrixType B = this->EvaluateLeftCauchyGreenDeformationTensorAtIndex( index ); MatrixType D; MatrixType V; typedef DecomposeTensorFunction DecomposerType; typename DecomposerType::Pointer decomposer = DecomposerType::New(); decomposer->EvaluateSymmetricEigenDecomposition( B, D, V ); MatrixType U; U.SetSize( B.Rows(), B.Cols() ); U.Fill( 0 ); for( unsigned int d = 0; d < B.Rows(); d++ ) { RealType lambda = sqrt( D[d][d] ); for( unsigned int i = 0; i < B.Rows(); i++ ) { for( unsigned int j = 0; j < B.Cols(); j++ ) { U[i][j] += lambda * V[i][d] * V[j][d]; } } } return U; } template void VectorFieldGradientImageFunction ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf(os, indent); } } // end namespace itk #endif ants-2.2.0/Utilities/itkVectorGaussianInterpolateImageFunction.h000066400000000000000000000232301311104306400251220ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkVectorGaussianInterpolateImageFunction_h #define __itkVectorGaussianInterpolateImageFunction_h #include "itkInterpolateImageFunction.h" #include "vnl/vnl_erf.h" #include "itkImageRegionConstIteratorWithIndex.h" namespace itk { /** \class VectorGaussianInterpolateImageFunction * \brief Gaussianly interpolate an image at specified positions. * * VectorGaussianInterpolateImageFunction linearly interpolates image intensity at * a non-integer pixel position. This class is templated * over the input image type and the coordinate representation type * (e.g. float or double). * * This function works for N-dimensional images. * * \ingroup ImageFunctions ImageInterpolators */ template class VectorGaussianInterpolateImageFunction : public InterpolateImageFunction { public: /** Standard class typedefs. */ typedef VectorGaussianInterpolateImageFunction Self; typedef InterpolateImageFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(VectorGaussianInterpolateImageFunction, InterpolateImageFunction); /** Method for creation through the object factory. */ itkNewMacro(Self); /** OutputType typedef support. */ typedef typename Superclass::OutputType OutputType; /** InputImageType typedef support. */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::InputImageType::PixelType PixelType; /** RealType typedef support. */ typedef typename Superclass::RealType RealType; /** Dimension underlying input image. */ itkStaticConstMacro(VDim, unsigned int, Superclass::ImageDimension); /** Index typedef support. */ typedef typename Superclass::IndexType IndexType; /** ContinuousIndex typedef support. */ typedef typename Superclass::ContinuousIndexType ContinuousIndexType; /** Compute internals */ virtual void ComputeBoundingBox() { const TInputImage *img = this->GetInputImage(); if( img == NULL ) { return; } // Set the bounding box for( size_t d = 0; d < VDim; d++ ) { bb_start[d] = -0.5; bb_end[d] = img->GetBufferedRegion().GetSize()[d] - 0.5; nt[d] = (int)(bb_end[d] - bb_start[d] + 0.5); dx[d].set_size(nt[d]); gx[d].set_size(nt[d]); this->sigma[d] = 1; sf[d] = 1.0 / (sqrt(2.0) * this->sigma[d] / img->GetSpacing()[d]); // std::cout << " sigma " << this->sigma[d] << " spc " << img->GetSpacing()[d] << " sf " << sf[d] << // std::endl; cut[d] = this->sigma[d] * alpha / img->GetSpacing()[d]; } this->m_ImageSize = this->GetInputImage()->GetLargestPossibleRegion().GetSize(); } /** Set input */ virtual void SetInputImage(const TInputImage *img) { // Call parent method Superclass::SetInputImage(img); this->ComputeBoundingBox(); } void SetParameters(double * /* sigma */, double Alpha) { // Set the parameters for( size_t d = 0; d < VDim; d++ ) { this->sigma[d] = 1.0; // sigma[d]; } this->alpha = Alpha; // If the image already set, recompute this->ComputeBoundingBox(); } /** Evaluate the function at a ContinuousIndex position * * Returns the linearly interpolated image intensity at a * specified point position. No bounds checking is done. * The point is assume to lie within the image buffer. * * ImageFunction::IsInsideBuffer() can be used to check bounds before * calling the method. */ virtual OutputType EvaluateAtContinuousIndex( const ContinuousIndexType & index ) const { return EvaluateAtContinuousIndex(index, NULL); } virtual OutputType EvaluateAtContinuousIndex( const ContinuousIndexType & index, OutputType *grad) const { OutputType Vout; Vout.Fill(0); // The bound variables for x, y, z int i0[VDim], i1[VDim]; // Compute the ERF difference arrays // std::cout << " index " << index << " VD " << VDim << std::endl; for( size_t d = 0; d < VDim; d++ ) { if( index[d] <= 0 || index[d] >= this->m_ImageSize[d] - 1 || vnl_math_isnan(index[d]) || vnl_math_isinf(index[d]) ) { return Vout; } double *pdx = const_cast(dx[d].data_block() ); double *pgx = grad ? const_cast(gx[d].data_block() ) : ITK_NULLPTR; compute_erf_array(pdx, i0[d], i1[d], bb_start[d], nt[d], cut[d], index[d], sf[d], pgx); } // Get a pointer to the output value // loop over vector length for( unsigned int qq = 0; qq < Vout.Size(); qq++ ) { double sum_me = 0.0, sum_m = 0.0; vnl_vector_fixed dsum_me(0.0), dsum_m(0.0), dw; // Loop over the voxels in the region identified ImageRegion region; for( size_t d = 0; d < VDim; d++ ) { region.SetIndex(d, i0[d]); region.SetSize(d, i1[d] - i0[d]); } for( ImageRegionConstIteratorWithIndex it(this->GetInputImage(), region); !it.IsAtEnd(); ++it ) { size_t j = it.GetIndex()[0]; double w = dx[0][j]; if( grad ) { dw[0] = gx[0][j]; for( size_t d = 1; d < VDim; d++ ) { dw[d] = dx[0][j]; } } for( size_t d = 1; d < VDim; d++ ) { j = it.GetIndex()[d]; w *= dx[d][j]; if( grad ) { for( size_t q = 0; q < VDim; q++ ) { dw[q] *= (d == q) ? gx[d][j] : dx[d][j]; } } } double V = it.Get()[qq]; sum_me += V * w; sum_m += w; if( grad ) { for( size_t q = 0; q < VDim; q++ ) { dsum_me[q] += V * dw[q]; dsum_m[q] += dw[q]; } } } double rc = sum_me / sum_m; if( grad ) { for( size_t q = 0; q < VDim; q++ ) { grad[q] = (dsum_me[q] - rc * dsum_m[q]) / sum_m; grad[q] /= -1.4142135623730951 * this->sigma[q]; } } if( vnl_math_isnan(rc) ) { rc = 0; } Vout[qq] = rc; } // std::cout << " gaussian " << std::endl; // return sum_me / sum_m; return Vout; } protected: VectorGaussianInterpolateImageFunction() { } ~VectorGaussianInterpolateImageFunction() { }; void PrintSelf(std::ostream& os, Indent indent) const { this->Superclass::PrintSelf(os, indent); } private: VectorGaussianInterpolateImageFunction( const Self & ); // purposely not implemented void operator=( const Self & ); // purposely not implemented /** Number of neighbors used in the interpolation */ static const unsigned long m_Neighbors; typename InputImageType::SizeType m_ImageSize; vnl_vector dx[VDim], gx[VDim]; double bb_start[VDim], bb_end[VDim], sf[VDim], cut[VDim]; int nt[VDim], stride[VDim]; double sigma[VDim], alpha; void compute_erf_array( double *dx_erf, // The output array of erf(p+i+1) - erf(p+i) int & k0, int & k1, // The range of integration 0 <= k0 < k1 <= n double b, // Lower bound of the bounding box int n, // Size of the bounding box in steps double Cut, // The distance at which to cut off double p, // the value p double sfac, // scaling factor 1 / (Sqrt[2] sigma) double *gx_erf = ITK_NULLPTR // Output derivative/erf array (optional) ) const { // Determine the range of voxels along the line where to evaluate erf k0 = (int) floor(p - b - Cut); k1 = (int) ceil(p - b + Cut); if( k0 < 0 ) { k0 = 0; } if( k1 > n ) { k1 = n; } // Start at the first voxel double t = (b - p + k0) * sfac; // std::cout << " t " << t << " b " << b << " p " << p << " k0 " << k0 << " sfat " << sfac << // std::endl; double e_last = vnl_erf(t); double g_last = gx_erf ? 1.128379167095513 * exp(-t * t) : 0.0; for( int i = k0; i < k1; i++ ) { t += sfac; // std::cout << " t2 " << t << std::endl; double e_now = vnl_erf(t); dx_erf[i] = e_now - e_last; if( gx_erf ) { double g_now = 1.128379167095513 * exp(-t * t); gx_erf[i] = g_now - g_last; g_last = g_now; } e_last = e_now; } } }; } // end namespace itk // Define instantiation macro for this template. #define ITK_TEMPLATE_VectorGaussianInterpolateImageFunction(_, EXPORT, x, y) namespace itk { \ _(2 (class EXPORT VectorGaussianInterpolateImageFunction ) ) \ namespace Templates { typedef VectorGaussianInterpolateImageFunction \ VectorGaussianInterpolateImageFunction##y; } \ } #endif ants-2.2.0/Utilities/itkVectorImageFileReader.h000066400000000000000000000146551311104306400214500ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkVectorImageFileReader_h #define __itkVectorImageFileReader_h #include "itkImageIOBase.h" #include "itkImageSource.h" #include "itkExceptionObject.h" #include "itkSize.h" #include "itkImageRegion.h" #include "itkDefaultConvertPixelTraits.h" namespace itk { /** \brief Base exception class for IO conflicts. */ class VectorImageFileReaderException : public ExceptionObject { public: /** Run-time information. */ itkTypeMacro( VectorImageFileReaderException, ExceptionObject ); /** Constructor. */ VectorImageFileReaderException(const char *file, unsigned int line, const char* message = "Error in IO", const char* loc = "Unknown") : ExceptionObject(file, line, message, loc) { } /** Constructor. */ VectorImageFileReaderException(const std::string & file, unsigned int line, const char* message = "Error in IO", const char* loc = "Unknown") : ExceptionObject(file, line, message, loc) { } }; /** \brief Data source that reads image data from a single file. * * This source object is a general filter to read data from * a variety of file formats. It works with a ImageIOBase subclass * to actually do the reading of the data. Object factory machinery * can be used to automatically create the ImageIOBase, or the * ImageIOBase can be manually created and set. Note that this * class reads data from a single file; if you wish to read data * from a series of files use ImageSeriesReader. * * TImage is the type expected by the external users of the * filter. If data stored in the file is stored in a different format * then specified by TImage, than this filter converts data * between the file type and the external expected type. The * ConvertTraits template argument is used to do the conversion. * * A Pluggable factory pattern is used this allows different kinds of readers * to be registered (even at run time) without having to modify the * code in this class. Normally just setting the FileName with the * appropriate suffix is enough to get the reader to instantiate the * correct ImageIO and read the file properly. However, some files (like * raw binary format) have no accepted suffix, so you will have to * manually create the ImageIO instance of the write type. * * \sa ImageSeriesReader * \sa ImageIOBase * * \ingroup IOFilters * */ template > class VectorImageFileReader : public ImageSource { public: /** Standard class typedefs. */ typedef VectorImageFileReader Self; typedef ImageSource Superclass; typedef SmartPointer Pointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(VectorImageFileReader, ImageSource); /** Image types */ typedef typename TImage::RegionType ImageRegionType; typedef typename TImage::InternalPixelType ImagePixelType; /** Deformation field types */ typedef typename TVectorImage::RegionType VectorImageRegionType; typedef typename TVectorImage::InternalPixelType VectorImagePixelType; /** Specify the file to read. This is forwarded to the IO instance. */ itkSetStringMacro(FileName); itkGetStringMacro(FileName); /** Set the Avants' naming convention On or Off */ itkSetMacro(UseAvantsNamingConvention, bool); itkGetConstReferenceMacro(UseAvantsNamingConvention, bool); itkBooleanMacro(UseAvantsNamingConvention); /** Set/Get the ImageIO helper class. Often this is created via the object * factory mechanism that determines whether a particular ImageIO can * read a certain file. This method provides a way to get the ImageIO * instance that is created. Or you can directly specify the ImageIO * to use to read a particular file in case the factory mechanism will * not work properly (e.g., unknown or unusual extension). */ void SetImageIO( ImageIOBase * imageIO ); itkGetModifiableObjectMacro(ImageIO, ImageIOBase); /** Prepare the allocation of the output image during the first back * propagation of the pipeline. */ virtual void GenerateOutputInformation(); /** Give the reader a chance to indicate that it will produce more * output than it was requested to produce. VectorImageFileReader cannot * currently read a portion of an image (since the ImageIO objects * cannot read a portion of an image), so the VectorImageFileReader must * enlarge the RequestedRegion to the size of the image on disk. */ virtual void EnlargeOutputRequestedRegion(DataObject *output); protected: VectorImageFileReader(); ~VectorImageFileReader(); void PrintSelf(std::ostream& os, Indent indent) const; /** Convert a block of pixels from one type to another. */ void DoConvertBuffer(void* buffer, unsigned long numberOfPixels); /** Test whether the given filename exist and it is readable, this is intended to be called before attempting to use ImageIO classes for actually reading the file. If the file doesn't exist or it is not readable, and exception with an approriate message will be thrown. */ void TestFileExistanceAndReadability(); /** Does the real work. */ virtual void GenerateData(); ImageIOBase::Pointer m_ImageIO; bool m_UserSpecifiedImageIO; // keep track whether the ImageIO is user specified /** The file to be read. */ std::string m_FileName; private: VectorImageFileReader(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented std::string m_ExceptionMessage; typename TImage::Pointer m_Image; bool m_UseAvantsNamingConvention; }; } // namespace ITK #ifndef ITK_MANUAL_INSTANTIATION #include "itkVectorImageFileReader.hxx" #endif #endif // __itkVectorImageFileReader_h ants-2.2.0/Utilities/itkVectorImageFileReader.hxx000066400000000000000000000557371311104306400220360ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkVectorImageFileReader_hxx #define _itkVectorImageFileReader_hxx #include "itkVectorImageFileReader.h" #include "itkObjectFactory.h" #include "itkImageIOFactory.h" #include "itkConvertPixelBuffer.h" #include "itkImageRegion.h" #include "itkPixelTraits.h" #include "itkVectorImage.h" #include "itkImageRegionIterator.h" #include #include namespace itk { template VectorImageFileReader ::VectorImageFileReader() { m_ImageIO = 0; m_FileName = ""; m_UserSpecifiedImageIO = false; m_UseAvantsNamingConvention = true; } template VectorImageFileReader ::~VectorImageFileReader() { } template void VectorImageFileReader ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); if( m_ImageIO ) { os << indent << "ImageIO: \n"; m_ImageIO->Print(os, indent.GetNextIndent() ); } else { os << indent << "ImageIO: (null)" << "\n"; } os << indent << "UserSpecifiedImageIO flag: " << m_UserSpecifiedImageIO << "\n"; os << indent << "m_FileName: " << m_FileName << "\n"; } template void VectorImageFileReader ::SetImageIO( ImageIOBase * imageIO) { itkDebugMacro("setting ImageIO to " << imageIO ); if( this->m_ImageIO != imageIO ) { this->m_ImageIO = imageIO; this->Modified(); } m_UserSpecifiedImageIO = true; } template void VectorImageFileReader ::GenerateOutputInformation(void) { typename TVectorImage::Pointer output = this->GetOutput(); itkDebugMacro(<< "Reading file for GenerateOutputInformation()" << m_FileName); // Check to see if we can read the file given the name or prefix // if( m_FileName == "" ) { throw VectorImageFileReaderException(__FILE__, __LINE__, "FileName must be specified", ITK_LOCATION); } // Test if the files exist and if it can be open. // and exception will be thrown otherwise. // std::string tmpFileName = this->m_FileName; // Test if the file exists and if it can be opened. // An exception will be thrown otherwise. // We catch the exception because some ImageIO's may not actually // open a file. Still reports file error if no ImageIO is loaded. try { m_ExceptionMessage = ""; this->TestFileExistanceAndReadability(); } catch( itk::ExceptionObject & err ) { m_ExceptionMessage = err.GetDescription(); } std::string::size_type pos = this->m_FileName.rfind( "." ); std::string extension( this->m_FileName, pos, this->m_FileName.length() - 1 ); std::string filename = std::string( this->m_FileName, 0, pos ); std::string gzExtension( "" ); if( extension == std::string( ".gz" ) ) { gzExtension = extension; std::string::size_type pos2 = filename.rfind( "." ); extension = std::string( filename, pos2, filename.length() - 1 ); filename = std::string( this->m_FileName, 0, pos2 ); } // unsigned int dimension = itk::GetVectorDimension // ::VectorDimension; // Assume that the first image read contains all the information to generate // the output image. for( unsigned int i = 0; i <= 1; i++ ) { this->m_FileName = filename; if( this->m_UseAvantsNamingConvention ) { switch( i ) { case 0: { this->m_FileName += std::string( "xvec" ); } break; case 1: { this->m_FileName += std::string( "yvec" ); } break; case 2: { this->m_FileName += std::string( "zvec" ); } break; default: { this->m_FileName += std::string( "you_are_screwed_vec" ); } break; } } else { std::ostringstream buf; buf << i; this->m_FileName += ( std::string( "." ) + std::string( buf.str().c_str() ) ); } this->m_FileName += extension; if( !gzExtension.empty() ) { this->m_FileName += std::string( ".gz" ); } if( i == 0 ) { itkDebugMacro( << "Generating output information from the file " << this->m_FileName ); if( m_UserSpecifiedImageIO == false ) // try creating via factory { m_ImageIO = ImageIOFactory::CreateImageIO( m_FileName.c_str(), ImageIOFactory::ReadMode ); } if( m_ImageIO.IsNull() ) { std::ostringstream msg; msg << " Could not create IO object for file " << m_FileName.c_str() << std::endl; if( m_ExceptionMessage.size() ) { msg << m_ExceptionMessage; } else { msg << " Tried to create one of the following:" << std::endl; std::list allobjects = ObjectFactoryBase::CreateAllInstance("itkImageIOBase"); for( std::list::iterator i = allobjects.begin(); i != allobjects.end(); ++i ) { ImageIOBase* io = dynamic_cast(i->GetPointer() ); msg << " " << io->GetNameOfClass() << std::endl; } msg << " You probably failed to set a file suffix, or" << std::endl; msg << " set the suffix to an unsupported type." << std::endl; } VectorImageFileReaderException e(__FILE__, __LINE__, msg.str().c_str(), ITK_LOCATION); throw e; return; } // Got to allocate space for the image. Determine the characteristics of // the image. // m_ImageIO->SetFileName(m_FileName.c_str() ); m_ImageIO->ReadImageInformation(); typename TVectorImage::SizeType dimSize; double spacing[TVectorImage::ImageDimension]; double origin[TVectorImage::ImageDimension]; typename TVectorImage::DirectionType direction; std::vector axis; for( unsigned int k = 0; k < TImage::ImageDimension; k++ ) { if( k < m_ImageIO->GetNumberOfDimensions() ) { dimSize[k] = m_ImageIO->GetDimensions(k); spacing[k] = m_ImageIO->GetSpacing(k); origin[k] = m_ImageIO->GetOrigin(k); // Please note: direction cosines are stored as columns of the // direction matrix axis = m_ImageIO->GetDirection(k); for( unsigned j = 0; j < TImage::ImageDimension; j++ ) { if( j < m_ImageIO->GetNumberOfDimensions() ) { direction[j][k] = axis[j]; } else { direction[j][k] = 0.0; } } } else { // Number of dimensions in the output is more than number of dimensions // in the ImageIO object (the file). Use default values for the size, // spacing, origin and direction for the final (degenerate) dimensions. dimSize[i] = 1; spacing[i] = 1.0; origin[i] = 0.0; for( unsigned j = 0; j < TImage::ImageDimension; j++ ) { if( i == j ) { direction[j][k] = 1.0; } else { direction[j][k] = 0.0; } } } } output->SetSpacing( spacing ); // Set the image spacing output->SetOrigin( origin ); // Set the image origin output->SetDirection( direction ); // Set the image direction cosines // Copy MetaDataDictionary from instantiated reader to output image. output->SetMetaDataDictionary(m_ImageIO->GetMetaDataDictionary() ); this->SetMetaDataDictionary(m_ImageIO->GetMetaDataDictionary() ); this->m_Image->SetSpacing( spacing ); this->m_Image->SetOrigin( origin ); this->m_Image->SetDirection( direction ); this->m_Image->SetMetaDataDictionary(m_ImageIO->GetMetaDataDictionary() ); typedef typename TVectorImage::IndexType IndexType; IndexType start; start.Fill(0); VectorImageRegionType region; region.SetSize(dimSize); region.SetIndex(start); ImageRegionType imageregion; imageregion.SetSize(dimSize); imageregion.SetIndex(start); // If a VectorImage, this requires us to set the // VectorLength before allocate // if( strcmp( output->GetNameOfClass(), "VectorImage" ) == 0 ) // { // typedef typename TImage::AccessorFunctorType AccessorFunctorType; // AccessorFunctorType::SetVectorLength( output, m_ImageIO->GetNumberOfComponents() ); // } output->SetLargestPossibleRegion( region ); this->m_Image->SetLargestPossibleRegion( imageregion ); } } this->m_FileName = tmpFileName; } template void VectorImageFileReader ::TestFileExistanceAndReadability() { std::string tmpFileName = this->m_FileName; std::string::size_type pos = this->m_FileName.rfind( "." ); std::string extension( this->m_FileName, pos, this->m_FileName.length() - 1 ); std::string filename = std::string( this->m_FileName, 0, pos ); std::string gzExtension( "" ); if( extension == std::string( ".gz" ) ) { gzExtension = extension; std::string::size_type pos2 = filename.rfind( "." ); extension = std::string( filename, pos2, filename.length() - 1 ); filename = std::string( this->m_FileName, 0, pos2 ); } unsigned int dimension = itk::GetVectorDimension ::VectorDimension; for( unsigned int i = 0; i < dimension; i++ ) { this->m_FileName = filename; if( this->m_UseAvantsNamingConvention ) { switch( i ) { case 0: { this->m_FileName += std::string( "xvec" ); } break; case 1: { this->m_FileName += std::string( "yvec" ); } break; case 2: { this->m_FileName += std::string( "zvec" ); } break; default: { this->m_FileName += std::string( "you_are_screwed_vec" ); } break; } } else { std::ostringstream buf; buf << i; this->m_FileName += ( std::string( "." ) + std::string( buf.str().c_str() ) ); } this->m_FileName += extension; if( !gzExtension.empty() ) { this->m_FileName += std::string( ".gz" ); } itkDebugMacro( << "Checking for the file " << this->m_FileName ); // Test if the file exists. if( !itksys::SystemTools::FileExists( m_FileName.c_str() ) ) { VectorImageFileReaderException e(__FILE__, __LINE__); std::ostringstream msg; msg << "The file doesn't exists. " << std::endl << "Filename = " << m_FileName << std::endl; e.SetDescription(msg.str().c_str() ); throw e; return; } // Test if the file can be open for reading access. std::ifstream readTester; readTester.open( m_FileName.c_str() ); if( readTester.fail() ) { readTester.close(); std::ostringstream msg; msg << "The file couldn't be opened for reading. " << std::endl << "Filename: " << m_FileName << std::endl; VectorImageFileReaderException e(__FILE__, __LINE__, msg.str().c_str(), ITK_LOCATION); throw e; return; } readTester.close(); } this->m_FileName = tmpFileName; } template void VectorImageFileReader ::EnlargeOutputRequestedRegion(DataObject *output) { typename TVectorImage::Pointer out = dynamic_cast(output); // the ImageIO object cannot stream, then set the RequestedRegion to the // LargestPossibleRegion if( !m_ImageIO->CanStreamRead() ) { if( out ) { out->SetRequestedRegion( out->GetLargestPossibleRegion() ); } else { throw VectorImageFileReaderException(__FILE__, __LINE__, "Invalid output object type"); } } } template void VectorImageFileReader ::GenerateData() { typename TVectorImage::Pointer output = this->GetOutput(); // allocate the output buffer output->SetBufferedRegion( output->GetRequestedRegion() ); output->Allocate(); this->m_Image->SetBufferedRegion( output->GetRequestedRegion() ); this->m_Image->Allocate(); // Test if the file exist and if it can be open. // and exception will be thrown otherwise. try { m_ExceptionMessage = ""; this->TestFileExistanceAndReadability(); } catch( itk::ExceptionObject & err ) { m_ExceptionMessage = err.GetDescription(); } std::string tmpFileName = this->m_FileName; std::string::size_type pos = this->m_FileName.rfind( "." ); std::string extension( this->m_FileName, pos, this->m_FileName.length() - 1 ); std::string filename = std::string( this->m_FileName, 0, pos ); std::string gzExtension( "" ); if( extension == std::string( ".gz" ) ) { gzExtension = extension; std::string::size_type pos2 = filename.rfind( "." ); extension = std::string( filename, pos2, filename.length() - 1 ); filename = std::string( this->m_FileName, 0, pos2 ); } unsigned int dimension = itk::GetVectorDimension ::VectorDimension; for( unsigned int i = 0; i < dimension; i++ ) { this->m_FileName = filename; if( this->m_UseAvantsNamingConvention ) { switch( i ) { case 0: { this->m_FileName += std::string( "xvec" ); } break; case 1: { this->m_FileName += std::string( "yvec" ); } break; case 2: { this->m_FileName += std::string( "zvec" ); } break; default: { this->m_FileName += std::string( "you_are_screwed_vec" ); } break; } } else { std::ostringstream buf; buf << i; this->m_FileName += ( std::string( "." ) + std::string( buf.str().c_str() ) ); } this->m_FileName += extension; if( !gzExtension.empty() ) { this->m_FileName += std::string( ".gz" ); } itkDebugMacro( << "Reading image buffer from the file " << this->m_FileName ); // Tell the ImageIO to read the file m_ImageIO->SetFileName(m_FileName.c_str() ); this->m_FileName = tmpFileName; ImageIORegion ioRegion(TImage::ImageDimension); ImageIORegion::SizeType ioSize = ioRegion.GetSize(); ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); typename TImage::SizeType dimSize; for( unsigned int j = 0; j < TImage::ImageDimension; j++ ) { if( j < m_ImageIO->GetNumberOfDimensions() ) { dimSize[j] = m_ImageIO->GetDimensions(j); } else { // Number of dimensions in the output is more than number of dimensions // in the ImageIO object (the file). Use default values for the size, // spacing, and origin for the final (degenerate) dimensions. dimSize[j] = 1; } } for( unsigned int j = 0; j < dimSize.GetSizeDimension(); ++j ) { ioSize[j] = dimSize[j]; } typedef typename TImage::IndexType IndexType; IndexType start; start.Fill(0); for( unsigned int j = 0; j < start.GetIndexDimension(); ++j ) { ioStart[j] = start[j]; } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); itkDebugMacro(<< "ioRegion: " << ioRegion); m_ImageIO->SetIORegion(ioRegion); char *loadBuffer = 0; // the size of the buffer is computed based on the actual number of // pixels to be read and the actual size of the pixels to be read // (as opposed to the sizes of the output) try { if( /** FIXME */ // m_ImageIO->GetComponentTypeInfo() // != typeid(ITK_TYPENAME ConvertPixelTraits::ComponentType) // || (m_ImageIO->GetNumberOfComponents() != ConvertPixelTraits::GetNumberOfComponents() ) ) { // the pixel types don't match so a type conversion needs to be // performed itkDebugMacro(<< "Buffer conversion required from: " << " FIXME m_ImageIO->GetComponentTypeInfo().name() " << " to: " << typeid(ITK_TYPENAME ConvertPixelTraits::ComponentType).name() ); loadBuffer = new char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read( static_cast(loadBuffer) ); // See note below as to why the buffered region is needed and // not actualIOregion this->DoConvertBuffer(static_cast(loadBuffer), output->GetBufferedRegion().GetNumberOfPixels() ); } else // a type conversion is not necessary { itkDebugMacro(<< "No buffer conversion required."); ImagePixelType *buffer = this->m_Image->GetPixelContainer()->GetBufferPointer(); m_ImageIO->Read( buffer ); } } catch( ... ) { // if an exception is thrown catch it if( loadBuffer ) { // clean up delete [] loadBuffer; loadBuffer = 0; } // then rethrow throw; } // clean up if( loadBuffer ) { delete [] loadBuffer; loadBuffer = 0; } ImageRegionIterator Id( this->GetOutput(), this->GetOutput()->GetLargestPossibleRegion() ); ImageRegionIterator It( this->m_Image, this->m_Image->GetLargestPossibleRegion() ); Id.GoToBegin(); It.GoToBegin(); while( !Id.IsAtEnd() || !It.IsAtEnd() ) { VectorImagePixelType V = Id.Get(); V[i] = static_cast( It.Get() ); Id.Set( V ); ++Id; ++It; } } } template void VectorImageFileReader ::DoConvertBuffer(void* inputData, unsigned long numberOfPixels) { // get the pointer to the destination buffer ImagePixelType *imageData = this->m_Image->GetPixelContainer()->GetBufferPointer(); // TODO: // Pass down the PixelType (RGB, VECTOR, etc.) so that any vector to // scalar conversion be type specific. i.e. RGB to scalar would use // a formula to convert to luminance, VECTOR to scalar would use // vector magnitude. // Create a macro as this code is a bit lengthy and repetitive // if the ImageIO pixel type is typeid(type) then use the ConvertPixelBuffer // class to convert the data block to TImage's pixel type // see DefaultConvertPixelTraits and ConvertPixelBuffer // The first else if block applies only to images of type itk::VectorImage // VectorImage needs to copy out the buffer differently.. The buffer is of // type InternalPixelType, but each pixel is really 'k' consecutive pixels. #define ITK_CONVERT_BUFFER_IF_BLOCK(type) \ else if( true /** FIXME m_ImageIO->GetComponentTypeInfo() == typeid(type) ) */ ) \ { \ if( strcmp( this->GetOutput()->GetNameOfClass(), "VectorImage" ) == 0 ) \ { \ ConvertPixelBuffer< \ type, \ ImagePixelType, \ ConvertPixelTraits \ > \ ::ConvertVectorImage( \ static_cast(inputData), \ m_ImageIO->GetNumberOfComponents(), \ imageData, \ numberOfPixels); \ } \ else \ { \ ConvertPixelBuffer< \ type, \ ImagePixelType, \ ConvertPixelTraits \ > \ ::Convert( \ static_cast(inputData), \ m_ImageIO->GetNumberOfComponents(), \ imageData, \ numberOfPixels); \ } \ } if( 0 ) { } ITK_CONVERT_BUFFER_IF_BLOCK(unsigned char) ITK_CONVERT_BUFFER_IF_BLOCK(char) ITK_CONVERT_BUFFER_IF_BLOCK(unsigned short) ITK_CONVERT_BUFFER_IF_BLOCK( short) ITK_CONVERT_BUFFER_IF_BLOCK(unsigned int) ITK_CONVERT_BUFFER_IF_BLOCK( int) ITK_CONVERT_BUFFER_IF_BLOCK(unsigned long) ITK_CONVERT_BUFFER_IF_BLOCK( long) ITK_CONVERT_BUFFER_IF_BLOCK(float) ITK_CONVERT_BUFFER_IF_BLOCK( double) else { VectorImageFileReaderException e(__FILE__, __LINE__); std::ostringstream msg; msg << "Couldn't convert component type: " << std::endl << " " << m_ImageIO->GetComponentTypeAsString(m_ImageIO->GetComponentType() ) << std::endl << "to one of: " << std::endl << " " << typeid(unsigned char).name() << std::endl << " " << typeid(char).name() << std::endl << " " << typeid(unsigned short).name() << std::endl << " " << typeid(short).name() << std::endl << " " << typeid(unsigned int).name() << std::endl << " " << typeid(int).name() << std::endl << " " << typeid(unsigned long).name() << std::endl << " " << typeid(long).name() << std::endl << " " << typeid(float).name() << std::endl << " " << typeid(double).name() << std::endl; e.SetDescription(msg.str().c_str() ); e.SetLocation(ITK_LOCATION); throw e; return; } #undef ITK_CONVERT_BUFFER_IF_BLOCK } } // namespace ITK #endif ants-2.2.0/Utilities/itkVectorImageFileWriter.h000066400000000000000000000160251311104306400215130ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Date: $$ Version: $ $ Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkVectorImageFileWriter_h #define __itkVectorImageFileWriter_h #include "itkProcessObject.h" #include "itkImageIOBase.h" #include "itkExceptionObject.h" #include "itkSize.h" #include "itkImageIORegion.h" namespace itk { /** \brief Base exception class for IO problems during writing. */ class VectorImageFileWriterException : public ExceptionObject { public: /** Run-time information. */ itkTypeMacro( VectorImageFileWriterException, ExceptionObject ); /** Constructor. */ VectorImageFileWriterException(const char *file, unsigned int line, const char* message = "Error in IO", const char* loc = "Unknown" ) : ExceptionObject(file, line, message, loc) { } /** Constructor. */ VectorImageFileWriterException(const std::string & file, unsigned int line, const char* message = "Error in IO", const char* loc = "Unknown" ) : ExceptionObject(file, line, message, loc) { } }; /** \class VectorImageFileWriter * \brief Writes the deformation field as component images files. * * \sa VectorImageFileWriter * \sa ImageSeriesReader * \sa ImageIOBase * * \ingroup IOFilters */ template class VectorImageFileWriter : public ProcessObject { public: /** Standard class typedefs. */ typedef VectorImageFileWriter Self; typedef ProcessObject Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(VectorImageFileWriter, VectorImageFileWriter); /** Some convenient typedefs. */ typedef TVectorImage VectorImageType; typedef typename VectorImageType::Pointer VectorImagePointer; typedef typename VectorImageType::RegionType VectorImageRegionType; typedef typename VectorImageType::PixelType VectorImagePixelType; typedef TImage ImageType; typedef typename ImageType::Pointer ImagePointer; typedef typename ImageType::RegionType ImageRegionType; typedef typename ImageType::PixelType ImagePixelType; /** Set/Get the image input of this writer. */ void SetInput(const VectorImageType *input); const VectorImageType * GetInput(); const VectorImageType * GetInput(unsigned int idx); /** Specify the name of the output file to write. */ itkSetStringMacro(FileName); itkGetStringMacro(FileName); /** Set/Get the ImageIO helper class. Usually this is created via the object * factory mechanism that determines whether a particular ImageIO can * write a certain file. This method provides a way to get the ImageIO * instance that is created, or one can be manually set where the * IO factory mechanism may not work (for example, raw image files or * image files with non-standard filename suffix's. * If the user specifies the ImageIO, we assume she makes the * correct choice and will allow a file to be created regardless of * the file extension. If the factory has set the ImageIO, the * extension must be supported by the specified ImageIO. */ void SetImageIO(ImageIOBase* io) { if( this->m_ImageIO != io ) { this->Modified(); this->m_ImageIO = io; } m_FactorySpecifiedImageIO = false; } itkGetModifiableObjectMacro(ImageIO, ImageIOBase); /** A special version of the Update() method for writers. It * invokes start and end events and handles releasing data. It * eventually calls GenerateData() which does the actual writing. * Note: the write method will write data specified by the * IORegion. If not set, then then the whole image is written. Note * that the region will be cropped to fit the input image's * LargestPossibleRegion. */ virtual void Write(); /** Specify the region to write. If left NULL, then the whole image * is written. */ void SetIORegion(const ImageIORegion & region); itkGetConstReferenceMacro( IORegion, ImageIORegion ); /** Aliased to the Write() method to be consistent with the rest of the * pipeline. */ virtual void Update() { this->Write(); } /** Set the compression On or Off */ itkSetMacro(UseCompression, bool); itkGetConstReferenceMacro(UseCompression, bool); itkBooleanMacro(UseCompression); /** Set the Avants' naming convention On or Off */ itkSetMacro(UseAvantsNamingConvention, bool); itkGetConstReferenceMacro(UseAvantsNamingConvention, bool); itkBooleanMacro(UseAvantsNamingConvention); /** Set the Avants' naming convention On or Off */ itkSetMacro(UseZhangNamingConvention, bool); itkGetConstReferenceMacro(UseZhangNamingConvention, bool); itkBooleanMacro(UseZhangNamingConvention); /** By default the MetaDataDictionary is taken from the input image and * passed to the ImageIO. In some cases, however, a user may prefer to * introduce her/his own MetaDataDictionary. This is often the case of * the ImageSeriesWriter. This flag defined whether the MetaDataDictionary * to use will be the one from the input image or the one already set in * the ImageIO object. */ itkSetMacro(UseInputMetaDataDictionary, bool); itkGetConstReferenceMacro(UseInputMetaDataDictionary, bool); itkBooleanMacro(UseInputMetaDataDictionary); protected: VectorImageFileWriter(); ~VectorImageFileWriter(); void PrintSelf(std::ostream& os, Indent indent) const; /** Does the real work. */ void GenerateData(); private: VectorImageFileWriter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented std::string m_FileName; std::string m_ComponentImageFileName; bool m_UseAvantsNamingConvention; bool m_UseZhangNamingConvention; ImagePointer m_Image; ImageIOBase::Pointer m_ImageIO; bool m_UserSpecifiedImageIO; // track whether the ImageIO is user specified ImageIORegion m_IORegion; bool m_UserSpecifiedIORegion; // // track whether the region is user specified bool m_FactorySpecifiedImageIO; // track whether the factory mechanism set the ImageIO bool m_UseCompression; bool m_UseInputMetaDataDictionary; // whether to use the MetaDataDictionary from the input or not. }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkVectorImageFileWriter.hxx" #endif #endif // __itkVectorImageFileWriter_h ants-2.2.0/Utilities/itkVectorImageFileWriter.hxx000066400000000000000000000311401311104306400220660ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 _itkVectorImageFileWriter_hxx #define _itkVectorImageFileWriter_hxx #include "itkVectorImageFileWriter.h" #include "itkImageFileWriter.h" #include "itkDataObject.h" #include "itkObjectFactoryBase.h" #include "itkImageIOFactory.h" #include "itkCommand.h" #include "vnl/vnl_vector.h" #include "itkVectorImage.h" #include "itkVectorIndexSelectionCastImageFilter.h" namespace itk { // --------------------------------------------------------- template VectorImageFileWriter ::VectorImageFileWriter() : m_FileName(""), m_ImageIO(0), m_UserSpecifiedImageIO(false), m_UserSpecifiedIORegion(false) { m_UseCompression = false; m_UseInputMetaDataDictionary = true; m_FactorySpecifiedImageIO = false; m_UseAvantsNamingConvention = true; m_UseZhangNamingConvention = false; } // --------------------------------------------------------- template VectorImageFileWriter ::~VectorImageFileWriter() { } // --------------------------------------------------------- template void VectorImageFileWriter ::SetInput(const VectorImageType *input) { this->ProcessObject::SetNthInput(0, const_cast(input ) ); } // --------------------------------------------------------- template const typename VectorImageFileWriter::VectorImageType * VectorImageFileWriter ::GetInput(void) { if( this->GetNumberOfInputs() < 1 ) { return 0; } return static_cast (this->ProcessObject::GetInput(0) ); } // --------------------------------------------------------- template const typename VectorImageFileWriter::VectorImageType * VectorImageFileWriter ::GetInput(unsigned int idx) { return static_cast (this->ProcessObject::GetInput(idx) ); } // --------------------------------------------------------- template void VectorImageFileWriter ::SetIORegion(const ImageIORegion& region) { itkDebugMacro("setting IORegion to " << region ); if( m_IORegion != region ) { m_IORegion = region; this->Modified(); m_UserSpecifiedIORegion = true; } } // --------------------------------------------------------- template void VectorImageFileWriter ::GenerateData(void) { itkDebugMacro(<< "Writing file: " << m_ComponentImageFileName); // Make sure that the image is the right type and no more than // four components. typedef typename ImageType::PixelType ScalarType; if( strcmp( m_Image->GetNameOfClass(), "VectorImage" ) == 0 ) { typedef typename ImageType::InternalPixelType VectorImageScalarType; // m_ImageIO->SetPixelTypeInfo( typeid(VectorImageScalarType) ); typedef typename ImageType::AccessorFunctorType AccessorFunctorType; m_ImageIO->SetNumberOfComponents( AccessorFunctorType::GetVectorLength( m_Image ) ); } else { // Set the pixel and component type; the number of components. // m_ImageIO->SetPixelTypeInfo(typeid(ScalarType)); } // Setup the image IO for writing. // m_ImageIO->SetFileName(m_ComponentImageFileName.c_str() ); // okay, now extract the data as a raw buffer pointer const void* dataPtr = (const void *) this->m_Image->GetBufferPointer(); m_ImageIO->Write(dataPtr); } // --------------------------------------------------------- template void VectorImageFileWriter ::Write() { typedef VectorIndexSelectionCastImageFilter SelectorType; typename SelectorType::Pointer selector = SelectorType::New(); selector->SetInput( this->GetInput() ); unsigned int dimension = VectorImageType::PixelType::VectorDimension; std::string filename = this->m_FileName; std::string::size_type pos = this->m_FileName.rfind( "." ); std::string extension( this->m_FileName, pos, this->m_FileName.length() - 1 ); std::string gzExtension( "" ); if( extension == std::string( ".gz" ) ) { gzExtension = extension; filename = std::string( filename, 0, pos ); pos = filename.rfind( "." ); extension = std::string( filename, pos, this->m_FileName.length() - 1 ); } for( unsigned int i = 0; i < dimension; i++ ) { selector->SetIndex( i ); selector->Update(); std::string filename( this->m_FileName, 0, pos ); if( this->m_UseAvantsNamingConvention && dimension <= 3 ) { switch( i ) { case 0: { filename += std::string( "xvec" ); } break; case 1: { filename += std::string( "yvec" ); } break; case 2: { filename += std::string( "zvec" ); } break; default: { filename += std::string( "you_are_screwed_vec" ); } break; } } else if( this->m_UseZhangNamingConvention && dimension == 6 ) { switch( i ) { case 0: { filename += std::string( "xx" ); } break; case 1: { filename += std::string( "yx" ); } break; case 2: { filename += std::string( "yy" ); } break; case 3: { filename += std::string( "zx" ); } break; case 4: { filename += std::string( "zy" ); } break; case 5: { filename += std::string( "zz" ); } break; default: { filename += std::string( "you_are_screwed" ); } break; } } else { std::ostringstream buf; buf << i; filename += ( std::string( "." ) + std::string( buf.str().c_str() ) ); } filename += extension; if( !gzExtension.empty() ) { filename += std::string( ".gz" ); } m_ComponentImageFileName = filename; ImageType *input = selector->GetOutput(); this->m_Image = selector->GetOutput(); itkDebugMacro( << "Writing an image file" ); // Make sure input is available if( input == 0 ) { itkExceptionMacro(<< "No input to writer!"); } // Make sure that we can write the file given the name // if( filename == "" ) { itkExceptionMacro(<< "No filename was specified"); } if( m_ImageIO.IsNull() ) // try creating via factory { itkDebugMacro(<< "Attempting factory creation of ImageIO for file: " << filename); m_ImageIO = ImageIOFactory::CreateImageIO( filename.c_str(), ImageIOFactory::WriteMode ); m_FactorySpecifiedImageIO = true; } else { if( m_FactorySpecifiedImageIO && !m_ImageIO->CanWriteFile( filename.c_str() ) ) { itkDebugMacro(<< "ImageIO exists but doesn't know how to write file:" << m_FileName ); itkDebugMacro(<< "Attempting creation of ImageIO with a factory for file:" << m_FileName); m_ImageIO = ImageIOFactory::CreateImageIO( filename.c_str(), ImageIOFactory::WriteMode ); m_FactorySpecifiedImageIO = true; } } if( m_ImageIO.IsNull() ) { ImageFileWriterException e(__FILE__, __LINE__); std::ostringstream msg; msg << " Could not create IO object for file " << filename.c_str() << std::endl; msg << " Tried to create one of the following:" << std::endl; std::list allobjects = ObjectFactoryBase::CreateAllInstance("itkImageIOBase"); for( std::list::iterator i = allobjects.begin(); i != allobjects.end(); ++i ) { ImageIOBase* io = dynamic_cast(i->GetPointer() ); msg << " " << io->GetNameOfClass() << std::endl; } msg << " You probably failed to set a file suffix, or" << std::endl; msg << " set the suffix to an unsupported type." << std::endl; e.SetDescription(msg.str().c_str() ); e.SetLocation(ITK_LOCATION); throw e; } // NOTE: this const_cast<> is due to the lack of const-correctness // of the ProcessObject. ImageType *nonConstImage = input; typedef typename TImage::RegionType RegionType; if( !m_UserSpecifiedIORegion ) { // Make sure the data is up-to-date. if( nonConstImage->GetSource() ) { nonConstImage->GetSource()->UpdateLargestPossibleRegion(); } // Write the whole image ImageIORegion ioRegion(TImage::ImageDimension); RegionType region = this->m_Image->GetLargestPossibleRegion(); for( unsigned int i = 0; i < TVectorImage::ImageDimension; i++ ) { ioRegion.SetSize(i, region.GetSize(i) ); ioRegion.SetIndex(i, region.GetIndex(i) ); } m_IORegion = ioRegion; // used by GenerateData } else { nonConstImage->Update(); } // Setup the ImageIO // m_ImageIO->SetNumberOfDimensions(TImage::ImageDimension); RegionType region = this->m_Image->GetLargestPossibleRegion(); const typename TImage::SpacingType& spacing = this->m_Image->GetSpacing(); const typename TImage::PointType& origin = this->m_Image->GetOrigin(); const typename TImage::DirectionType& direction = this->m_Image->GetDirection(); for( unsigned int k = 0; k < TVectorImage::ImageDimension; k++ ) { m_ImageIO->SetDimensions(k, region.GetSize(k) ); m_ImageIO->SetSpacing(k, spacing[k]); m_ImageIO->SetOrigin(k, origin[k]); vnl_vector axisDirection(TVectorImage::ImageDimension); // Please note: direction cosines are stored as columns of the // direction matrix for( unsigned int j = 0; j < TImage::ImageDimension; j++ ) { axisDirection[j] = direction[j][k]; } m_ImageIO->SetDirection( k, axisDirection ); } m_ImageIO->SetUseCompression(m_UseCompression); m_ImageIO->SetIORegion(m_IORegion); if( m_UseInputMetaDataDictionary ) { m_ImageIO->SetMetaDataDictionary(input->GetMetaDataDictionary() ); } // Notify start event observers this->InvokeEvent( StartEvent() ); // Actually do something this->GenerateData(); // Notify end event observers this->InvokeEvent( EndEvent() ); // Release upstream data if requested if( input->ShouldIReleaseData() ) { nonConstImage->ReleaseData(); } } } // --------------------------------------------------------- template void VectorImageFileWriter ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "File Name: " << (m_FileName.data() ? m_FileName.data() : "(none)") << std::endl; os << "Number of vector components (i.e. number of images created): " << VectorImageType::PixelType::VectorDimension << std::endl; os << indent << "Image IO: "; if( m_ImageIO.IsNull() ) { os << "(none)\n"; } else { os << m_ImageIO << "\n"; } os << indent << "IO Region: " << m_IORegion << "\n"; if( m_UseCompression ) { os << indent << "Compression: On\n"; } else { os << indent << "Compression: Off\n"; } if( m_UseInputMetaDataDictionary ) { os << indent << "UseInputMetaDataDictionary: On\n"; } else { os << indent << "UseInputMetaDataDictionary: Off\n"; } if( m_FactorySpecifiedImageIO ) { os << indent << "FactorySpecifiedmageIO: On\n"; } else { os << indent << "FactorySpecifiedmageIO: Off\n"; } } } // end namespace itk #endif ants-2.2.0/Utilities/itkWarpImageMultiTransformFilter.h000066400000000000000000000314531311104306400232440ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 ITKWARPIMAGEMULTITRANSFORMFILTER_H_ #define ITKWARPIMAGEMULTITRANSFORMFILTER_H_ #include "itkImageToImageFilter.h" #include "itkInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkVectorGaussianInterpolateImageFunction.h" #include "itkPoint.h" #include "itkFixedArray.h" #include "itkRecursiveGaussianImageFilter.h" #include namespace itk { /** \class WarpImageMultiTransformFilter * \brief Warps an image using an input deformation field. * * WarpImageMultiTransformFilter warps an existing image with respect to * a given deformation field. * * A deformation field is represented as a image whose pixel type is some * vector type with at least N elements, where N is the dimension of * the input image. The vector type must support element access via operator * []. * * The output image is produced by inverse mapping: the output pixels * are mapped back onto the input image. This scheme avoids the creation of * any holes and overlaps in the output image. * * Each vector in the deformation field represent the distance between * a geometric point in the input space and a point in the output space such * that: * * \f[ p_{in} = p_{out} + d \f] * * Typically the mapped position does not correspond to an integer pixel * position in the input image. Interpolation via an image function * is used to compute values at non-integer positions. The default * interpolation typed used is the LinearInterpolateImageFunction. * The user can specify a particular interpolation function via * SetInterpolator(). Note that the input interpolator must derive * from base class InterpolateImageFunction. * * Position mapped to outside of the input image buffer are assigned * a edge padding value. * * The LargetPossibleRegion for the output is inherited from the * input deformation field. The output image spacing and origin may be set * via SetOutputSpacing, SetOutputOrigin. The default are respectively a * vector of 1's and a vector of 0's. * * This class is templated over the type of the input image, the * type of the output image and the type of the deformation field. * * The input image is set via SetInput. The input deformation field * is set via SetDisplacementField. * * This filter is implemented as a multithreaded filter. * * \warning This filter assumes that the input type, output type * and deformation field type all have the same number of dimensions. * * \ingroup GeometricTransforms MultiThreaded */ template < class TInputImage, class TOutputImage, class TDisplacementField, class TTransform > class WarpImageMultiTransformFilter : public ImageToImageFilter { public: /** transform order type **/ // typedef enum _TransformOrderType {AffineFirst=0, AffineLast} TransformOrderType; /** transform type **/ typedef enum _SingleTransformType { EnumAffineType = 0, EnumDisplacementFieldType } SingleTransformType; /** Standard class typedefs. */ typedef WarpImageMultiTransformFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods) */ itkTypeMacro( WarpImageMultiTransformFilter, ImageToImageFilter ); /** Typedef to describe the output image region type. */ typedef typename TOutputImage::RegionType OutputImageRegionType; /** Inherit some types from the superclass. */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::InputImagePointer InputImagePointer; typedef typename Superclass::OutputImageType OutputImageType; typedef typename Superclass::OutputImagePointer OutputImagePointer; typedef typename Superclass::InputImageConstPointer InputImageConstPointer; typedef typename OutputImageType::IndexType IndexType; typedef typename OutputImageType::SizeType SizeType; typedef typename OutputImageType::PixelType PixelType; typedef typename OutputImageType::SpacingType SpacingType; typedef typename OutputImageType::DirectionType DirectionType; /** Determine the image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension ); itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension ); itkStaticConstMacro(DisplacementFieldDimension, unsigned int, TDisplacementField::ImageDimension ); /** base type for images of the current ImageDimension */ typedef ImageBase ImageBaseType; /** Deformation field typedef support. */ typedef TDisplacementField DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldPointer; typedef typename DisplacementFieldType::PixelType DisplacementType; typedef typename DisplacementType::ValueType DisplacementScalarValueType; /** songgang: Affine transform typedef support. */ typedef TTransform TransformType; typedef typename TransformType::Pointer TransformTypePointer; /** Interpolator typedef support. */ typedef double CoordRepType; typedef InterpolateImageFunction InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; typedef LinearInterpolateImageFunction DefaultInterpolatorType; typedef VectorLinearInterpolateImageFunction DefaultVectorInterpolatorType; typedef VectorGaussianInterpolateImageFunction DefaultVectorInterpolatorType2; typedef typename DefaultVectorInterpolatorType::Pointer VectorInterpolatorPointer; /** Point type */ typedef Point PointType; typedef struct _DeformationTypeEx { DisplacementFieldPointer field; VectorInterpolatorPointer vinterp; } DeformationTypeEx; typedef struct _AffineTypeEx { TransformTypePointer aff; } AffineTypeEx; typedef struct _VarTransformType { AffineTypeEx aex; DeformationTypeEx dex; } VarTransformType; typedef std::pair SingleTransformItemType; typedef std::list TransformListType; /** Set the interpolator function. */ itkSetObjectMacro( Interpolator, InterpolatorType ); /** Get a pointer to the interpolator function. */ itkGetModifiableObjectMacro( Interpolator, InterpolatorType ); /** Convenience funciton to set all the necessary items */ virtual void SetOutputParametersFromImage(const ImageBaseType *image); /** Set the output image spacing. */ itkSetMacro(OutputSpacing, SpacingType); virtual void SetOutputSpacing( const double* values); /** Get the output image spacing. */ itkGetConstReferenceMacro(OutputSpacing, SpacingType); /** Set the output image spacing. */ itkSetMacro(OutputDirection, DirectionType); /** Get the output image spacing. */ itkGetConstReferenceMacro(OutputDirection, DirectionType); /** Set the output image origin. */ itkSetMacro(OutputOrigin, PointType); virtual void SetOutputOrigin( const double* values); /** Get the output image origin. */ itkGetConstReferenceMacro(OutputOrigin, PointType); /** Set the output image size. */ itkSetMacro(OutputSize, SizeType); // virtual void SetOutputSize( const double *values); itkGetConstReferenceMacro(OutputSize, SizeType); /** Set the start index of the output largest possible region. * The default is an index of all zeros. */ itkSetMacro(OutputStartIndex, IndexType); /** Get the start index of the output largest possible region. */ itkGetConstReferenceMacro(OutputStartIndex, IndexType); /** Set the edge padding value */ itkSetMacro( EdgePaddingValue, PixelType ); /** Get the edge padding value */ itkGetMacro( EdgePaddingValue, PixelType ); TransformListType & GetTransformList() { return m_TransformList; } void PrintTransformList(); /** WarpImageMultiTransformFilter produces an image which is a different * size than its input image. As such, it needs to provide an * implemenation for GenerateOutputInformation() which set * the output information according the OutputSpacing, OutputOrigin * and the deformation field's LargestPossibleRegion. */ virtual void GenerateOutputInformation() ITK_OVERRIDE; /** It is difficult to compute in advance the input image region * required to compute the requested output region. Thus the safest * thing to do is to request for the whole input image. * * For the deformation field, the input requested region * set to be the same as that of the output requested region. */ virtual void GenerateInputRequestedRegion() ITK_OVERRIDE; /** This method is used to set the state of the filter before * multi-threading. */ virtual void BeforeThreadedGenerateData() ITK_OVERRIDE; /** This method is used to set the state of the filter after * multi-threading. */ virtual void AfterThreadedGenerateData() ITK_OVERRIDE; /** precompute the smoothed image if necessary **/ void SetSmoothScale(double scale); double GetSmoothScale() { return m_SmoothScale; }; // void UpdateSizeByScale(); void PushBackAffineTransform(const TransformType* t); void PushBackDisplacementFieldTransform(const DisplacementFieldType* t); void ComposeAffineOnlySequence(const PointType & center_output, TransformTypePointer & affine_output); bool MultiInverseAffineOnlySinglePoint(const PointType & point1, PointType & point2); bool MultiTransformSinglePoint(const PointType & point1, PointType & point2); bool MultiTransformPoint(const PointType & point1, PointType & point2, bool bFisrtDeformNoInterp, const IndexType & index); void DetermineFirstDeformNoInterp(); inline bool IsOutOfNumericBoundary(const PointType & p); // set interpolator from outside // virtual void SetInterpolator(InterpolatorPointer interp); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(SameDimensionCheck1, (Concept::SameDimension ) ); itkConceptMacro(SameDimensionCheck2, (Concept::SameDimension ) ); // removed to be compatible with vector form input image // itkConceptMacro(InputHasNumericTraitsCheck, // (Concept::HasNumericTraits)); itkConceptMacro(DisplacementFieldHasNumericTraitsCheck, (Concept::HasNumericTraits ) ); /** End concept checking */ #endif bool m_bFirstDeformNoInterp; protected: WarpImageMultiTransformFilter(); ~WarpImageMultiTransformFilter() { }; void PrintSelf(std::ostream& os, Indent indent) const ITK_OVERRIDE; /** WarpImageMultiTransformFilter is implemented as a multi-threaded filter. * As such, it needs to provide and implementation for * ThreadedGenerateData(). */ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) ITK_OVERRIDE; PointType m_OutputOrigin; SpacingType m_OutputSpacing; DirectionType m_OutputDirection; SizeType m_OutputSize; IndexType m_OutputStartIndex; // output image start index InterpolatorPointer m_Interpolator; PixelType m_EdgePaddingValue; TransformListType m_TransformList; double m_SmoothScale; InputImagePointer m_CachedSmoothImage; private: WarpImageMultiTransformFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkWarpImageMultiTransformFilter.hxx" #endif #endif /*ITKWARPIMAGEMULTITRANSFORMFILTER_H_*/ ants-2.2.0/Utilities/itkWarpImageMultiTransformFilter.hxx000066400000000000000000000511551311104306400236250ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkWarpImageMultiTransformFilter_hxx #define __itkWarpImageMultiTransformFilter_hxx #include "itkWarpImageMultiTransformFilter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkNumericTraits.h" #include "itkProgressReporter.h" #include "itkVectorLinearInterpolateImageFunction.h" #include namespace itk { /** * Default constructor. */ template WarpImageMultiTransformFilter ::WarpImageMultiTransformFilter() { // Setup default values m_OutputOrigin.Fill(0.0); m_OutputSpacing.Fill(1.0); m_OutputDirection.SetIdentity(); m_OutputSize.Fill(0); m_OutputStartIndex.Fill(0); // Setup the number of required inputs this->SetNumberOfRequiredInputs( 1 ); // PixelType zeropix = NumericTraits::ZeroValue(); // m_EdgePaddingValue = zeropix; // Setup default interpolator typename DefaultInterpolatorType::Pointer interp = DefaultInterpolatorType::New(); m_Interpolator = static_cast( interp.GetPointer() ); m_SmoothScale = -1; // m_bOutputDisplacementField = false; // m_TransformOrder = AffineFirst; } // template // void // WarpImageMultiTransformFilter // ::SetInterpolator1(InterpolatorPointer interp) // { // m_Interpolator = static_cast (interp.GetPointer()); // std::cout << "set interpolator in WarpImage:" << interp << std::endl; // } template void WarpImageMultiTransformFilter ::PrintTransformList() { std::cout << "transform list: " << std::endl; typename TransformListType::iterator it = (m_TransformList.begin() ); for( int ii = 0; it != m_TransformList.end(); it++, ii++ ) { switch( it->first ) { case EnumAffineType: { std::cout << '[' << ii << "]: EnumAffineType" << std::endl; std::cout << it->second.aex.aff << std::endl; } break; case EnumDisplacementFieldType: std::cout << '[' << ii << "]: EnumDisplacementFieldType: size" << it->second.dex.field->GetLargestPossibleRegion().GetSize() << std::endl; } } } /** * Standard PrintSelf method. */ template void WarpImageMultiTransformFilter ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "OutputSpacing: " << m_OutputSpacing << std::endl;; os << indent << "OutputOrigin: " << m_OutputOrigin << std::endl; os << indent << "EdgePaddingValue: " << static_cast::PrintType>(m_EdgePaddingValue) << std::endl; os << indent << "Interpolator: " << m_Interpolator.GetPointer() << std::endl; os << indent << "m_bFirstDeformNoInterp = " << m_bFirstDeformNoInterp << std::endl; } /** * Set the output image spacing. * */ template void WarpImageMultiTransformFilter ::SetOutputParametersFromImage(const ImageBaseType *image) { this->SetOutputOrigin( image->GetOrigin() ); this->SetOutputSpacing( image->GetSpacing() ); this->SetOutputDirection( image->GetDirection() ); this->SetOutputStartIndex( image->GetLargestPossibleRegion().GetIndex() ); this->SetOutputSize( image->GetLargestPossibleRegion().GetSize() ); } /** * Set the output image spacing. * */ template void WarpImageMultiTransformFilter ::SetOutputSpacing( const double* spacing) { SpacingType s(spacing); this->SetOutputSpacing( s ); } /** * Set the output image origin. * */ template void WarpImageMultiTransformFilter ::SetOutputOrigin( const double* origin) { PointType p(origin); this->SetOutputOrigin(p); } /** * Setup state of filter before multi-threading. * InterpolatorType::SetInputImage is not thread-safe and hence * has to be setup before ThreadedGenerateData */ template void WarpImageMultiTransformFilter ::BeforeThreadedGenerateData() { if( !m_Interpolator ) { itkExceptionMacro(<< "Interpolator not set"); } // Connect input image to interpolator // m_Interpolator->SetInputImage( this->GetInput() ); if( m_CachedSmoothImage.IsNull() && (this->GetInput() ) ) { m_CachedSmoothImage = const_cast(this->GetInput() ); } m_Interpolator->SetInputImage( m_CachedSmoothImage ); } /** * Setup state of filter after multi-threading. */ template void WarpImageMultiTransformFilter ::AfterThreadedGenerateData() { // Disconnect input image from interpolator m_Interpolator->SetInputImage( ITK_NULLPTR ); } template void WarpImageMultiTransformFilter ::GenerateInputRequestedRegion() { // call the superclass's implementation Superclass::GenerateInputRequestedRegion(); // request the largest possible region for the input image InputImagePointer inputPtr = const_cast( this->GetInput() ); if( inputPtr ) { inputPtr->SetRequestedRegionToLargestPossibleRegion(); } return; } template void WarpImageMultiTransformFilter ::GenerateOutputInformation() { // call the superclass's implementation of this method Superclass::GenerateOutputInformation(); OutputImagePointer outputPtr = this->GetOutput(); if( !outputPtr ) { return; } typename TOutputImage::RegionType outputLargestPossibleRegion; outputLargestPossibleRegion.SetSize( this->m_OutputSize ); outputLargestPossibleRegion.SetIndex(this->m_OutputStartIndex); outputPtr->SetLargestPossibleRegion( outputLargestPossibleRegion ); outputPtr->SetSpacing( this->m_OutputSpacing ); outputPtr->SetOrigin( this->m_OutputOrigin ); outputPtr->SetDirection( this->m_OutputDirection ); // determine if the deformation field is the same dimension as the image // so that it does not need interpolation in the first step DetermineFirstDeformNoInterp(); } template void WarpImageMultiTransformFilter ::SetSmoothScale(double /* scale */) { /* if (m_SmoothScale != scale){ // compute the new cached // std::cout << "change smooth scale: " << m_SmoothScale << " ---> " << scale << std::endl; m_SmoothScale = scale; typename InputImageType::SpacingType inputSpacing = this->GetInput()->GetSpacing(); typename InputImageType::RegionType::SizeType inputSize = this->GetInput()->GetLargestPossibleRegion().GetSize(); typename InputImageType::SpacingType outputSpacing; typename InputImageType::RegionType::SizeType outputSize; double minimumSpacing = inputSpacing.GetVnlVector().min_value(); double maximumSpacing = inputSpacing.GetVnlVector().max_value(); InputImagePointer image = const_cast (this->GetInput()); for ( unsigned int d = 0; d < ImageDimension; d++ ) { double scaling = vnl_math_min( 1.0 / scale * minimumSpacing / inputSpacing[d], static_cast( inputSize[d] ) / 32.0 ); outputSpacing[d] = inputSpacing[d] * scaling; outputSize[d] = static_cast( inputSpacing[d] * static_cast( inputSize[d] ) / outputSpacing[d] + 0.5 ); double sigma = 0.25 * ( outputSpacing[d] / inputSpacing[d] ); if (sigma < 0) sigma=0; typedef RecursiveGaussianImageFilter GaussianFilterType; typename GaussianFilterType::Pointer smoother = GaussianFilterType::New(); smoother->SetInputImage( image ); smoother->SetDirection( d ); smoother->SetNormalizeAcrossScale( false ); // std::cout << "scale = " << scale << " => " << "sigma of dim " << d << ": " << sigma << " out size " << outputSize << " spc1 " << outputSpacing << " in " << inputSpacing << std::endl; smoother->SetSigma( sigma ); if ( smoother->GetSigma() > 0.0 ) { smoother->Update(); image = smoother->GetOutput(); } } SetOutputSpacing( outputSpacing ); SetOutputOrigin( this->GetInput()->GetOrigin() ); SetOutputSize(outputSize); } */ InputImagePointer image = const_cast(this->GetInput() ); m_CachedSmoothImage = image; } /** * Compute the output for the region specified by outputRegionForThread. */ template void WarpImageMultiTransformFilter ::ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) { InputImageConstPointer inputPtr = this->GetInput(); OutputImagePointer outputPtr = this->GetOutput(); // std::cout << "inputPtr->GetOrigin():" << inputPtr->GetOrigin() << std::endl; // std::cout << "outputPtr->GetOrigin():" << outputPtr->GetOrigin() << std::endl; // std::exception(); IndexType index; index.Fill(0); this->m_EdgePaddingValue = inputPtr->GetPixel(index); // support progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels() ); // iterator for the output image ImageRegionIteratorWithIndex outputIt(outputPtr, outputRegionForThread); while( !outputIt.IsAtEnd() ) { PointType point1, point2; // get the output image index IndexType _index = outputIt.GetIndex(); outputPtr->TransformIndexToPhysicalPoint( _index, point1 ); bool isinside = MultiTransformPoint(point1, point2, m_bFirstDeformNoInterp, _index); // std::cout << "point1:" << point1 << " point2:" << point2 << " index:" << index << std::endl; // std::exception(); // warp the image // get the interpolated value if( isinside && (m_Interpolator->IsInsideBuffer( point2 ) ) ) { PixelType value = static_cast(m_Interpolator->Evaluate(point2) ); outputIt.Set( value ); } else { // std::cout << "OUTSIDE" << " isinside:" << isinside << " m_Interpolator->IsInsideBuffer( point2 ):" << // m_Interpolator->IsInsideBuffer( point2 ) << std::endl; outputIt.Set( m_EdgePaddingValue ); } ++outputIt; } progress.CompletedPixel(); } // template // void // WarpImageMultiTransformFilter // ::UpdateSizeByScale() // { // // // DisplacementFieldPointer field = this->GetDisplacementField(); // // // // SetOutputSpacing( field->GetSpacing() / m_SmoothScale ); // // SetOutputOrigin( field->GetOrigin() ); // // // // typename InputImageType::SizeType imgsz = field->GetLargestPossibleRegion().GetSize(); // // for(int ii = 0; ii < InputImageType::ImageDimension; ii++) imgsz[ii] = (typename // InputImageType::SizeType::SizeValueType) (imgsz[ii] * m_SmoothScale + 0.5); // // // // SetOutputSize(imgsz); // // } template void WarpImageMultiTransformFilter ::PushBackAffineTransform(const TransformType* t) { if( t ) { VarTransformType t1; t1.aex.aff = const_cast(t); m_TransformList.push_back(SingleTransformItemType(EnumAffineType, t1) ); } } template void WarpImageMultiTransformFilter ::PushBackDisplacementFieldTransform(const DisplacementFieldType* t) { if( t ) { VarTransformType t1; t1.dex.field = const_cast(t); t1.dex.vinterp = DefaultVectorInterpolatorType::New(); t1.dex.vinterp->SetInputImage(t1.dex.field); // t1.dex.vinterp->SetParameters(NULL,1); m_TransformList.push_back(SingleTransformItemType(EnumDisplacementFieldType, t1) ); } } template bool WarpImageMultiTransformFilter ::MultiTransformSinglePoint(const PointType & point1, PointType & point2) { IndexType null_index; bool isinside = MultiTransformPoint(point1, point2, false, null_index); return isinside; } template bool WarpImageMultiTransformFilter ::IsOutOfNumericBoundary(const PointType & p) { const DisplacementScalarValueType kMaxDisp = itk::NumericTraits::max(); bool b = false; for( unsigned int i = 0; i < ImageDimension; i++ ) { if( p[i] >= kMaxDisp ) { b = true; break; } } return b; } template void WarpImageMultiTransformFilter ::ComposeAffineOnlySequence(const PointType & center_output, TransformTypePointer & affine_output) { affine_output->SetIdentity(); affine_output->SetCenter(center_output); for( typename TransformListType::iterator it = m_TransformList.begin(); it != m_TransformList.end(); it++ ) { SingleTransformType ttype = it->first; switch( ttype ) { case EnumAffineType: { TransformTypePointer aff = it->second.aex.aff; affine_output->Compose(aff, 0); break; } case EnumDisplacementFieldType: { std::cout << " Ignore deformation type ... " << std::endl; itkExceptionMacro(<< "Affine Only Sequence must only contain Affine Transforms, DisplacementField Found!"); } break; default: itkExceptionMacro(<< "Single Transform Not Supported!"); } } return; } template bool WarpImageMultiTransformFilter ::MultiInverseAffineOnlySinglePoint(const PointType & p1, PointType & point2) { bool isinside = true; PointType point1 = p1; for( typename TransformListType::iterator it = m_TransformList.begin(); it != m_TransformList.end(); it++ ) { SingleTransformType ttype = it->first; switch( ttype ) { case EnumAffineType: { TransformTypePointer aff = it->second.aex.aff; TransformTypePointer aff_inv = TransformTypePointer::ObjectType::New(); // std::cout << "aff before:" << aff << std::endl; aff->GetInverse(aff_inv); // std::cout << "aff after:" << aff << std::endl; // std::cout << "aff_inv after:" << aff_inv << std::endl; point2 = aff_inv->TransformPoint(point1); point1 = point2; isinside = true; break; } case EnumDisplacementFieldType: { itkExceptionMacro(<< "Affine Only Sequence must only contain Affine Transforms, DisplacementField Found!"); } break; default: itkExceptionMacro(<< "Single Transform Not Supported!"); } if( IsOutOfNumericBoundary(point2) ) { isinside = false; break; } point1 = point2; } return isinside; } template bool WarpImageMultiTransformFilter ::MultiTransformPoint(const PointType & p1, PointType & point2, bool bFirstDeformNoInterp, const IndexType & index) { bool isinside = false; PointType point1 = p1; for( typename TransformListType::iterator it = m_TransformList.begin(); it != m_TransformList.end(); it++ ) { SingleTransformType ttype = it->first; switch( ttype ) { case EnumAffineType: { TransformTypePointer aff = it->second.aex.aff; point2 = aff->TransformPoint(point1); point1 = point2; isinside = true; } break; case EnumDisplacementFieldType: { DisplacementFieldPointer fieldPtr = it->second.dex.field; if( bFirstDeformNoInterp && it == m_TransformList.begin() ) { // use discrete coordinates DisplacementType displacement = fieldPtr->GetPixel(index); for( unsigned int j = 0; j < ImageDimension; j++ ) { point2[j] = point1[j] + displacement[j]; } isinside = true; } else { // use continous coordinates typename DefaultVectorInterpolatorType::ContinuousIndexType contind; // use ITK implementation to use orientation header fieldPtr->TransformPhysicalPointToContinuousIndex(point1, contind); isinside = fieldPtr->GetLargestPossibleRegion().IsInside( contind ); VectorInterpolatorPointer vinterp = it->second.dex.vinterp; typename DefaultVectorInterpolatorType::OutputType disp2; if( isinside ) { disp2 = vinterp->EvaluateAtContinuousIndex( contind ); } else { disp2.Fill(0); } for( unsigned int jj = 0; jj < ImageDimension; jj++ ) { point2[jj] = disp2[jj] + point1[jj]; } } } break; default: itkExceptionMacro(<< "Single Transform Not Supported!"); } if( IsOutOfNumericBoundary(point2) ) { isinside = false; break; } point1 = point2; } return isinside; } template void WarpImageMultiTransformFilter ::DetermineFirstDeformNoInterp() { m_bFirstDeformNoInterp = false; if( m_TransformList.size() > 0 ) { if( m_TransformList.front().first == EnumDisplacementFieldType ) { DisplacementFieldPointer field = m_TransformList.front().second.dex.field; m_bFirstDeformNoInterp = (this->GetOutputSize() == field->GetLargestPossibleRegion().GetSize() ) & (this->GetOutputSpacing() == field->GetSpacing() ) & (this->GetOutputOrigin() == field->GetOrigin() ); // std::cout << "in set: field size: " << field->GetLargestPossibleRegion().GetSize() // << "output spacing: " << this->GetOutputSize() << std::endl; // std::cout << field->GetSpacing() << " | " << this->GetOutputSpacing() << std::endl; // std::cout << field->GetOrigin() << " | " << this->GetOutputOrigin() << std::endl; } } } } // end namespace itk #endif // __itkWarpImageMultiTransformFilter_hxx ants-2.2.0/Utilities/itkWarpImageWAffineFilter.h000066400000000000000000000243611311104306400215750ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkWarpImageWAffineFilter_h #define __itkWarpImageWAffineFilter_h #include "itkImageToImageFilter.h" #include "itkInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkPoint.h" #include "itkFixedArray.h" #include "itkRecursiveGaussianImageFilter.h" namespace itk { /** \class WarpImageWAffineFilter * \brief Warps an image using an input deformation field. * * WarpImageWAffineFilter warps an existing image with respect to * a given deformation field. * * A deformation field is represented as a image whose pixel type is some * vector type with at least N elements, where N is the dimension of * the input image. The vector type must support element access via operator * []. * * The output image is produced by inverse mapping: the output pixels * are mapped back onto the input image. This scheme avoids the creation of * any holes and overlaps in the output image. * * Each vector in the deformation field represent the distance between * a geometric point in the input space and a point in the output space such * that: * * \f[ p_{in} = p_{out} + d \f] * * Typically the mapped position does not correspond to an integer pixel * position in the input image. Interpolation via an image function * is used to compute values at non-integer positions. The default * interpolation typed used is the LinearInterpolateImageFunction. * The user can specify a particular interpolation function via * SetInterpolator(). Note that the input interpolator must derive * from base class InterpolateImageFunction. * * Position mapped to outside of the input image buffer are assigned * a edge padding value. * * The LargetPossibleRegion for the output is inherited from the * input deformation field. The output image spacing and origin may be set * via SetOutputSpacing, SetOutputOrigin. The default are respectively a * vector of 1's and a vector of 0's. * * This class is templated over the type of the input image, the * type of the output image and the type of the deformation field. * * The input image is set via SetInput. The input deformation field * is set via SetDisplacementField. * * This filter is implemented as a multithreaded filter. * * \warning This filter assumes that the input type, output type * and deformation field type all have the same number of dimensions. * * \ingroup GeometricTransforms MultiThreaded */ template < class TInputImage, class TOutputImage, class TDisplacementField, class TTransform > class WarpImageWAffineFilter : public ImageToImageFilter { public: /** transform order type **/ typedef enum _TransformOrderType { AffineFirst = 0, AffineLast } TransformOrderType; /** Standard class typedefs. */ typedef WarpImageWAffineFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods) */ itkTypeMacro( WarpImageWAffineFilter, ImageToImageFilter ); /** Typedef to describe the output image region type. */ typedef typename TOutputImage::RegionType OutputImageRegionType; /** Inherit some types from the superclass. */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::InputImagePointer InputImagePointer; typedef typename Superclass::OutputImageType OutputImageType; typedef typename Superclass::OutputImagePointer OutputImagePointer; typedef typename Superclass::InputImageConstPointer InputImageConstPointer; typedef typename OutputImageType::IndexType IndexType; typedef typename OutputImageType::SizeType SizeType; typedef typename OutputImageType::PixelType PixelType; typedef typename OutputImageType::SpacingType SpacingType; /** Determine the image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension ); itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension ); itkStaticConstMacro(DisplacementFieldDimension, unsigned int, TDisplacementField::ImageDimension ); /** Deformation field typedef support. */ typedef TDisplacementField DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldPointer; typedef typename DisplacementFieldType::PixelType DisplacementType; /** songgang: Affine transform typedef support. */ typedef TTransform TransformType; typedef typename TransformType::Pointer TransformTypePointer; /** Interpolator typedef support. */ typedef double CoordRepType; typedef InterpolateImageFunction InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; typedef LinearInterpolateImageFunction DefaultInterpolatorType; /** Point type */ typedef Point PointType; /** Set the deformation field. */ void SetDisplacementField( const DisplacementFieldType * field ); /** Get a pointer the deformation field. */ DisplacementFieldType * GetDisplacementField(); /** songgang: Set / Get the affine transform. */ // void SetAffineTransform( const TransformType * aff ); // TransformType * GetAffineTransform(); itkSetObjectMacro(AffineTransform, TransformType); itkGetModifiableObjectMacro(AffineTransform, TransformType); itkSetEnumMacro(TransformOrder, TransformOrderType); itkGetEnumMacro(TransformOrder, TransformOrderType); /** Set the interpolator function. */ itkSetObjectMacro( Interpolator, InterpolatorType ); /** Get a pointer to the interpolator function. */ itkGetModifiableObjectMacro( Interpolator, InterpolatorType ); /** Set the output image spacing. */ itkSetMacro(OutputSpacing, SpacingType); virtual void SetOutputSpacing( const double* values); /** Get the output image spacing. */ itkGetConstReferenceMacro(OutputSpacing, SpacingType); /** Set the output image origin. */ itkSetMacro(OutputOrigin, PointType); virtual void SetOutputOrigin( const double* values); /** Set the output image size. */ itkSetMacro(OutputSize, SizeType); // virtual void SetOutputSize( const double *values); itkGetConstReferenceMacro(OutputSize, SizeType); /** Get the output image origin. */ itkGetConstReferenceMacro(OutputOrigin, PointType); /** Set the edge padding value */ itkSetMacro( EdgePaddingValue, PixelType ); /** Get the edge padding value */ itkGetMacro( EdgePaddingValue, PixelType ); /** WarpImageWAffineFilter produces an image which is a different * size than its input image. As such, it needs to provide an * implemenation for GenerateOutputInformation() which set * the output information according the OutputSpacing, OutputOrigin * and the deformation field's LargestPossibleRegion. */ virtual void GenerateOutputInformation(); /** It is difficult to compute in advance the input image region * required to compute the requested output region. Thus the safest * thing to do is to request for the whole input image. * * For the deformation field, the input requested region * set to be the same as that of the output requested region. */ virtual void GenerateInputRequestedRegion(); /** This method is used to set the state of the filter before * multi-threading. */ virtual void BeforeThreadedGenerateData(); /** This method is used to set the state of the filter after * multi-threading. */ virtual void AfterThreadedGenerateData(); /** precompute the smoothed image if necessary **/ void SetSmoothScale(double scale); double GetSmoothScale() { return m_SmoothScale; }; void UpdateSizeByScale(); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(SameDimensionCheck1, (Concept::SameDimension ) ); itkConceptMacro(SameDimensionCheck2, (Concept::SameDimension ) ); itkConceptMacro(InputHasNumericTraitsCheck, (Concept::HasNumericTraits ) ); itkConceptMacro(DisplacementFieldHasNumericTraitsCheck, (Concept::HasNumericTraits ) ); /** End concept checking */ #endif protected: WarpImageWAffineFilter(); ~WarpImageWAffineFilter() { }; void PrintSelf(std::ostream& os, Indent indent) const; /** WarpImageWAffineFilter is implemented as a multi-threaded filter. * As such, it needs to provide and implementation for * ThreadedGenerateData(). */ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ); private: WarpImageWAffineFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented PixelType m_EdgePaddingValue; SpacingType m_OutputSpacing; PointType m_OutputOrigin; SizeType m_OutputSize; TransformOrderType m_TransformOrder; // songgang: affine transform TransformTypePointer m_AffineTransform; InterpolatorPointer m_Interpolator; /** cached smoothed image (for downsampling) **/ InputImagePointer m_CachedSmoothImage; double m_SmoothScale; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkWarpImageWAffineFilter.hxx" #endif #endif ants-2.2.0/Utilities/itkWarpImageWAffineFilter.hxx000066400000000000000000000364131311104306400221560ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkWarpImageWAffineFilter_hxx #define __itkWarpImageWAffineFilter_hxx #include "itkWarpImageWAffineFilter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkNumericTraits.h" #include "itkProgressReporter.h" #include "itkVectorLinearInterpolateImageFunction.h" namespace itk { /** * Default constructor. */ template WarpImageWAffineFilter ::WarpImageWAffineFilter() { // Setup the number of required inputs this->SetNumberOfRequiredInputs( 2 ); // Setup default values m_OutputSpacing.Fill( 1.0 ); m_OutputOrigin.Fill( 0.0 ); m_EdgePaddingValue = NumericTraits::ZeroValue(); // Setup default interpolator typename DefaultInterpolatorType::Pointer interp = DefaultInterpolatorType::New(); m_Interpolator = static_cast( interp.GetPointer() ); m_SmoothScale = -1; m_TransformOrder = AffineFirst; } /** * Standard PrintSelf method. */ template void WarpImageWAffineFilter ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "OutputSpacing: " << m_OutputSpacing << std::endl;; os << indent << "OutputOrigin: " << m_OutputOrigin << std::endl; os << indent << "EdgePaddingValue: " << static_cast::PrintType>(m_EdgePaddingValue) << std::endl; os << indent << "Interpolator: " << m_Interpolator.GetPointer() << std::endl; } /** * Set the output image spacing. * */ template void WarpImageWAffineFilter ::SetOutputSpacing( const double* spacing) { SpacingType s(spacing); this->SetOutputSpacing( s ); } /** * Set the output image origin. * */ template void WarpImageWAffineFilter ::SetOutputOrigin( const double* origin) { PointType p(origin); this->SetOutputOrigin(p); } /** * Set deformation field as Inputs[1] for this ProcessObject. * */ template void WarpImageWAffineFilter ::SetDisplacementField( const DisplacementFieldType * field ) { // const cast is needed because the pipeline is not const-correct. DisplacementFieldType * input = const_cast( field ); this->ProcessObject::SetNthInput( 1, input ); } /** * Return a pointer to the deformation field. */ template typename WarpImageWAffineFilter ::DisplacementFieldType * WarpImageWAffineFilter ::GetDisplacementField(void) { return static_cast ( this->ProcessObject::GetInput( 1 ) ); } /** * Setup state of filter before multi-threading. * InterpolatorType::SetInputImage is not thread-safe and hence * has to be setup before ThreadedGenerateData */ template void WarpImageWAffineFilter ::BeforeThreadedGenerateData() { if( !m_Interpolator ) { itkExceptionMacro(<< "Interpolator not set"); } // Connect input image to interpolator // m_Interpolator->SetInputImage( this->GetInput() ); if( m_CachedSmoothImage.IsNull() ) { m_CachedSmoothImage = const_cast(this->GetInput() ); } m_Interpolator->SetInputImage( m_CachedSmoothImage ); } /** * Setup state of filter after multi-threading. */ template void WarpImageWAffineFilter ::AfterThreadedGenerateData() { // Disconnect input image from interpolator m_Interpolator->SetInputImage( NULL ); } // /** // * Compute the output for the region specified by outputRegionForThread. // */ // template // void // WarpImageWAffineFilter // ::ThreadedGenerateData( // const OutputImageRegionType& outputRegionForThread, // int threadId ) // { // // InputImageConstPointer inputPtr = this->GetInput(); // OutputImagePointer outputPtr = this->GetOutput(); // DisplacementFieldPointer fieldPtr = this->GetDisplacementField(); // TransformTypePointer aff = this->GetAffineTransform(); // // // std::cout << aff << std::endl; // // // support progress methods/callbacks // ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels()); // // // iterator for the output image // ImageRegionIteratorWithIndex outputIt( // outputPtr, outputRegionForThread ); // // // iterator for the deformation field // ImageRegionIterator fieldIt( // fieldPtr, outputRegionForThread ); // // IndexType index; // PointType point1, point2, point3; // DisplacementType displacement; // // // int cnt = 0; // while( !outputIt.IsAtEnd() ) // { // // get the output image index // index = outputIt.GetIndex(); // outputPtr->TransformIndexToPhysicalPoint( index, point1 ); // // // get the required displacement // displacement = fieldIt.Get(); // // // compute the required input image point // for(unsigned int j = 0; j < ImageDimension; j++ ) // { // point2[j] = point1[j] + displacement[j]; // } // // // songgang: followed by affine transform // // use affine transform // point3 = aff->TransformPoint(point2); // // // get the interpolated value // if( m_Interpolator->IsInsideBuffer( point3 ) ) // { // PixelType value = static_cast( // m_Interpolator->Evaluate( point3 ) ); // outputIt.Set( value ); // } // else // { // outputIt.Set( m_EdgePaddingValue ); // } // ++outputIt; // ++fieldIt; // progress.CompletedPixel(); // } // // } template void WarpImageWAffineFilter ::GenerateInputRequestedRegion() { // call the superclass's implementation Superclass::GenerateInputRequestedRegion(); // request the largest possible region for the input image InputImagePointer inputPtr = const_cast( this->GetInput() ); if( inputPtr ) { inputPtr->SetRequestedRegionToLargestPossibleRegion(); } // just propagate up the output requested region for the // deformation field. DisplacementFieldPointer fieldPtr = this->GetDisplacementField(); // OutputImagePointer outputPtr = this->GetOutput(); if( fieldPtr ) { // fieldPtr->SetRequestedRegion( outputPtr->GetRequestedRegion() ); fieldPtr->SetRequestedRegionToLargestPossibleRegion(); } return; } template void WarpImageWAffineFilter ::GenerateOutputInformation() { // call the superclass's implementation of this method Superclass::GenerateOutputInformation(); OutputImagePointer outputPtr = this->GetOutput(); if( !outputPtr ) { return; } typename TOutputImage::RegionType outputLargestPossibleRegion; outputLargestPossibleRegion.SetSize( m_OutputSize ); // outputLargestPossibleRegion.SetIndex( 0 ); outputPtr->SetLargestPossibleRegion( outputLargestPossibleRegion ); outputPtr->SetSpacing( m_OutputSpacing ); outputPtr->SetOrigin( m_OutputOrigin ); // DisplacementFieldPointer fieldPtr = this->GetDisplacementField(); // if( fieldPtr ) // { // outputPtr->SetLargestPossibleRegion( fieldPtr-> // GetLargestPossibleRegion() ); // } } template void WarpImageWAffineFilter ::SetSmoothScale(double scale) { if( m_SmoothScale != scale ) { // compute the new cached m_SmoothScale = scale; typename InputImageType::SpacingType inputSpacing = this->GetInput()->GetSpacing(); typename InputImageType::RegionType::SizeType inputSize = this->GetInput()->GetLargestPossibleRegion().GetSize(); typename InputImageType::SpacingType outputSpacing; typename InputImageType::RegionType::SizeType outputSize; double minimumSpacing = inputSpacing.GetVnlVector().min_value(); InputImagePointer image = const_cast(this->GetInput() ); for( unsigned int d = 0; d < ImageDimension; d++ ) { double scaling = vnl_math_min( 1.0 / scale * minimumSpacing / inputSpacing[d], static_cast( inputSize[d] ) / 32.0 ); outputSpacing[d] = inputSpacing[d] * scaling; outputSize[d] = static_cast( inputSpacing[d] * static_cast( inputSize[d] ) / outputSpacing[d] + 0.5 ); double sigma = 0.2 * ( outputSpacing[d] / inputSpacing[d] ); typedef RecursiveGaussianImageFilter GaussianFilterType; typename GaussianFilterType::Pointer smoother = GaussianFilterType::New(); smoother->SetInputImage( image ); smoother->SetDirection( d ); smoother->SetNormalizeAcrossScale( false ); // std::cout << "scale = " << scale << " => " << "sigma of dim " << d << ": " << sigma << " outsize " // << // outputSize << std::endl; smoother->SetSigma( sigma ); if( smoother->GetSigma() > 0.0 ) { smoother->Update(); image = smoother->GetOutput(); } } m_CachedSmoothImage = image; SetOutputSpacing( outputSpacing ); SetOutputOrigin( this->GetInput()->GetOrigin() ); SetOutputSize(outputSize); } } /** * Compute the output for the region specified by outputRegionForThread. */ template void WarpImageWAffineFilter ::ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) { InputImageConstPointer inputPtr = this->GetInput(); OutputImagePointer outputPtr = this->GetOutput(); DisplacementFieldPointer fieldPtr = this->GetDisplacementField(); TransformTypePointer aff = this->GetAffineTransform(); // std::cout << aff << std::endl; // support progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels() ); // iterator for the output image ImageRegionIteratorWithIndex outputIt( outputPtr, outputRegionForThread ); // iterator for the deformation field ImageRegionIterator fieldIt( fieldPtr, outputRegionForThread ); IndexType index; PointType point1, point2, point3; DisplacementType displacement; std::cout << "m_TransformOrder: " << m_TransformOrder << std::endl; while( !outputIt.IsAtEnd() ) { // get the output image index index = outputIt.GetIndex(); outputPtr->TransformIndexToPhysicalPoint( index, point1 ); // get the required displacement displacement = fieldIt.Get(); // compute the required input image point bool isinside = false; switch( m_TransformOrder ) { case AffineFirst: { for( unsigned int j = 0; j < ImageDimension; j++ ) { point2[j] = point1[j] + displacement[j]; } point3 = aff->TransformPoint(point2); isinside = true; // affine transform is always valid } break; case AffineLast: { point2 = aff->TransformPoint(point1); typedef itk::VectorLinearInterpolateImageFunction VecLinInterpolatorType; typename VecLinInterpolatorType::Pointer vinterp = DefaultInterpolatorType::New(); vinterp->SetInputImage(fieldPtr); typename DefaultInterpolatorType::ContinuousIndexType contind; // isinside = fieldPtr->TransformPhysicalPointToContinuousIndex(point2, contind); // explicitly written to avoid double / float type dismatching for( unsigned int i = 0; i < ImageDimension; i++ ) { contind[i] = ( (point2[i] - fieldPtr->GetOrigin()[i]) / fieldPtr->GetSpacing()[i] ); } isinside = fieldPtr->GetLargestPossibleRegion().IsInside( contind ); typename DefaultInterpolatorType::OutputType disp2; if( isinside ) { disp2 = vinterp->EvaluateAtContinuousIndex( contind ); } else { disp2.Fill(0); } for( int jj = 0; jj < ImageDimension; jj++ ) { point3[jj] = disp2[jj] + point2[jj]; } } break; default: itkExceptionMacro(<< "Affine order not set"); } // get the interpolated value if( isinside && (m_Interpolator->IsInsideBuffer( point3 ) ) ) { PixelType value = static_cast( m_Interpolator->Evaluate( point3 ) ); outputIt.Set( value ); } else { outputIt.Set( m_EdgePaddingValue ); } ++outputIt; ++fieldIt; progress.CompletedPixel(); } } template void WarpImageWAffineFilter ::UpdateSizeByScale() { DisplacementFieldPointer field = this->GetDisplacementField(); SetOutputSpacing( field->GetSpacing() / m_SmoothScale ); SetOutputOrigin( field->GetOrigin() ); typename InputImageType::SizeType imgsz = field->GetLargestPossibleRegion().GetSize(); for( int ii = 0; ii < InputImageType::ImageDimension; ii++ ) { imgsz[ii] = (typename InputImageType::SizeType::SizeValueType)(imgsz[ii] * m_SmoothScale + 0.5); } SetOutputSize(imgsz); } } // end namespace itk #endif ants-2.2.0/Utilities/itkWarpTensorImageMultiTransformFilter.h000066400000000000000000000300271311104306400244330ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 ITKWarpTensorImageMultiTRANSFORMFILTER_H_ #define ITKWarpTensorImageMultiTRANSFORMFILTER_H_ #include "itkImageToImageFilter.h" #include "itkVectorInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkPoint.h" #include "itkFixedArray.h" #include "itkRecursiveGaussianImageFilter.h" #include namespace itk { /** \class WarpTensorImageMultiTransformFilter * \brief Warps an image using an input deformation field. * * WarpTensorImageMultiTransformFilter warps an existing image with respect to * a given deformation field. * * A deformation field is represented as a image whose pixel type is some * vector type with at least N elements, where N is the dimension of * the input image. The vector type must support element access via operator * []. * * The output image is produced by inverse mapping: the output pixels * are mapped back onto the input image. This scheme avoids the creation of * any holes and overlaps in the output image. * * Each vector in the deformation field represent the distance between * a geometric point in the input space and a point in the output space such * that: * * \f[ p_{in} = p_{out} + d \f] * * Typically the mapped position does not correspond to an integer pixel * position in the input image. Interpolation via an image function * is used to compute values at non-integer positions. The default * interpolation typed used is the LinearInterpolateImageFunction. * The user can specify a particular interpolation function via * SetInterpolator(). Note that the input interpolator must derive * from base class InterpolateImageFunction. * * Position mapped to outside of the input image buffer are assigned * a edge padding value. * * The LargetPossibleRegion for the output is inherited from the * input deformation field. The output image spacing and origin may be set * via SetOutputSpacing, SetOutputOrigin. The default are respectively a * vector of 1's and a vector of 0's. * * This class is templated over the type of the input image, the * type of the output image and the type of the deformation field. * * The input image is set via SetInput. The input deformation field * is set via SetDisplacementField. * * This filter is implemented as a multithreaded filter. * * \warning This filter assumes that the input type, output type * and deformation field type all have the same number of dimensions. * * \ingroup GeometricTransforms MultiThreaded */ template < class TInputImage, class TOutputImage, class TDisplacementField, class TTransform > class WarpTensorImageMultiTransformFilter : public ImageToImageFilter { public: /** transform order type **/ // typedef enum _TransformOrderType {AffineFirst=0, AffineLast} TransformOrderType; /** transform type **/ typedef enum _SingleTransformType { EnumAffineType = 0, EnumDisplacementFieldType } SingleTransformType; /** Standard class typedefs. */ typedef WarpTensorImageMultiTransformFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods) */ itkTypeMacro( WarpTensorImageMultiTransformFilter, ImageToImageFilter ); /** Typedef to describe the output image region type. */ typedef typename TOutputImage::RegionType OutputImageRegionType; /** Inherit some types from the superclass. */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::InputImagePointer InputImagePointer; typedef typename Superclass::OutputImageType OutputImageType; typedef typename Superclass::OutputImagePointer OutputImagePointer; typedef typename Superclass::InputImageConstPointer InputImageConstPointer; typedef typename OutputImageType::IndexType IndexType; typedef typename OutputImageType::SizeType SizeType; typedef typename OutputImageType::PixelType PixelType; typedef typename OutputImageType::SpacingType SpacingType; typedef typename OutputImageType::DirectionType DirectionType; /** Determine the image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension ); itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension ); itkStaticConstMacro(DisplacementFieldDimension, unsigned int, TDisplacementField::ImageDimension ); /** Deformation field typedef support. */ typedef TDisplacementField DisplacementFieldType; typedef typename DisplacementFieldType::Pointer DisplacementFieldPointer; typedef typename DisplacementFieldType::PixelType DisplacementType; typedef typename DisplacementType::ValueType DisplacementScalarValueType; /** songgang: Affine transform typedef support. */ typedef TTransform TransformType; typedef typename TransformType::Pointer TransformTypePointer; /** Interpolator typedef support. */ typedef double CoordRepType; typedef VectorInterpolateImageFunction InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointer; typedef VectorLinearInterpolateImageFunction DefaultInterpolatorType; typedef VectorLinearInterpolateImageFunction DefaultVectorInterpolatorType; typedef typename DefaultVectorInterpolatorType::Pointer VectorInterpolatorPointer; /** Point type */ typedef Point PointType; typedef struct _DeformationTypeEx { DisplacementFieldPointer field; VectorInterpolatorPointer vinterp; } DeformationTypeEx; typedef struct _AffineTypeEx { TransformTypePointer aff; } AffineTypeEx; typedef struct _VarTransformType { AffineTypeEx aex; DeformationTypeEx dex; } VarTransformType; typedef std::pair SingleTransformItemType; typedef std::list TransformListType; /** Set the interpolator function. */ itkSetObjectMacro( Interpolator, InterpolatorType ); /** Get a pointer to the interpolator function. */ itkGetModifiableObjectMacro( Interpolator, InterpolatorType ); /** Set the output image spacing. */ itkSetMacro(OutputSpacing, SpacingType); virtual void SetOutputSpacing( const double* values); /** Get the output image spacing. */ itkGetConstReferenceMacro(OutputSpacing, SpacingType); /** Set the output image spacing. */ itkSetMacro(OutputDirection, DirectionType); /** Get the output image spacing. */ itkGetConstReferenceMacro(OutputDirection, DirectionType); /** Set the output image origin. */ itkSetMacro(OutputOrigin, PointType); virtual void SetOutputOrigin( const double* values); /** Get the output image origin. */ itkGetConstReferenceMacro(OutputOrigin, PointType); /** Set the output image size. */ itkSetMacro(OutputSize, SizeType); // virtual void SetOutputSize( const double *values); itkGetConstReferenceMacro(OutputSize, SizeType); /** Set the edge padding value */ itkSetMacro( EdgePaddingValue, PixelType ); /** Get the edge padding value */ itkGetMacro( EdgePaddingValue, PixelType ); TransformListType & GetTransformList() { return m_TransformList; } void PrintTransformList(); /** WarpTensorImageMultiTransformFilter produces an image which is a different * size than its input image. As such, it needs to provide an * implemenation for GenerateOutputInformation() which set * the output information according the OutputSpacing, OutputOrigin * and the deformation field's LargestPossibleRegion. */ virtual void GenerateOutputInformation(); /** It is difficult to compute in advance the input image region * required to compute the requested output region. Thus the safest * thing to do is to request for the whole input image. * * For the deformation field, the input requested region * set to be the same as that of the output requested region. */ virtual void GenerateInputRequestedRegion(); /** This method is used to set the state of the filter before * multi-threading. */ virtual void BeforeThreadedGenerateData(); /** This method is used to set the state of the filter after * multi-threading. */ virtual void AfterThreadedGenerateData(); /** precompute the smoothed image if necessary **/ void SetSmoothScale(double scale); double GetSmoothScale() { return m_SmoothScale; }; // void UpdateSizeByScale(); void PushBackAffineTransform(const TransformType* t); void PushBackDisplacementFieldTransform(const DisplacementFieldType* t); void ComposeAffineOnlySequence(const PointType & center_output, TransformTypePointer & affine_output); bool MultiInverseAffineOnlySinglePoint(const PointType & point1, PointType & point2); bool MultiTransformSinglePoint(const PointType & point1, PointType & point2); bool MultiTransformPoint(const PointType & point1, PointType & point2, bool bFisrtDeformNoInterp, const IndexType & index); void DetermineFirstDeformNoInterp(); inline bool IsOutOfNumericBoundary(const PointType & p); // set interpolator from outside // virtual void SetInterpolator(InterpolatorPointer interp); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(SameDimensionCheck1, (Concept::SameDimension ) ); itkConceptMacro(SameDimensionCheck2, (Concept::SameDimension ) ); // removed to be compatible with vector form input image // itkConceptMacro(InputHasNumericTraitsCheck, // (Concept::HasNumericTraits)); itkConceptMacro(DisplacementFieldHasNumericTraitsCheck, (Concept::HasNumericTraits ) ); /** End concept checking */ #endif bool m_bFirstDeformNoInterp; protected: WarpTensorImageMultiTransformFilter(); ~WarpTensorImageMultiTransformFilter() { }; void PrintSelf(std::ostream& os, Indent indent) const; /** WarpTensorImageMultiTransformFilter is implemented as a multi-threaded filter. * As such, it needs to provide and implementation for * ThreadedGenerateData(). */ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ); InterpolatorPointer m_Interpolator; PixelType m_EdgePaddingValue; SpacingType m_OutputSpacing; PointType m_OutputOrigin; SizeType m_OutputSize; DirectionType m_OutputDirection; TransformListType m_TransformList; double m_SmoothScale; InputImagePointer m_CachedSmoothImage; private: WarpTensorImageMultiTransformFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkWarpTensorImageMultiTransformFilter.hxx" #endif #endif /*ITKWarpTensorImageMultiTRANSFORMFILTER_H_*/ ants-2.2.0/Utilities/itkWarpTensorImageMultiTransformFilter.hxx000066400000000000000000000500041311104306400250100ustar00rootroot00000000000000/*========================================================================= Program: Advanced Normalization Tools Copyright (c) ConsortiumOfANTS. All rights reserved. See accompanying COPYING.txt or https://github.com/stnava/ANTs/blob/master/ANTSCopyright.txt 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 __itkWarpTensorImageMultiTransformFilter_hxx #define __itkWarpTensorImageMultiTransformFilter_hxx #include "itkWarpTensorImageMultiTransformFilter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkNumericTraits.h" #include "itkProgressReporter.h" #include "itkVectorLinearInterpolateImageFunction.h" #include namespace itk { /** * Default constructor. */ template WarpTensorImageMultiTransformFilter ::WarpTensorImageMultiTransformFilter() { // Setup the number of required inputs this->SetNumberOfRequiredInputs( 1 ); // Setup default values m_OutputSpacing.Fill( 1.0 ); m_OutputOrigin.Fill( 0.0 ); PixelType Zero; Zero.SetIdentity(); Zero = Zero * 1.e-6; m_EdgePaddingValue = Zero; // Setup default interpolator typename DefaultInterpolatorType::Pointer interp = DefaultInterpolatorType::New(); m_Interpolator = static_cast( interp.GetPointer() ); m_SmoothScale = -1; // m_bOutputDisplacementField = false; // m_TransformOrder = AffineFirst; } // template // void // WarpTensorImageMultiTransformFilter // ::SetInterpolator1(InterpolatorPointer interp) // { // m_Interpolator = static_cast (interp.GetPointer()); // std::cout << "set interpolator in WarpImage:" << interp << std::endl; // } template void WarpTensorImageMultiTransformFilter ::PrintTransformList() { std::cout << "transform list: " << std::endl; typename TransformListType::iterator it = (m_TransformList.begin() ); for( int ii = 0; it != m_TransformList.end(); it++, ii++ ) { switch( it->first ) { case EnumAffineType: { std::cout << '[' << ii << "]: EnumAffineType" << std::endl; std::cout << it->second.aex.aff << std::endl; } break; case EnumDisplacementFieldType: std::cout << '[' << ii << "]: EnumDisplacementFieldType: size" << it->second.dex.field->GetLargestPossibleRegion().GetSize() << std::endl; } } } /** * Standard PrintSelf method. */ template void WarpTensorImageMultiTransformFilter ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "OutputSpacing: " << m_OutputSpacing << std::endl;; os << indent << "OutputOrigin: " << m_OutputOrigin << std::endl; os << indent << "EdgePaddingValue: " << static_cast::PrintType>(m_EdgePaddingValue) << std::endl; os << indent << "Interpolator: " << m_Interpolator.GetPointer() << std::endl; os << indent << "m_bFirstDeformNoInterp = " << m_bFirstDeformNoInterp << std::endl; } /** * Set the output image spacing. * */ template void WarpTensorImageMultiTransformFilter ::SetOutputSpacing( const double* spacing) { SpacingType s(spacing); this->SetOutputSpacing( s ); } /** * Set the output image origin. * */ template void WarpTensorImageMultiTransformFilter ::SetOutputOrigin( const double* origin) { PointType p(origin); this->SetOutputOrigin(p); } /** * Setup state of filter before multi-threading. * InterpolatorType::SetInputImage is not thread-safe and hence * has to be setup before ThreadedGenerateData */ template void WarpTensorImageMultiTransformFilter ::BeforeThreadedGenerateData() { if( !m_Interpolator ) { itkExceptionMacro(<< "Interpolator not set"); } // Connect input image to interpolator // m_Interpolator->SetInputImage( this->GetInput() ); if( m_CachedSmoothImage.IsNull() && (this->GetInput() ) ) { m_CachedSmoothImage = const_cast(this->GetInput() ); } m_Interpolator->SetInputImage( m_CachedSmoothImage ); } /** * Setup state of filter after multi-threading. */ template void WarpTensorImageMultiTransformFilter ::AfterThreadedGenerateData() { // Disconnect input image from interpolator m_Interpolator->SetInputImage( NULL ); } template void WarpTensorImageMultiTransformFilter ::GenerateInputRequestedRegion() { // call the superclass's implementation Superclass::GenerateInputRequestedRegion(); // request the largest possible region for the input image InputImagePointer inputPtr = const_cast( this->GetInput() ); if( inputPtr ) { inputPtr->SetRequestedRegionToLargestPossibleRegion(); } return; } template void WarpTensorImageMultiTransformFilter ::GenerateOutputInformation() { // call the superclass's implementation of this method Superclass::GenerateOutputInformation(); OutputImagePointer outputPtr = this->GetOutput(); if( !outputPtr ) { return; } typename TOutputImage::RegionType outputLargestPossibleRegion; outputLargestPossibleRegion.SetSize( this->m_OutputSize ); // outputLargestPossibleRegion.SetIndex( 0 ); outputPtr->SetLargestPossibleRegion( outputLargestPossibleRegion ); outputPtr->SetSpacing( this->m_OutputSpacing ); outputPtr->SetOrigin( this->m_OutputOrigin ); outputPtr->SetDirection( this->m_OutputDirection ); // determine if the deformation field is the same dimension as the image // so that it does not need interpolation in the first step DetermineFirstDeformNoInterp(); } template void WarpTensorImageMultiTransformFilter ::SetSmoothScale(double scale) { /* if (m_SmoothScale != scale){ // compute the new cached // std::cout << "change smooth scale: " << m_SmoothScale << " ---> " << scale << std::endl; m_SmoothScale = scale; typename InputImageType::SpacingType inputSpacing = this->GetInput()->GetSpacing(); typename InputImageType::RegionType::SizeType inputSize = this->GetInput()->GetLargestPossibleRegion().GetSize(); typename InputImageType::SpacingType outputSpacing; typename InputImageType::RegionType::SizeType outputSize; double minimumSpacing = inputSpacing.GetVnlVector().min_value(); double maximumSpacing = inputSpacing.GetVnlVector().max_value(); InputImagePointer image = const_cast (this->GetInput()); for ( unsigned int d = 0; d < ImageDimension; d++ ) { double scaling = vnl_math_min( 1.0 / scale * minimumSpacing / inputSpacing[d], static_cast( inputSize[d] ) / 32.0 ); outputSpacing[d] = inputSpacing[d] * scaling; outputSize[d] = static_cast( inputSpacing[d] * static_cast( inputSize[d] ) / outputSpacing[d] + 0.5 ); double sigma = 0.25 * ( outputSpacing[d] / inputSpacing[d] ); if (sigma < 0) sigma=0; typedef RecursiveGaussianImageFilter GaussianFilterType; typename GaussianFilterType::Pointer smoother = GaussianFilterType::New(); smoother->SetInputImage( image ); smoother->SetDirection( d ); smoother->SetNormalizeAcrossScale( false ); // std::cout << "scale = " << scale << " => " << "sigma of dim " << d << ": " << sigma << " out size " << outputSize << " spc1 " << outputSpacing << " in " << inputSpacing << std::endl; smoother->SetSigma( sigma ); if ( smoother->GetSigma() > 0.0 ) { smoother->Update(); image = smoother->GetOutput(); } } SetOutputSpacing( outputSpacing ); SetOutputOrigin( this->GetInput()->GetOrigin() ); SetOutputSize(outputSize); } */ InputImagePointer image = const_cast(this->GetInput() ); m_CachedSmoothImage = image; } /** * Compute the output for the region specified by outputRegionForThread. */ template void WarpTensorImageMultiTransformFilter ::ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId ) { InputImageConstPointer inputPtr = this->GetInput(); OutputImagePointer outputPtr = this->GetOutput(); // std::cout << "inputPtr->GetOrigin():" << inputPtr->GetOrigin() << std::endl; // std::cout << "outputPtr->GetOrigin():" << outputPtr->GetOrigin() << std::endl; // std::exception(); IndexType index; index.Fill(0); this->m_EdgePaddingValue = inputPtr->GetPixel(index); // support progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels() ); // iterator for the output image ImageRegionIteratorWithIndex outputIt(outputPtr, outputRegionForThread); while( !outputIt.IsAtEnd() ) { PointType point1, point2; // get the output image index IndexType index = outputIt.GetIndex(); outputPtr->TransformIndexToPhysicalPoint( index, point1 ); bool isinside = MultiTransformPoint(point1, point2, m_bFirstDeformNoInterp, index); // std::cout << "point1:" << point1 << " point2:" << point2 << " index:" << index << std::endl; // std::exception(); // warp the image // get the interpolated value if( isinside && (m_Interpolator->IsInsideBuffer( point2 ) ) ) { PixelType value = static_cast(m_Interpolator->Evaluate(point2) ); outputIt.Set( value ); } else { // std::cout << "OUTSIDE" << " isinside:" << isinside << " m_Interpolator->IsInsideBuffer( point2 ):" << // m_Interpolator->IsInsideBuffer( point2 ) << std::endl; outputIt.Set( m_EdgePaddingValue ); } ++outputIt; } progress.CompletedPixel(); } // template // void // WarpTensorImageMultiTransformFilter // ::UpdateSizeByScale() // { // // // DisplacementFieldPointer field = this->GetDisplacementField(); // // // // SetOutputSpacing( field->GetSpacing() / m_SmoothScale ); // // SetOutputOrigin( field->GetOrigin() ); // // // // typename InputImageType::SizeType imgsz = field->GetLargestPossibleRegion().GetSize(); // // for(int ii = 0; ii < InputImageType::ImageDimension; ii++) imgsz[ii] = (typename // InputImageType::SizeType::SizeValueType) (imgsz[ii] * m_SmoothScale + 0.5); // // // // SetOutputSize(imgsz); // // } template void WarpTensorImageMultiTransformFilter ::PushBackAffineTransform(const TransformType* t) { if( t ) { VarTransformType t1; t1.aex.aff = const_cast(t); m_TransformList.push_back(SingleTransformItemType(EnumAffineType, t1) ); } } template void WarpTensorImageMultiTransformFilter ::PushBackDisplacementFieldTransform(const DisplacementFieldType* t) { if( t ) { VarTransformType t1; t1.dex.field = const_cast(t); t1.dex.vinterp = DefaultVectorInterpolatorType::New(); t1.dex.vinterp->SetInputImage(t1.dex.field); m_TransformList.push_back(SingleTransformItemType(EnumDisplacementFieldType, t1) ); } } template bool WarpTensorImageMultiTransformFilter ::MultiTransformSinglePoint(const PointType & point1, PointType & point2) { IndexType null_index; bool isinside = MultiTransformPoint(point1, point2, false, null_index); return isinside; } template bool WarpTensorImageMultiTransformFilter ::IsOutOfNumericBoundary(const PointType & p) { const DisplacementScalarValueType kMaxDisp = itk::NumericTraits::max(); bool b = false; for( int i = 0; i < ImageDimension; i++ ) { if( p[i] >= kMaxDisp ) { b = true; break; } } return b; } template void WarpTensorImageMultiTransformFilter ::ComposeAffineOnlySequence(const PointType & center_output, TransformTypePointer & affine_output) { // std::cout << "test " ; // TransformTypePointer affine_output = TransformType::New(); affine_output->SetIdentity(); affine_output->SetCenter(center_output); // std::cout << affine_output; typename TransformListType::iterator it = m_TransformList.begin(); for( ; it != m_TransformList.end(); it++ ) { SingleTransformType ttype = it->first; switch( ttype ) { case EnumAffineType: { TransformTypePointer aff = it->second.aex.aff; affine_output->Compose(aff, 0); // std::cout << affine_output; break; } case EnumDisplacementFieldType: { std::cout << " Ignore deformation type ... " << std::endl; } break; default: itkExceptionMacro(<< "Single Transform Not Supported!"); } } return; } template bool WarpTensorImageMultiTransformFilter ::MultiInverseAffineOnlySinglePoint(const PointType & p1, PointType & p2) { bool isinside = true; PointType point1 = p1, point2; typename TransformListType::iterator it = m_TransformList.begin(); for( ; it != m_TransformList.end(); it++ ) { SingleTransformType ttype = it->first; switch( ttype ) { case EnumAffineType: { TransformTypePointer aff = it->second.aex.aff; TransformTypePointer aff_inv = TransformTypePointer::ObjectType::New(); // std::cout << "aff before:" << aff << std::endl; aff->GetInverse(aff_inv); // aff->GetInverse(aff); // std::cout << "aff after:" << aff << std::endl; // std::cout << "aff_inv after:" << aff_inv << std::endl; point2 = aff_inv->TransformPoint(point1); point1 = point2; isinside = true; break; } case EnumDisplacementFieldType: { } break; default: itkExceptionMacro(<< "Single Transform Not Supported!"); } if( IsOutOfNumericBoundary(point2) ) { isinside = false; break; } point1 = point2; } p2 = point2; return isinside; } template bool WarpTensorImageMultiTransformFilter ::MultiTransformPoint(const PointType & p1, PointType & p2, bool bFisrtDeformNoInterp, const IndexType & index) { bool isinside = false; PointType point1 = p1, point2; typename TransformListType::iterator it = m_TransformList.begin(); for( ; it != m_TransformList.end(); it++ ) { SingleTransformType ttype = it->first; switch( ttype ) { case EnumAffineType: { TransformTypePointer aff = it->second.aex.aff; point2 = aff->TransformPoint(point1); point1 = point2; isinside = true; } break; case EnumDisplacementFieldType: { DisplacementFieldPointer fieldPtr = it->second.dex.field; if( bFisrtDeformNoInterp && it == m_TransformList.begin() ) { // use discrete coordinates DisplacementType displacement = fieldPtr->GetPixel(index); for( int j = 0; j < ImageDimension; j++ ) { point2[j] = point1[j] + displacement[j]; } isinside = true; } else { // use continous coordinates typename DefaultVectorInterpolatorType::ContinuousIndexType contind; // use ITK implementation to use orientation header fieldPtr->TransformPhysicalPointToContinuousIndex(point1, contind); isinside = fieldPtr->GetLargestPossibleRegion().IsInside( contind ); VectorInterpolatorPointer vinterp = it->second.dex.vinterp; typename DefaultVectorInterpolatorType::OutputType disp2; if( isinside ) { disp2 = vinterp->EvaluateAtContinuousIndex( contind ); } else { disp2.Fill(0); } for( int jj = 0; jj < ImageDimension; jj++ ) { point2[jj] = disp2[jj] + point1[jj]; } } } break; default: itkExceptionMacro(<< "Single Transform Not Supported!"); } if( IsOutOfNumericBoundary(point2) ) { isinside = false; break; } point1 = point2; } p2 = point2; return isinside; } template void WarpTensorImageMultiTransformFilter ::DetermineFirstDeformNoInterp() { m_bFirstDeformNoInterp = false; if( m_TransformList.size() > 0 ) { if( (m_TransformList.front().first == EnumDisplacementFieldType) ) { DisplacementFieldPointer field = m_TransformList.front().second.dex.field; m_bFirstDeformNoInterp = (this->GetOutputSize() == field->GetLargestPossibleRegion().GetSize() ) & (this->GetOutputSpacing() == field->GetSpacing() ) & (this->GetOutputOrigin() == field->GetOrigin() ); // std::cout << "in set: field size: " << field->GetLargestPossibleRegion().GetSize() // << "output spacing: " << this->GetOutputSize() << std::endl; // std::cout << field->GetSpacing() << " | " << this->GetOutputSpacing() << std::endl; // std::cout << field->GetOrigin() << " | " << this->GetOutputOrigin() << std::endl; } } } } // end namespace itk #endif // __itkWarpTensorImageMultiTransformFilter_hxx ants-2.2.0/Utilities/itkantsReadWriteTransform.h000066400000000000000000000253331311104306400217630ustar00rootroot00000000000000#ifndef itkantsReadWriteTransform_h #define itkantsReadWriteTransform_h #include "itkDisplacementFieldTransform.h" #include "itkGaussianExponentialDiffeomorphicTransform.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkTransformFileReader.h" #include "itkTransformFileWriter.h" #include "itkCompositeTransform.h" namespace itk { namespace ants { template typename itk::Transform::Pointer ReadTransform(const std::string & filename, const bool useStaticCastForR = false) // This parameter changes to true by the programs that use R, so this code // returns a different output for them. { // We must explicitly check for file existance because failed reading is an acceptable // state for non-displacment feilds. if( !itksys::SystemTools::FileExists( filename.c_str() ) ) { std::cerr << "Transform file does not exist: " << filename << std::endl; return ITK_NULLPTR; } bool hasTransformBeenRead = false; typedef typename itk::DisplacementFieldTransform DisplacementFieldTransformType; typedef typename DisplacementFieldTransformType::DisplacementFieldType DisplacementFieldType; typedef itk::ImageFileReader DisplacementFieldReaderType; typename DisplacementFieldReaderType::Pointer fieldReader = DisplacementFieldReaderType::New(); typedef typename itk::CompositeTransform CompositeTransformType; // There are known tranform type extentions that should not be considered as imaging files // That would be used as deformatino feilds // If file is an hdf5 file, assume it is a tranform instead of an image. if( filename.find(".h5") == std::string::npos && filename.find(".hdf5") == std::string::npos && filename.find(".hdf4") == std::string::npos && filename.find(".mat") == std::string::npos && filename.find(".txt") == std::string::npos && filename.find(".xfm") == std::string::npos ) { try { fieldReader->SetFileName( filename.c_str() ); fieldReader->Update(); hasTransformBeenRead = true; } catch( ... ) { hasTransformBeenRead = false; } } typedef typename itk::Transform TransformType; typename TransformType::Pointer transform; if( hasTransformBeenRead ) { typename DisplacementFieldTransformType::Pointer displacementFieldTransform = DisplacementFieldTransformType::New(); displacementFieldTransform->SetDisplacementField( fieldReader->GetOutput() ); transform = dynamic_cast( displacementFieldTransform.GetPointer() ); } else { typename itk::TransformFileReaderTemplate::Pointer transformReader = itk::TransformFileReaderTemplate::New(); transformReader->SetFileName( filename.c_str() ); try { transformReader->Update(); } catch( const itk::ExceptionObject & e ) { std::cerr << "Transform reader for " << filename << " caught an ITK exception:\n"; e.Print( std::cerr ); return transform; } catch( const std::exception & e ) { std::cerr << "Transform reader for " << filename << " caught an exception:\n"; std::cerr << e.what() << std::endl; return transform; } catch( ... ) { std::cerr << "Transform reader for " << filename << " caught an unknown exception!!!\n"; return transform; } const typename itk::TransformFileReaderTemplate::TransformListType * const listOfTransforms = transformReader->GetTransformList(); if(listOfTransforms->size()>1) { typename CompositeTransformType::Pointer comp_transform=CompositeTransformType::New(); for(typename itk::TransformFileReaderTemplate::TransformListType::const_iterator i=listOfTransforms->begin(); i!=listOfTransforms->end(); ++i) { comp_transform->AddTransform( dynamic_cast(i->GetPointer()) ); } transform = dynamic_cast(comp_transform.GetPointer()); } else { transform = dynamic_cast( listOfTransforms->front().GetPointer() ); } /** below is a bad thing but it's the only temporary fix i could find for ANTsR on unix --- B.A. */ if ( transform.IsNull() && ( useStaticCastForR == true ) ) { transform = static_cast( listOfTransforms->front().GetPointer() ); } } return transform; } template int WriteTransform(typename itk::Transform::Pointer & xfrm, const std::string & filename) { typedef typename itk::Transform GenericTransformType; typedef typename itk::DisplacementFieldTransform DisplacementFieldTransformType; typedef typename DisplacementFieldTransformType::DisplacementFieldType DisplacementFieldType; typedef typename itk::ImageFileWriter DisplacementFieldWriter; typedef itk::TransformFileWriterTemplate TransformWriterType; DisplacementFieldTransformType *dispXfrm = dynamic_cast(xfrm.GetPointer() ); // if it's a displacement field transform or output file indicates it should be a transform try { if( dispXfrm != ITK_NULLPTR && filename.find(".mat") == std::string::npos && filename.find(".txt") == std::string::npos ) { typename DisplacementFieldType::Pointer dispField = dispXfrm->GetModifiableDisplacementField(); if( filename.find(".xfm" ) == std::string::npos && filename.find(".h5" ) == std::string::npos && filename.find(".hdf5") == std::string::npos && filename.find(".hdf4") == std::string::npos ) { typename DisplacementFieldWriter::Pointer writer = DisplacementFieldWriter::New(); writer->SetInput(dispField); writer->SetFileName(filename.c_str() ); writer->Update(); } else // creating a DisplacementFieldTransformType object to make everybody happy { typename DisplacementFieldTransformType::Pointer tmp_xfrm=DisplacementFieldTransformType::New(); tmp_xfrm->SetDisplacementField(dispField); typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetInput(tmp_xfrm); transformWriter->SetFileName(filename.c_str() ); transformWriter->Update(); } } else // regular transform, hope that everything works as expected! { typedef itk::CompositeTransform CompositeTransformType; typedef typename CompositeTransformType::Pointer CompositeTransformPointer; typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); CompositeTransformType *comp_xfm = dynamic_cast(xfrm.GetPointer() ); if( comp_xfm != ITK_NULLPTR ) { //this is a composite transform, make sure it doesn't contain wiered stuff CompositeTransformPointer tmp_comp_xfm=CompositeTransformType::New(); size_t numTransforms = comp_xfm->GetNumberOfTransforms(); for( size_t i = 0; i < numTransforms; i++ ) { GenericTransformType *_xfm=comp_xfm->GetNthTransform( i ); DisplacementFieldTransformType *_dispXfrm = dynamic_cast(_xfm ); if ( _dispXfrm != ITK_NULLPTR ) { //assume that we have to make it DisplacementFieldTransform typename DisplacementFieldTransformType::Pointer _xfm_disp=DisplacementFieldTransformType::New(); _xfm_disp->SetDisplacementField(_dispXfrm->GetModifiableDisplacementField()); tmp_comp_xfm->AddTransform(_xfm_disp); } else { //asume we just pass it on tmp_comp_xfm->AddTransform(_xfm); } } transformWriter->SetInput(tmp_comp_xfm); } else { //this is a simple transform transformWriter->SetInput(xfrm); } transformWriter->SetFileName(filename.c_str() ); transformWriter->Update(); } } catch( itk::ExceptionObject & err ) { std::cerr << "Can't write transform file " << filename << std::endl; std::cerr << "Exception Object caught: " << std::endl; std::cerr << err << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } template int WriteInverseTransform(typename itk::DisplacementFieldTransform::Pointer & xfrm, const std::string & filename) { typedef typename itk::DisplacementFieldTransform DisplacementFieldTransformType; typedef typename DisplacementFieldTransformType::DisplacementFieldType DisplacementFieldType; typedef typename itk::ImageFileWriter DisplacementFieldWriter; typedef itk::TransformFileWriterTemplate TransformWriterType; typename DisplacementFieldType::Pointer inverseDispField = xfrm->GetModifiableInverseDisplacementField(); try { if( filename.find(".xfm" ) == std::string::npos && filename.find(".h5" ) == std::string::npos && filename.find(".hdf5") == std::string::npos && filename.find(".hdf4") == std::string::npos ) { typename DisplacementFieldWriter::Pointer writer = DisplacementFieldWriter::New(); writer->SetInput(inverseDispField); writer->SetFileName(filename.c_str() ); writer->Update(); } else // regular transform, but need to create inverse of the right type { typename DisplacementFieldTransformType::Pointer inv_xfrm=DisplacementFieldTransformType::New(); inv_xfrm->SetDisplacementField(inverseDispField); typename TransformWriterType::Pointer transformWriter = TransformWriterType::New(); transformWriter->SetInput(inv_xfrm); transformWriter->SetFileName(filename.c_str() ); transformWriter->Update(); } } catch( itk::ExceptionObject & err ) { std::cerr << "Can't write transform file " << filename << std::endl; std::cerr << "Exception Object caught: " << std::endl; std::cerr << err << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } } // namespace ants } // namespace itk #endif // itkantsReadWriteTransform_h ants-2.2.0/Version.cmake000066400000000000000000000022671311104306400151070ustar00rootroot00000000000000# # ONLY MODIFY TO CHANGE VERSION # # The number of commits since last this file has changed it used to # define "dev" and "post", modification of this file will reset that # version. # # Version info set(${PROJECT_NAME}_VERSION_MAJOR 2) set(${PROJECT_NAME}_VERSION_MINOR 1) set(${PROJECT_NAME}_VERSION_PATCH 0) #set(${PROJECT_NAME}_VERSION_TWEAK "") include(ProjectSourceVersion) # pre-release codes are defined based on suffix of most recent tags. # a[N]+ Registration ANTS Registration) ANTS (Advanced Normalization Tools) Registration 1.0 http://www.apache.org/licenses/LICENSE-2.0.txt Brian Avants, Jeff Duda, Gang Song, Sandhitsu Das, Jon Pluta, Nick Tustison, Hans Johnson, Kent Williams Dimensionality dimensionality This option forces the image to be treated as a specified-dimensional image. If not specified, N4 tries to infer the dimensionality from the input image. 3 OutputTransformPrefix outputTransformPrefix input Specify the output transform prefix (output format is .nii.gz ). Optionally, one can choose to warp the moving image to the fixed space and, if the inverse transform exists, one can also output the warped fixed image. InitialTransform initial-transform input Specify the initial transform(s) which get immediately incorporated into the composite transform. The order of the transforms is stack-esque in that the last transform specified on the command line is the first to be applied. See antsApplyTransforms. + std::string( "for additional information. ImageMetric metric These image metrics are available--- CC: ANTS neighborhood cross correlation, MI: Mutual information, and Demons: Thirion's Demons (modified mean-squares). GC, Global Correlation. Note that the metricWeight is currently not used. Rather, it is a temporary place holder until multivariate metrics are available for a single stage. The metrics can also employ a sampling strategy defined by a sampling percentage. The sampling strategy defaults to dense, otherwise it defines a point set over which to optimize the metric. The point set can be on a regular lattice or a random lattice of points slightly perturbed to minimize aliasing artifacts. samplingPercentage defines the fraction of points to select from the domain. CC MI Mattes Demons GC Transform transform Several transform options are available. The gradientStep or learningRate characterizes the gradient descent optimization and is scaled appropriately for each transform using the shift scales estimator. Subsequent parameters are transform-specific and can be determined from the usage. Rigid Affine CompositeAffine Similarity Translation GaussianDisplacementField BSplineDisplacementField TimeVaryingVelocityField Syn SmoothingSigmas Specify the amount of smoothing at each level. smoothingSigmas ShrinkFactors Specify the shrink factor for the virtual domain (typically the fixed image) at each level. shrinkFactors UseHistogramMatch useHistogramMatch Histogram match the images before registration. false WinsorizeImageIntensities winsorizeImageIntensities Winsorize data based on specified quantiles. ants-2.2.0/forhtml/000077500000000000000000000000001311104306400141245ustar00rootroot00000000000000ants-2.2.0/forhtml/README000066400000000000000000000004321311104306400150030ustar00rootroot00000000000000 edit the index.html then run (from the root dir) ./forhtml/push-website e.g. git checkout master vi index.html git add index.html git commit -m 'ENH: grant ref' git push origin master # push your new index.html changes ./forhtml/push-website # send the site to github ants-2.2.0/forhtml/ants.bib000066400000000000000000005337001311104306400155570ustar00rootroot00000000000000% This file was created with JabRef 2.10. % Encoding: UTF8 @Article{Adler2014, Title = {Histology-derived volumetric annotation of the human hippocampal subfields in postmortem {MRI}.}, Author = {Adler, Daniel H. and Pluta, John and Kadivar, Salmon and Craige, Caryne and Gee, James C. and Avants, Brian B. and Yushkevich, Paul A.}, Journal = {Neuroimage}, Year = {2014}, Month = jan, Pages = {505--523}, Volume = {84}, Abstract = {Recently, there has been a growing effort to analyze the morphometry of hippocampal subfields using both in vivo and postmortem magnetic resonance imaging (MRI). However, given that boundaries between subregions of the hippocampal formation (HF) are conventionally defined on the basis of microscopic features that often lack discernible signature in MRI, subfield delineation in MRI literature has largely relied on heuristic geometric rules, the validity of which with respect to the underlying anatomy is largely unknown. The development and evaluation of such rules are challenged by the limited availability of data linking MRI appearance to microscopic hippocampal anatomy, particularly in three dimensions (3D). The present paper, for the first time, demonstrates the feasibility of labeling hippocampal subfields in a high resolution volumetric MRI dataset based directly on microscopic features extracted from histology. It uses a combination of computational techniques and manual post-processing to map subfield boundaries from a stack of histology images (obtained with 200$\mu$m spacing and 5$\mu$m slice thickness; stained using the Kluver-Barrera method) onto a postmortem 9.4Tesla MRI scan of the intact, whole hippocampal formation acquired with 160$\mu$m isotropic resolution. The histology reconstruction procedure consists of sequential application of a graph-theoretic slice stacking algorithm that mitigates the effects of distorted slices, followed by iterative affine and diffeomorphic co-registration to postmortem MRI scans of approximately 1cm-thick tissue sub-blocks acquired with 200$\mu$m isotropic resolution. These 1cm blocks are subsequently co-registered to the MRI of the whole HF. Reconstruction accuracy is evaluated as the average displacement error between boundaries manually delineated in both the histology and MRI following the sequential stages of reconstruction. The methods presented and evaluated in this single-subject study can potentially be applied to multiple hippocampal tissue samples in order to construct a histologically informed MRI atlas of the hippocampal formation.}, Doi = {10.1016/j.neuroimage.2013.08.067}, Institution = {Penn Image Computing and Science Laboratory (PICSL), Department of Radiology, University of Pennsylvania, 3600 Market Street, Suite 370, Philadelphia, PA 19104, USA; Department of Bioengineering, University of Pennsylvania, USA. Electronic address: danadler@seas.upenn.edu.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {S1053-8119(13)00932-4}, Pmid = {24036353}, Timestamp = {2014.08.26} } @Article{Aguirre2007, Title = {Canine and human visual cortex intact and responsive despite early retinal blindness from RPE65~{m}utation}, Author = {Aguirre, Geoffrey K. and Komaromy, Andras M. and Cideciyan, Artur V. and Brainard, David H. and Aleman, Tomas S. and Roman, Alejandro J. and Avants, Brian B. and Gee, James C. and Korczykowski, Marc and Hauswirth, William W. and Acland, Gregory M. and Aguirre, Gustavo D. and Jacobson, Samuel G.}, Journal = {PLoS Med.}, Year = {2007}, Month = jun, Number = {6}, Pages = {1117--1128}, Volume = {4}, Article-number = {e230}, Doi = {10.1371/journal.pmed.0040230}, ISSN = {1549-1277}, Orcid-numbers = {Cideciyan, Artur/0000-0002-2018-0905}, Researcherid-numbers = {Cideciyan, Artur/A-1075-2007}, Unique-id = {ISI:000247476300023} } @Article{Ash2010, Title = {Speech errors in progressive non-fluent aphasia}, Author = {Ash, Sharon and McMillan, Corey and Gunawardena, Delani and Avants, Brian and Morgan, Brianna and Khan, Alea and Moore, Peachie and Gee, James and Grossman, Murray}, Journal = {Brain Lang.}, Year = {2010}, Month = apr, Number = {1}, Pages = {13--20}, Volume = {113}, Doi = {10.1016/j.bandl.2009.12.001}, ISSN = {0093-934X}, Unique-id = {ISI:000276287100002} } @Article{Ash2009, Title = {Non-fluent speech in frontotemporal lobar degeneration}, Author = {Ash, Sharon and Moore, Peachie and Vesely, Luisa and Gunawardena, Delani and McMillan, Corey and Anderson, Chivon and Avants, Brian and Grossman, Murray}, Journal = {Journal of Neurolinguistics}, Year = {2009}, Month = jul, Number = {4}, Pages = {370--383}, Volume = {22}, Doi = {10.1016/j.jneuroling.2008.12.001}, ISSN = {0911-6044}, Unique-id = {ISI:000266893500004} } @Article{Ashtari2011, Title = {Medial temporal structures and memory functions in adolescents with heavy cannabis use}, Author = {Ashtari, Manzar and Avants, Brian and Cyckowski, Laura and Cervellione, Kelly L. and Roofeh, David and Cook, Philip and Gee, James and Sevy, Serge and Kumra, Sanjiv}, Journal = {J. Psychiatr. Res.}, Year = {2011}, Month = aug, Number = {8}, Pages = {1055--1066}, Volume = {45}, Doi = {10.1016/j.jpsychires.2011.01.004}, ISSN = {0022-3956}, Unique-id = {ISI:000293938900009} } @Article{Avants2007, Title = {Spatiotemporal normalization for longitudinal analysis of gray matter atrophy in frontotemporal dementia.}, Author = {Avants, Brian and Anderson, Chivon and Grossman, Murray and Gee, James C.}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2007}, Number = {Pt 2}, Pages = {303--310}, Volume = {10}, __markedentry = {[stnava:1]}, Abstract = {We present a unified method, based on symmetric diffeomorphisms, for studying longitudinal neurodegeneration. Our method first uses symmetric diffeomorphic normalization to find a spatiotemporal parameterization of an individual's image time series. The second step involves mapping a representative image or set of images from the time series into an optimal template space. The template mapping is then combined with the intrasubject spatiotemporal map to enable pairwise statistical tests to be performed on a population of normalized time series images. Here, we apply this longitudinal analysis protocol to study the gray matter atrophy patterns induced by frontotemporal dementia (FTD). We sample our normalized spatiotemporal maps at baseline (time zero) and time one year to generate an annualized atrophy map (AAM) that estimates the annual effect of FTD. This spatiotemporal normalization enables us to locate neuroanatomical regions that consistently undergo significant annual gray matter atrophy across the population. We found the majority of annual atrophy to occur in the frontal and temporal lobes in our population of 20 subjects. We also found significant effects in the hippocampus, insula and cingulate gyrus. Our novel results, significant at p < 0.05 after false discovery rate correction, are represented in local template space but also assigned Talairach coordinates and Brodmann and Anatomical Automatic Labeling (AAL) labels. This paper shows the statistical power of symmetric diffeomorphic normalization for performing deformation-based studies of longitudinal atrophy.}, Institution = {Dept. of Radiology, University of Pennsylvania, Philadelphia, PA 19104-6389, USA. avants@grasp.cis.upenn.edu}, Keywords = {Algorithms; Atrophy, pathology; Cerebral Cortex, pathology; Dementia, pathology; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Imaging, Three-Dimensional, methods; Longitudinal Studies; Magnetic Resonance Imaging, methods; Neurons, pathology; Reproducibility of Results; Sensitivity and Specificity}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {18044582}, Timestamp = {2013.05.29} } @Article{Avants2010, Title = {Sparse unbiased analysis of anatomical variance in longitudinal imaging.}, Author = {Avants, Brian and Cook, Philip A. and McMillan, Corey and Grossman, Murray and Tustison, Nicholas J. and Zheng, Yuanjie and Gee, James C.}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2010}, Number = {Pt 1}, Pages = {324--331}, Volume = {13}, __markedentry = {[stnava:1]}, Abstract = {We present a new algorithm for reliable, unbiased, multivariate longitudinal analysis of cortical and white matter atrophy rates with penalized statistical methods. The pipeline uses a step-wise approach to transform and personalize template information first to a single-subject template (SST) and then to the individual's time series data. The first stream of information flows from group template to the SST; the second flows from the SST to the individual time-points and provides unbiased, prior-based segmentation and measurement of cortical thickness. MRI-bias correction, consistent longitudinal segmentation, cortical parcellation and cortical thickness estimation are all based on strong use of the subject-specific priors built from initial diffeomorphic mapping between the SST and optimal group template. We evaluate our approach with both test-retest data and with application to a driving biological problem. We use test-retest data to show that this approach produces (a) zero change when the retest data contains the same image content as the test data and (b) produces normally distributed, low variance estimates of thickness change centered at zero when test-retest data is collected near in time to test data. We also show that our approach--when combined with sparse canonical correlation analysis--reveals plausible, significant, annualized decline in cortical thickness and white matter volume when contrasting frontotemporal dementia and normal aging.}, Institution = {Dept. of Radiology, University of Pennsylvania, Philadelphia, PA 19104-6389 USA. avants@grasp.cis.upenn.edu}, Keywords = {Algorithms; Analysis of Variance; Brain Diseases, pathology; Brain, pathology; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Longitudinal Studies; Magnetic Resonance Imaging, methods; Pattern Recognition, Automated, methods; Prognosis; Reproducibility of Results; Sensitivity and Specificity; Subtraction Technique}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {20879247}, Timestamp = {2013.05.29} } @Article{Avants2012, Title = {Eigenanatomy improves detection power for longitudinal cortical change.}, Author = {Avants, Brian and Dhillon, Paramveer and Kandel, Benjamin M. and Cook, Philip A. and McMillan, Corey T. and Grossman, Murray and Gee, James C.}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2012}, Number = {Pt 3}, Pages = {206--213}, Volume = {15}, __markedentry = {[stnava:1]}, Abstract = {We contribute a novel and interpretable dimensionality reduction strategy, eigenanatomy, that is tuned for neuroimaging data. The method approximates the eigendecomposition of an image set with basis functions (the eigenanatomy vectors) that are sparse, unsigned and are anatomically clustered. We employ the eigenanatomy vectors as anatomical predictors to improve detection power in morphometry. Standard voxel-based morphometry (VBM) analyzes imaging data voxel-by-voxel--and follows this with cluster-based or voxel-wise multiple comparisons correction methods to determine significance. Eigenanatomy reverses the standard order of operations by first clustering the voxel data and then using standard linear regression in this reduced dimensionality space. As with traditional region-of-interest (ROI) analysis, this strategy can greatly improve detection power. Our results show that eigenanatomy provides a principled objective function that leads to localized, data-driven regions of interest. These regions improve our ability to quantify biologically plausible rates of cortical change in two distinct forms of neurodegeneration. We detail the algorithm and show experimental evidence of its efficacy.}, Institution = {Department of Radiology, University of Pennsylvania, Philadelphia, PA 19104, USA.}, Keywords = {Aging, physiology; Algorithms; Brain, anatomy /\&/ histology/physiology; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Information Storage and Retrieval, methods; Longitudinal Studies; Magnetic Resonance Imaging, methods; Pattern Recognition, Automated, methods; Reproducibility of Results; Sensitivity and Specificity}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {23286132}, Timestamp = {2013.05.29} } @Article{Avants2008, Title = {Multivariate Analysis of Structural and Diffusion Imaging in Traumatic Brain Injury}, Author = {Avants, Brian and Duda, Jeffrey T. and Kim, Junghoon and Zhang, Hui and Pluta, John and Gee, James C. and Whyte, John}, Journal = {Acad. Radiol.}, Year = {2008}, Month = nov, Number = {11}, Pages = {1360--1375}, Volume = {15}, Doi = {10.1016/j.acra.2008.07.007}, ISSN = {1076-6332}, Unique-id = {ISI:000260707600004} } @InProceedings{Avants2005a, Title = {Geodesic image interpolation: Parameterizing and interpolating spatiotemporal images}, Author = {Avants, BB and Epstein, CL and Gee, JC}, Booktitle = {VARIATIONAL, GEOMETRIC, AND LEVEL SET METHODS IN COMPUTER VISION, PROCEEDINGS}, Year = {2005}, Editor = {Paragios, N and Faugeras, O and Chan, T and Schnorr, C}, Note = {3rd International Workshop on Variational, Geometric, and Level Set Methods in Computer Vision, Beijing, PEOPLES R CHINA, OCT 16, 2005}, Pages = {247--258}, Series = {Lecture Notes in Computer Science}, Volume = {3752}, __markedentry = {[stnava:1]}, ISBN = {3-540-29348-5}, ISSN = {0302-9743}, Unique-id = {ISI:000233133600021} } @InProceedings{Avants2003a, Title = {Formulation and evaluation of variational curve matching with prior constraints}, Author = {Avants, B and Gee, J}, Booktitle = {BIOMEDICAL IMAGE REGISTRATION}, Year = {2003}, Editor = {Gee, JC and Maintz, JBA and Vannier, MW}, Note = {2nd International Workshop on Biomedical Image Registration, PHILADELPHIA, PENNSYLVANIA, JUN 23-24, 2003}, Organization = {Siemens Med Solut; Siemens Corp Res; Natl Lib Med; Univ Penn, Vice Provost Res}, Pages = {21--30}, Series = {LECTURE NOTES IN COMPUTER SCIENCE}, Volume = {2717}, __markedentry = {[stnava:1]}, ISBN = {3-540-20343-5}, ISSN = {0302-9743}, Unique-id = {ISI:000187954800003} } @InProceedings{Avants2003b, Title = {Continuous curve matching with scale-space curvature and extrema-based scale selection}, Author = {Avants, B and Gee, J}, Booktitle = {SCALE SPACE METHODS IN COMPUTER VISION, PROCEEDINGS}, Year = {2003}, Editor = {Griffin, LD and Lillholm, M}, Note = {4th International Conference on Scale Space Methods in Computer Vision, ISLE SKYE, SCOTLAND, JUN 10-12, 2003}, Organization = {British Machine Vis Assoc; Kings Coll London; IT Univ Copenhagen}, Pages = {798--813}, Series = {LECTURE NOTES IN COMPUTER SCIENCE}, Volume = {2695}, __markedentry = {[stnava:1]}, ISBN = {3-540-40368-X}, ISSN = {0302-9743}, Unique-id = {ISI:000185043200056} } @InProceedings{Avants2004, Title = {Symmetric geodesic shape averaging and shape interpolation}, Author = {Avants, B and Gee, J}, Booktitle = {COMPUTER VISION AND MATHEMATICAL METHODS IN MEDICAL AND BIOMEDICAL IMAGE ANALYSIS}, Year = {2004}, Editor = {Sonka, M and Kakadiaris, IA and Kybic, J}, Note = {Workshop on Computer Vision Approaches to Medical Image Analysis (CVAMIA)/Mathematical Methods in Biomedical Image Analysis (MMBIA) held in conjunction with the 8th ECCV, Prague, CZECH REPUBLIC, MAY 15, 2004}, Pages = {99--110}, Series = {Lecture Notes in Computer Science}, Volume = {3117}, __markedentry = {[stnava:1]}, ISBN = {3-540-22675-3}, ISSN = {0302-9743}, Unique-id = {ISI:000224372600009} } @Article{Avants2004a, Title = {Geodesic estimation for large deformation anatomical shape averaging and interpolation}, Author = {Avants, B and Gee, JC}, Journal = {Neuroimage}, Year = {2004}, Note = {Conference on Mathermatics in Brain Imaging, Univ Calif Los Angeles, Inst Pure \& Appl Math, Los Angeles, CA, JUL 12-23, 2004}, Number = {1}, Pages = {S139-S150}, Volume = {23}, Doi = {10.1016/j.neuroimage.2004.07.010}, ISSN = {1053-8119}, Unique-id = {ISI:000225374100013} } @Article{Avants2003, Title = {The shape operator for differential analysis of images.}, Author = {Avants, Brian and Gee, James}, Journal = {Inf Process Med Imaging}, Year = {2003}, Month = jul, Pages = {101--113}, Volume = {18}, Abstract = {This work provides a new technique for surface oriented volumetric image analysis. The method makes no assumptions about topology, instead constructing a local neighborhood from image information, such as a segmentation or edge map, to define a surface patch. Neighborhood constructions using extrinsic and intrinsic distances are given. This representation allows one to estimate differential properties directly from the image's Gauss map. We develop a novel technique for this purpose which estimates the shape operator and yields both principal directions and curvatures. Only first derivatives need be estimated, making the method numerically stable. We show the use of these measures for multi-scale classification of image structure by the mean and Gaussian curvatures. Finally, we propose to register image volumes by surface curvature. This is particularly useful when geometry is the only variable. To illustrate this, we register binary segmented data by surface curvature, both rigidly and non-rigidly. A novel variant of Demons registration, extensible for use with differentiable similarity metrics, is also applied for deformable curvature-driven registration of medical images.}, Institution = {University of Pennsylvania, Philadelphia, PA 19104-6389, USA. avants@grasp.cis.upenn.edu}, Keywords = {Algorithms; Animals; Brain, anatomy /&/ histology; Computer Simulation; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Imaging, Three-Dimensional, methods; Magnetic Resonance Imaging, methods; Models, Biological; Models, Statistical; Pan troglodytes; Pattern Recognition, Automated; Subtraction Technique}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {15344450}, Timestamp = {2014.08.26} } @Article{Avants2005b, Title = {A new method for assessing endbcast morphology: calculating local curvature from {3D CT} images.}, Author = {Avants, B and Gee, J and Schoenemann, PT and Monge, J and Lewis, JE and Holloway, RL}, Journal = {Am. J. Phys. Anthropol.}, Year = {2005}, Number = {40}, Pages = {67}, ISSN = {0002-9483}, Unique-id = {ISI:000227214900023} } @Article{Avants2004b, Title = {Validation of plaster endocast morphology through {3D CT} image analysis}, Author = {Avants, B. and Gee, J. and Schoenemann, P. T. and Monge, J. and Lewis, J. E. and Holloway, R. L.}, Journal = {Am. J. Phys. Anthropol.}, Year = {2004}, Number = {38}, Pages = {56}, ISSN = {0002-9483}, Unique-id = {ISI:000207846400027} } @Article{Avants2005, Title = {The correlation of cognitive decline with frontotemporal dementia induced annualized gray matter loss using diffeomorphic morphometry.}, Author = {Avants, Brian and Grossman, Murray and Gee, James C.}, Journal = {Alzheimer Dis. Assoc. Disord.}, Year = {2005}, Pages = {S25--S28}, Volume = {19 Suppl 1}, Abstract = {This study uses large deformation medical image registration to analyze, in a disease-specific normalized space, the annual rate of gray matter atrophy caused by frontotemporal dementia (FTD) and its correlation with cognitive decline. The analysis consists of three parts. First, a labeled structural MRI atlas is deformed into the shape of an average FTD brain. Second, annualized FTD-related atrophy of gray matter structures is estimated for each patient in the database. Third, the group-wise annualized atrophy rate caused by FTD is correlated, for each gray matter voxel, with declining performance on cognitive tests. This study gives insight into the relationship between FTD-related progressive cortical atrophy and loss in cognitive function.}, Institution = {University of Pennsylvania School of Medicine, Philadelphia, 19104-6389, USA. avants@grasp.cis.upenn.edu}, Keywords = {Aged; Atrophy, pathology/psychology; Cerebral Cortex, pathology; Cognition Disorders, etiology/pathology; Disease Progression; Humans; Image Processing, Computer-Assisted; Longitudinal Studies; Magnetic Resonance Imaging; Middle Aged; Pick Disease of the Brain, pathology/psychology}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {00002093-200510001-00006}, Pmid = {16317254}, Timestamp = {2014.08.26} } @Article{Avants2009, Title = {Longitudinal Cortical Atrophy in Amyotrophic Lateral Sclerosis With Frontotemporal Dementia}, Author = {Avants, Brian and Khan, Alea and McCluskey, Leo and Elman, Lauren and Grossman, Murray}, Journal = {Arch. Neurol.}, Year = {2009}, Month = jan, Number = {1}, Pages = {138--139}, Volume = {66}, ISSN = {0003-9942}, Unique-id = {ISI:000262398900022} } @Article{Avants1999, Title = {Measuring the electrical conductivity of the earth}, Author = {Avants, B and Soodak, D and Ruppeiner, G}, Journal = {American Journal of Physics}, Year = {1999}, Month = jul, Number = {7}, Pages = {593--598}, Volume = {67}, Doi = {10.1119/1.19329}, ISSN = {0002-9505}, Unique-id = {ISI:000081137000005} } @InProceedings{Avants2000, Title = {An adaptive minimal path generation technique for vessel tracking in {CT}A/CE-MRA volume images}, Author = {Avants, BB and Williams, JP}, Booktitle = {MEDICAL IMAGE COMPUTING AND COMPUTER-ASSISTED INTERVENTION - MICCAI 2000}, Year = {2000}, Editor = {Delp, S and DiGioia, AM and Jaramaz, B}, Note = {3rd International Conference on Medical Image Computing and Computer-Assisted Intervention, PITTSBURGH, PA, OCT 11-14, 2000}, Pages = {707--716}, Series = {Lecture Notes in Computer Science}, Volume = {1935}, __markedentry = {[stnava:1]}, ISBN = {3-540-41189-5}, ISSN = {0302-9743}, Unique-id = {ISI:000171938700073} } @Article{Avants2010a, Title = {Dementia induces correlated reductions in white matter integrity and cortical thickness: A multivariate neuroimaging study with sparse canonical correlation analysis}, Author = {Avants, Brian B. and Cook, Philip A. and Ungar, Lyle and Gee, James C. and Grossman, Murray}, Journal = {Neuroimage}, Year = {2010}, Month = apr, Number = {3}, Pages = {1004--1016}, Volume = {50}, Doi = {10.1016/j.neuroimage.2010.01.041}, ISSN = {1053-8119}, Unique-id = {ISI:000275408200015} } @Article{Avants2015, Title = {The pediatric template of brain perfusion}, Author = {Avants, Brian B and Duda, Jeffrey T and Kilroy, Emily and Krasileva, Kate and Jann, Kay and Kandel, Benjamin T and Tustison, Nicholas J and Yan, Lirong and Jog, Mayank and Smith, Robert and Wang, Yi and Dapretto, Mirella and Wang, Danny J J}, Journal = {Scientific Data}, Year = {2015}, Month = {02}, Pages = { EP -}, Volume = {2}, Bdsk-url-1 = {http://dx.doi.org/10.1038/sdata.2015.3}, Date = {2015/02/03/online}, Date-added = {2015-03-16 15:15:11 +0000}, Date-modified = {2015-03-16 15:15:11 +0000}, Day = {03}, L3 = {10.1038/sdata.2015.3; http://www.nature.com/articles/sdata20153#supplementary-information}, M3 = {Data Descriptor}, Owner = {stnava}, Publisher = {Macmillan Publishers Limited SN -}, Timestamp = {2015.03.16}, Ty = {JOUR}, Url = {http://dx.doi.org/10.1038/sdata.2015.3} } @InProceedings{Avants2006, Title = {Geodesic image normalization and temporal parameterization in the space of diffeomorphisms}, Author = {Avants, Brian B. and Epstein, C. L. and Gee, J. C.}, Booktitle = {MEDICAL IMAGING AND AUGMENTED REALITY}, Year = {2006}, Editor = {Yang, GZ and Jiang, T and Shen, DG and Gu, L and Yang, J}, Note = {3rd International Workshop on Medical Imaging and Augmented Reality (MIAR 2006), Shanghai, PEOPLES R CHINA, AUG 17-18, 2006}, Pages = {9--16}, Series = {LECTURE NOTES IN COMPUTER SCIENCE}, Volume = {4091}, __markedentry = {[stnava:1]}, ISBN = {3-540-37220-2}, ISSN = {0302-9743}, Unique-id = {ISI:000240079500002} } @Article{Avants2008a, Title = {Symmetric diffeomorphic image registration with cross-correlation: Evaluating automated labeling of elderly and neurodegenerative brain}, Author = {Avants, B. B. and Epstein, C. L. and Grossman, M. and Gee, J. C.}, Journal = {Med. Image Anal.}, Year = {2008}, Month = feb, Note = {3rd International Workshop on Biomedical Image Registration, Utrecht Univ, Utrecht, NETHERLANDS, JUL 09-11, 2006}, Number = {1}, Pages = {26--41}, Volume = {12}, Doi = {10.1016/j.media.2007.06.004}, ISSN = {1361-8415}, Organization = {Philips Med Syst}, Unique-id = {ISI:000254032800004} } @InProceedings{Avants2006a, Title = {Symmetric diffeomorphic image registration: Evaluating automated labeling of elderly and neurodegenerative cortex and frontal lobe}, Author = {Avants, Brian B. and Grossman, Murray and Gee, James C.}, Booktitle = {BIOMEDICAL IMAGE REGISTRATION, PROCEEDINGS}, Year = {2006}, Editor = {Pluim, JPW and Likar, B and Gerritsen, FA}, Note = {3rd International Workshop on Biomedical Image Registration, Utrecht Univ, Utrecht, NETHERLANDS, JUL 09-11, 2006}, Organization = {Philips Med Syst}, Pages = {50--57}, Series = {LECTURE NOTES IN COMPUTER SCIENCE}, Volume = {4057}, __markedentry = {[stnava:1]}, ISBN = {3-540-35648-7}, ISSN = {0302-9743}, Unique-id = {ISI:000239485200007} } @Article{Avants2015a, Title = {Relation of Childhood Home Environment to Cortical Thickness in Late Adolescence: Specificity of Experience and Timing}, Author = {Avants, B. B. and Hackman, D. and Betancourt, L. and Lawson, G. M. and Hurt, H. and Farah}, Journal = {PLO}, Year = {2015}, Owner = {stnava}, Timestamp = {2015.03.16} } @Article{Avants2007a, Title = {Effects of heavy in utero cocaine exposure on adolescent caudate morphology}, Author = {Avants, Brian B. and Hurt, Hallam and Giannetta, Joan M. and Epstein, Charles L. and Shera, David M. and Rao, Hengyi and Wang, Jiongjiong and Gee, James C.}, Journal = {Pediatr. Neurol.}, Year = {2007}, Month = oct, Number = {4}, Pages = {275--279}, Volume = {37}, Doi = {10.1016/j.pediatrneurol.2007.06.012}, ISSN = {0887-8994}, Researcherid-numbers = {Rao, Hengyi/A-7064-2009}, Unique-id = {ISI:000250295000007} } @Article{Avants2014a, Title = {Sparse canonical correlation analysis relates network-level atrophy to multivariate cognitive measures in a neurodegenerative population.}, Author = {Avants, Brian B. and Libon, David J. and Rascovsky, Katya and Boller, Ashley and McMillan, Corey T. and Massimo, Lauren and Coslett, H Branch and Chatterjee, Anjan and Gross, Rachel G. and Grossman, Murray}, Journal = {Neuroimage}, Year = {2014}, Month = jan, Pages = {698--711}, Volume = {84}, Abstract = {This study establishes that sparse canonical correlation analysis (SCCAN) identifies generalizable, structural MRI-derived cortical networks that relate to five distinct categories of cognition. We obtain multivariate psychometrics from the domain-specific sub-scales of the Philadelphia Brief Assessment of Cognition (PBAC). By using a training and separate testing stage, we find that PBAC-defined cognitive domains of language, visuospatial functioning, episodic memory, executive control, and social functioning correlate with unique and distributed areas of gray matter (GM). In contrast, a parallel univariate framework fails to identify, from the training data, regions that are also significant in the left-out test dataset. The cohort includes164 patients with Alzheimer's disease, behavioral-variant frontotemporal dementia, semantic variant primary progressive aphasia, non-fluent/agrammatic primary progressive aphasia, or corticobasal syndrome. The analysis is implemented with open-source software for which we provide examples in the text. In conclusion, we show that multivariate techniques identify biologically-plausible brain regions supporting specific cognitive domains. The findings are identified in training data and confirmed in test data.}, Doi = {10.1016/j.neuroimage.2013.09.048}, Institution = {Department of Radiology, University of Pennsylvania School of Medicine, Philadelphia, PA, USA.}, Keywords = {Aged; Atrophy; Brain, pathology/physiopathology; Cognition, physiology; Female; Humans; Image Processing, Computer-Assisted; Magnetic Resonance Imaging; Male; Middle Aged; Multivariate Analysis; Neurodegenerative Diseases, pathology/physiopathology; Neuropsychological Tests}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {S1053-8119(13)00984-1}, Pmid = {24096125}, Timestamp = {2014.08.26} } @Article{Avants2006b, Title = {Lagrangian frame diffeomorphic image registration: Morphometric comparison of human and chimpanzee cortex}, Author = {Avants, Brian B. and Schoenemann, P. Thomas and Gee, James C.}, Journal = {Med. Image Anal.}, Year = {2006}, Month = jun, Note = {2nd International Workshop on Biomedical Image Registration, Univ Penn, PHILADELPHIA, PA, JUN 23-24, 2003}, Number = {3}, Pages = {397--412}, Volume = {10}, Doi = {10.1016/j.media.2005.03.005}, ISSN = {1361-8415}, Organization = {Siemens Med Solut; Siemens Corp Res; Natl Lib Med; Univ Penn, Vice Provost Res}, Unique-id = {ISI:000237818100009} } @Article{Avants2011, Title = {A reproducible evaluation of ANTs similarity metric performance in brain image registration}, Author = {Avants, Brian B. and Tustison, Nicholas J. and Song, Gang and Cook, Philip A. and Klein, Arno and Gee, James C.}, Journal = {Neuroimage}, Year = {2011}, Month = feb, Number = {3}, Pages = {2033--2044}, Volume = {54}, Doi = {10.1016/j.neuroimage.2010.09.025}, ISSN = {1053-8119}, Unique-id = {ISI:000286302000027} } @Article{Avants2014, Title = {The Insight ToolKit image registration framework.}, Author = {Avants, Brian B. and Tustison, Nicholas J. and Stauffer, Michael and Song, Gang and Wu, Baohua and Gee, James C.}, Journal = {Front Neuroinform}, Year = {2014}, Pages = {44}, Volume = {8}, Abstract = {Publicly available scientific resources help establish evaluation standards, provide a platform for teaching and improve reproducibility. Version 4 of the Insight ToolKit (ITK(4)) seeks to establish new standards in publicly available image registration methodology. ITK(4) makes several advances in comparison to previous versions of ITK. ITK(4) supports both multivariate images and objective functions; it also unifies high-dimensional (deformation field) and low-dimensional (affine) transformations with metrics that are reusable across transform types and with composite transforms that allow arbitrary series of geometric mappings to be chained together seamlessly. Metrics and optimizers take advantage of multi-core resources, when available. Furthermore, ITK(4) reduces the parameter optimization burden via principled heuristics that automatically set scaling across disparate parameter types (rotations vs. translations). A related approach also constrains steps sizes for gradient-based optimizers. The result is that tuning for different metrics and/or image pairs is rarely necessary allowing the researcher to more easily focus on design/comparison of registration strategies. In total, the ITK(4) contribution is intended as a structure to support reproducible research practices, will provide a more extensive foundation against which to evaluate new work in image registration and also enable application level programmers a broad suite of tools on which to build. Finally, we contextualize this work with a reference registration evaluation study with application to pediatric brain labeling.}, Doi = {10.3389/fninf.2014.00044}, Institution = {Penn Image Computing and Science Laboratory, Department of Radiology, University of Pennsylvania Philadelphia, PA, USA.}, Language = {eng}, Medline-pst = {epublish}, Owner = {stnava}, Pmid = {24817849}, Timestamp = {2014.08.26} } @Article{Avants2011a, Title = {An Open Source Multivariate Framework for n-Tissue Segmentation with Evaluation on Public Data}, Author = {Avants, Brian B. and Tustison, Nicholas J. and Wu, Jue and Cook, Philip A. and Gee, James C.}, Journal = {Neuroinformatics}, Year = {2011}, Month = dec, Number = {4}, Pages = {381--400}, Volume = {9}, Doi = {10.1007/s12021-011-9109-y}, ISSN = {1539-2791}, Unique-id = {ISI:000297150000006} } @Article{Avants2010b, Title = {The optimal template effect in hippocampus studies of diseased populations}, Author = {Avants, Brian B. and Yushkevich, Paul and Pluta, John and Minkoff, David and Korczykowski, Marc and Detre, John and Gee, James C.}, Journal = {Neuroimage}, Year = {2010}, Month = feb, Number = {3}, Pages = {2457--2466}, Volume = {49}, Doi = {10.1016/j.neuroimage.2009.09.062}, ISSN = {1053-8119}, Unique-id = {ISI:000273626400048} } @Article{Badea2012, Title = {Quantitative mouse brain phenotyping based on single and multispectral MR protocols}, Author = {Badea, Alexandra and Gewalt, Sally and Avants, Brian B. and Cook, James J. and Johnson, G. Allan}, Journal = {Neuroimage}, Year = {2012}, Month = nov, Number = {3}, Pages = {1633--1645}, Volume = {63}, Doi = {10.1016/j.neuroimage.2012.07.021}, ISSN = {1053-8119}, Unique-id = {ISI:000310379100063} } @Article{Boller2011, Title = {Philadelphia Brief Assessment of Cognition (PBAC): A Validated Screening Measure for Dementia}, Author = {Boller, Ashley and Libon, David and Rascovsky, Katya and Gross, Rachel Goldmann and Dreyfuss, Michael and Avants, Brian and Massimo, Lauren and Moore, Peachie and Kitain, Jessica and Coslett, H. and Chatterjee, Anjan and Grossman, Murray}, Journal = {Neurology}, Year = {2011}, Month = mar, Note = {63rd AAN Annual Meeting, Honolulu, HI, APR 09-16, 2011}, Number = {9, 4}, Pages = {A511}, Volume = {76}, ISSN = {0028-3878}, Unique-id = {ISI:000288149302599} } @Article{Bonner2009, Title = {Reversal of the concreteness effect in semantic dementia}, Author = {Bonner, Michael F. and Vesely, Luisa and Price, Catherine and Anderson, Chivon and Richmond, Lauren and Farag, Christine and Avants, Brian and Grossman, Murray}, Journal = {Cognitive Neuropsychology}, Year = {2009}, Number = {6}, Pages = {568--579}, Volume = {26}, Doi = {10.1080/02643290903512305}, ISSN = {0264-3294}, Unique-id = {ISI:000275195300004} } @Article{Cook2012, Title = {Multimodal neuroimaging reveals gray and white matter associations with verbal fluency in frontotemporal degeneration}, Author = {Cook, P. A. and Avants, B. B. and McMillan, C. T. and Powers, J. and Gee, J. C. and Grossman, M.}, Journal = {Dement. Geriatr. Cogn. Disord.}, Year = {2012}, Note = {8th International Conference on Frontotemporal Dementias, Manchester, ENGLAND, SEP 05-07, 2012}, Number = {1}, Pages = {154--155}, Volume = {33}, ISSN = {1420-8008}, Unique-id = {ISI:000308612400222} } @Article{Cook2014, Title = {Relating brain anatomy and cognitive ability using a multivariate multimodal framework.}, Author = {Cook, Philip A. and McMillan, Corey T. and Avants, Brian B. and Peelle, Jonathan E. and Gee, James C. and Grossman, Murray}, Journal = {Neuroimage}, Year = {2014}, Month = oct, Pages = {477--486}, Volume = {99}, Abstract = {Linking structural neuroimaging data from multiple modalities to cognitive performance is an important challenge for cognitive neuroscience. In this study we examined the relationship between verbal fluency performance and neuroanatomy in 54 patients with frontotemporal degeneration (FTD) and 15 age-matched controls, all of whom had T1- and diffusion-weighted imaging. Our goal was to incorporate measures of both gray matter (voxel-based cortical thickness) and white matter (fractional anisotropy) into a single statistical model that relates to behavioral performance. We first used eigenanatomy to define data-driven regions of interest (DD-ROIs) for both gray matter and white matter. Eigenanatomy is a multivariate dimensionality reduction approach that identifies spatially smooth, unsigned principal components that explain the maximal amount of variance across subjects. We then used a statistical model selection procedure to see which of these DD-ROIs best modeled performance on verbal fluency tasks hypothesized to rely on distinct components of a large-scale neural network that support language: category fluency requires a semantic-guided search and is hypothesized to rely primarily on temporal cortices that support lexical-semantic representations; letter-guided fluency requires a strategic mental search and is hypothesized to require executive resources to support a more demanding search process, which depends on prefrontal cortex in addition to temporal network components that support lexical representations. We observed that both types of verbal fluency performance are best described by a network that includes a combination of gray matter and white matter. For category fluency, the identified regions included bilateral temporal cortex and a white matter region including left inferior longitudinal fasciculus and frontal-occipital fasciculus. For letter fluency, a left temporal lobe region was also selected, and also regions of frontal cortex. These results are consistent with our hypothesized neuroanatomical models of language processing and its breakdown in FTD. We conclude that clustering the data with eigenanatomy before performing linear regression is a promising tool for multimodal data analysis.}, Doi = {10.1016/j.neuroimage.2014.05.008}, Institution = {Penn Frontotemporal Degeneration Center, Department of Neurology, Perelman School of Medicine, University of Pennsylvania, Philadelphia, PA 19104, USA.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {S1053-8119(14)00373-5}, Pmid = {24830834}, Timestamp = {2014.08.26} } @InProceedings{Cook2005, Title = {An automated approach to connectivity-based partitioning of brain structures}, Author = {Cook, PA and Zhang, H and Avants, BB and Yushkevich, P and Alexander, DC and Gee, JC and Ciccarelli, O and Thompson, AJ}, Booktitle = {MEDICAL IMAGE COMPUTING AND COMPUTER-ASSISTED INTERVENTION - MICCAI 2005, PT 1}, Year = {2005}, Editor = {Duncan, JS and Gerig, G}, Note = {8th International Conference on Medical Image Computing and Computer-Assisted Intervention, Palm Springs, CA, OCT 26-29, 2005}, Organization = {No Digital Inc Waterloo; Springer Lecture Notes Comp Sci; GE Healthcare; Medtron Navigat; Siemens Corp Res; NIBIB}, Pages = {164--171}, Series = {Lecture Notes in Computer Science}, Volume = {3749}, __markedentry = {[stnava:1]}, ISBN = {3-540-29327-2}, ISSN = {0302-9743}, Researcherid-numbers = {Thompson, Alan/C-2654-2008}, Unique-id = {ISI:000233337000021} } @Article{Das2009, Title = {Registration based cortical thickness measurement.}, Author = {Das, Sandhitsu R. and Avants, Brian B. and Grossman, Murray and Gee, James C.}, Journal = {Neuroimage}, Year = {2009}, Month = apr, Number = {3}, Pages = {867--879}, Volume = {45}, Abstract = {Cortical thickness is an important biomarker for image-based studies of the brain. A diffeomorphic registration based cortical thickness (DiReCT) measure is introduced where a continuous one-to-one correspondence between the gray matter-white matter interface and the estimated gray matter-cerebrospinal fluid interface is given by a diffeomorphic mapping in the image space. Thickness is then defined in terms of a distance measure between the interfaces of this sheet like structure. This technique also provides a natural way to compute continuous estimates of thickness within buried sulci by preventing opposing gray matter banks from intersecting. In addition, the proposed method incorporates neuroanatomical constraints on thickness values as part of the mapping process. Evaluation of this method is presented on synthetic images. As an application to brain images, a longitudinal study of thickness change in frontotemporal dementia (FTD) spectrum disorder is reported.}, Doi = {10.1016/j.neuroimage.2008.12.016}, Institution = {Department of Radiology, University of Pennsylvania School of Medicine, Philadelphia, PA, USA. sudas@seas.upenn.edu}, Keywords = {Aged; Algorithms; Brain Mapping, methods; Cerebral Cortex, anatomy /&/ histology; Dementia, pathology; Humans; Image Interpretation, Computer-Assisted; Magnetic Resonance Imaging; Middle Aged}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {S1053-8119(08)01278-0}, Pmid = {19150502}, Timestamp = {2014.08.26} } @Article{Das2012, Title = {Measuring longitudinal change in the hippocampal formation from in vivo high-resolution T2-weighted {MRI}}, Author = {Das, Sandhitsu R. and Avants, Brian B. and Pluta, John and Wang, Hongzhi and Suh, Jung W. and Weiner, Michael W. and Mueller, Susanne G. and Yushkevich, Paul A.}, Journal = {Neuroimage}, Year = {2012}, Month = apr, Number = {2}, Pages = {1266--1279}, Volume = {60}, Doi = {10.1016/j.neuroimage.2012.01.098}, ISSN = {1053-8119}, Unique-id = {ISI:000303272300042} } @Article{Das2009b, Title = {Structure Specific Analysis of the Hippocampus in Temporal Lobe Epilepsy}, Author = {Das, Sandhitsu R. and Mechanic-Hamilton, Dawn and Korczykowski, Marc and Pluta, John and Glynn, Simon and Avants, Brian B. and Detre, John A. and Yushkevich, Paul A.}, Journal = {Hippocampus}, Year = {2009}, Note = {1st Computational Hippocampal Anatomy and Physiology Workshop, New York Univ, New York, NY, SEP 06, 2008}, Number = {6}, Pages = {517--525}, Volume = {19}, Doi = {10.1002/hipo.20620}, ISSN = {1050-9631}, Unique-id = {ISI:000266824000003} } @Article{Das2011, Title = {Heterogeneity of functional activation during memory encoding across hippocampal subfields in temporal lobe epilepsy.}, Author = {Das, Sandhitsu R. and Mechanic-Hamilton, Dawn and Pluta, John and Korczykowski, Marc and Detre, John A. and Yushkevich, Paul A.}, Journal = {Neuroimage}, Year = {2011}, Month = oct, Number = {4}, Pages = {1121--1130}, Volume = {58}, Abstract = {Pathology studies have shown that the anatomical subregions of the hippocampal formation are differentially affected in various neurological disorders, including temporal lobe epilepsy (TLE). Analysis of structure and function within these subregions using magnetic resonance imaging (MRI) has the potential to generate insights on disease associations as well as normative brain function. In this study, an atlas-based normalization method (Yushkevich, P.A., Avants, B.B., Pluta, J., Das, S., Minkoff, D., Mechanic-Hamilton, D., Glynn, S., Pickup, S., Liu, W., Gee, J.C., Grossman, M., Detre, J.A., 2009. A high-resolution computational atlas of the human hippocampus from postmortem magnetic resonance imaging at 9.4 T. NeuroImage 44 (2), 385-398) was used to label hippocampal subregions, making it possible to examine subfield-level functional activation during an episodic memory task in two different cohorts of healthy controls and subjects diagnosed with intractable unilateral TLE. We report, for the first time, functional activation patterns within hippocampal subfields in TLE. We detected group differences in subfield activation between patients and controls as well as inter-hemispheric activation asymmetry within subfields in patients, with dentate gyrus (DG) and the anterior hippocampus region showing the greatest effects. DG was also found to be more active than CA1 in controls, but not in patients' epileptogenic side. These preliminary results will encourage further research on the utility of subfield-based biomarkers in TLE.}, Doi = {10.1016/j.neuroimage.2011.06.085}, Institution = {Penn Image Computing and Science Laboratory (PICSL), Department of Radiology, University of Pennsylvania, PA, USA. sudas@seas.upenn.edu}, Keywords = {Algorithms; Atlases as Topic; CA1 Region, Hippocampal, physiology; Cadaver; Cohort Studies; Epilepsy, Temporal Lobe, physiopathology/psychology/surgery; Hippocampus, physiology; Humans; Image Processing, Computer-Assisted; Linear Models; Magnetic Resonance Imaging; Memory, physiology}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {S1053-8119(11)00750-6}, Pmid = {21763431}, Timestamp = {2013.05.29} } @Article{Datta2012, Title = {A Digital Atlas of the Dog Brain}, Author = {Datta, Ritobrato and Lee, Jongho and Duda, Jeffrey and Avants, Brian B. and Vite, Charles H. and Tseng, Ben and Gee, James C. and Aguirre, Gustavo D. and Aguirre, Geoffrey K.}, Journal = {PLOS ONE}, Year = {2012}, Month = dec, Number = {12}, Volume = {7}, Article-number = {e52140}, Doi = {10.1371/journal.pone.0052140}, ISSN = {1932-6203}, Unique-id = {ISI:000312794500099} } @InProceedings{Dhillon2013, Title = {Prior-based Eigenanatomy}, Author = {Paramveer Dhillon and Ben Kandel and Lyle Ungar and David A. Wolk and James C. Gee and Brian Avants}, Booktitle = {Pattern Recognition in Neuroimaging}, Year = {2013}, __markedentry = {[stnava:1]}, Owner = {stnava}, Timestamp = {2013.05.30} } @Article{Dhillon2014, Title = {Subject-specific functional parcellation via Prior Based Eigenanatomy.}, Author = {Dhillon, Paramveer S. and Wolk, David A. and Das, Sandhitsu R. and Ungar, Lyle H. and Gee, James C. and Avants, Brian B.}, Journal = {Neuroimage}, Year = {2014}, Month = oct, Pages = {14--27}, Volume = {99}, Abstract = {We present a new framework for prior-constrained sparse decomposition of matrices derived from the neuroimaging data and apply this method to functional network analysis of a clinically relevant population. Matrix decomposition methods are powerful dimensionality reduction tools that have found widespread use in neuroimaging. However, the unconstrained nature of these totally data-driven techniques makes it difficult to interpret the results in a domain where network-specific hypotheses may exist. We propose a novel approach, Prior Based Eigenanatomy (p-Eigen), which seeks to identify a data-driven matrix decomposition but at the same time constrains the individual components by spatial anatomical priors (probabilistic ROIs). We formulate our novel solution in terms of prior-constrained {\ell}1 penalized (sparse) principal component analysis. p-Eigen starts with a common functional parcellation for all the subjects and refines it with subject-specific information. This enables modeling of the inter-subject variability in the functional parcel boundaries and allows us to construct subject-specific networks with reduced sensitivity to ROI placement. We show that while still maintaining correspondence across subjects, p-Eigen extracts biologically-relevant and patient-specific functional parcels that facilitate hypothesis-driven network analysis. We construct default mode network (DMN) connectivity graphs using p-Eigen refined ROIs and use them in a classification paradigm. Our results show that the functional connectivity graphs derived from p-Eigen significantly aid classification of mild cognitive impairment (MCI) as well as the prediction of scores in a Delayed Recall memory task when compared to graph metrics derived from 1) standard registration-based seed ROI definitions, 2) totally data-driven ROIs, 3) a model based on standard demographics plus hippocampal volume as covariates, and 4) Ward Clustering based data-driven ROIs. In summary, p-Eigen incarnates a new class of prior-constrained dimensionality reduction tools that may improve our understanding of the relationship between MCI and functional connectivity.}, Doi = {026}, Institution = {Penn Image Computing and Science Laboratory (PICSL), Department of Radiology, University of Pennsylvania, Philadelphia, PA, USA.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {S1053-8119(14)00391-7}, Pmid = {24852460}, Timestamp = {2014.08.26}, Url = {http://dx.doi.org/026} } @Article{Dubb2003, Title = {Characterization of sexual dimorphism in the human corpus callosum}, Author = {Dubb, A and Gur, R and Avants, B and Gee, J}, Journal = {Neuroimage}, Year = {2003}, Month = sep, Number = {1}, Pages = {512--519}, Volume = {20}, Doi = {10.1016/S1053-8119(03)00313-6}, ISSN = {1053-8119}, Unique-id = {ISI:000185746400047} } @Article{Duda2013, Title = {Fusing functional signals by sparse canonical correlation analysis improves network reproducibility.}, Author = {Duda, Jeffrey T. and Detre, John A. and Kim, Junghoon and Gee, James C. and Avants, Brian B.}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2013}, Number = {Pt 3}, Pages = {635--642}, Volume = {16}, __markedentry = {[stnava:1]}, Abstract = {We contribute a novel multivariate strategy for computing the structure of functional networks in the brain from arterial spin labeling (ASL) MRI. Our method fuses and correlates multiple functional signals by employing an interpretable dimensionality reduction method, sparse canonical correlation analysis (SCCA). There are two key aspects of this contribution. First, we show how SCCA may be used to compute a multivariate correlation between different regions of interest (ROI). In contrast to averaging the signal over the ROI, this approach exploits the full information within the ROI. Second, we show how SCCA may simultaneously exploit both the ASL-BOLD and ASL-based cerebral blood flow (CBF) time series to produce network measurements. Our approach to fusing multiple time signals in network studies improves reproducibility over standard approaches while retaining the interpretability afforded by the classic ROI region-averaging methods. We show experimentally in test-retest data that our sparse CCA method extracts biologically plausible and stable functional network structures from ASL. We compare the ROI approach to the CCA approach while using CBF measurements alone. We then compare these results to the joint BOLD-CBF networks in a reproducibility study and in a study of functional network structure in traumatic brain injury (TBI). Our results show that the SCCA approach provides significantly more reproducible results compared to region-averaging, and in TBI the SCCA approach reveals connectivity differences not seen with the region averaging approach.}, Institution = {University of Pennsylvania, USA.}, Keywords = {Brain Injuries, diagnosis/physiopathology; Brain Mapping, methods; Brain, physiopathology; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Magnetic Resonance Imaging, methods; Nerve Net, physiopathology; Reproducibility of Results; Sensitivity and Specificity; Statistics as Topic}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {24505815}, Timestamp = {2014.08.26} } @Article{Fan2007, Title = {Multivariate examination of brain abnormality using both structural and functional {MRI}}, Author = {Fan, Yong and Rao, Hengyi and Hurt, Hallam and Giannetta, Joan and Korczykowski, Marc and Shera, David and Avants, Brian B. and Gee, James C. and Wang, Jiongjiong and Shen, Dinggang}, Journal = {Neuroimage}, Year = {2007}, Month = jul, Number = {4}, Pages = {1189--1199}, Volume = {36}, Doi = {10.1016/j.neuroimage.2007.04.009}, ISSN = {1053-8119}, Researcherid-numbers = {Rao, Hengyi/A-7064-2009 Fan, Yong/B-8306-2012}, Unique-id = {ISI:000248152400013} } @Article{Farag2010, Title = {Hierarchical Organization of Scripts: Converging Evidence from fMRI and Frontotemporal Degeneration}, Author = {Farag, Christine and Troiani, Vanessa and Bonner, Michael and Powers, Chivon and Avants, Brian and Gee, James and Grossman, Murray}, Journal = {Cereb. Cortex}, Year = {2010}, Month = oct, Number = {10}, Pages = {2453--2463}, Volume = {20}, Doi = {10.1093/cercor/bhp313}, ISSN = {1047-3211}, Unique-id = {ISI:000281715500017} } @Article{Ghosh2012, Title = {Learning from open source software projects to improve scientific review.}, Author = {Ghosh, Satrajit S. and Klein, Arno and Avants, Brian and Millman, K Jarrod}, Journal = {Front Comput Neurosci}, Year = {2012}, Pages = {18}, Volume = {6}, Abstract = {Peer-reviewed publications are the primary mechanism for sharing scientific results. The current peer-review process is, however, fraught with many problems that undermine the pace, validity, and credibility of science. We highlight five salient problems: (1) reviewers are expected to have comprehensive expertise; (2) reviewers do not have sufficient access to methods and materials to evaluate a study; (3) reviewers are neither identified nor acknowledged; (4) there is no measure of the quality of a review; and (5) reviews take a lot of time, and once submitted cannot evolve. We propose that these problems can be resolved by making the following changes to the review process. Distributing reviews to many reviewers would allow each reviewer to focus on portions of the article that reflect the reviewer's specialty or area of interest and place less of a burden on any one reviewer. Providing reviewers materials and methods to perform comprehensive evaluation would facilitate transparency, greater scrutiny, and replication of results. Acknowledging reviewers makes it possible to quantitatively assess reviewer contributions, which could be used to establish the impact of the reviewer in the scientific community. Quantifying review quality could help establish the importance of individual reviews and reviewers as well as the submitted article. Finally, we recommend expediting post-publication reviews and allowing for the dialog to continue and flourish in a dynamic and interactive manner. We argue that these solutions can be implemented by adapting existing features from open-source software management and social networking technologies. We propose a model of an open, interactive review system that quantifies the significance of articles, the quality of reviews, and the reputation of reviewers.}, Doi = {10.3389/fncom.2012.00018}, Institution = {McGovern Institute for Brain Research, Massachusetts Institute of Technology, Cambridge MA, USA.}, Language = {eng}, Medline-pst = {epublish}, Owner = {stnava}, Pmid = {22529798}, Timestamp = {2014.08.26} } @Article{Gross2012, Title = {Sentence processing in Lewy body spectrum disorder: The role of working memory}, Author = {Gross, Rachel G. and McMillan, Corey T. and Chandrasekaran, Keerthi and Dreyfuss, Michael and Ash, Sharon and Avants, Brian and Cook, Philip and Moore, Peachie and Libon, David J. and Siderowf, Andrew and Grossman, Murray}, Journal = {Brain Cogn.}, Year = {2012}, Month = mar, Number = {2}, Pages = {85--93}, Volume = {78}, Doi = {10.1016/j.bandc.2011.12.004}, ISSN = {0278-2626}, Unique-id = {ISI:000299853900001} } @Article{Grossman2008, Title = {Impaired action knowledge in amyotrophic lateral sclerosis}, Author = {Grossman, M. and Anderson, C. and Khan, A. and Avants, B. and Elman, L. and McCluskey, L.}, Journal = {Neurology}, Year = {2008}, Month = oct, Number = {18}, Pages = {1396--1401}, Volume = {71}, Doi = {10.1212/01.wnl.0000319701.50168.8c}, ISSN = {0028-3878}, Unique-id = {ISI:000260426100004} } @Article{Grossman2008a, Title = {Neural basis for impaired action knowledge in amyotrophic lateral sclerosis}, Author = {Grossman, Murray and Anderson, Chivon and Khan, Alea and Avants, Brian and Elman, Lauren and McCluskey, Leo}, Journal = {Neurology}, Year = {2008}, Month = mar, Note = {6th Annual Meeting of the American-Academy-of-Neurology, Chicago, IL, APR 12-19, 2008}, Number = {11, 1}, Pages = {A248}, Volume = {70}, ISSN = {0028-3878}, Organization = {Amer Acad Neurol}, Unique-id = {ISI:000257197201336} } @Article{Grossman2010, Title = {The role of ventral medial prefrontal cortex in social decisions Converging evidence from fMRI and frontotemporal lobar degeneration}, Author = {Grossman, Murray and Eslinger, Paul J. and Troiani, Vanessa and Anderson, Chivon and Avants, Brian and Gee, James C. and McMillan, Corey and Massimo, Lauren and Khan, Alea and Antani, Shweta}, Journal = {Neuropsychologia}, Year = {2010}, Month = oct, Number = {12}, Pages = {3505--3512}, Volume = {48}, Doi = {10.1016/j.neuropsychologia.2010.07.036}, ISSN = {0028-3932}, Unique-id = {ISI:000284017300015} } @Article{Gunawardena2010, Title = {Why are patients with progressive nonfluent aphasia nonfluent?}, Author = {Gunawardena, D. and Ash, S. and McMillan, C. and Avants, B. and Gee, J. and Grossman, M.}, Journal = {Neurology}, Year = {2010}, Month = aug, Number = {7}, Pages = {588--594}, Volume = {75}, ISSN = {0028-3878}, Unique-id = {ISI:000281066700004} } @Article{Hanson2012, Title = {Structural Variations in Prefrontal Cortex Mediate the Relationship between Early Childhood Stress and Spatial Working Memory}, Author = {Hanson, Jamie L. and Chung, Moo K. and Avants, Brian B. and Rudolph, Karen D. and Shirtcliff, Elizabeth A. and Gee, James C. and Davidson, Richard J. and Pollak, Seth D.}, Journal = {J. Neurosci.}, Year = {2012}, Month = jun, Number = {23}, Pages = {7917--7925}, Volume = {32}, Doi = {10.1523/JNEUROSCI.0307-12.2012}, ISSN = {0270-6474}, Unique-id = {ISI:000305091800016} } @Article{Hanson2010, Title = {Early Stress Is Associated with {Altera}tions in the Orbitofrontal Cortex: A Tensor-Based Morphometry Investigation of Brain Structure and Behavioral Risk}, Author = {Hanson, Jamie L. and Chung, Moo K. and Avants, Brian B. and Shirtcliff, Elizabeth A. and Gee, James C. and Davidson, Richard J. and Pollak, Seth D.}, Journal = {J. Neurosci.}, Year = {2010}, Month = jun, Number = {22}, Pages = {7466--7472}, Volume = {30}, Doi = {10.1523/JNEUROSCI.0859-10.2010}, ISSN = {0270-6474}, Researcherid-numbers = {Pollak, Seth/G-2345-2011}, Unique-id = {ISI:000278288200004} } @Article{Hanson2012a, Title = {Robust Automated Amygdala Segmentation via Multi-Atlas Diffeomorphic Registration.}, Author = {Hanson, Jamie L. and Suh, Jung W. and Nacewicz, Brendon M. and Sutterer, Matthew J. and Cayo, Amelia A. and Stodola, Diane E. and Burghy, Cory A. and Wang, Hongzhi and Avants, Brian B. and Yushkevich, Paul A. and Essex, Marilyn J. and Pollak, Seth D. and Davidson, Richard J.}, Journal = {Front Neurosci}, Year = {2012}, Pages = {166}, Volume = {6}, Abstract = {Here, we describe a novel method for volumetric segmentation of the amygdala from MRI images collected from 35 human subjects. This approach is adapted from open-source techniques employed previously with the hippocampus (Suh et al., 2011; Wang et al., 2011a,b). Using multi-atlas segmentation and machine learning-based correction, we were able to produce automated amygdala segments with high Dice (Mean?=?0.918 for the left amygdala; 0.916 for the right amygdala) and Jaccard coefficients (Mean?=?0.850 for the left; 0.846 for the right) compared to rigorously hand-traced volumes. This automated routine also produced amygdala segments with high intra-class correlations (consistency?=?0.830, absolute agreement?=?0.819 for the left; consistency?=?0.786, absolute agreement?=?0.783 for the right) and bivariate (r?=?0.831 for the left; r?=?0.797 for the right) compared to hand-drawn amygdala. Our results are discussed in relation to other cutting-edge segmentation techniques, as well as commonly available approaches to amygdala segmentation (e.g., Freesurfer). We believe this new technique has broad application to research with large sample sizes for which amygdala quantification might be needed.}, Doi = {10.3389/fnins.2012.00166}, Institution = {Department of Psychology, University of Wisconsin-Madison Madison, WI, USA ; Waisman Center, University of Wisconsin-Madison Madison, WI, USA.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {23226114}, Timestamp = {2013.05.29} } @Article{Hopkins2013, Title = {Regional and Hemispheric Variation in Cortical Thickness in Chimpanzees (Pan troglodytes)}, Author = {Hopkins, William D. and Avants, Brian B.}, Journal = {J. Neurosci.}, Year = {2013}, Month = mar, Number = {12}, Pages = {5241--5248}, Volume = {33}, Doi = {10.1523/JNEUROSCI.2996-12.2013}, ISSN = {0270-6474}, Unique-id = {ISI:000316553800017} } @Article{Hurst2012, Title = {How well does endocranial morphology predict behavioral differences in primates?}, Author = {Hurst, Delanie R. and Schoenemann, P. Thomas and Loyet, Mackenzie M. and Avants, Brian B. and Gee, James C.}, Journal = {Am. J. Phys. Anthropol.}, Year = {2012}, Note = {81st Annual Meeting of the American-Association-of-Physical-Anthropologists, Portland, OR, 2012}, Number = {54}, Pages = {171}, Volume = {147}, ISSN = {0002-9483}, Organization = {Amer Assoc Phys Anthropol}, Unique-id = {ISI:000300498700404} } @Article{Isgum2015, Title = {Evaluation of automatic neonatal brain segmentation algorithms: The NeoBrainS12 challenge.}, Author = {I{\v{s}}gum, Ivana and Benders, Manon J N L. and Avants, Brian and Cardoso, M Jorge and Counsell, Serena J. and Gomez, Elda Fischi and Gui, Laura and Hűppi, Petra S. and Kersbergen, Karina J. and Makropoulos, Antonios and Melbourne, Andrew and Moeskops, Pim and Mol, Christian P. and Kuklisova-Murgasova, Maria and Rueckert, Daniel and Schnabel, Julia A. and Srhoj-Egekher, Vedran and Wu, Jue and Wang, Siying and {de Vries}, Linda S. and Viergever, Max A.}, Journal = {Med Image Anal}, Year = {2015}, Month = {Feb}, Number = {1}, Pages = {135--151}, Volume = {20}, Abstract = {A number of algorithms for brain segmentation in preterm born infants have been published, but a reliable comparison of their performance is lacking. The NeoBrainS12 study (http://neobrains12.isi.uu.nl), providing three different image sets of preterm born infants, was set up to provide such a comparison. These sets are (i) axial scans acquired at 40weeks corrected age, (ii) coronal scans acquired at 30weeks corrected age and (iii) coronal scans acquired at 40weeks corrected age. Each of these three sets consists of three T1- and T2-weighted MR images of the brain acquired with a 3T MRI scanner. The task was to segment cortical grey matter, non-myelinated and myelinated white matter, brainstem, basal ganglia and thalami, cerebellum, and cerebrospinal fluid in the ventricles and in the extracerebral space separately. Any team could upload the results and all segmentations were evaluated in the same way. This paper presents the results of eight participating teams. The results demonstrate that the participating methods were able to segment all tissue classes well, except myelinated white matter.}, Doi = {10.1016/j.media.2014.11.001}, Institution = {Image Sciences Institute, University Medical Center Utrecht, Netherlands.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {S1361-8415(14)00158-3}, Pmid = {25487610}, Timestamp = {2015.02.08}, Url = {http://dx.doi.org/10.1016/j.media.2014.11.001} } @Article{Jain2012, Title = {Longitudinal Reproducibility and Accuracy of Pseudo-Continuous Arterial Spin-labeled Perfusion MR Imaging in Typically Developing Children}, Author = {Jain, Varsha and Duda, Jeffrey and Avants, Brian and Giannetta, Mariel and Xie, Sharon X. and Roberts, Timothy and Detre, John A. and Hurt, Hallam and Wehrli, Felix W. and Wang, Danny J. J.}, Journal = {Radiology}, Year = {2012}, Month = may, Number = {2}, Pages = {527--536}, Volume = {263}, Doi = {10.1148/radiol.12111509}, ISSN = {0033-8419}, Unique-id = {ISI:000303104300025} } @Article{Kandel2015, Title = {Decomposing cerebral blood flow MRI into functional and structural components: a non-local approach based on prediction.}, Author = {Kandel, Benjamin M. and Wang, Danny J J. and Detre, John A. and Gee, James C. and Avants, Brian B.}, Journal = {Neuroimage}, Year = {2015}, Month = {Jan}, Pages = {156--170}, Volume = {105}, Abstract = {We present RIPMMARC (Rotation Invariant Patch-based Multi-Modality Analysis aRChitecture), a flexible and widely applicable method for extracting information unique to a given modality from a multi-modal data set. We use RIPMMARC to improve the interpretation of arterial spin labeling (ASL) perfusion images by removing the component of perfusion that is predicted by the underlying anatomy. Using patch-based, rotation invariant descriptors derived from the anatomical image, we learn a predictive relationship between local neuroanatomical structure and the corresponding perfusion image. This relation allows us to produce an image of perfusion that would be predicted given only the underlying anatomy and a residual image that represents perfusion information that cannot be predicted by anatomical features. Our learned structural features are significantly better at predicting brain perfusion than tissue probability maps, which are the input to standard partial volume correction techniques. Studies in test-retest data show that both the anatomically predicted and residual perfusion signals are highly replicable for a given subject. In a pediatric population, both the raw perfusion and structurally predicted images are tightly linked to age throughout adolescence throughout the brain. Interestingly, the residual perfusion also shows a strong correlation with age in selected regions including the hippocampi (corr = 0.38, p-value <10(-6)), precuneus (corr = -0.44, p < 10(-5)), and combined default mode network regions (corr = -0.45, p < 10(-8)) that is independent of global anatomy-perfusion trends. This finding suggests that there is a regionally heterogeneous pattern of functional specialization that is distinct from that of cortical structural development.}, Doi = {10.1016/j.neuroimage.2014.10.052}, Institution = {Penn Image Computing and Science Laboratory, University of Pennsylvania, Philadelphia, PA, USA; Department of Radiology, Hospital of the University of Pennsylvania, Philadelphia, PA, USA.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {S1053-8119(14)00891-X}, Pmid = {25449745}, Timestamp = {2015.02.08}, Url = {http://dx.doi.org/10.1016/j.neuroimage.2014.10.052} } @Article{Kandel2014, Title = {Single-subject structural networks with closed-form rotation invariant matching mprove power in developmental studies of the cortex.}, Author = {Kandel, Benjamin M. and Wang, Danny J J. and Gee, James C. and Avants, Brian B.}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2014}, Number = {Pt 3}, Pages = {137--144}, Volume = {17}, __markedentry = {[stnava:1]}, Abstract = {Although much attention has recently been focused on single-subject functional networks, using methods such as resting-state functional MRI, methods for constructing single-subject structural networks are in their infancy. Single-subject cortical networks aim to describe the self-similarity across the cortical structure, possibly signifying convergent developmental pathways. Previous methods for constructing single-subject cortical networks have used patch-based correlations and distance metrics based on curvature and thickness. We present here a method for constructing similarity-based cortical structural networks that utilizes a rotation-invariant representation of structure. The resulting graph metrics are closely linked to age and indicate an increasing degree of closeness throughout development in nearly all brain regions, perhaps corresponding to a more regular structure as the brain matures. The derived graph metrics demonstrate a four-fold increase in power for detecting age as compared to cortical thickness. This proof of concept study indicates that the proposed metric may be useful in identifying biologically relevant cortical patterns.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {25320792}, Timestamp = {2014.11.01} } @Article{Kandel2014a, Title = {Eigenanatomy: Sparse dimensionality reduction for multi-modal medical image analysis.}, Author = {Kandel, Benjamin M. and Wang, Danny J J. and Gee, James C. and Avants, Brian B.}, Journal = {Methods}, Year = {2014}, Month = {Oct}, Abstract = {Rigorous statistical analysis of multimodal imaging datasets is challenging. Mass-univariate methods for extracting correlations between image voxels and outcome measurements are not ideal for multimodal datasets, as they do not account for interactions between the different modalities. The extremely high dimensionality of medical images necessitates dimensionality reduction, such as principal component analysis (PCA) or independent component analysis (ICA). These dimensionality reduction techniques, however, consist of contributions from every region in the brain and are therefore difficult to interpret. Recent advances in sparse dimensionality reduction have enabled construction of a set of image regions that explain the variance of the images while still maintaining anatomical interpretability. The projections of the original data on the sparse eigenvectors, however, are highly collinear and therefore difficult to incorporate into multi-modal image analysis pipelines. We propose here a method for clustering sparse eigenvectors and selecting a subset of the eigenvectors to make interpretable predictions from a multi-modal dataset. Evaluation on a publicly available dataset shows that the proposed method outperforms PCA and ICA-based regressions while still maintaining anatomical meaning. To facilitate reproducibility, the complete dataset used and all source code is publicly available.}, Doi = {10.1016/j.ymeth.2014.10.016}, Institution = {Penn Image Computing and Science Laboratory, University of Pennsylvania, Philadelphia, PA, United States; Department of Radiology, Hospital of the University of Pennsylvania, Philadelphia, PA, United States.}, Language = {eng}, Medline-pst = {aheadofprint}, Owner = {stnava}, Pii = {S1046-2023(14)00333-8}, Pmid = {25448483}, Timestamp = {2015.02.08}, Url = {http://dx.doi.org/10.1016/j.ymeth.2014.10.016} } @Article{Kandel2013a, Title = {Predicting cognitive data from medical images using sparse linear regression.}, Author = {Kandel, Benjamin M. and Wolk, David A. and Gee, James C. and Avants, Brian}, Journal = {Inf Process Med Imaging}, Year = {2013}, Pages = {86--97}, Volume = {23}, __markedentry = {[stnava:1]}, Abstract = {We present a new framework for predicting cognitive or other continuous-variable data from medical images. Current methods of probing the connection between medical images and other clinical data typically use voxel-based mass univariate approaches. These approaches do not take into account the multivariate, network-based interactions between the various areas of the brain and do not give readily interpretable metrics that describe how strongly cognitive function is related to neuroanatomical structure. On the other hand, high-dimensional machine learning techniques do not typically provide a direct method for discovering which parts of the brain are used for making predictions. We present a framework, based on recent work in sparse linear regression, that addresses both drawbacks of mass univariate approaches, while preserving the direct spatial interpretability that they provide. In addition, we present a novel optimization algorithm that adapts the conjugate gradient method for sparse regression on medical imaging data. This algorithm produces coefficients that are more interpretable than existing sparse regression techniques.}, Keywords = {Algorithms; Brain, pathology; Computer Simulation; Data Interpretation, Statistical; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Linear Models; Magnetic Resonance Imaging, methods; Mild Cognitive Impairment, diagnosis; Models, Statistical; Pattern Recognition, Automated, methods; Prognosis; Regression Analysis; Reproducibility of Results; Sensitivity and Specificity}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {24683960}, Timestamp = {2014.11.01} } @Article{Kim2008, Title = {Structural consequences of diffuse traumatic brain injury: A large deformation tensor-based morphometry study}, Author = {Kim, Junghoon and Avants, Brian and Patel, Sunil and Whyte, John and Coslett, Branch H. and Pluta, John and Detre, John A. and Gee, James C.}, Journal = {Neuroimage}, Year = {2008}, Month = feb, Number = {3}, Pages = {1014--1026}, Volume = {39}, Doi = {10.1016/j.neuroimage.2007.10.005}, ISSN = {1053-8119}, Unique-id = {ISI:000252691800011} } @Article{Kim2013, Title = {Methodological considerations in longitudinal morphometry of traumatic brain injury.}, Author = {Kim, Junghoon and Avants, Brian and Whyte, John and Gee, James C.}, Journal = {Front Hum Neurosci}, Year = {2013}, Pages = {52}, Volume = {7}, Abstract = {Traumatic brain injury (TBI) has recently been reconceptualized as a chronic, evolving disease process. This new view necessitates quantitative assessment of post-injury changes in brain structure that may allow more accurate monitoring and prediction of recovery. In particular, TBI is known to trigger neurodegenerative processes and therefore quantifying progression of diffuse atrophy over time is currently of utmost interest. However, there are various methodological issues inherent to longitudinal morphometry in TBI. In this paper, we first overview several of these methodological challenges: lesion evolution, neurosurgical procedures, power, bias, and non-linearity. We then introduce a sensitive, reliable, and unbiased longitudinal multivariate analysis protocol that combines dimensionality reduction and region of interest approaches. This analysis pipeline is demonstrated using a small dataset consisting of four chronic TBI survivors.}, Doi = {10.3389/fnhum.2013.00052}, Institution = {Moss Rehabilitation Research Institute Elkins Park, PA, USA.}, Language = {eng}, Medline-pst = {epublish}, Owner = {stnava}, Pmid = {23549059}, Timestamp = {2014.08.26} } @Article{Kim2010, Title = {Resting Cerebral Blood Flow {Altera}tions in Chronic Traumatic Brain Injury: An Arterial Spin Labeling Perfusion fMRI Study}, Author = {Kim, Junghoon and Whyte, John and Patel, Sunil and Avants, Brian and Europa, Eduardo and Wang, Jiongjiong and Slattery, John and Gee, James C. and Coslett, H. Branch and Detre, John A.}, Journal = {J. Neurotrauma}, Year = {2010}, Month = aug, Number = {8}, Pages = {1399--1411}, Volume = {27}, Doi = {10.1089/neu.2009.1215}, ISSN = {0897-7151}, Unique-id = {ISI:000280984900005} } @Article{Klein2009, Title = {Evaluation of 14 nonlinear deformation algorithms applied to human brain {MRI} registration}, Author = {Klein, Arno and Andersson, Jesper and Ardekani, Babak A. and Ashburner, John and Avants, Brian and Chiang, Ming-Chang and Christensen, Gary E. and Collins, D. Louis and Gee, James and Hellier, Pierre and Song, Joo Hyun and Jenkinson, Mark and Lepage, Claude and Rueckert, Daniel and Thompson, Paul and Vercauteren, Tom and Woods, Roger P. and Mann, J. John and Parsey, Ramin V.}, Journal = {Neuroimage}, Year = {2009}, Month = jul, Number = {3}, Pages = {786--802}, Volume = {46}, Doi = {10.1016/j.neuroimage.2008.12.037}, ISSN = {1053-8119}, Researcherid-numbers = {Rueckert, Daniel/C-4393-2008}, Unique-id = {ISI:000265938700025} } @Article{Klein2010, Title = {Evaluation of volume-based and surface-based brain image registration methods}, Author = {Klein, Arno and Ghosh, Satrajit S. and Avants, Brian and Yeo, B. T. T. and Fischl, Bruce and Ardekani, Babak and Gee, James C. and Mann, J. J. and Parsey, Ramin V.}, Journal = {Neuroimage}, Year = {2010}, Month = may, Number = {1}, Pages = {214--220}, Volume = {51}, Doi = {10.1016/j.neuroimage.2010.01.091}, ISSN = {1053-8119}, Unique-id = {ISI:000276480200021} } @Article{Lawson2013, Title = {Associations between children's socioeconomic status and prefrontal cortical thickness.}, Author = {Lawson, Gwendolyn M. and Duda, Jeffrey T. and Avants, Brian B. and Wu, Jue and Farah, Martha J.}, Journal = {Dev Sci}, Year = {2013}, Month = sep, Number = {5}, Pages = {641--652}, Volume = {16}, Abstract = {Childhood socioeconomic status (SES) predicts executive function performance and measures of prefrontal cortical function, but little is known about its anatomical correlates. Structural MRI and demographic data from a sample of 283 healthy children from the NIH MRI Study of Normal Brain Development were used to investigate the relationship between SES and prefrontal cortical thickness. Specifically, we assessed the association between two principal measures of childhood SES, family income and parental education, and gray matter thickness in specific subregions of prefrontal cortex and on the asymmetry of these areas. After correcting for multiple comparisons and controlling for potentially confounding variables, parental education significantly predicted cortical thickness in the right anterior cingulate gyrus and left superior frontal gyrus. These results suggest that brain structure in frontal regions may provide a meaningful link between SES and cognitive function among healthy, typically developing children.}, Doi = {10.1111/desc.12096}, Institution = {Department of Psychology, University of Pennsylvania, USA.}, Keywords = {Adolescent; Child; Educational Status; Executive Function, physiology; Female; Humans; Image Processing, Computer-Assisted; Income; Linear Models; Magnetic Resonance Imaging; Male; Organ Size; Parents; Prefrontal Cortex, anatomy /&/ histology/physiology; Social Class; United States}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {24033570}, Timestamp = {2014.08.26} } @Article{Libon2012, Title = {Deficits in Concept Formation in Amyotrophic Lateral Sclerosis}, Author = {Libon, David J. and McMillan, Corey and Avants, Brian and Boller, Ashley and Morgan, Brianna and Burkholder, Lisa and Chandrasekaran, Keerthi and Elman, Lauren and McCluskey, Leo and Grossman, Murray}, Journal = {Neuropsychology}, Year = {2012}, Month = jul, Number = {4}, Pages = {422--429}, Volume = {26}, Doi = {10.1037/a0028668}, ISSN = {0894-4105}, Unique-id = {ISI:000306036300003} } @Article{Loyet2012, Title = {Associations between localized variation in brain anatomy and social behavior in healthy human subjects.}, Author = {Loyet, Mackenzie M. and Schoenemann, P. Thomas and Avants, Brian B. and Gee, James C.}, Journal = {Am. J. Phys. Anthropol.}, Year = {2012}, Note = {81st Annual Meeting of the American-Association-of-Physical-Anthropologists, Portland, OR, 2012}, Number = {54}, Pages = {196}, Volume = {147}, ISSN = {0002-9483}, Organization = {Amer Assoc Phys Anthropol}, Unique-id = {ISI:000300498700518} } @Article{Massimo2009, Title = {Neuroanatomy of Apathy and Disinhibition in Frontotemporal Lobar Degeneration}, Author = {Massimo, Lauren and Powers, Chivon and Moore, Peachie and Vesely, Luisa and Avants, Brian and Gee, James and Libon, David J. and Grossman, Murray}, Journal = {Dement. Geriatr. Cogn. Disord.}, Year = {2009}, Number = {1}, Pages = {96--104}, Volume = {27}, Doi = {10.1159/000194658}, ISSN = {1420-8008}, Unique-id = {ISI:000262899200013} } @Article{Massimo2008, Title = {Neuroanatomical correlates of apathy and disinhibition in frontotemporal dementia}, Author = {Massimo, Lauren M. and Anderson, Chivon and Moore, Peachie and Avants, Brian and Libon, David and Cynwyd, Bala and Grossman, Murray}, Journal = {Neurology}, Year = {2008}, Month = mar, Note = {6th Annual Meeting of the American-Academy-of-Neurology, Chicago, IL, APR 12-19, 2008}, Number = {11, 1}, Pages = {A443}, Volume = {70}, ISSN = {0028-3878}, Organization = {Amer Acad Neurol}, Unique-id = {ISI:000257197202464} } @Article{McMillan2013, Title = {Can {MRI} screen for CSF biomarkers in neurodegenerative disease?}, Author = {McMillan, Corey T. and Avants, Brian and Irwin, David J. and Toledo, Jon B. and Wolk, David A. and Van Deerlin, Vivianna M. and Shaw, Leslie M. and Trojanoswki, John Q. and Grossman, Murray}, Journal = {Neurology}, Year = {2013}, Month = jan, Number = {2}, Pages = {132--138}, Volume = {80}, Doi = {10.1212/WNL.0b013e31827b9147}, ISSN = {0028-3878}, Unique-id = {ISI:000313344700008} } @Article{McMillan2014, Title = {The power of neuroimaging biomarkers for screening frontotemporal dementia.}, Author = {McMillan, Corey T. and Avants, Brian B. and Cook, Philip and Ungar, Lyle and Trojanowski, John Q. and Grossman, Murray}, Journal = {Hum. Brain Mapp.}, Year = {2014}, Month = sep, Number = {9}, Pages = {4827--4840}, Volume = {35}, Abstract = {Frontotemporal dementia (FTD) is a clinically and pathologically heterogeneous neurodegenerative disease that can result from either frontotemporal lobar degeneration (FTLD) or Alzheimer's disease (AD) pathology. It is critical to establish statistically powerful biomarkers that can achieve substantial cost-savings and increase the feasibility of clinical trials. We assessed three broad categories of neuroimaging methods to screen underlying FTLD and AD pathology in a clinical FTD series: global measures (e.g., ventricular volume), anatomical volumes of interest (VOIs) (e.g., hippocampus) using a standard atlas, and data-driven VOIs using Eigenanatomy. We evaluated clinical FTD patients (N\hspace{0.167em}=\hspace{0.167em}93) with cerebrospinal fluid, gray matter (GM) magnetic resonance imaging (MRI), and diffusion tensor imaging (DTI) to assess whether they had underlying FTLD or AD pathology. Linear regression was performed to identify the optimal VOIs for each method in a training dataset and then we evaluated classification sensitivity and specificity in an independent test cohort. Power was evaluated by calculating minimum sample sizes required in the test classification analyses for each model. The data-driven VOI analysis using a multimodal combination of GM MRI and DTI achieved the greatest classification accuracy (89\% sensitive and 89\% specific) and required a lower minimum sample size (N\hspace{0.167em}=\hspace{0.167em}26) relative to anatomical VOI and global measures. We conclude that a data-driven VOI approach using Eigenanatomy provides more accurate classification, benefits from increased statistical power in unseen datasets, and therefore provides a robust method for screening underlying pathology in FTD patients for entry into clinical trials. Hum Brain Mapp 35:4827-4840, 2014. {\copyright} 2014 Wiley Periodicals, Inc.}, Doi = {10.1002/hbm.22515}, Institution = {Department of Neurology, Penn Frontotemporal Degeneration Center, University of Pennsylvania Perelman School of Medicine, Philadelphia, Pennsylvania.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {24687814}, Timestamp = {2014.08.26} } @Article{McMillan2013a, Title = {White matter imaging helps dissociate tau from TDP-43 in frontotemporal lobar degeneration.}, Author = {McMillan, Corey T. and Irwin, David J. and Avants, Brian B. and Powers, John and Cook, Philip A. and Toledo, Jon B. and {McCarty Wood}, Elisabeth and {Van Deerlin}, Vivianna M. and Lee, Virginia M-Y. and Trojanowski, John Q. and Grossman, Murray}, Journal = {J. Neurol. Neurosurg. Psychiatry}, Year = {2013}, Month = mar, Abstract = {BACKGROUND: Frontotemporal lobar degeneration (FTLD) is most commonly associated with TAR-DNA binding protein (TDP-43) or tau pathology at autopsy, but there are no in vivo biomarkers reliably discriminating between sporadic cases. As disease-modifying treatments emerge, it is critical to accurately identify underlying pathology in living patients so that they can be entered into appropriate etiology-directed clinical trials. Patients with tau inclusions (FTLD-TAU) appear to have relatively greater white matter (WM) disease at autopsy than those patients with TDP-43 (FTLD-TDP). In this paper, we investigate the ability of white matter (WM) imaging to help discriminate between FTLD-TAU and FTLD-TDP during life using diffusion tensor imaging (DTI). METHODS: Patients with autopsy-confirmed disease or a genetic mutation consistent with FTLD-TDP or FTLD-TAU underwent multimodal T1 volumetric MRI and diffusion weighted imaging scans. We quantified cortical thickness in GM and fractional anisotropy (FA) in WM. We performed Eigenanatomy, a statistically robust dimensionality reduction algorithm, and used leave-one-out cross-validation to predict underlying pathology. Neuropathological assessment of GM and WM disease burden was performed in the autopsy-cases to confirm our findings of an ante-mortem GM and WM dissociation in the neuroimaging cohort. RESULTS: ROC curve analyses evaluated classification accuracy in individual patients and revealed 96\% sensitivity and 100\% specificity for WM analyses. FTLD-TAU had significantly more WM degeneration and inclusion severity at autopsy relative to FTLD-TDP. CONCLUSIONS: These neuroimaging and neuropathological investigations provide converging evidence for greater WM burden associated with FTLD-TAU, and emphasise the role of WM neuroimaging for in vivo discrimination between FTLD-TAU and FTLD-TDP.}, Doi = {10.1136/jnnp-2012-304418}, Institution = {Department of Neurology, Perelman School of Medicine, Frontotemporal Degeneration Center, University of Pennsylvania, , Philadelphia, Pennsylvania, USA.}, Language = {eng}, Medline-pst = {aheadofprint}, Owner = {stnava}, Pii = {jnnp-2012-304418}, Pmid = {23475817}, Timestamp = {2013.05.29} } @Article{McMillan2014a, Title = {Genetic and neuroanatomic associations in sporadic frontotemporal lobar degeneration.}, Author = {McMillan, Corey T. and Toledo, Jon B. and Avants, Brian B. and Cook, Philip A. and Wood, Elisabeth M. and Suh, Eunran and Irwin, David J. and Powers, John and Olm, Christopher and Elman, Lauren and McCluskey, Leo and Schellenberg, Gerard D. and Lee, Virginia M-Y. and Trojanowski, John Q. and {Van Deerlin}, Vivianna M. and Grossman, Murray}, Journal = {Neurobiol. Aging}, Year = {2014}, Month = jun, Number = {6}, Pages = {1473--1482}, Volume = {35}, Abstract = {Genome-wide association studies have identified single nucleotide polymorphisms (SNPs) that are sensitive for tau or TDP-43 pathology in frontotemporal lobar degeneration (FTLD). Neuroimaging analyses have revealed distinct distributions of disease in FTLD patients with genetic mutations. However, genetic influences on neuroanatomic structure in sporadic FTLD have not been assessed. In this report, we use novel multivariate tools, Eigenanatomy, and sparse canonical correlation analysis to identify associations between SNPs and neuroanatomic structure in sporadic FTLD. Magnetic resonance imaging analyses revealed that rs8070723 (MAPT) was associated with gray matter variance in the temporal cortex. Diffusion tensor imaging analyses revealed that rs1768208 (MOBP), rs646776 (near SORT1), and rs5848 (PGRN) were associated with white matter variance in the midbrain and superior longitudinal fasciculus. In an independent autopsy series, we observed that rs8070723 and rs1768208 conferred significant risk of tau pathology relative to TDP-43, and rs646776 conferred increased risk of TDP-43 pathology relative to tau. Identified brain regions and SNPs may help provide an in vivo screen for underlying pathology in FTLD and contribute to our understanding of sporadic FTLD.}, Doi = {10.1016/j.neurobiolaging.2013.11.029}, Institution = {Department of Neurology, University of Pennsylvania Perelman School of Medicine, Philadelphia, PA, USA; Penn Frontotemporal Degeneration Center, University of Pennsylvania Perelman School of Medicine, Philadelphia, PA, USA.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {S0197-4580(13)00613-1}, Pmid = {24373676}, Timestamp = {2014.08.26} } @Article{Morgan2011, Title = {Some is not enough: Quantifier comprehension in corticobasal syndrome and behavioral variant frontotemporal dementia}, Author = {Morgan, Brianna and Gross, Rachel G. and Clark, Robin and Dreyfuss, Michael and Boller, Ashley and Camp, Emily and Liang, Tsao-Wei and Avants, Brian and McMillan, Corey T. and Grossman, Murray}, Journal = {Neuropsychologia}, Year = {2011}, Month = nov, Number = {13}, Pages = {3532--3541}, Volume = {49}, Doi = {10.1016/j.neuropsychologia.2011.09.005}, ISSN = {0028-3932}, Unique-id = {ISI:000297455900003} } @Article{Murphy2011, Title = {Evaluation of Registration Methods on Thoracic {CT}: The EMPIRE10~{C}hallenge}, Author = {Murphy, Keelin and van Ginneken, Bram and Reinhardt, Joseph M. and Kabus, Sven and Ding, Kai and Deng, Xiang and Cao, Kunlin and Du, Kaifang and Christensen, Gary E. and Garcia, Vincent and Vercauteren, Tom and Ayache, Nicholas and Commowick, Olivier and Malandain, Gregoire and Glocker, Ben and Paragios, Nikos and Navab, Nassir and Gorbunova, Vladlena and Sporring, Jon and de Bruijne, Marleen and Han, Xiao and Heinrich, Mattias P. and Schnabel, Julia A. and Jenkinson, Mark and Lorenz, Cristian and Modat, Marc and McClelland, Jamie R. and Ourselin, Sebastien and Muenzing, Sascha E. A. and Viergever, Max A. and De Nigris, Dante and Collins, D. Louis and Arbel, Tal and Peroni, Marta and Li, Rui and Sharp, Gregory C. and Schmidt-Richberg, Alexander and Ehrhardt, Jan and Werner, Rene and Smeets, Dirk and Loeckx, Dirk and Song, Gang and Tustison, Nicholas and Avants, Brian and Gee, James C. and Staring, Marius and Klein, Stefan and Stoel, Berend C. and Urschler, Martin and Werlberger, Manuel and Vandemeulebroucke, Jef and Rit, Simon and Sarrut, David and Pluim, Josien P. W.}, Journal = {IEEE Trans Med Imaging}, Year = {2011}, Month = nov, Number = {11}, Pages = {1901--1920}, Volume = {30}, Doi = {10.1109/TMI.2011.2158349}, ISSN = {0278-0062}, Orcid-numbers = {van Ginneken, Bram/0000-0003-2028-8972 }, Researcherid-numbers = {van Ginneken, Bram/A-3728-2012 Staring, Marius/A-9517-2009 Ding, Kai/E-1943-2013}, Unique-id = {ISI:000296455500003} } @Article{Ng2007, Title = {Neuroinformatics for genome-wide {3D} gene expression mapping in the mouse brain}, Author = {Ng, Lydia and Pathak, Sayan D. and Kuan, Chihchau and Lau, Christopher and Dong, Hongwei and Sodt, Andrew and Dang, Chinh and Avants, Brian and Yushkevich, Paul and Gee, James C. and Haynor, David and Lein, Ed and Jones, Allan and Hawrylycz, Mike}, Journal = {IEEE-ACM T. Comput. Bi.}, Year = {2007}, Month = jul, Number = {3}, Pages = {382--393}, Volume = {4}, Doi = {10.1109/TCBB.2007.1035}, ISSN = {1545-5963}, Unique-id = {ISI:000248414700005} } @Article{Pluta2009, Title = {Appearance and Incomplete Label Matching for Diffeomorphic Template Based Hippocampus Segmentation}, Author = {Pluta, John and Avants, Brian B. and Glynn, Simon and Awate, Sttyash and Gee, James C. and Detre, John A.}, Journal = {Hippocampus}, Year = {2009}, Note = {1st Computational Hippocampal Anatomy and Physiology Workshop, New York Univ, New York, NY, SEP 06, 2008}, Number = {6}, Pages = {565--571}, Volume = {19}, Doi = {10.1002/hipo.20619}, ISSN = {1050-9631}, Unique-id = {ISI:000266824000009} } @Article{Rao2010, Title = {Early parental care is important for hippocampal maturation: Evidence from brain morphology in humans}, Author = {Rao, Hengyi and Betancourt, Laura and Giannetta, Joan M. and Brodsky, Nancy L. and Korczykowski, Marc and Avants, Brian B. and Gee, James C. and Wang, Jiongjiong and Hurt, Hallam and Detre, John A. and Farah, Martha J.}, Journal = {Neuroimage}, Year = {2010}, Month = jan, Number = {1}, Pages = {1144--1150}, Volume = {49}, Doi = {10.1016/j.neuroimage.2009.07.003}, ISSN = {1053-8119}, Researcherid-numbers = {Rao, Hengyi/A-7064-2009}, Unique-id = {ISI:000272031700116} } @Article{Rao2007, Title = {Altered resting cerebral blood flow in adolescents with in utero cocaine exposure revealed by perfusion functional {MRI}}, Author = {Rao, Hengyi and Wang, Jiongjiong and Giannetta, Joan and Korczykowski, Marc and Shera, David and Avants, Brian B. and Gee, James and Detre, John A. and Hurt, Hallam}, Journal = {Pediatrics}, Year = {2007}, Month = nov, Number = {5}, Pages = {E1245-E1254}, Volume = {120}, Doi = {10.1542/peds.2006-2596}, ISSN = {0031-4005}, Researcherid-numbers = {Rao, Hengyi/A-7064-2009}, Unique-id = {ISI:000250618900059} } @Article{Rohlfing2012, Title = {"Nonparametric Local Smoothing" is not image registration.}, Author = {Rohlfing, Torsten and Avants, Brian}, Journal = {BMC Res Notes}, Year = {2012}, Pages = {610}, Volume = {5}, Abstract = {Image registration is one of the most important and universally useful computational tasks in biomedical image analysis. A recent article by Xing \& Qiu (IEEE Transactions on Pattern Analysis and Machine Intelligence, 33(10):2081-2092, 2011) is based on an inappropriately narrow conceptualization of the image registration problem as the task of making two images look alike, which disregards whether the established spatial correspondence is plausible. The authors propose a new algorithm, Nonparametric Local Smoothing (NLS) for image registration, but use image similarities alone as a measure of registration performance, although these measures do not relate reliably to the realism of the correspondence map.Using data obtained from its authors, we show experimentally that the method proposed by Xing \& Qiu is not an effective registration algorithm. While it optimizes image similarity, it does not compute accurate, interpretable transformations. Even judged by image similarity alone, the proposed method is consistently outperformed by a simple pixel permutation algorithm, which is known by design not to compute valid registrations.This study has demonstrated that the NLS algorithm proposed recently for image registration, and published in one of the most respected journals in computer science, is not, in fact, an effective registration method at all. Our results also emphasize the general need to apply registration evaluation criteria that are sensitive to whether correspondences are accurate and mappings between images are physically interpretable. These goals cannot be achieved by simply reporting image similarities.}, Doi = {10.1186/1756-0500-5-610}, Institution = {Neuroscience Program, SRI International, 333 Ravenswood Avenue, Menlo Park, CA 94025, USA. rohlfing@ieee.org}, Language = {eng}, Medline-pst = {epublish}, Owner = {stnava}, Pii = {1756-0500-5-610}, Pmid = {23116330}, Timestamp = {2013.05.29} } @Article{Schoenemann2004, Title = {Analysis of chimp-human brain differences via non-rigid deformation of {3D} MR images}, Author = {Schoenemann, P. T. and Avants, B. B. and Gee, J. C. and Glotzer, L. D. and Sheehan, M. J.}, Journal = {Am. J. Phys. Anthropol.}, Year = {2004}, Number = {38}, Pages = {174--175}, ISSN = {0002-9483}, Unique-id = {ISI:000207846400484} } @Article{Schoenemann2007, Title = {Validation of plaster endocast morphology through {3D CT} image analysis}, Author = {Schoenemann, P. Thomas and Gee, James and Avants, Brian and Holloway, Ralph L. and Monge, Janet and Lewis, Jason}, Journal = {Am. J. Phys. Anthropol.}, Year = {2007}, Month = feb, Number = {2}, Pages = {183--192}, Volume = {132}, Doi = {10.1002/ajpa.20499}, ISSN = {0002-9483}, Unique-id = {ISI:000243782700003} } @Article{Schoenemann2007b, Title = {Validation of plaster endocast morphology through 3D CT image analysis.}, Author = {Schoenemann, P Thomas and Gee, James and Avants, Brian and Holloway, Ralph L. and Monge, Janet and Lewis, Jason}, Journal = {Am J Phys Anthropol}, Year = {2007}, Month = {Feb}, Number = {2}, Pages = {183--192}, Volume = {132}, __markedentry = {[stnava:6]}, Abstract = {A crucial component of research on brain evolution has been the comparison of fossil endocranial surfaces with modern human and primate endocrania. The latter have generally been obtained by creating endocasts out of rubber latex shells filled with plaster. The extent to which the method of production introduces errors in endocast replicas is unknown. We demonstrate a powerful method of comparing complex shapes in 3-dimensions (3D) that is broadly applicable to a wide range of paleoanthropological questions. Pairs of virtual endocasts (VEs) created from high-resolution CT scans of corresponding latex/plaster endocasts and their associated crania were rigidly registered (aligned) in 3D space for two Homo sapiens and two Pan troglodytes specimens. Distances between each cranial VE and its corresponding latex/plaster VE were then mapped on a voxel-by-voxel basis. The results show that between 79.7\% and 91.0\% of the voxels in the four latex/plaster VEs are within 2 mm of their corresponding cranial VEs surfaces. The average error is relatively small, and variation in the pattern of error across the surfaces appears to be generally random overall. However, inferior areas around the cranial base and the temporal poles were somewhat overestimated in both human and chimpanzee specimens, and the area overlaying Broca's area in humans was somewhat underestimated. This study gives an idea of the size of possible error inherent in latex/plaster endocasts, indicating the level of confidence we can have with studies relying on comparisons between them and, e.g., hominid fossil endocasts.}, Doi = {10.1002/ajpa.20499}, Institution = {Department of Behavioral Sciences, University of Michigan-Dearborn, Dearborn, MI 48128, USA. ptoms@umd.umich.edu}, Keywords = {Animals; Fossils; Humans; Imaging, Three-Dimensional; Paleontology; Pan troglodytes, anatomy /&/ histology; Skull, anatomy /&/ histology; Tomography, X-Ray Computed, methods}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {17103425}, Timestamp = {2015.02.23}, Url = {http://dx.doi.org/10.1002/ajpa.20499} } @Article{Schoenemann2011, Title = {Differences in endocranial shape between Homo and Pongids assessed through non-rigid deformation analysis of high-resolution {CT} images.}, Author = {Schoenemann, P. Thomas and Holloway, Ralph and Monge, Janet and Avants, Brian and Gee, James}, Journal = {Am. J. Phys. Anthropol.}, Year = {2011}, Note = {80th Annual Meeting of the American-Association-of-Physical-Anthropologists, Minneapolis, MN, APR 11-16, 2011}, Number = {52}, Pages = {265--266}, Volume = {144}, ISSN = {0002-9483}, Organization = {Amer Assoc Phys Anthropol}, Unique-id = {ISI:000288034000754} } @Article{Schoenemann2008, Title = {Endocast asymmetry in pongids assessed via non-rigid deformation analysis of high-resolution {CT} images.}, Author = {Schoenemann, P. T. and Holloway, R. L. and Avants, B. B. and Gee, J. C.}, Journal = {Am. J. Phys. Anthropol.}, Year = {2008}, Note = {77th Annual Meeting of the American-Association-of-Physical-Anthropologists, Columbus, OH, APR 09-12, 2008}, Number = {46}, Pages = {188}, ISSN = {0002-9483}, Organization = {Amer Assoc Phys Anthropol}, Unique-id = {ISI:000253342000576} } @Article{Schoenemann2008a, Title = {The role of micro-morphological stress markers in the differential diagnosis of infectious bone diseases.}, Author = {Schoenemann, P. T. and Holloway, R. L. and AvantS, B. B. and Gee, J. C.}, Journal = {Am. J. Phys. Anthropol.}, Year = {2008}, Note = {77th Annual Meeting of the American-Association-of-Physical-Anthropologists, Columbus, OH, APR 09-12, 2008}, Number = {46}, Pages = {188--189}, ISSN = {0002-9483}, Organization = {Amer Assoc Phys Anthropol}, Unique-id = {ISI:000253342000580} } @Article{Schoenemann2009, Title = {An atlas of modern human cranial morpbology constructed via non-rigid deformation analysis of high-resolution {CT} images.}, Author = {Schoenemann, P. T. and Monge, J. and Avants, B. B. and Gee, J. C.}, Journal = {Am. J. Phys. Anthropol.}, Year = {2009}, Note = {78th Annual Meeting of the American-Association-of-Physical-Anthropologists, Chicago, IL, MAR 31-APR 03, 2009}, Pages = {231}, ISSN = {0002-9483}, Organization = {Amer Assoc Phys Anthropol}, Unique-id = {ISI:000263442701254} } @Article{Schoenemann2007a, Title = {Sex differences in cranial form assessed via non-rigid deformation analysis of high-resolution {CT} images.}, Author = {Schoenemann, P. T. and Monge, J. and Avants, B. B. and Glotzer, D. and Gee, J. C.}, Journal = {Am. J. Phys. Anthropol.}, Year = {2007}, Number = {44}, Pages = {209}, ISSN = {0002-9483}, Unique-id = {ISI:000244656500644} } @Article{Schoenemann2010, Title = {Creating statistical atlases of modern primate endocranial morphology rising non-rigid deformation analysis of high-resolution {CT} images.}, Author = {Schoenemann, P. Thomas and Monge, Janet and Holloway, Ralph L. and Avants, Brian B. and Gee, James C.}, Journal = {Am. J. Phys. Anthropol.}, Year = {2010}, Note = {79th Annual Meeting of the American-Association-of-Physical-Anthropologists, Albuquerque, NM, APR 14-17, 2010}, Number = {50}, Pages = {208--209}, ISSN = {0002-9483}, Organization = {Amer Assoc Phys Anthropol}, Unique-id = {ISI:000275295200695} } @Article{Simon2008a, Title = {Atypical cortical connectivity and visuospatial cognitive impairments are related in children with chromosome 22q11.2 deletion syndrome.}, Author = {Simon, Tony J. and Wu, Zhongle and Avants, Brian and Zhang, Hui and Gee, James C. and Stebbins, Glenn T.}, Journal = {Behav Brain Funct}, Year = {2008}, Pages = {25}, Volume = {4}, Abstract = {Chromosome 22q11.2 deletion syndrome is one of the most common genetic causes of cognitive impairment and developmental disability yet little is known about the neural bases of those challenges. Here we expand upon our previous neurocognitive studies by specifically investigating the hypothesis that changes in neural connectivity relate to cognitive impairment in children with the disorder.Whole brain analyses of multiple measures computed from diffusion tensor image data acquired from the brains of children with the disorder and typically developing controls. We also correlated diffusion tensor data with performance on a visuospatial cognitive task that taps spatial attention.Analyses revealed four common clusters, in the parietal and frontal lobes, that showed complementary patterns of connectivity in children with the deletion and typical controls. We interpreted these results as indicating differences in connective complexity to adjoining cortical regions that are critical to the cognitive functions in which affected children show impairments. Strong, and similarly opposing patterns of correlations between diffusion values in those clusters and spatial attention performance measures considerably strengthened that interpretation.Our results suggest that atypical development of connective patterns in the brains of children with chromosome 22q11.2 deletion syndrome indicate a neuropathology that is related to the visuospatial cognitive impairments that are commonly found in affected individuals.}, Doi = {10.1186/1744-9081-4-25}, Institution = {M,I,N,D, Institute, University of California, Davis, 2825 50th Street, Sacramento, CA 95817, USA. tjsimon@ucdavis.edu.}, Language = {eng}, Medline-pst = {epublish}, Owner = {stnava}, Pii = {1744-9081-4-25}, Pmid = {18559106}, Timestamp = {2013.05.29} } @Article{Song2013, Title = {Using region trajectories to construct an accurate and efficient polyaffine transform model.}, Author = {Song, Gang and Liu, Yang and Wu, Baohua and Avants, Brian and Gee, James C.}, Journal = {Inf Process Med Imaging}, Year = {2013}, Pages = {668--679}, Volume = {23}, __markedentry = {[stnava:1]}, Abstract = {In this paper we propose a novel way to construct a diffeomorphic polyaffine model. Each affine transform is defined on a local region and the resulting diffeomorphism encapsulates all the local transforms by a smooth and invertible displacement field. Compared with traditional weighting schemes used in combining local transforms, our new scheme guarantees that the resulting transform precisely preserves the value of each local affine transform. By introducing the trajectory of local regions instead of using regions themselves, the new approach encodes precisely each local affine transform using a diffeomorphism with one or more stationary velocity fields. Experiments show that our new polyaffine model is both accurate and efficient.}, Keywords = {Algorithms; Computer Simulation; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Models, Biological; Models, Statistical; Reproducibility of Results; Sensitivity and Specificity}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {24684008}, Timestamp = {2014.08.26} } @Article{Song2006a, Title = {Integrated graph cuts for brain {MRI} segmentation.}, Author = {Song, Zhuang and Tustison, Nicholas and Avants, Brian and Gee, James C.}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2006}, Number = {Pt 2}, Pages = {831--838}, Volume = {9}, __markedentry = {[stnava:1]}, Abstract = {Brain MRI segmentation remains a challenging problem in spite of numerous existing techniques. To overcome the inherent difficulties associated with this segmentation problem, we present a new method of information integration in a graph based framework. In addition to image intensity, tissue priors and local boundary information are integrated into the edge weight metrics in the graph. Furthermore, inhomogeneity correction is incorporated by adaptively adjusting the edge weights according to the intermediate inhomogeneity estimation. In the validation experiments of simulated brain MRIs, the proposed method outperformed a segmentation method based on iterated conditional modes (ICM), which is a commonly used optimization method in medical image segmentation. In the experiments of real neonatal brain MRIs, the results of the proposed method have good overlap with the manual segmentations by human experts.}, Institution = {Penn Image Computing and Science Lab, University of Pennsylvania, USA. songz@seas.upenn.edu}, Keywords = {Algorithms; Artificial Intelligence; Brain, anatomy /\&/ histology; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Imaging, Three-Dimensional, methods; Magnetic Resonance Imaging, methods; Pattern Recognition, Automated, methods; Reproducibility of Results; Sensitivity and Specificity}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {17354850}, Timestamp = {2013.05.29} } @Article{Sun2008, Title = {Cardiac medial modeling and time-course heart wall thickness analysis.}, Author = {Sun, Hui and Avants, Brian B. and Frangi, Alejandro F. and Sukno, Federico and Geel, James C. and Yushkevich, Paul A.}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2008}, Number = {Pt 2}, Pages = {766--773}, Volume = {11}, __markedentry = {[stnava:1]}, Abstract = {The medial model is a powerful shape representation method that models a 3D object by explicitly defining its skeleton (medial axis) and deriving the boundary geometry according to medial geometry. It has been recently extended to model complex shapes with multi-figures, i.e., shapes whose skeletons can not be described by a single sheet in 3D. This paper applied the medial model to a 2-chamber heart data set consisting of 428 cardiac shapes from 90 subjects. The results show that the medial model can capture the heart shape accurately. To demonstrate the usage of the medial model, the changes of the heart wall thickness over time are analyzed. We calculated the mean heart wall thickness map of 90 subjects for different phases of the cardiac cycle, as well as the mean thickness change between phases.}, Institution = {Department of Radiology, University of Pennsylvania, Philadelphia, PA, USA.}, Keywords = {Algorithms; Computer Simulation; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Imaging, Three-Dimensional, methods; Magnetic Resonance Imaging, methods; Models, Cardiovascular; Myocardium, pathology; Reproducibility of Results; Sensitivity and Specificity; Subtraction Technique}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {18982674}, Timestamp = {2013.05.29} } @Article{Sundaram2005, Title = {Towards a dynamic model of pulmonary parenchymal deformation: evaluation of methods for temporal reparameterization of lung data.}, Author = {Sundaram, Tessa A. and Avants, Brian B. and Gee, James C.}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2005}, Number = {Pt 2}, Pages = {328--335}, Volume = {8}, Abstract = {We approach the problem of temporal reparameterization of dynamic sequences of lung MR images. In earlier work, we employed capacity-based reparameterization to co-register temporal sequences of 2-D coronal images of the human lungs. Here, we extend that work to the evaluation of a ventilator-acquired 3-D dataset from a normal mouse. Reparameterization according to both deformation and lung volume is evaluated. Both measures provide results that closely approximate normal physiological behavior, as judged from the original data. Our ultimate goal is to be able to characterize normal parenchymal biomechanics over a population of healthy individuals, and to use this statistical model to evaluate lung deformation under various pathological states.}, Institution = {University of Pennsylvania, Philadelphia PA 19104, USA.}, Keywords = {Algorithms; Animals; Computer Simulation; Databases, Factual; Elasticity; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Imaging, Three-Dimensional, methods; Lung, anatomy /\&/ histology/physiology; Magnetic Resonance Imaging, methods; Mice; Models, Biological; Reproducibility of Results; Respiratory Mechanics; Sensitivity and Specificity; Subtraction Technique}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {16685976}, Timestamp = {2013.05.29} } @InProceedings{Sundaram2004, Title = {A dynamic model of average lung deformation using capacity-based reparameterization and shape averaging of lung MR images}, Author = {Sundaram, TA and Avants, BB and Gee, JC}, Booktitle = {MEDICAL IMAGE COMPUTING AND COMPUTER-ASSISTED INTERVENTION - MICCAI 2004, PT 2, PROCEEDINGS}, Year = {2004}, Editor = {Barillot, C and Haynor, DR and Hellier, P}, Note = {7th International Conference on Medical Image Computing and Computer-Assisted Intervention (MICCAI 2004), St Malo, FRANCE, SEP 26-29, 2004}, Number = {2}, Organization = {IRISA; CNRS; Univ Rennes}, Pages = {1000--1007}, Series = {LECTURE NOTES IN COMPUTER SCIENCE}, Volume = {3217}, __markedentry = {[stnava:1]}, ISBN = {3-540-22977-9}, ISSN = {0302-9743}, Unique-id = {ISI:000224322400121} } @Article{Tustison2011, Title = {MULTIVARIATE ANALYSIS OF DIFFUSION TENSOR IMAGING {AND} CORTICAL THICKNESS {MAP}S IN A TRAUMATIC BRAIN INJURY (TBI) COHORT USING ADVANCED {NOR}MALIZATION TOOLS (ANTS)}, Author = {Tustison, Nicholas and Avants, Brian and Cook, Philip and Kim, Junghoon and Whyte, John and Gee, James and Ahlers, Stephen and Stone, James}, Journal = {J. Neurotrauma}, Year = {2011}, Month = jun, Note = {29th Annual National Neurotrauma Symposium, Hollywood Beach, FL, JUL 10-13, 2011}, Number = {6}, Pages = {A111}, Volume = {28}, ISSN = {0897-7151}, Unique-id = {ISI:000292457600346} } @Article{Tustison2013, Title = {Explicit B-spline regularization in diffeomorphic image registration.}, Author = {Tustison, Nicholas J. and Avants, Brian B.}, Journal = {Front Neuroinform}, Year = {2013}, Pages = {39}, Volume = {7}, Abstract = {Diffeomorphic mappings are central to image registration due largely to their topological properties and success in providing biologically plausible solutions to deformation and morphological estimation problems. Popular diffeomorphic image registration algorithms include those characterized by time-varying and constant velocity fields, and symmetrical considerations. Prior information in the form of regularization is used to enforce transform plausibility taking the form of physics-based constraints or through some approximation thereof, e.g., Gaussian smoothing of the vector fields [a la Thirion's Demons (Thirion, 1998)]. In the context of the original Demons' framework, the so-called directly manipulated free-form deformation (DMFFD) (Tustison et al., 2009) can be viewed as a smoothing alternative in which explicit regularization is achieved through fast B-spline approximation. This characterization can be used to provide B-spline "flavored" diffeomorphic image registration solutions with several advantages. Implementation is open source and available through the Insight Toolkit and our Advanced Normalization Tools (ANTs) repository. A thorough comparative evaluation with the well-known SyN algorithm (Avants et al., 2008), implemented within the same framework, and its B-spline analog is performed using open labeled brain data and open source evaluation tools.}, Doi = {10.3389/fninf.2013.00039}, Institution = {Penn Image Computing and Science Laboratory, Department of Radiology, University of Pennsylvania Philadelphia, PA, USA.}, Language = {eng}, Medline-pst = {epublish}, Owner = {stnava}, Pmid = {24409140}, Timestamp = {2014.08.26} } @Article{Tustison2012, Title = {Logical circularity in voxel-based analysis: Normalization strategy may induce statistical bias.}, Author = {Tustison, Nicholas J. and Avants, Brian B. and Cook, Philip A. and Kim, Junghoon and Whyte, John and Gee, James C. and Stone, James R.}, Journal = {Hum. Brain Mapp.}, Year = {2012}, Month = nov, Abstract = {Recent discussions within the neuroimaging community have highlighted the problematic presence of selection bias in experimental design. Although initially centering on the selection of voxels during the course of fMRI studies, we demonstrate how this bias can potentially corrupt voxel-based analyses. For such studies, template-based registration plays a critical role in which a representative template serves as the normalized space for group alignment. A standard approach maps each subject's image to a representative template before performing statistical comparisons between different groups. We analytically demonstrate that in these scenarios the popular sum of squared difference (SSD) intensity metric, implicitly surrogating as a quantification of anatomical alignment, instead explicitly maximizes effect size-an experimental design flaw referred to as "circularity bias." We illustrate how this selection bias varies in strength with the similarity metric used during registration under the hypothesis that while SSD-related metrics, such as Demons, will manifest similar effects, other metrics which are not formulated based on absolute intensity differences will produce less of an effect. Consequently, given the variability in voxel-based analysis outcomes with similarity metric choice, we caution researchers specifically in the use of SSD and SSD-related measures where normalization and statistical analysis involve the same image set. Instead, we advocate a more cautious approach where normalization of the individual subject images to the reference space occurs through corresponding image sets which are independent of statistical testing. Alternatively, one can use similarity terms that are less sensitive to this bias. Hum Brain Mapp, 2012. {\copyright} 2012 Wiley Periodicals, Inc.}, Doi = {10.1002/hbm.22211}, Institution = {Department of Radiology and Medical Imaging, University of Virginia, Charlottesville, Virginia. ntustison@virginia.edu.}, Language = {eng}, Medline-pst = {aheadofprint}, Owner = {stnava}, Pmid = {23151955}, Timestamp = {2013.05.29} } @Article{Tustison2010, Title = {N4ITK: improved N3~{b}ias correction.}, Author = {Tustison, Nicholas J. and Avants, Brian B. and Cook, Philip A. and Zheng, Yuanjie and Egan, Alexander and Yushkevich, Paul A. and Gee, James C.}, Journal = {IEEE Trans Med Imaging}, Year = {2010}, Month = jun, Number = {6}, Pages = {1310--1320}, Volume = {29}, Abstract = {A variant of the popular nonparametric nonuniform intensity normalization (N3) algorithm is proposed for bias field correction. Given the superb performance of N3 and its public availability, it has been the subject of several evaluation studies. These studies have demonstrated the importance of certain parameters associated with the B-spline least-squares fitting. We propose the substitution of a recently developed fast and robust B-spline approximation routine and a modified hierarchical optimization scheme for improved bias field correction over the original N3 algorithm. Similar to the N3 algorithm, we also make the source code, testing, and technical documentation of our contribution, which we denote as "N4ITK," available to the public through the Insight Toolkit of the National Institutes of Health. Performance assessment is demonstrated using simulated data from the publicly available Brainweb database, hyperpolarized (3)He lung image data, and 9.4T postmortem hippocampus data.}, Doi = {10.1109/TMI.2010.2046908}, Institution = {Department of Radiology, University of Pennsylvania, Philadelphia, PA 19140, USA. ntustison@wustl.edu}, Keywords = {Algorithms; Artifacts; Brain, anatomy /&/ histology; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Magnetic Resonance Imaging, methods; Reproducibility of Results; Sensitivity and Specificity}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {20378467}, Timestamp = {2014.08.26} } @Article{Tustison2010a, Title = {N4ITK: improved N3 bias correction.}, Author = {Tustison, Nicholas J. and Avants, Brian B. and Cook, Philip A. and Zheng, Yuanjie and Egan, Alexander and Yushkevich, Paul A. and Gee, James C.}, Journal = {IEEE Trans Med Imaging}, Year = {2010}, Month = {Jun}, Number = {6}, Pages = {1310--1320}, Volume = {29}, __markedentry = {[stnava:6]}, Abstract = {A variant of the popular nonparametric nonuniform intensity normalization (N3) algorithm is proposed for bias field correction. Given the superb performance of N3 and its public availability, it has been the subject of several evaluation studies. These studies have demonstrated the importance of certain parameters associated with the B-spline least-squares fitting. We propose the substitution of a recently developed fast and robust B-spline approximation routine and a modified hierarchical optimization scheme for improved bias field correction over the original N3 algorithm. Similar to the N3 algorithm, we also make the source code, testing, and technical documentation of our contribution, which we denote as "N4ITK," available to the public through the Insight Toolkit of the National Institutes of Health. Performance assessment is demonstrated using simulated data from the publicly available Brainweb database, hyperpolarized (3)He lung image data, and 9.4T postmortem hippocampus data.}, Doi = {10.1109/TMI.2010.2046908}, Institution = {Department of Radiology, University of Pennsylvania, Philadelphia, PA 19140, USA. ntustison@wustl.edu}, Keywords = {Algorithms; Artifacts; Brain, anatomy /&/ histology; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Magnetic Resonance Imaging, methods; Reproducibility of Results; Sensitivity and Specificity}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {20378467}, Timestamp = {2015.02.23}, Url = {http://dx.doi.org/10.1109/TMI.2010.2046908} } @Article{Tustison2011a, Title = {Ventilation-Based Segmentation of the Lungs Using Hyperpolarized He-3 {MRI}}, Author = {Tustison, Nicholas J. and Avants, Brian B. and Flors, Lucia and Altes, Talissa A. and de lange, Eduard E. and Mugler, III, John P. and Gee, James C.}, Journal = {J. Magn. Reson. Imaging}, Year = {2011}, Month = oct, Number = {4}, Pages = {831--841}, Volume = {34}, Doi = {10.1002/jmri.22738}, ISSN = {1053-1807}, Orcid-numbers = {Mugler, John/0000-0002-4140-308X}, Researcherid-numbers = {Mugler, John/B-9432-2013}, Unique-id = {ISI:000295394400012} } @Article{Tustison2011c, Title = {Ventilation-based segmentation of the lungs using hyperpolarized (3)He MRI.}, Author = {Tustison, Nicholas J. and Avants, Brian B. and Flors, Lucia and Altes, Talissa A. and {de Lange}, Eduard E. and Mugler, 3rd, John P and Gee, James C.}, Journal = {J Magn Reson Imaging}, Year = {2011}, Month = {Oct}, Number = {4}, Pages = {831--841}, Volume = {34}, __markedentry = {[stnava:6]}, Abstract = {To develop an automated segmentation method to differentiate the ventilated lung volume on (3) He magnetic resonance imaging (MRI).Computational processing (CP) for each subject consisted of the following three essential steps: 1) inhomogeneity bias correction, 2) whole lung segmentation, and 3) subdivision of the lung segmentation into regions of similar ventilation. Evaluation consisted of two comparative analyses: i) comparison of the number of defects scored by two human readers in 43 subjects, and ii) simultaneous truth and performance level estimation (STAPLE) in 18 subjects in which the ventilation defects were manually segmented by four human readers.There was excellent correlation between the number of ventilation defects tabulated by CP and reader #1 (intraclass correlation coefficient [ICC] = 0.86), CP and reader #2 (ICC = 0.85), and between the two readers (ICC = 0.97). The STAPLE results from the second analysis yielded the following sensitivity/specificity numbers: CP (0.898/0.905), radiologist #1 (0.743/0.897), radiologist #2 (0.501/0.985), radiologist #3 (0.898/0.848), and the first author (0.600/0.984).We developed and evaluated an automated method for quantifying the ventilated lung volume on (3) He MRI. The findings strongly indicate that our proposed algorithmic processing may be a reliable, automatic method for quantitating ventilation defects.}, Doi = {10.1002/jmri.22738}, Institution = {Department of Radiology and Medical Imaging, University of Virginia, Charlottesville, Virginia, USA. ntustison@virginia.edu}, Keywords = {Administration, Inhalation; Asthma, diagnosis; Automation; Case-Control Studies; Cystic Fibrosis, diagnosis; Female; Helium, diagnostic use; Humans; Image Processing, Computer-Assisted; Lung, pathology; Magnetic Resonance Imaging, methods; Male; Pulmonary Gas Exchange, physiology; Pulmonary Ventilation, physiology; Sensitivity and Specificity}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {21837781}, Timestamp = {2015.02.23}, Url = {http://dx.doi.org/10.1002/jmri.22738} } @Article{Tustison2009, Title = {Directly Manipulated Free-Form Deformation Image Registration}, Author = {Tustison, Nicholas J. and Avants, Brian B. and Gee, James C.}, Journal = {IEEE Trans Image Process}, Year = {2009}, Month = mar, Number = {3}, Pages = {624--635}, Volume = {18}, Doi = {10.1109/TIP.2008.2010072}, ISSN = {1057-7149}, Unique-id = {ISI:000263604700014} } @Article{Tustison2011b, Title = {Topological Well-Composedness and Glamorous Glue: A Digital Gluing Algorithm for Topologically Constrained Front Propagation}, Author = {Tustison, Nicholas J. and Avants, Brian B. and Siqueira, Marcelo and Gee, James C.}, Journal = {IEEE Trans Image Process}, Year = {2011}, Month = jun, Number = {6}, Pages = {1756--1761}, Volume = {20}, Doi = {10.1109/TIP.2010.2095021}, ISSN = {1057-7149}, Unique-id = {ISI:000290732600023} } @InProceedings{Tustison2006, Title = {A generalization of Free-Form Deformation image registration within the ITK finite element framework}, Author = {Tustison, Nicholas J. and Avants, Brian B. and Sundaram, Tessa A. and Duda, Jeffrey T. and Gee, James C.}, Booktitle = {BIOMEDICAL IMAGE REGISTRATION, PROCEEDINGS}, Year = {2006}, Editor = {Pluim, JPW and Likar, B and Gerritsen, FA}, Note = {3rd International Workshop on Biomedical Image Registration, Utrecht Univ, Utrecht, NETHERLANDS, JUL 09-11, 2006}, Organization = {Philips Med Syst}, Pages = {238--246}, Series = {LECTURE NOTES IN COMPUTER SCIENCE}, Volume = {4057}, __markedentry = {[stnava:1]}, ISBN = {3-540-35648-7}, ISSN = {0302-9743}, Unique-id = {ISI:000239485200029} } @Article{Tustison2014, Title = {Large-scale evaluation of ANTs and FreeSurfer cortical thickness measurements.}, Author = {Tustison, Nicholas J. and Cook, Philip A. and Klein, Arno and Song, Gang and Das, Sandhitsu R. and Duda, Jeffrey T. and Kandel, Benjamin M. and {van Strien}, Niels and Stone, James R. and Gee, James C. and Avants, Brian B.}, Journal = {Neuroimage}, Year = {2014}, Month = oct, Pages = {166--179}, Volume = {99}, Abstract = {Many studies of the human brain have explored the relationship between cortical thickness and cognition, phenotype, or disease. Due to the subjectivity and time requirements in manual measurement of cortical thickness, scientists have relied on robust software tools for automation which facilitate the testing and refinement of neuroscientific hypotheses. The most widely used tool for cortical thickness studies is the publicly available, surface-based FreeSurfer package. Critical to the adoption of such tools is a demonstration of their reproducibility, validity, and the documentation of specific implementations that are robust across large, diverse imaging datasets. To this end, we have developed the automated, volume-based Advanced Normalization Tools (ANTs) cortical thickness pipeline comprising well-vetted components such as SyGN (multivariate template construction), SyN (image registration), N4 (bias correction), Atropos (n-tissue segmentation), and DiReCT (cortical thickness estimation). In this work, we have conducted the largest evaluation of automated cortical thickness measures in publicly available data, comparing FreeSurfer and ANTs measures computed on 1205 images from four open data sets (IXI, MMRR, NKI, and OASIS), with parcellation based on the recently proposed Desikan-Killiany-Tourville (DKT) cortical labeling protocol. We found good scan-rescan repeatability with both FreeSurfer and ANTs measures. Given that such assessments of precision do not necessarily reflect accuracy or an ability to make statistical inferences, we further tested the neurobiological validity of these approaches by evaluating thickness-based prediction of age and gender. ANTs is shown to have a higher predictive performance than FreeSurfer for both of these measures. In promotion of open science, we make all of our scripts, data, and results publicly available which complements the use of open image data sets and the open source availability of the proposed ANTs cortical thickness pipeline.}, Doi = {10.1016/j.neuroimage.2014.05.044}, Institution = {Penn Image Computing and Science Laboratory, University of Pennsylvania, Philadelphia, PA, USA.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pii = {S1053-8119(14)00409-1}, Pmid = {24879923}, Timestamp = {2014.10.27} } @Article{Tustison2013a, Title = {Instrumentation bias in the use and evaluation of scientific software: recommendations for reproducible practices in the computational sciences.}, Author = {Tustison, Nicholas J. and Johnson, Hans J. and Rohlfing, Torsten and Klein, Arno and Ghosh, Satrajit S. and Ibanez, Luis and Avants, Brian B.}, Journal = {Front Neurosci}, Year = {2013}, Pages = {162}, Volume = {7}, Doi = {10.3389/fnins.2013.00162}, Institution = {Department of Radiology and Medical Imaging, University of Virginia Charlottesville, VA, USA.}, Language = {eng}, Medline-pst = {epublish}, Owner = {stnava}, Pmid = {24058331}, Timestamp = {2014.08.26} } @Article{Tustison2014a, Title = {Optimal Symmetric Multimodal Templates and Concatenated Random Forests for Supervised Brain Tumor Segmentation (Simplified) with ANTsR.}, Author = {Tustison, Nicholas J. and Shrinidhi, K. L. and Wintermark, Max and Durst, Christopher R. and Kandel, Benjamin M. and Gee, James C. and Grossman, Murray C. and Avants, Brian B.}, Journal = {Neuroinformatics}, Year = {2014}, Month = {Nov}, __markedentry = {[stnava:]}, Abstract = {Segmenting and quantifying gliomas from MRI is an important task for diagnosis, planning intervention, and for tracking tumor changes over time. However, this task is complicated by the lack of prior knowledge concerning tumor location, spatial extent, shape, possible displacement of normal tissue, and intensity signature. To accommodate such complications, we introduce a framework for supervised segmentation based on multiple modality intensity, geometry, and asymmetry feature sets. These features drive a supervised whole-brain and tumor segmentation approach based on random forest-derived probabilities. The asymmetry-related features (based on optimal symmetric multimodal templates) demonstrate excellent discriminative properties within this framework. We also gain performance by generating probability maps from random forest models and using these maps for a refining Markov random field regularized probabilistic segmentation. This strategy allows us to interface the supervised learning capabilities of the random forest model with regularized probabilistic segmentation using the recently developed ANTsR package-a comprehensive statistical and visualization interface between the popular Advanced Normalization Tools (ANTs) and the R statistical project. The reported algorithmic framework was the top-performing entry in the MICCAI 2013 Multimodal Brain Tumor Segmentation challenge. The challenge data were widely varying consisting of both high-grade and low-grade glioma tumor four-modality MRI from five different institutions. Average Dice overlap measures for the final algorithmic assessment were 0.87, 0.78, and 0.74 for "complete", "core", and "enhanced" tumor components, respectively.}, Doi = {10.1007/s12021-014-9245-2}, Institution = {Department of Radiology and Medical Imaging, University of Virginia, Charlottesville, VA, USA, ntustison@virginia.edu.}, Language = {eng}, Medline-pst = {aheadofprint}, Owner = {stnava}, Pmid = {25433513}, Timestamp = {2015.02.08}, Url = {http://dx.doi.org/10.1007/s12021-014-9245-2} } @Article{Wang2010a, Title = {Estimation of Perfusion and Arterial Transit Time in Myocardium Using Free-Breathing Myocardial Arterial Spin Labeling With Navigator-Echo}, Author = {Wang, Danny J. J. and Bi, Xiaoming and Avants, Brian B. and Meng, Tongbai and Zuehlsdorff, Sven and Detre, John A.}, Journal = {Magn. Reson. Med.}, Year = {2010}, Month = nov, Number = {5}, Pages = {1289--1295}, Volume = {64}, Doi = {10.1002/mrm.22630}, ISSN = {0740-3194}, Unique-id = {ISI:000283616900008} } @Article{Wang2010, Title = {Standing on the shoulders of giants: improving medical image segmentation via bias correction.}, Author = {Wang, Hongzhi and Das, Sandhitsu and Pluta, John and Craige, Caryne and Altinay, Murat and Avants, Brian and Weiner, Michael and Mueller, Susanne and Yushkevich, Paul}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2010}, Number = {Pt 3}, Pages = {105--112}, Volume = {13}, __markedentry = {[stnava:1]}, Abstract = {We propose a simple strategy to improve automatic medical image segmentation. The key idea is that without deep understanding of a segmentation method, we can still improve its performance by directly calibrating its results with respect to manual segmentation. We formulate the calibration process as a bias correction problem, which is addressed by machine learning using training data. We apply this methodology on three segmentation problems/methods and show significant improvements for all of them.}, Institution = {Department of Radiology, University of Pennsylvania, USA.}, Keywords = {Algorithms; Artifacts; Brain, anatomy /\&/ histology; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Magnetic Resonance Imaging, methods; Pattern Recognition, Automated, methods; Reproducibility of Results; Sensitivity and Specificity}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {20879389}, Timestamp = {2013.05.29} } @Article{Wang2011, Title = {A learning-based wrapper method to correct systematic errors in automatic image segmentation: Consistently improved performance in hippocampus, cortex and brain segmentation}, Author = {Wang, Hongzhi and Das, Sandhitsu R. and Suh, Jung Wook and Altinay, Murat and Pluta, John and Craige, Caryne and Avants, Brian and Yushkevich, Paul A. and Alzheimers Dis Neuroimaging Initia}, Journal = {Neuroimage}, Year = {2011}, Month = apr, Number = {3}, Pages = {968--985}, Volume = {55}, Doi = {10.1016/j.neuroimage.2011.01.006}, ISSN = {1053-8119}, Unique-id = {ISI:000288313800012} } @Article{Weber2013, Title = {Reproducibility of functional network metrics and network structure: a comparison of task-related BOLD, resting ASL with BOLD contrast, and resting cerebral blood flow.}, Author = {Weber, Matthew J. and Detre, John A. and Thompson-Schill, Sharon L. and Avants, Brian B.}, Journal = {Cogn. Affect. Behav. Neurosci.}, Year = {2013}, Month = sep, Number = {3}, Pages = {627--640}, Volume = {13}, Abstract = {Network analysis is an emerging approach to functional connectivity in which the brain is construed as a graph and its connectivity and information processing estimated by mathematical characterizations of graphs. There has been little to no work examining the reproducibility of network metrics derived from different types of functional magnetic resonance imaging (fMRI) data (e.g., resting vs. task related, or pulse sequences other than standard blood oxygen level dependent [BOLD] data) or of measures of network structure at levels other than summary statistics. Here, we take up these questions, comparing the reproducibility of graphs derived from resting arterial spin-labeling perfusion fMRI with those derived from BOLD scans collected while the participant was performing a task. We also examine the reproducibility of the anatomical connectivity implied by the graph by investigating test-retest consistency of the graphs' edges. We compare two measures of graph-edge consistency both within versus between subjects and across data types. We find a dissociation in the reproducibility of network metrics, with metrics from resting data most reproducible at lower frequencies and metrics from task-related data most reproducible at higher frequencies; that same dissociation is not recapitulated, however, in network structure, for which the task-related data are most consistent at all frequencies. Implications for the practice of network analysis are discussed.}, Doi = {10.3758/s13415-013-0181-7}, Institution = {Department of Psychology, University of Pennsylvania, Philadelphia, PA, USA, mweb@psych.upenn.edu.}, Keywords = {Adult; Brain Mapping, methods; Brain, blood supply/physiology; Cerebrovascular Circulation, physiology; Female; Humans; Magnetic Resonance Imaging, methods; Male; Reproducibility of Results; Rest, physiology; Time Factors; Young Adult}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {23813017}, Timestamp = {2014.08.26} } @Article{Xie2014, Title = {Automatic clustering and thickness measurement of anatomical variants of the human perirhinal cortex.}, Author = {Xie, Long and Pluta, John and Wang, Hongzhi and Das, Sandhitsu R. and Mancuso, Lauren and Kliot, Dasha and Avants, Brian B. and Ding, Song-Lin and Wolk, David A. and Yushkevich, Paul A.}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2014}, Number = {Pt 3}, Pages = {81--88}, Volume = {17}, __markedentry = {[stnava:1]}, Abstract = {The entorhinal cortex (ERC) and the perirhinal cortex (PRC) are subregions of the medial temporal lobe (MTL) that play important roles in episodic memory representations, as well as serving as a conduit between other neocortical areas and the hippocampus. They are also the sites where neuronal damage first occurs in Alzheimer's disease (AD). The ability to automatically quantify the volume and thickness of the ERC and PRC is desirable because these localized measures can potentially serve as better imaging biomarkers for AD and other neurodegenerative diseases. However, large anatomical variation in the PRC makes it a challenging area for analysis. In order to address this problem, we propose an automatic segmentation, clustering, and thickness measurement approach that explicitly accounts for anatomical variation. The approach is targeted to highly anisotropic (0.4x0.4x2.0mm3 ) T2-weighted MRI scans that are preferred by many authors for detailed imaging of the MTL, but which pose challenges for segmentation and shape analysis. After automatically labeling MTL substructures using multi-atlas segmentation, our method clusters subjects into groups based on the shape of the PRC, constructs unbiased population templates for each group, and uses the smooth surface representations obtained during template construction to extract regional thickness measurements in the space of each subject. The proposed thickness measures are evaluated in the context of discrimination between patients with Mild Cognitive Impairment (MCI) and normal controls (NC).}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {25320785}, Timestamp = {2014.11.01} } @Article{Yushkevich2010, Title = {Bias in estimation of hippocampal atrophy using deformation-based morphometry arises from asymmetric global normalization: An illustration in ADNI 3~{T MRI} data}, Author = {Yushkevich, Paul A. and Avants, Brian B. and Das, Sandhitsu R. and Pluta, John and Altinay, Murat and Craige, Caryne and Alzheimer's Dis Neuroimaging Initi}, Journal = {Neuroimage}, Year = {2010}, Month = apr, Number = {2}, Pages = {434--445}, Volume = {50}, Doi = {10.1016/j.neuroimage.2009.12.007}, ISSN = {1053-8119}, Unique-id = {ISI:000274948400009} } @InProceedings{Yushkevich2006, Title = {{3D} mouse brain reconstruction from histology using a coarse-to-fine approach}, Author = {Yushkevich, Paul A. and Avants, Brian B. and Ng, Lydia and Hawrylycz, Michael and Burstein, Pablo D. and Zhang, Hui and Gee, James C.}, Booktitle = {BIOMEDICAL IMAGE REGISTRATION, PROCEEDINGS}, Year = {2006}, Editor = {Pluim, JPW and Likar, B and Gerritsen, FA}, Note = {3rd International Workshop on Biomedical Image Registration, Utrecht Univ, Utrecht, NETHERLANDS, JUL 09-11, 2006}, Organization = {Philips Med Syst}, Pages = {230--237}, Series = {LECTURE NOTES IN COMPUTER SCIENCE}, Volume = {4057}, __markedentry = {[stnava:1]}, ISBN = {3-540-35648-7}, ISSN = {0302-9743}, Unique-id = {ISI:000239485200028} } @Article{Yushkevich2009, Title = {A high-resolution computational atlas of the human hippocampus from postmortem magnetic resonance imaging at 9.4~{T}}, Author = {Yushkevich, Paul A. and Avants, Brian B. and Pluta, John and Das, Sandhitsu and Minkoff, David and Mechanic-Hamilton, Dawn and Glynn, Simon and Pickup, Stephen and Liu, Weixia and Gee, James C. and Grossman, Murray and Detre, John A.}, Journal = {Neuroimage}, Year = {2009}, Month = jan, Number = {2}, Pages = {385--398}, Volume = {44}, Doi = {10.1016/j.neuroimage.2008.08.042}, ISSN = {1053-8119}, Unique-id = {ISI:000262301100010} } @Article{Yushkevich2008, Title = {Shape-based alignment of hippocampal subfields: evaluation in postmortem {MRI}.}, Author = {Yushkevich, Paul A. and Avants, Brian B. and Pluta, John and Minkoff, David and Detre, John A. and Grossman, Murray and Gee, James C.}, Journal = {Med Image Comput Comput Assist Interv}, Year = {2008}, Number = {Pt 1}, Pages = {510--517}, Volume = {11}, __markedentry = {[stnava:1]}, Abstract = {This paper estimates the accuracy of hippocampal subfield alignment via shape-based normalization. Evaluation takes place in postmortem MRI dataset acquired at 9.4 Tesla with many averages and approximately 0.01 mm3 voxel resolution. Continuous medial representations (cm-reps) are used to establish geometrical correspondences between hippocampal formations in different images; the extent to which these correspondences match up subfields is evaluated and compared to normalization driven by image forces. Shape-based normalization is shown to perform only slightly worse than image-based normalization; this is encouraging because the former is more applicable to in vivo MRI, which typically lacks features that distinguish hippocampal subfields.}, Institution = {Department of Radiology, University of Pennsylvania, USA.}, Keywords = {Algorithms; Artificial Intelligence; Cadaver; Computer Simulation; Hippocampus, anatomy /\&/ histology; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Information Storage and Retrieval, methods; Magnetic Resonance Imaging, methods; Models, Biological; Models, Statistical; Pattern Recognition, Automated, methods; Reproducibility of Results; Sensitivity and Specificity; Subtraction Technique}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {18979785}, Timestamp = {2013.05.29} } @Article{Yushkevich2012, Title = {From label fusion to correspondence fusion: a new approach to unbiased groupwise registration.}, Author = {Yushkevich, Paul A. and Wang, Hongzhi and Pluta, John and Avants, Brian B.}, Journal = {Proc IEEE Comput Soc Conf Comput Vis Pattern Recognit}, Year = {2012}, Pages = {956--963}, __markedentry = {[stnava:1]}, Abstract = {Label fusion strategies are used in multi-atlas image segmentation approaches to compute a consensus segmentation of an image, given a set of candidate segmentations produced by registering the image to a set of atlases [19, 11, 8]. Effective label fusion strategies, such as local similarity-weighted voting [1, 13] substantially reduce segmentation errors compared to single-atlas segmentation. This paper extends the label fusion idea to the problem of finding correspondences across a set of images. Instead of computing a consensus segmentation, weighted voting is used to estimate a consensus coordinate map between a target image and a reference space. Two variants of the problem are considered: (1) where correspondences between a set of atlases are known and are propagated to the target image; (2) where correspondences are estimated across a set of images without prior knowledge. Evaluation in synthetic data shows that correspondences recovered by fusion methods are more accurate than those based on registration to a population template. In a 2D example in real MRI data, fusion methods result in more consistent mappings between manual segmentations of the hippocampus.}, Doi = {10.1109/CVPR.2012.6247771}, Institution = {Department of Radiology, University of Pennsylvania, Philadelphia, USA.}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {24457950}, Timestamp = {2014.08.26} } @Article{Yushkevich2010a, Title = {Nearly automatic segmentation of hippocampal subfields in in vivo focal T2-weighted {MRI}}, Author = {Yushkevich, Paul A. and Wang, Hongzhi and Pluta, John and Das, Sandhitsu R. and Craige, Caryne and Avants, Brian B. and Weiner, Michael W. and Mueller, Susanne}, Journal = {Neuroimage}, Year = {2010}, Month = dec, Number = {4}, Pages = {1208--1224}, Volume = {53}, Doi = {10.1016/j.neuroimage.2010.06.040}, ISSN = {1053-8119}, Unique-id = {ISI:000282165800004} } @Article{Zhang2007a, Title = {High-dimensional spatial normalization of diffusion tensor images improves the detection of white matter differences: an example study using amyotrophic lateral sclerosis.}, Author = {Zhang, Hui and Avants, Brian B. and Yushkevich, Paul A. and Woo, John H. and Wang, Sumei and McCluskey, Leo F. and Elman, Lauren B. and Melhem, Elias R. and Gee, James C.}, Journal = {IEEE Trans Med Imaging}, Year = {2007}, Month = nov, Number = {11}, Pages = {1585--1597}, Volume = {26}, Abstract = {Spatial normalization of diffusion tensor images plays a key role in voxel-based analysis of white matter (WM) group differences. Currently, it has been achieved using low-dimensional registration methods in the large majority of clinical studies. This paper aims to motivate the use of high-dimensional normalization approaches by generating evidence of their impact on the findings of such studies. Using an ongoing amyotrophic lateral sclerosis (ALS) study, we evaluated three normalization methods representing the current range of available approaches: low-dimensional normalization using the fractional anisotropy (FA), high-dimensional normalization using the FA, and high-dimensional normalization using full tensor information. Each method was assessed in terms of its ability to detect significant differences between ALS patients and controls. Our findings suggest that inadequate normalization with low-dimensional approaches can result in insufficient removal of shape differences which in turn can confound FA differences in a complex manner, and that utilizing high-dimensional normalization can both significantly minimize the confounding effect of shape differences to FA differences and provide a more complete description of WM differences in terms of both size and tissue architecture differences. We also found that high-dimensional approaches, by leveraging full tensor features instead of tensor-derived indices, can further improve the alignment of WM tracts.}, Doi = {10.1109/TMI.2007.906784}, Institution = {Penn Image Computing and Science Laboratory, University of Pennsylvania, Philadelphia, PA 19104, USA.}, Keywords = {Adult; Aged; Algorithms; Amyotrophic Lateral Sclerosis, pathology; Artificial Intelligence; Brain, pathology; Diffusion Magnetic Resonance Imaging, methods; Female; Humans; Image Enhancement, methods; Image Interpretation, Computer-Assisted, methods; Imaging, Three-Dimensional, methods; Male; Middle Aged; Nerve Fibers, Myelinated, pathology; Pattern Recognition, Automated, methods; Reproducibility of Results; Sensitivity and Specificity}, Language = {eng}, Medline-pst = {ppublish}, Owner = {stnava}, Pmid = {18041273}, Timestamp = {2013.05.29} } ants-2.2.0/forhtml/ants_progress_report.Rmd000066400000000000000000000165371311104306400210700ustar00rootroot00000000000000--- title: Mulivariate analysis of neural development in GSE$^\dagger$ adolescents with ANTs author: "Brian B. Avants et al." date: "March 16, 2015" output: html_document bibliography: ants.bib --- # Specific Aims The overall specific aims have not changed. $\dagger$: Gestational substance exposure # Studies and Results We summarize current results in the context of the original aims. ## Aim 1 Aim 1 proposed to develop and evaluate a publicly available computational tool for template construction and image registration of multiple modality datasets. The links below are available from the root ANTs website [http://stnava.github.io/ANTs/](http://stnava.github.io/ANTs/) or the ANTsR website [http://stnava.github.io/ANTs/](http://stnava.github.io/ANTs/): **Tutorial materials:** We have contributed several different examples for template construction and related analysis with ANTs. * Template creation: * [high-level guide](http://miykael.github.io/nipype-beginner-s-guide/ANTS.html) * [reproducible examples in article form](http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3065962/) * [in a stand-alone github repository](http://ntustison.github.io/TemplateBuildingExample/) * [pre-built templates](http://figshare.com/articles/ANTs_ANTsR_Brain_Templates/915436) * [how to build priors](https://github.com/ntustison/antsCookTemplatePriorsExample) * Morphometry: * [Shape asymmetry with the jacobian](http://stnava.github.io/asymmetry/) * [Multivariate tumor segmentation](https://github.com/ntustison/BRATS2013) * [Multi-atlas segmentation](https://github.com/ntustison/MalfLabelingExample) * Functional image analysis (based on ANTsR) * [Cerebral blood flow](https://github.com/stnava/fMRIANTs/blob/master/ANTsRBayesianCBF2.Rmd) * [Basic fMRI *pre*-processing](http://stnava.github.io/fMRIANTs/) * [Basic fMRI processing](http://htmlpreview.github.io/?https://github.com/stnava/fMRIANTs/blob/master/ANTsRfMRI_FAQ.html) * [fMRI reproducibility](http://stnava.github.io/RfMRI/) * [fMRI decoding](http://stnava.github.io/Haxby2001/) * [More advanced fMRI decoding analyses](http://stnava.github.io/RKRNS/) **Evaluation Studies**: As mentioned above, (@Avants2011) provides a reproducible template creation, evaluation and morphometry study. More recently, (@Avants2014) and (@Tustison2014) provide complementary morphometry and large-scale analyses based on more recent ANTs versions that derive from our latest contributions to the Insight ToolKit. ## Aim 2 Aim 2 focused on extending basic scalar image mapping to multiple modalities in the context of large deformation mapping. Tutorial examples on this topic include: * [multiple modality basics](https://github.com/stnava/ANTS_MultiModality) * [with additional fully reproducible analyses with adolescent data](http://jeffduda.github.io/NeuroBattery/) We also used these methods in a multiple modality study of tumor segmentation (@Tustison2014a) which includes tensor-appropriate large deformation mapping. Others have also evaluated this work [in dementia (http://www.ncbi.nlm.nih.gov/pubmed/24650605)](http://www.ncbi.nlm.nih.gov/pubmed/24650605). ## Aim 3 We recently analyzed a demographically similar cohort with the methods developed in this grant (@Avants2015). This established and evaluated core approaches to multiple modality analysis in a relatively large control cohort. However, we have yet to apply this same full analysis strategy to the final GSE cohort. This is, in part, because additional data curation is needed due to the long-standing nature of the original study (over 20 years). The study length led to changes in data quality and organization that we are currently seeking to resolve in a consistent manner before proceeding to the final analysis ( see Plans section.) However, a smaller subset of the cohort (n=53) with a focus on age 18 was used in a publication that is currently under revision in PLoSOne (@Avants2015a). Along with this manuscript, we have made substantial progress towards integrative statistical analyses as described here: * [SCCAN tutorial](http://stnava.github.io/sccanTutorial/) * [R in medical imaging](https://github.com/stnava/RMI) from MICCAI 2013 These methods form the core for the analysis of (@Avants2015) with full details at the [Pediatric Template of Brain Perfusion Figshare site](http://figshare.com/articles/The_Pediatric_Template_of_Brain_Perfusion_PTBP_/923555). # Significance While the effect of gestational substance exposure on brain development is detectable in some brain structures (@Avants2007a), it is possible that other aspects of post-natal experience may have an even stronger and more long-lasting impact on outcomes (@Avants2015a). The current study is the first to elaborate, in detail, on these differential effects and their longevity. While more work is needed to elucidate this interplay over time (i.e. a full analysis on the final cohort), the current studies have established that both toxic prenatal environment and normal early experience may influence developmental pathways that lead to observable effects in structural MRI. The next frontier relevant to both of these areas of research is to seek understanding of how both prenatal and postnatal environment interact, mitigate or amplify effects on neural structure, function and, ultimately, positive lifetime experience. # Plans We currently plan to proceed with additional analysis relevant to aim 3 as part of a data release project. The full longitudinal cohort with T1, DTI, BOLD and ASL will be released via an open publication. A reproducible analysis pipeline will accompany the data release along with our final set of findings for this dataset. The analysis will be based on a mixed effects model and eigenanatomy. An example of a recent pilot analysis in control data that parallels our ultimate wrap-up study for this grant is here (@Kandel2014a). # Project generated resources The project has fed resources into further development of the Insight ToolKit (www.itk.org), Advanced Normalization Tools (http://stnava.github.io/ANTs/) and ANTsR (ANTs with *R*, http://stnava.github.io/ANTsR/). ANTs, ITK and ANTsR are tested regularly (with every public change to the software) via unit-testing frameworks. Several tutorials, talks and reproducible analysis examples are available via the ANTs and ANTsR websites, as detailed above. According to the ANTs google scholar page, there were nearly 2000 citations to the website or related papers in 2014 (https://scholar.google.com/citations?user=ox-mhOkAAAAJ&hl=en). The citation trend is also rising steadily in each of the last 5 years. Futhermore, the primary (new) ANTs website receives over 100 unique visitors every two weeks about half of whom clone the source code. The other half tend to download binaries. The original ANTs website at sourceforge received several thousand visits over the last year with nearly 4000 binary downloads. ANTs is also accessed via Brainssuite, Neurodebian, NiPy and the Slicer tools. While it is challenging to document specific ANTs usage by these packages, regular interaction with the developers of these tools assures us that there is demand and relevance of ANTs within external software contexts. Thus, ANTs is useful as both a *developer library* on which others build and also as a user-level package that provides practical functionality otherwise not available. # Publications ants-2.2.0/forhtml/nazca-1.jpg000066400000000000000000043514621311104306400160770ustar00rootroot00000000000000JFIFC  !"$"$CE" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?.(Ms5p2+֌eaIti:R ӄZZCAuz i ʬSQzWfWdsX˾BpL5*q֢vV2JBGhv! <ʌZʿO6;.ओ)HnXx?.;A`ķ ,6>\JQ;!/MS7icPpΥ֯j|/̔e A^QXQ$Ow'*~5bomgflJJnCXcUiaE2_=q\ڨ5E<X.[Ks($vХ2yI9ɮ(39=t.yc i$>R.1?nS8[ڙֱswbGճɇX՗ZRQN k:r\RnJ6.xY<4 x?o1?S~r`d^xHOtRI$X$sUoH{F 3|s%t'.ihURK+|l[={,s "PȱS]$`~_vz>&t*ű/QCq &b(R:~jӛ hcB}#@-%^n=qYm,;lhV,sn \ר޹v)S%tk$SrGJnDUʹ_iiFܜ>v$ȚE;c{[m &jY;R;VdUUȁT|K *l+l%Q}o vpH }=zWmou HbddwxuF>E$qg@O+~{h嘴qEO`~I[|PČkdF(nO/V%`; #]U'rب$ U2ڵ$Fi˕\v\$wHb 翧jΛdVFx 냚te.UemJDlŏ{}+RxV#RĎUPsLx?1 U-kj*nJ[쌸$OѼ-D@9ӿYԷ5)9Ćn4iMu(ڏƤ[?Gn쎉/uz|F\+3Mծ U7S.dt܂r'O*#ߏZds(Io qL>ih ۩Nԩyh'Z0iaDS#zty}Ď$GN3ު"M]t0iI$nѠ!g@7ӧNZ  6nHmgbnwQʍ6Yj, ʎ SjQSt7.}΁V;K A:g:Ge:UVY!ZB dȧ֤]1Cn!r?q}[鯸RA;1:y*1$Tk|+VxKM 5t"8RT!Oi(@G5c+^ֶ$(=xZ(Q@Q@Q@(@Q֊.EdQF)V/ I[fd2aIr:?\XO_,嚍ݸU˝olºO y$17wn212K0Բ>X"G]Y,|ċq޼ڍ#W""DHQӐ?V"â 9?:!ǽuhSN<ьyvu-v3C'5RȇRz$ZQgK9!⢶` B B1qQnk2kk>a py8ǭZr˂IǿLR4-1\\UBLl|7wL2B4|X=52țew0w/kgWjm]LOFcLїQǷ5C_rn}'AvO RG>/aJ5u/:?64ĀlcV>}̗"R"$F9lzVpgʠs׏{;R@dt1qm+6O=I0AE\rڢĞRvrpwrS"mĈIm@Яk ?p`lII++(n *o`Gjm-.< Me9TtE\$KmBde2ZMܺdl4qݺ𣬙WXQDR3sYڅP7}I4S_Z̯,iZoA,RD ƽG%1 жOJ,tpi lǍ߽R/ }Xq֊ӄϱx-<2*o#w,([zoVpb'Ԑzkۃ <޸ifNJ:)ndGt009Ir]Eh'qڥFNBR/)jsAk$%DlTz 6v'\*=SRO;$g4%q^[P%?,G^87wwri_,\k¶m4ms*)oH"$H4l=}kw:t;)T֒N=M~WaSyrXxfn.㻀'E_cfn-p2eOfL's;pFON x.{fU%A+|QYqjERm=~ Յ5;xXSto+QG QB&14DPDɰ~]'%4GD  #/uM܎IcbO›qCb"j>Ux{WA:"VCvܔs(b-ݼRA* cxӾ+7#ҪHZ.,Q 4z2A<"Mm9 HMGa##/,VD ^s[rJ 9|;#Xa}1;m>XöQ3,$ch-y\Zv9B}?.L9գjWmrZuuw{; 5 im+=}kq C9##@*CƲII;4 !%߀pJ.m㌓֡}֌Ҧ$'Lqm=kHms8JwfIy3X؜(޵5;E>I ֚'+a,s[ 6F,tZXJv_JسDxqex&Z#[p3>BUo-8"n<$)P\ӹQ^69\6z:۞.l7+9JFBLۆ7"  (;qJ%)t)5asQ(L(OBzS核$6;SURl4I92OO^?ZA<*p|Alco5ap_ݵ4iQ?I Á5^ٛlǔ'9 8c,9':Z5'$io a2O>m-"`> 2ZCR䓇vI<ƣo19k*un+7+B\#) X0 鞵:CnR8d,+^I ݿj^`wζ(i*c |o5*n;4xbpI$R8*N{=mmv ' 9X1ְth/at1/_lml֡T΂@C*WfEU+\vr=rϳRGr `~F aDH~TN^iKqGlli+30aŽ|?* ƳJ6T>嶊u$F @H t?LXE6ZYm%v^C4Ml’Hת{md^\;TR^=tq,gp70ݜti ,@X/?ʷs6w/I 7 h'>%• \Ė$.b`r#P[47J1[%[bAx{ nYݚE9W={Ϯ@\9+H6̛;M: yY]W,3k0чC?Z`ȩ+'ӓ6ʃsʺJϡϊoujōA;T)[0b+R+AV9=Ur[ֻcA<?/C泻#sIH2Y?).vf0s]P)QZY 8隣\GT6qǾ3V.h8\3dP]܆ :确lV"+oBr.{aN}Χ#c4rz|J5ܣ/ȁzf" "QJ8$O>qǧMMhry5oo9gzulS=H\[ۣq@y+3*O/DdU;ʍ¹{[aH9 yLs/,kaUG;vEPwYH =8N>M!ꯜ ۨں3 ~Q6R>$Ji)©V:mVnJ\ /c `LVPW8 cI,G7һiQtgRsi>#e֫|AI{UVȆY +KSϊtK ̣sggLW:(:cN6w)Z j3PsҴtKpUrN=U$qmUlE؊[}bP/Ab}:ڷ2]:TsV R0G`z~BLg@-Vdh31i(6?*rnj*Ru i{iÿ4$Gsy8JU=$G"W&T:%{QK0l إI_h`Oqڴ^tJpi;FF -U(GB[QEc ACڀ8ij"((EPEPER$H2Wӊ}Jmƃ@NR҆RiGJ )I|+=z4Vظ)>a9%lv6-OzRp厦0T.Hsް๓kpJMY9ެ88 (N+Կ&S=Z2l:\\DH -'Ӟ1Y8J3xF;[!yHJ\d/¯dRNt$5M@],X+xP;/ֹNunU:N^#٣H.9.B!kbiZ]'zyvu;coJpGX^v+W-4ɩF;:lHNs6ؚTHn 9㷰w%\WO֩ZZ-&| $qŒs|望3\{8.vG#'j^Z?-Jn _5Ę.ypO5BoMvy3])#y <Ֆ`&,.."TDN:]OֵlڷO˝vRDƭ.WOި5^8fr>zUdzJ LO$!\ժ2Jcuo4Fy"،5~D lN::}DM- L<};q{-OB)FFqF'+޵~[CpO 8L7N95Y-ב<b9!'Gfϖ?.V\N1wqu%81:9+M|>p#2G%~+OCIYsK ~mZu'24a[ 1O֨yܙnsWD5]sv[Kłb]N 隽wuj+(Bcs׷-yqL 'ZrnPA$r{\ʳTnU6dY-]"BӠ$ίiG%I)O'5Yw͒I}j0Щ/۷=? NږR[o-[ ~L!kʣ:эUGrX+ԵkS*NzW L߇%S45UcE>hio3j.Wӗ6)D*#8`Ԟ5Oy3c'߯T3 m8]|ʣY)9\֛os|[;C0?Iʀq|k2Eo8c|Ď}jfy/VY sۯ)+2i`eb{ӚiM3G 0VuqitB<ˇ`0?>f֐f8'$(YRjI"K ě,u5^kZM4c8qַ۫Q2>NS5kw?k@mQqҊT6Of@B!1ɫ\NS {/u$H cl۩5$bgpy^=XwxgлNf`$rA{ڄc˹²$H<`w]O +5 йܥzZ4K VT/Z?ݦqڮc;UZedsI|9ulG :{V2hC13TQ-̌OQW$VǨ©\q_JV6Q(ƹ*j*g&MOkl$88CNoowqvf1aRW=xUχmD2e;\"23zls+mߞw9dO:K/-̄5.gЮ.F!UGֹ"Y3NsZN^࢖EjtkÈz`HaY$C9(1FV'a2Uyxm͕S xW$ c\OMA#_‚Be# Q|H%+s:K19']e]L㣹-ZNU3joNRƜ2~TPop":U_˒%$Z29|'S̰3,Hv=Yz@Yxr~i 4eR 8vjg"SjxϵsƒzIrXCt\=EixJhNX:sRYY%.PzGݲ G~dOdk.xV =uǵ2?ZΫ$rkLϏj{+6o$T17jiܩF*aұow1 -9j%/pv{uA#w=T*.åo7m/Cfj&ky6SSO(s7Nm50ܠIm伈e[vºHY_HjajUBŴdtTcw^{pX42WIJz k;EbGƭi/1Izҧ%8J.af F`dֲdpfS\VA!D7%?tW7|b(XĪ֕DI9=V >1!>.D!Y EW5wRC`ʉ~a5Z&WfP*s"%f$r08\:&cy\$G3v8_j,l:rIE'da7oQJՓdR;tzEg GRyYXg OP泚rrQÿHA Q\ЈҾvn_4ra  [eSgB?8GE]!M۷dÔf]ߏ\֭䫀p![wm6P$ 8QZF1\$h15uYDjc7ҵf)|G{ZM$?R) 7?N:ݲn1oX-8I*j8Dda$"U'ۈqZ]e[W*֜Ѳl~beFĶ?=/R p ҂HmJH ,֢nZT1L{Rmn [6F>+oa<E'h$<7=*Ege&0yJO. EjZt#89oZDʄ ަC(Ypj)r#;9$E:zِݵD6BtOje!ryF];5Э"ɉ37'ʄܸ D#;GSҰ[%u'C jr9=WӣCpNSTiiE `Y pL68QT<` [tJriGq&AH=Z ڭqsIBrsVd*~SQ]´kD4RN79Z*yd~E1D`'{瞕QI2Op{b>e,9RW40NŨIU r g<Ъxb?Kݔ1bs-S\Ӭ%&6ѷ,hcp!z R6/Oኂ_T±9z6@5nW~>p6pAqUmC9Pk)Yd;Sh0\^(nrW;z*qfVž88,]/'9]VrP۽7c ƞ\vyQ#F;ܬ(t#v\D Mi$ܣskP'sI3Ȯq9?O1ܠr\0@$=Zv[M͒OT i;ݽ. a2ȭ;HvʢL#wRTnjnQO$ϗϮHNHQ4l, z~5yv32IK/2׋os*XѮ"3ĪB~UD: WVpUCbv0PsT40H <V/*sP4#n~&D򊝲>wJM“׎jVPFPt#Tg2WS] <Ѩ6})΂;arInY <#o;[ 25.Ltzqb/dBdU- 1~umI\1^He!pNG_IHذcΛ{W Rzjd6ry94%B'*֡}ͺ@w]sq%ܻHR8g>ӌ8j KIC(q>lVksIE4\GFiYQs="20{ (? uey =+}yfhbmPyݾR_JJOEbAV5vG+\ҔBo$”@)8#>ݭԁT֖NE94-PFkW )QEQEQEҺO>i61(MUX('&m;HF}!s⺽i?g|EBR;ik&iTX,W!J;Jfj߉勒;xKwC'i]OnHLB'ϽsE+GTwً*uǩnaYJΑAA|z{t;G9ɀIee9VfF1#gY"\7>+1jD1L1X+mw$v0GӒH`6$ui+8_#nxVSFݩY ԬRO92}_z5xeOp7ѳ}Ќn HTU?6??XQZZ./F%B9Y]fxz&AoXlH$LX4Hڦ0q==v&V 9nQpQn8f.d;9IbIJ'ިSIqo2Ⱦ[jTח)nQ=k_fV؇Ncd85tޫ v:vay+6Ka$î{r])[:iFƽE'szsU1:@'nI'\GWR+qz33wdW1iĒG԰RG#4wޫȅbqDoPݒ<L\hԒOnas}*!C F:#=yLKz[`޵PeFc%i%RwU)hz4Z|d Zlf9>Գ\Gg~ }VA*2cG>[V:pj;XBuevd!Ha7rFњMք9Thtqg;|)|8Nq]EF8Gk6D]TkmǙ摍*03W$ӯM̒8i1 NO\z`gUW[{|v~淵2_yRzt h~q!t<.8<%;E@bP7c? ؽ7ZZ'ҭiuWPZ1.܂=s(I;G )F=[[?DfVڃ_SտF"M!wHI^ 7EdUY<6A;ymz^W]8AQw;hid@TuT[L-wjH5̊g?Zي+D0K[)BkM]H! $pkҙImjsIHe1QZ6Mv)+*y5Vfi ZV{g5Trsڨfً:!Zf-qt#To,2G>[Z=Tcb>b ShM/͑pfsT{#3USO$-x'q(tk#'̱3Nsj!(b9'h՛dER ViԊQERwrm% (SV|11T;b-k-÷s9u#i2Qlntzm |W#)q<28Ĩ=д5R|M$ʹ |*Czջ۴+Xq\uվ'1A>0#&e 8ް$B7񫠒i(!b(l+9֦k(k!yG=k3K=!<"2u*EI^Ybrmw0fc8HeN&,hpGOjۼATv8?JuӤW(*ԥ|a1s*Ιe)cj84nCzV:hdN34{7k9)1v EVT_'Pg߈x,2ǭQiG<9{Ԩ46xe D2xPUmwxLw'm̥`h 78\ z$V cCp!ONSOP8sD"%ad1N{P [So5/-TO: KwCYh^HOJ18l;1/08Jk. cҴ,r4). 0OH1 \Жjveo/nޠ֢:$Q2{U,CU8nFxʐ$ pkX̏afh%M%2Hcln1E_FU[ݢ}_wt]A>=*ݤ/m=7 ʆ$qV}ŝƬ{4R0 AuJ-pIGtl"AEnמ}I-kVzp@3\ޯ{3F3 G,8*M|H%Y ī:)ԯ+^)3-vxYBȒ|>p{tþV9c&*H'}|9jī>)* 힝A_C9, )2zd~b&RūEnVRaU!b ~Gs@- , wW9]C!T( |5wCGF#PYFI8kU8J>4Uc{F )i<`\d`䢨D\wBɈA~l2BI9ugɮsK\ƣ8b}@}*ڤef5\~q'Kyra5 8ck3SHn?+|l9n1qIݩ$M(I۱wHR,S_W"ɎH8Umv8/9T@H7͏N?]4GRa'($>;F?$4O#)P15hqM|A&eH >^{r+w펟Τ, IF2ZיE6pmWz(N[5D}p@ )mJ) GU'cHYHG!1ּY5/))E.=LOp6 m'D N;vJXZ!$g:c zjpEesNMVLG %bÅ$gMR&S ]=23Wf"J5H+F7gcYK3a\ZiYT)# `bn<;ZpuYu:iEƛT`Ҟq֚Eqw]N"8ެm:F4ʅ=Yo38lR3g} xPtv]Y^VlRRXj%1L\95Q7٤U}>Y?*\[2.Cm 0UbM$2-ZC1b> ĶSVtH|{p瞙ri0GNg3a1AssK$r,[u:Jv}]J63w(+v6C'jK3N2#L}*W܂OR:VTk)l5I$ IM.x'һi%v̘P78oJs(`X ʞ>RI ~45%[74(SϽlRHCڇǧsU9BNrioSvLedW G CGX GR>ise[O#θW3oqgyX-\U? P&Ă^8?J[u+ Gː?OZqI'3fbp@ET.1 RO| ¯%$zur2xר֟sS`FɴnA}s["9n U RLbȘ;s׍l2kB4Z4+8hQ޶ˆOV&F0(M44HǠZ̖y;X \+2kFg9IEjȤʹl2w:q 5Irwk#;}0>l^cn0>io7Gq̖FQ O E`b#$ln$]F<#mάv~c8jC+.6篡5eܱ ?bnRP8V, 9$GueA`:g>;yR*m@OO;V@?$ۜquIM3GRXQb6t%a;54`,vv#1`yz\+j#| 1.I#'hϥET\ m q,X}q.IrdkŦ:b7nnދ8~Eo;ap cUf?7ڜg esӊƒQԘnVfgX&HNp9:=’1;XgI{a"b%3=:gNkYqIiqr%yѧPtɨo}$ >i6Tn <5 ~,(TUȧ I7Pbgn%3(֖lJLPQcV%YDm5)~2G>Ƶ8mNGˌ"4xRngĹ%q{xV4MGiGN8kb>1l')4&CB2;Ȼ%A!aW "\`8oNo]I)?ԝq7| Љ#`c@kK+!L`M^I 2R&9W7s[ltCh[!d.<$$(0OȮ&;|dO_f_Hog$GiiFHA2).?~Ni Gk#OVl1cwl+PaWuYN(Ҿ,ŜIO{ 1Pqw?s:=4)&[E>k+F~[/$72_yPo\۽eq`v7o0ySw_J )c1IdtT?lڨ Ν!I%J譞EvS[ '9LRy088kIBRNls}aZǛq+KrFmEެXxc$loaqgↈjJQD^[$vX5yhmY#Di7>q}xOݏO^託&dIej-DvO;kX{͙V@Aw qsHQTHHېxjjIŪi\\zwisid̅%.7+0_ֳVBm=Bu$>pђq2<}~wWӤ(eJyJ6iJRQnȾEnexU"E@{1V]5*(E=>Z\kAplJsIA ,$!KI͒v &%dE[uyBT߭dkZjfiK` /uP/ Sa,eGAX:u)#XWV9iEUo\,H &ݘ#e+% [>lTPX)$_ʛcq-Vy3#nG'Z*-{w}uķ7P["LS ]]ZO,:}T1 w?:\6x u@`wNI'Y(1y=y2 \qF>U#%KdwkBK4Pǖ89Ҳ<K8̌R}ֹfѥ/x~6teW-\ܣ@ E Ku@0y. '_N;x*ֲԈ= g2=}< s!Qc8If̈Em-`/jK% +*1П R쥡P}+4*점0GÑKj@T'9?5HDNq]6Zf`2Շ*qJcvk C޸R_f + TYg 4Wc2vs6SFFF,UOX]GWyp<4fl99X֩9e9z:èk#ʱYA~޺2cz-GS4+/#֟oPeY}GST}E'3k(UK3 <|Wr4,=dtԾ ^? :S^U9X4K|Im˃׊#U!%V#`vӗ2r3@9FE-u+jf6A\H9J~yd! ^m|+?]W#*x 'փK;G=1}+ѩ ݹ݆q]vhae*o5[rN.0Hn'3w沭P}VîD.k=4:wC[y_̈́dS޹˥4e"]R ɱz8t/onp7gX'؀mgg#dž6-2Hfw%޻]ON66*.VRSJ.̯,9m? H`TevqGMZM:$dENA>ƲT[I24ny&YH+*_4y=k^Y"";NxbyxF# 8t׳'6XZnU%#\i_itBA#4nnX2@n\ӑ<55cH+]k/*_1޹Vy#c69}zO(!nr572Z(=RVwQCuk|2cQ$( ` r+OrC| wXGbt?{3Tx樓Zo34Vt{vSz|T1n}96}3R9UܨӴNė!bHaff[YFXAJKfI7"c:1 ʞ»C1n0w#ܨPw F - xck! q9jփH|͵vs֪0p) 9S WwU[q[D)8^1WV Ƭ8YM=9rSro9ϹZ\,#c'L%Xu++4e$K7͕pO6# Rr <ԨTZC@djT[Zng'Z%KL};V#Ww?ƢG^kM@$FǕf?q\ߪPH2=qBG 8M[ED 'RO* ӾIW@jund)ׂJy>֋"8#kHrw1ZsqpCP)=KJwUDIQ Mq"kW[mL6X4k+^xgF}-yj]=zU. spfCDHVj]QpNrn;\+9===w.Q8o-L?αci҃טJ̿qs% mުc g k*e8Yri[\lB\+!βo`'f9O,r\܍5h{?tE 3``(b#EZHv}mR6Y7FfEbӈJue#NɆ9e2DK8 [6Rr.᷺bvhgjaN-5mv(]j#N1)}é-湝3Vpwlq!gX/..JyD.qWw0Ϫ[CI DX\3+X1n;2++nREqCc[&hW0E+DAVxp븟JHH%U=c2@#wī5A*DO@P2Ik$Þߖ[ibdn,G$q{V"_0Qo!n#P$q4FF$o/d  R40Ea!v;٬/݊v9i g%\Һ+41   s yk1\NJvē>Ux{B0K+zrZ$S>d=qB m}@Oz4X 0dgiOo<`q1o^kNie+]@t+sh+R ;Jkʅwb;Vtw9U( y,wj#85$:ұY_g?Lʟs8hs*$y6.;r_͑f0~V<@H?Lt|3bNW⪥ٮgtE@~+Yw*|rfL8T)9CbE=ƥ#Qc$xڧ'UGYdy)޲pN +9.mn#zv湡j{XGw:8!oTv{`zt72< jͱdzzSL̗JY!@,ߪ| lT`?ɫ*wάӸ_*GGvYT =xX";%9ޡ\3 r>nyg#a@d3?0F$>iM9?/D(Uy qDCa;?X6iOE89[2,ާcg>~O;ƾcsn{dּܒ\:-l$aA\sYMH]FAY8}zghQq9V)-aly84b0v zP@ RPwQko 3+ȭ-ǘZeĨ=ާ%Dv銒]AK̜Сnncsrϼ$jv44jf 8E:4el9⚍b #:Ҥ=)r}[BlMzQ#cޖ@vHT6'%rwӵc4"ԤȮ$!D&*{%p $DQ9 9zc a\nR>d4ѫX0f8*ՇݜdS-!dݜ?wUNp#\:ZBZ3>𥲓sh@VXPoPy3LK3REQҊ_9&ƺHpww MT$s\Nd;"5!idAӡK1~8w^1T;;d"/ҦI@[9<‡"sb@$zmWU @!^PyH2! =*{hs HV"9~(mre~Di-x'ncbrC&(ϧ=*r2̷.svB?/uǙHOz`Sqɦ"?~J}R(ƣ^d$ǕT͉ H犜`Ǧi*m`=랴%&YcmWSw`~A8h8Rq+hIrN۠Tdr r3K]P_ zZ("VB (Ҋ( 1)mIN3m.(AjL3*=",L/=z#d] ?ZdJmҧ朓DB0% W=:Q*H ,q,}8oMW )@p@z*RyzfHyQYB B7IE$ WjԽE},2#%Fp'UR񗕂xmm0{ջ6 lPA}+ )TfiGR*-U]X6q֠(wyn;osۂ1ZW?#lNz:ج+o95##@=kOS$гg#$ ;ǐjRmٰ0 P=9 бV >bxԚ{}80 1\;'Q~)'O3bXmqC0ګY#H ӜuQ&j&ff0 {뢚rJ Dc '#zA `Iڰ&w2Or~צ>F#dɬ/tG*"1jiޭɼn2OֳntD?JȺkFP2yH1ORC ۴6-Q5dTNI}PB sXO"5b19'߭r -UV6oN}G*Aɨ2N ;d/+];t2$O<~5L%QC -f|wAchv1+rm4o jT5+n]0Ž$MIB&2r})ۅ7UW;w,zTr6v@uRЊ4k2orscԬkJ#}Z Ēs;A)hIp;.1Tz?*V% KºjmdaA*ַ@cv!3g9簬ĖYBm.2܂kFPI$"I.R?Bpv_SlGw(PH#QYH hV#'ާb'`Ř(Gvpeތ]&ˌMMP ;N[:%y"9V~~gNMJPY l԰ZLƘ$*TN9eaE] a䖙rD2kF ዕ=1뚎+H%l'D$_SHVM$Enþ7bz9e "]0 Ӟ'5׸UU 5[i # FV W9'ʌ]/K^Hygu&kx+ .zs\ƣ'˗h`a qejS_u E )^>wWn'RUjG\|]ƲmkDVW4C,),۳,Vfoz #@$ )rB A'֖[-X>WZsdHU%>z8绺MonLJб׎o+;?{#1˲r21X,n#m> Cmز39]IaZ14jWFNgUIY3HfGjIJR[A;e繊k)Y@ߜW-D9IBX_ğʺW I[zgZ4;<m32s׾EL*:Oa#y9dV`1}H`X/~vx䞧[r%x$ڹ`=n`X^q6Ken i $*33ȢqTIXӭ ,E'V>z LSkk-He˝XL^sZ̶f܆VqvS~ؤ0t[)Kl [fn>ֈm=q=X~aG%ǚͻ.Wn<}˴UR=}iJB1MHʵQR\n^]V=ѯ1o'8 #шp 9ֵ0$k68 q&;\&^V }+F-bA"=qXK)(WVeXۨE|uB#>QFt;Ƶz&Eg[Yk(Ĭ9,ۻkH2Ϧk;ĂH$8#'kF!$ɕrqq޻iԍ7hN Y8V4.[< K6 ''tc6'@{b,Χ=lW&)(ulSN[h`}-Rs1Pt{ךE}zx;=95FH\?tnѓti} F2y2e\89il<*nj)p$O>f/FD UjjTfTWSWb<]G,3(!fRt#'V׈ÉQWo>p0=Mm;J]tnS.ND7n?O9,* CA91v,q{M^4(y ۷(ڃTr3*;tdu/:WeȢ&'HXkN:>ͼCs#7N,V>bV*I-dw"U$*sջ 4|9T`; Q`cA޴WZSs_3p;cmu墯pI_β4maMԈO*UEFe=Gs f2u^VܠVKw F.0GYUwu\#:ut%)g$)dqW:5[Y>#{)E]ΥFF@Ed72N8T|wUa\qb 5:(It8'K[;#!Urvsr7 Ev2n3nO\W]8-&H˔98?U7cgy[l{lW-,G4Z;mfzb(nEXKk_6IKDH)^I˭8uU` ]^\(PmݹGo]HQozA3BMh$&[|$@XC]6k)#!FJqYMoq(Ex1y¯evZ$~YwdsIԦ/H m۰c~1xȒ2g8zJԂW4qGQ|s찂#$ zqXFq\`+M+tdDQЊⶅtĚ$-1mǽ\EXGY~$s]̑вz ݕ8)Ċ(ޣ❧m>`7%̬/#(,P{D /ns[Sig\Ʋ#nc.f ui|FKp6L'p]vۨ ,\\q_L[TqОk<8tLn,=>jmQ;+Ӭ5,UyZȢBU/Oy$qB䚗L6J#֡PU{fv}梤h7?Z)fjS-,X(\9ԦI.w䌲Zr/ڤIlœnZ*[넘Gl`/TЮ>lRg,F^mYDzS @oo:8dgomc$ "H(NrOkUs$HCK| LH(==צ!dH~` G\(>v>n}[PH9mߞWBaI'TT0M%GI< JGYڻm^?3u;=i g9BX!cjŬ1OؙO\`cQi)XdG5cQ-M71B69݃G!lմpܖ^HX_)g֣E1R"*G?ic)3YA,Ҵoο7_\|/\ @ 硦lFG,08fCޞ-H';['I^ds(HdYh#h6l;БTmIVT z66 XNi4]ɬKcŽJViO@,=p6֑fbLOR.Ո7޳l*{Esj{1`'QɵN2jƗV2d':P7ngKɾdTZH& zT!,\gߥ:ۼH0,Ֆ%f*3̒;)^F -$6cP. [ c*`;*k,UM5deV䊅ܾe="Xns3VvBe3zĢ-TkF(Y+r֗Ϳr96i$ȠݎMQ6Om?1n_lP;L#lW*zUq V;]^k2-XԜ)V@9'V%^̥c*A þ*[8&"Er9 CT׹ǝ/9郐\pi;[)XH9jӡv !_Vqukis5I9SZKH ~=W7v+݅u;j̾/ѥs޿ywmj0fSwV$Ѵb%2ԄS.><ףxoIk]6+a)1 >'oy"VʀA^VIn(Sj5[HΪv8f--cn̠C$x)V[O2eʌgcl[nePH$O;2M/%,`YN?:V:抗RKۨy#XOtomB#In!a0_.0ݥI Hq~?gI{u}(}Dq8.p sӂzsJS+j|G(,q\Kᑒ210ʯMt^ϦJӬj2 ;U` U&ebȑ $mÖ `c+Y.E{汕!.u+)wqLc?g*}J{hjAaW $l 8`~F\θF3W-8TսU١gϱ ⭯·p jTQ4ofl9^ºa{8gvbTd%^r?UcXۭ-"O=%1f2F<>EҒ9mJ*)$~5d;1;AAap2HMtp>"Q׎JNyHj*F>Pzҁ4SMfQE8P;K}sR=zR*lwj27< sovA>ǩ?3-~CJ螁ӥ =?JBrzֲVbcm8ݎOR{M]@>*T٢OɕBzj-:`cm͒ҩr6~00vNTnb[E4WOc4QXH$:X~,>ry92yML>Q^$޽ {L9•Q9X#(tFmh~. ?/_zthsHCaݽ*0j[`,LvF0qЅE\ʵW$'i !xv&kؐc(@;qz$8лw5*?m/C3izZfE$pOO^UTUx?Z [9̩NISKI͕0Z+A%}&_AOӁV LX*rN׷7p4vB# F 7#ӿf]:wvKq ;Fp3BMrEQX*r鏚۰ZIY39%q$q{F OK))EkTȽg}#Lm;X#kqsu9̌Bxp3jGb rv`09>zԷ3ȑ_sֵNԜ9|4&+ƍN+h]m5Ϫ\փcR0H@'6M]Fftm:,Df $w/PU!QHTll1u*a p7gU(yf6^;sf2PnQ*4ۖY*1X.sla((3u?(~Yާ/ 8?F?%EdFe0{ץgx5xIЊD{O\,= *̩:0$z~UR5?°$?_+pɇcRI[:ݜ[I OvPڕlb?HC[ )[OCNlt[ve>Z08*,HDQgxw9PN$:ְZa'+KBDS#N] xڄZӵIIJ@#X0=L U Y2qlۤx~#gڳ\M*_۷,@ۊ9`~y?Z#JIr;đ20 V~byJ}8yUVVFR>b3Y֧J5Ili OZ5+g\Ggx+y$`zf8Ҹhu P\$ =qh%Zⱸ1J΍^P0k KYedoǿs^,+s.RGrir^j$[ˉ-XBJRY.L(+7P"Ga}2X4XvvPR23?5l08 1Lz=ߨBdb5Gg;ۧOzRuB.4讵gdU’ãd8=+LkcZ.NEK fVUTz η+b g+{\I[DJ Uz9}CP{tW].$=:ϗU{29w?z \޵ė3J9-ҝB1w_O*aswX!h!^=7Q<0ۅy$`tYc|~R\ 8ϧ\}˦e&6|>jB7 _6õd  ֧!BC9Fލ"q`9'r ٌAG0ϭg Gkx. #],?錃^k&PӑC i?ہ\|v x}yVCB/m#}Ox=y󭟴ֈP5(+xX9۸'W[H2IО1{`fm} i6AݟNG5S^ 6%31g ]9jJXYDh{Үx3Gúj0ѥV ?^Zy2 3U^޴x7>c OF*3tzY,căsx y޹{}Vґ&3.^8 2FGpwo/78VKy$zV媙l J:1*@$E-\0m -U{s1E;O9֛u͘ʄrGZP2 < 8.hKc/igrVqOU' =W~̎vn䓐1ҳ*T+jʄgRW9f\/cA J&OXzz+dvƊARN:`ןƴ9ro Ar´!Sn2;Y^ONJno]Dp SݪmqݛTJz]l,p#\u[RWA-2iq=2{ :tvoVW 1~գjd.wnOG qۓE97 *RՏ'v}{}*Gq5>IJHodQ|~oa]Nsו c.GqƇ-巩fRq@?*K%=A{ 1mOGqYTn-rSNqZI<q+Tiwl̎0XZffUt ?tk6:SJ멤 =j Ŋ&iI? @W!S"9;=9t'%+Y\F xgQ$289\dc.V\.[z=Ogje6Ώ_\N8 |5^3l6Fq=(٦:"G dt 1ümHĒGֶYrۡ]O>J͊xۉxČ^Vze[(i6)sλGN!'Kѡբbc'yc?Qc4Eؤ||)նqC{h$$mpWDkCNc??arBUV}[Dcq:S4+pD1;w?Uij(X緮EhE<1naQZzP[ANߜ\-cT`Kmօ&(H@y>T-|*CQ}+,%3r4*RԞFm[M5L<`/i&$ Kn[w[K#2ITWI܃rhkӳ;Iy&RI\*csƫ+J Jj="$2Ux c<|GʽTo֢T\dwe\Bifw9(eF'UVA&b:*mOtĘ GtnV"N2y$aToV6/1YTwk6ٹZV="7o}sMQVCe;ͻ/Ӧ*%sHO__Zш`=j9j5 $39Q$/#3P^qZO&,2)j2<ţ'nՋH̒r^j)F73mФU&}ķ bo ؔhGw:T>Jps[b2x9}hV%*`},B b91W6 >ˣz#WiWbBgjZ( $U(#Y6หL(TS 2dx)sfVi_iPCiʬLmUTs`V&2? r+7h-V˼Q#ޝJ :BU{Ȧ>\qہ6ª][,0wjWJE3WF1BZ@Ny4m;MIGJvfndn޹,H wre;FsZM!*N *8F9K!{h.0\ЋF%El+XԺIAkV$ε XFDk&~ޝ89?"%R[FwKE,{ :T.r[h={%2.=jܚvBjgJSHEkv.f}vgrfR=wk;wqcˁ zDΥZFr=T B[Mǻ(&Ҽq4NtwcZ$2̣;HV 8Uw4n$hXG"c,QC$r1Wm<:[m:d{Һ7V:,^N"w=E.aJ_e5]ipNj2AZXFşϔl+,':qS~Zw-K)ܯ e8*QfMǟJc"FƢy\[ܻ2cLݬv82:ec0:skzU-d1$J.H݌`Ԛ[awaw|vmO4;W24Mӥ_J;(0_'<==kzO.72ہs/o}шf1 6sϦ+GÖDHX_q (=}=>I^*fQr;pzr*[%v>vOzVFpj n-K$ < ?Cjh0USg哑c^+ITQFNTjKV)II 8㿭sڎ+)P +9=tC$dʠgi3&S/43#*(џLd{RpnESR+lZJ#yʜ}<~5b $*BB5zͧoU7t$q=*#|NwOc9uvg@@nN:Oc{30#Hf+<ȸ t\j468pd i13`w +{Sهu|=J[hCJLn |z :wD#)u0q)ldzU#!QB c۱-1Ư ՘!c[\ DUF1~:ktV,b Pd/NH[+DK8#;zĤv/=-~Z9#S_CUL (f0~s\(V34ލzbjtD^3\xʕ.rAiEf1Ua(qY&,#nN *, ܑ:ѩHB ƪ۬hd9T EyU*)_#5vi7c#ǯLOy棙ԁTjKX.I茜&7I#9<)ysPߴL42%۹WpK-fL%ynx޹fkj왉3l"p԰S?fQ3$ȭ?ŏˮ}BMXĜg\o>Eb+t >Hy-iSelL3qӦk:[Ԓ|r1LsW-*_9P\ 霐=1UM9q=#E)_z|„\})a@4+ԧKٴ ܊MA:ZvdNqݐs_jrCs:NR%UA;t®1Jc&O#=)=i%ΧMNcZr@8b~bT~f''PT;d۽*ҕo}r]oCҙ {r[;1jI /* #8jAGq):OCKɕFw<V\RڽGNՙ=v3GnF:x)WO@d*XAڴu{&}e8#Q̡iXDJ˰f ʒ0pO$毤FY%@`5+v*j:=$s!uH!C$v8jeH,৘3_i\3lS);@]csϠPAnK<9ZTzVb`X4o$i."lH`7g*wƹUH)2W<mne_,ڸnEqΪ6^^6E2ȸTV0rA<N]0$0Rq5_S̍v$dXbsߟJtIpn O.ܪFی=GNѺz꭬|F' r?Zcv6$K;F9m6XpdsW*M%򚌝7A (UdͲ'ʕ##$2G@|̃΃zHTK?Հ$a%ʁTZҖLŨO6"(샓#8MN)KvZ6-c e``7cmPAX!-#=jZ0fM(b+ eN \(_StKR!e;XLOB*K D FK(IgێNtHL;k:~I]"o%<ˢ PnEm{1C lsk*{KcZ?_d19P&ē '5bGez֑1ou^';YIM͙'}ʭIteY?0j}`W&fa+;4\4>*р3Lhu/i3* .MpsI#t7dc=}xcuMSty._ sy c'Q/+w&H'ɮgŷPk?+ĥYIԏ|EYsmvG2B#r+ [-wjjH,/u>Q1c]"M H8$?uҦRBavΖս {MWWi!SyW{jd5ܑFp2]?+*C?\]SRBsBp0 )V}ٝ:iCݘ-#xI@졗 x8SW*.FysZҝYԊ&-<lL9GO–{{q˓1 о* ؇ 6$UFLXZHЩ#JnU#1FpZ4{zc).n~߶,G6S]߆Yl. Q' ʷnl-GGUE~^=?C] 3I^]yxbL}'0O^{#I Hu%hs֥y'% VYN Z<. @ӎk92~FCPy#,3GLzF c<: siϕ۸ҡr~C+G}. Fmݒ>j1XR4jI(X.zq}Myy茻cA'5[+ mOA8YI3s?q5<m$Phm"{a6X'ԟ؉lDHv͌ۧzึ"*e'Ux"m#qqr܂q:RV47CLm^G_$U]7Gnf ?F29dIX1 ?:RjLX0Izv7; F)BQn>(UXBnPub].3 m}tI=lMX:nkCa GzqQJq'hCN*qnǕ 5ReXƣ U=*ȫ8k9\+)pŽW"q.ﱭ(ܴҬ- )${RM!1QU&ə2/o9fI&e&5aL`m=H~udz o<8r7H%+ӒI9~10@5xRYzu-&kXFUgd2]%Tn_AmiI.X3t' b,xVHSMMΧT0W,0={.B& +~I=\ᑗK4i v=:W+wo'8늚DAmہ{Q걼{EG\y^khJp^5d`3w~gq7m޳ӧXA/!p8uzmDmL[yb;w?\~rQxKIټ 4қfCÊ5 il_ 8\C%2/+]r8Vmh$7(I1IZ6s&wTuҕlkvGVqݵYfhD,}j_Q$lx /ַm"ͻج\=“3WbVg] Xe[koJCR8bDh q>՜f^Gm B+Rw aG>DMq)G|98E)IHSqv3TW]U̯Ru'Ҹ[a0H+&bxQֺDKGW)z"E٢g|ZddJ!#*\~uQ+f|˅nkR)D]{I9EG+&35u;H&,q}kBvzZ.קOJÕQqsՋ rF 2dp1ڱvieQ0”ܚKbFtU/e9u%inXƑn8YHFs[(=I\3 "AT{Z7 mPovQX3 jb\ݖwGGҰbR{^1߁TH0w*/3͵ $RlY˦ppZ~2 .eh"X~6&~n=*q+(Cu*[["2e^&ٵǴ78=k/SXgČԒUÐp*o.6ul`k8eIfWyD3(xZ>*ճu,0AwOYWQi6;FCZ\̞e} $Z 5 ZT0N$'b@c} kndAȤ#b FXh[x#n.Lm9Qv񫁏ښ3ˎJ[mV+#`tQƷ֯l 僀Gp\DCX`9O[VW.jw ¬<2n"(q'8$Ic}03Q\]e*7 t95*:7Ԓ{NI^غ*;Ku{5FIazcy,n;lCvܛQi=5XȪT6 dSXrif%!N7 Jp1qڢr/L5n F8~akk*/TC*?Rw O$sj6/BY!NG5j7Ґ ̡)5v[rC6ۙ^:cQ XuR1stҰ3ct_P^j\!OOAyTzMnbA:0)KJO‘ԜcΘ@V;Cd)_'Vs\yf.߀;fd1ƪ~``$pryx^$xVucrIu8|.'I4亚IܰE#i'9?EPٙeKi؛\xyELO,CD{ܔֽM}GoW~I w~‘'L2I=)2:w#91[ҊQ1rapw3 F9Ҫ" gסݵFu2ybx}*ΟuL͍x#̧N&{uTp:+렲[ 2:YIMYۧ?KhzXz~ʎ)[8,L5}C Z3vaHJԜf8\JQwSI%ݒb@=h< )jٵ~VHÝî 884g+O`p8Ǧj`B)>a:ԫYx<6~u" QGnͱ\fxy<~~݃xc܎?ϵ'Kk^zc49 \n#{v8w'Ҵ?+1Tf@\ĮجrTcjR|Esv{fyPM[!Qу3ۇ͸ tĚ[襛ʎ5bDddrxJE%-#DCŒ9?/>6+titlr?*KYp>ApQ ƥJTI^B+Rxź*"` %ZHQ;=>Mw'2` (SJ%!~cLdwI~wF\/i41:<$H088 c hƱ*4d;ݑlcP&2ASE+(O\`~\ތkRv%Xxv㪎 h~)^U,jMJ8Peeve+y ,m9z5Eyp>ֹA=60͚u,̫1ӝ8ی=>yIˡEǛ-&A0 -.r28NE{cl##d lpTce_OjޣPjʤn&sߵe[–,ǂ쫒J|$,qHh'R1ߊ:RmXDڧz$0 ׿?_©D..sB"FqېGYEXRn{JGj[Y02ă{``XINQ'fZ†2cԞy>[[d `'=8S\lX錧aq5y12;\4A[jh;3o#l|0>@ 1֤_٢hk82g?^5IypW8 8䜂ke{$Tn»NI<=O_Ɯ+̹G> Âh7)21ùat" m-udcYE?̊8jhj @ g@[e 0޷p(PYԏkmfUQNztT娯ʹmEJ,r>0@>bK+ETI"Vyڠpq\~֤k"Il( ~**,]^m\> yp?:ۑtB吂IN-gcI,rrO=*HiYS $bp{N?@1+'U.R#${yqg,쎨ś!LUEchRniQEK{zi\!vʫtV=7d;X9H>Uv|6HOS\ܞOim4+jw{01mocGJ^ gjpA#I'}!K0G(Bl#5JX#0~:pRfFg":{ diU]dTkF2'׏JCouZeZ=ۼ[oJO8lI!G̀ϾsZ(ht}e$rFfďXLA+gtϽjX /g,0>dM2}g[bft%uUf9㌌~}eevjBmA8<.)8I;$do :Sڣ6ȝ=Au;vkbb0Tk~eh%ldlTֲWQ! |ΊG%=8>^+W::cp+V&%[e`l}x5z j|ŋE gvtHmGwgw@?C7ؔ`wM\E6a0K ]4gGu$G%ەa}vZǰxpC{γ(b癞7*\g''eJ\ҴMT\~"֫38 cWm73qU 8錏ʹM*D_c8#޶9io#HyޮIiRVi%ezzVƩ*[i3,9VmbUFGGγdc{?X,qqŒw:gV1Z-?tINJ*^%ʷ0>46G+SXaNs<=lmn.};f{nR6r[k(ۍqՃIX[Z, !!ԮCEynL\5 くNzk]95'.k>6vu!Y!de7/ \ͦSRG[x-_c36,dr *Ouؕ>l~qӜsfiR @0nUҺE;Kgn&13e@=Uk8V䣐wUZ @23)']Ы7+wv?LdB(ŤqWQZ\/@">DF. t:%iZ. cw'HDD ,'泝F͊Ե"jV_gi;@vvQʪSOQ ~#fJB,RB3P=zuL*YG4LȌ8*zG[wцE?H\jxK%92o~r<{{U-;Kc3;G Kg<~#5`W%E >Y ,}8Tpvncd@Fi#HCI@VbzSy˚I k c]/2DLg- ֿvKf(k&T ^Amœg~Un:()[M,J|݀~o ıL`kJ $rFJZ!yb3 'ڰU;_5RTW OZ (GrXrz`,'[&e1 Ҭ]j/y<ѥg(A~⪥5%qХ^[w5」Fua=YxF&5g vw IboF <wzW%w!M@EUbS!i+\Sd0C绉yj4:H yy>s}+&`IOTNOMb8•[9$֡ et64UL`qMcALI9 (3Pgޱ䅮m@<$vɫTv*=qK94 }qU<.Lo>묇N[dRqz/,"S*ʐr03r@hUh4p$I_-9b=k.m=\<2+yt[CaosXZ5!!,T?sNw5#̖v͚1e#|OOge=322ޭPiaAt&xGLR&fvQ%'bE6`}|S'>h&EYy;# {k%" )}:חTYgSaXrrwت5qahl2)$0SԟL-,h2'uC)X#r*TrP"Ce$A>k$T>oČFˎzVUE8=|TYyq9ǧֶԈ) hؔkpګUsrܨUeb!a\$EVceO+-YlpW J,fvg1>mfL84͌f%2\|;֯O~Nn#Il99O]ctRMk>C[c`&GzDqyP>pGSYNRJqD,blIT}!2 ŽOEuni#NH5u$!h9#nc;렢DB H+*)p#SBf3E#ުI"aq#ufg=Ko}GSsX!:H-݋nlQVIăb9֭ZCR5&ie3唯qU8#8t"8 2q}eqE?k.H銱i"HZRvӚR9| 3Bo dcI9hrGJYmSt\3e@i HߥP»vǖ?ݫ:tQT^rHǥ21$#$ԇkJ̭r7N(Pd=諌t%]ϸu 2GB!O,%uN})$hZ?̱qn]i}ԱZF+Xr?AIʊX\gi=Fqgeޤ>Ӟ->壶Alg#OwyWf`p@['Ԁէ,ΑQ@8 ȑyApq4F2Gq${`r@@'']?)ה~[kCk1[We+ȧ ׯd76}&R9\`Rm.){Ie]BD@=@SueXdH{Xfg},=Aֺ[:Η; ˁ}qN9S2J]HC9d:+\ٵ)j`i3"XTO ׷%{"gIU@ҶdB<8.Nx mgJ<(auIFnLR+TY 2m~`ۯJfUpc4KXQ"P7X`TNpTø`}뢗{]U/9i1 Q;&UcLP$sBW;qӊ=9C6rFSQS':wrzS;1 ݯ힣ZeeCQLW V ԑ3+< w9`8ʷC?[+OTwsapj*ai't*2ԓ)Gcfod.8~ЀzxzJvR{ E"9C: "HG$qD 3? *W{qJ۳!ߔ NԲ~ܐ,pqVb՛9%681B8(Pq1Wj6;OQ=+,HV}zCA^}j91' ֳnvk] \RLu֕IǯAy Ta&捀:ڥ%HF(; x^a,jr&|W3j;Se*5WϴjX"Dscާҳ*SEQF(^; K0_$)LG*Tr#s" P@N>x9{zӳH8#҆ f/7l8H bTWp" >݊]\X).G=:Pxbq&;)i@)q?SItTR;*@= F/%*sQeFpD ە@->"sN; Z| 9F3GPI)J!x?Ґ #}2,9ֳgW ̐$>9#=T+1(4@ R]uc;@8svDb jwa\WR;`b4o$!3 `On~#n@j[Qx q'?&xjXPF>mZ'(6zҪ4%nn݌)-=}3WПΘXm+<淝umC(ͫgx+1x|SW :Pc0ƖX Ry#"iSqЊK7(W=H$?Jѩelá*Ӣ׃b #>L( )BnwePù<Ǯ@?&;:;B&wJ ?18$ca; Aګ1{i+y Ix?汌XB!b*caPsVdLٗas ncڟvtqѶb׉$ob;X.fبcAI$}l@a<`'O,>MPqI4RO&sNV1w+@=jʠgTsk7 4,HBI(OӡUid"$rdc߀1ZҴwGjh^?x0*7zq+l.Cy1G0Gd䐶ܶݱ'ڥ ; B F0q@0N2sFr: r+1 rzfuuWUWTrp29>U [mmv…Y2bqyg)rp>zq,Ү$9?V,q2Hхw((*9#W jV: ;82#bsס楸%IFc9;MW 4 2!(ip}N:}jyPe n9)B;_+6̂3>UPGP}b6y+{g"iZ*y;ۀqw^3ǵi-bh&Qϧa𨪧qm$ )[4g`~A "8E1pqǨγIDw!pAӏ$U]V"pzƵE'Qr$I]euœrzqR&-rn)PUג1Gh:ݕ*T< 1ϸRGgo*vˍpۍ *h-AkZMi;Lv|t8>k{[cPĩQ=s^9$)1x$|BG?5͝313]5[u&[b02D} s+?Wgl$-c$F>?OyƵ%̖kuTP` tIX}eEYk8{##y;2Tzcڒ$fG)N㯵nĢ2u>"i/w) za0O[N-%8jN* ^Uv:=9fxk ˅-92k[ڳrUA':μY^<*HGYsOh֜ܟ1K IOq}xEV-iNdm7'%1 o:B R*P [Ia tU9;4kk‘\[OLLV^H O_ Yq88y!s:}"C#C7r4E3*>*ΛJK^\o)g1Ip =x9IemI#R#&G柢iyk:Ir YJci 9]J(@O1Үud䉖!S\zVI8yasִQ<em3ꩮ.!z{f_-jd<C<眓1UTVRGAqzm8pXz[:i"q{olf<YU 71c$z |J*Gt !aӵb>tښK$vc+ :__*Ʊvg].G98yPjLg)ݸtb9lUxיBÑ?o8KGܥi- 3I=2b0'*3.s(T0psƺz u.}#Ȼv2FOLr sࣗfAߌW7TI9;Xt. b" 1MGeuO˷wZ쿲RM OQ͠]fPya\ae=V&-Ywc $P\TAa/dCN?͕kYYI-pM v=bsu[ HSR朴 Emյ[!#YsHG\g9J^Ḱp?Z&, ATnE2塱f'l㴁E7̧t:vFzhWzҝla 9kmUWjzz ͛Wě0*`g-Ͻb>u&%X )Ce$x+Ozy:莢RrH ͵T{q輎y @ `%q!_QY^[K|En5pҁ<a^{:SPȽNk) .mTnެK=P}_ ggR4Q_O-KCA|N8qXww5ĩf@aN6rGR/BZd_#V m p&gqvC7s#żG؃:Trh>hBm $XtMȇ7tN5G.B# 멖(nĬ(2;~eu.DBPI~$V4,G.9P^]|̪sAusۤ:"YYcl ojm# $yXړEy*QI&,>SאxQՋ2rd.@xT1E,H],0 q'dD&BwKV΍#R˅ی{)I:!ʑJi6E$w乖&W fz_E=Ŝ<8hԎ9<;=2->#y>|sº`[ѣ:ɞfYV YT''jZ]r6!T==dIO(uܥ@ aߵg򷒛嶪Q垍SJH68!p 8϶h W?*@98gO8Sda0axW(  TgM&#AvmN|ָXomrN?+VrH\98f}B\m jy ٳV.q{COY+$]0 wxӬ*ڥt%S((~NM.,r˷ry'p*6}[jҰ%f 0W u68Mv1G$aWpⵠX,(ʱE˓'5[0B\g(`z*!Aw'O0ݴtֶQ]N[?xG5'8*_,.rtb}X! m'aӚ688J束Shbɓ*_ f df?!1*O!L!?Ssqe9`gұemѥ75',dpPAn}<(!p]KOOʭEzb̹$elazZ%Ԓ+ʽ?:DuM)3ZVka*"` +,y72ni<@=.2Ȅ1,'V.ubdPӬN#&9bEt:imhгa@^xhLhXlSO =+[KSw F,Aݏ"'mh֬{$X ' [,#$`<5ys4 2\2W]sڻۧ7wܙ'KxLCK74Že,.|mVafDV@Ѯe ζ9ncդUF'8 coyKkfnD?@1tyEU G^3V|]d%IRݻj!Przj? 2? .y ΊDة8nέcu?%NUI /[jk%O0C_z HV"AHm=ΎY&snQ" W @c6"q5x̬%R=cn[l&WyH*>${jtӝ1CWhy08GYOk%3C|āk_Hx.Kh2&vcpzzVS~wSY{I'gq4wfR&x?z.kZC)y3kkn–;/D b,Xz]jqFI96-H^qԾI#n=V) |c>Zް㼸[LeyZjH(sIrφ<&dĘdV?Z{<iɮFB8hSd9U9cC>Us2ݢijd -E37A}+^.{JK%<aڵ6W9x?I}j'%w*{[!HfC1ԚoG,ϸNǵ$ܣ&(aڲX_s3`m/j.Uy=>(T(Biʤ>W4dMgb2UY7*pp"6qn)1WI25GڐeǜO U֎%bx#A.EHub!CcCٷަt`j*Oz%n(_spNAɴT<o᫢|[6$#wU5.n$kɅzSlMUf8'v+E}ʾB*jHZh@[3bMȪ76 Af?U;yσx;zV綍Up9 xmd<$s* r7:5'v4݊6Mt>@'9E&V}-'C. Q!{zcW1n6s9S)G&/p$`ujYgG#p+2r;U7)9Im0H\SQmbTɸOs)ۙ%G'ԪIv dAέӅX;vN%y# Ut;m9 f$:UU3uWwKF`{g~AZ)7w(S\)\T)˗JR4TN*õc?AsVpLk LԁMC$pWv@a٧MEDK!f +|pa#8U-%]T``|u ,kq}O`uWDm3Rζydgr33W-׿SJ۴r0H0ⷡE"K* Is=yBnψ}ʎH3xHpxi-N1ڞ]96ޣkApJFeHR)\xJ,!c Ґr9O$]BF1ϭ>*B)w NR4g9-R@SYs)Šrf`JZ)@1җfZ d*9R G"nebOֹݵTw$.rORsEC&P B{SUVT:5,d/Jd0#3z" #5Q!h nmB+@T{++FYL|'߹$t!Xܶvar@gMNKϴɒ@?כVԂѽ3Q* F0yJ&\^o‹t0#}ȹj&QGNsa;?/z)7ZIlh3n3NAz$(J7LojhI+,0N=J#ܳhN3Ԟ*T]uqBI^;Uo7poYf@'jo9^1WG,M[AT|{/@zcsWЧ̬kj# 0rۏOlU]?N@ r譸o_֜J8 ;U{@ٔbĝs7E?j^|By5٭40veB 뚲 {ܠyB9H?fCu +)N[dgtv[ys1H3F;?#Yź>͛[F\0}cߜHO 2@۷}1DrIv Qo|Ϸ_42FI H=N 32YUR?9 zu(I05[eFWA5TnxHF1Vγ-v:%ˡS3Mʲ3M c5˫}>TٯtZ;XC!''lQNkqn^9AU67jh`pw95wÑ=ܫ#p\ikgk4-^'tdD1N9A=+Zԣ[`Qs$?c\֝,UrR rxoµcfIĉI’{c{:]Vxep ~wmÂG,NZ͂(PI7;;뎃58[/` 9'#X /|ϝ(+eI d~|T*DF7eqjZ;i2cPIbqZX2`gޥ˨΍+:l2 k\*Wu%ا\ZZ-+BLzл@eS2{5cd}*$3#}jq&m)$(AlB8_au$w  䯮vV,]b8y$ xȮ#WnmYG^8=Ӵ{*!,T*'Nҭlavnrp?¥S擔 jԌw9/xZ;~gul]%p%$ WckF6ɪy*E)G8';I*׵x…h!BŝXg5,„(KI>ð]JQR #. A9^eh%KX %GQZFӕ7mf_tl{D(B;@<\e#f9ozҮL+"8$>9TK]NzRWlׄN1sIݜc<N,w+95,B՜q+-~&FԠ?7p_]"3P{^b(E(+3gnyϸVӴ;Rc%@Pws\Sևw"QG -;NBc8 OBG`"[n4!bC0q`GrO=Fo"'_iswH4٣b$3SM%\;h{̟Yb';TPH'Q.-)$o!NϘ0uR0۹HՁj}B[!/.c#h y)6;j3>vP<ǭT[>\ 1e\|}3]ko-f- MjF4M[~7 M'ۿJ^oV9=%x=: e۽1BpJ 'nZ0db|Br~SHnzļ׾Xfcڰ.l~U@$sX˫IR(!N=GAk9]{NZ4 2UI}9?(Ԥ̐G+$XvwH _VOVFV}wN@xTZ*kK!m4eK)DEn$p^;ʪ;QI@O2Qls|1\޻wl]R&Iw.Hw$2y9$_c&kNW`0q;WMsV"?4Oぴ~=մin%In܏~CKA = ?5IsNj\ <*#R.'ܝB&Ϟ[lۚM- yLc1cN5~w$`]A&.-垞;_0NI5vVq-xf(p?޽jh, 4RLd n sڃZ~UB8=}j_"! ys635;rzx2Q䑇)-Qqz$rU|>5ۉuiN7xoccroӖ(_<7n݆ 8EK,GI;pGY^o$ ʃcֵ4a2+WockwKxƝAIK* 2{crz˨$$:?Ύ6'8=+VdXb\FGXڬNDO!N{Z7|B/QjIKc{M/W-.gnqo{f*O ~?UɝFgjgǩ$,I>ⵅ7(:fUkrk 2CpAtWNr3;r:-V`y0?CT6|U5%{-LqV wXiMIHUo,/cִa>ac\N I,O s|QCͥ:$s77VL?Zg,b62(G8zce֜kG2c#Lt' өka%2e(EX`V3] 4[\ IT;wjC=qyr#N~f'kW2K-@K xg;t5]`$%=Os.̱pV8~A-6r޸NҜlށI&>w2)CES-ݒs9QR,Sl jԷj#eRѮqNbfZMW[46F>T>TVJu|0 L%!bc\v~99_lJIsZ65'CAEHhe*m՜t)pΛp dRVPNңmd)$Veffє>V32N (%8 (Zᔀ {s2%ej7-p#;-U ں, @ZF@ʯ[Ni-v2gXC/95NKc!kl˹GOoJ}ȆLJCZ?gtJN̅;d%BnNaLRcIiQؒ #?ҲWzP]G'csۥCC,LXy*vOD#GZmyq)BM-GRK[I"$Y䑌u=kr-[$8 YZ!Q r9-ZBy1n 3܇YBA#jEU*ۈ28J`{q$joOju;GV'qG)]XK+;۝tMc'\ ekrIjU2@U=$K:V26w;eF9Z@̑QNKg431vY-c0EW Չ8i=3MוdI6NJ>*KthapIҕE#Uk(F=ŭߔRqTWw2K1|Ep?5j,1b8̮l/'<}kb&fa9+jummB6v#$dފ@GQSUϻH@ $$gWF n FN Au5$qvf_;0v떤fsgtD͠#B1gB[aه-zG= \yr?rNFGv9LOșKNC:5,c1r-VEn`u9e #vpTVo $FT&YPS2䟘tڕ2pzR3.AsSwxVׄ*]ËVH@v :QF88 1K0fQV㸹uԎyG •֜S̑Y6ϥ>~N}=@T RznrKa Vfri8$vVu Uc l'[Gʥ$p۬R!@=U@esޘ$o;NKY#'ЧǜҶQA FIN 0qֲt0x挳QQWyܮ 5T$?yo*1!Y8QSfS^h;$JqUUq8 mlfՂ8Ww߿)\`ʆqeC0*>''=YVZ;퍒EsU&c=xl;{prs5V5&ߴZjW3,H+o0k?Ah1qS I8"*+8jp|]I޽v Nqqi)iաXU# )A"2֭۔IZ$ >43B #XF9n{}:Mrݛ3S9n͝#feی[ש7+ #߮=k}t2Bf!kvـHR5EW cjƒ.FtUQJVhnVH6nz|mЕH{HH 2x*4 l|Yr`H⺣>nnaݾ T<b"-g8bWO[PȒC$oU@dN1!hoKvésO`҈$׼uSo^ơN%81m6g#I"{uwwA ^QXq$5/v]+|듐W+i6Ʒ_#d]@I9U+ `w\iY=@9B99ae-bڊ<wgcҠ'yXw.@=+W%ȜmuAnT3K2a~85$qFy:`埮jഇ#tje59`Ң(lmf]v|Ua#DUBO>SG]64t͐c?rЌv~w"Ht ;{If \VJ;Gˎz{cұ"vZIIʺ#׊ս1iΓΎ͹G1F3W1=7o HFDa l%W2!=?3U,~>_Ͻ74cQ3vj6=;c>fJ>'R5ʎ&frAN87~UhΦ.&1`O.QR*.vU Q󪕒7Ѕ֚M&b;CqO%ȓ+"|XrOGki6VW7v?S=+jmї2oh\p7}KJem/+nl#9{d֪$ENJQ6ְ#I4;˂O>}:~U& 1Tt[EQy=E~UOcw}+ŗ* ȫ)_jTK&I3&r8u*=Ske+ 0v>h7ʷ;bܝwimw$yd+IxKln1NӬ vkӴXd&й ~u1Jk+&SVEtt'n'9 ҷK(L3"Hzt9jZrʪXcӚʹף)gUrNIe.D{I5&^L-B7eC|kIHҏ,x+΅*ՙpf \Fp[R4-[5IWo_~Oz!XH(|g85'G j}-C *+myEiJTl%]3OjsJ.ђV^snW0~FXvi4.u8DGmV~>oAW2pXn~b8N\AKM[}*v6DU8\wϯJճmILLPQǽim̅Q"wֵ w} </Bb5֍ <ԝe}@X[O,i_x5Ee!yr38kKc*̫x} \ZoSB+|X|^ifx!G$,8A@蠯_SK$2l! Dxny#MSIym*4f' @ko{Y= ojGG1*v|ʺkKF[y0qzt1nhy gCW.P4{ pq9ⴝQ_aAėPfFŁ>k75Fp9Q8j=&12xvL]H#3.c^zcv`Z!2H(t*3']跟d/x Tyq+Z$Uee ==ƷfIlnXJww4g)MjԳ#3$WA@ dn'v1U{x碌uDLHIâjOq$gJcaֵM;]Ez=W56p4"PoA, <ƵEט#Y&BOnxiY213H\pq`XE5I5й>5٬gGkm>֍k %h+/x:ӆ3+S[IPq ocݤEG n%NZWj׮"H}QkBѭmwRTA*=Dltdfs˚%ō´q9=xW5{4rmIp:T n0!$j_ۏ~*SpIْD,ȀRXK3ݩ~=kU&"i|ǡ qu5tlP=?QMrOQ"ɐ\ˎ[uTPF0~82m B `&XZϐJ嘀G+ir2_-vt)<>a@3Qcm۫u! @R}= 7$J>C8Zuwb;F<=5%7;7"w:?h$@q#i<]̄7x=[BnD*(ִ}]aQXAsӔݙmYVl!ăQh2d20r7aA}>j%oιeOQJU26pL[ zp9d{Ylv(>n82c̟qu$qeqwj4/bcd9 k'h\5ĮRL>=}kѿy` d=<~\T%̎BV#,,nS2kμQqo V(>u&%oҼ]wϚc;i^$t:G&#!o-z[)]e>pLxAV!W{=]69OP?*7*64(eKVIn\~]a)[Jydxϕfu)渺"R0啰cBV)EEs{ d%770xX+I6q5["VҦӴI%rʳki^,7)i ($q8ֳ-77\5#jy5l(>m;[<8i'4PROeNKp5L[RpzȾ2^Xr? ]"J`ݜzc51i0ˆ=?*;e;N,}g %$e YFT9wjAa_N2zd/ۼDxVmջ!I '$jkN#n("'sg}j;C[Hdf8'8 }kSCԂs+HZ-rp)kZf}ݤr}]֒a%X#PsySP%6c7)ٕQ+t]4-]<[ʑQvOq"~Uzqk$pֶY- evq>\q1 40p{WECo*Vu#bΝr;rn1CUn4Ut'5Ф[瞄{RAdN r}*!7f79GU3d#|Otz՜vLc-?>\r#55Aȁ@zgҁ(Z6%B"2 l 9T1|x9QOΖ r6 ;SXHn[R i=OSҼSKcyKVk]C-ը*r'`I^?Z8dHr01^V%T8ɐFv 7_*9$p?{>(SrYD 9SZRwp8N٩9Ua= Gz^ΤΞ˚V7p T|Lj(|61E_bv4JeBqЙI4,'cҕb:ce *G^OJq%兤rcuq}?9v鬈$2mV2sNA1f5Sm7tdv+h5" Mf ?LƐ:)U RGj2" \A231]7Ը,:8 8Slg0OG|^9pu wT98Ϸ&yme~jtPzpM$aJUKsZc_rPZqJKWb8Nq5+r[QfhL8>t5ChH!@ez59/Xc략Zi< 4A=1\NnOD΅4m_r|T"ۂwN(^s6Xˮ*Է939T(l`qI(uEmȲLG ?:pߩ9Ǻyoo9bKXclZRXXЫ7UR 3FCu!$Ab@* |.2H"ʌ}+K˙Yb22(*A9[։a4e䚈F^k䡪ZCgI$eJp{{vn`I murOZQbd`v?ʚ͙9II6wVN`ԅ=OJ'S#8##5k?)%{3&nfrO3jgKJ5[AoLPobN2F3ڨ]ܪ$qaA5 E(r{z4 TNrrrH?:O"Rrz r 敌#p\MQ*2*\z{`rjy. )X#`NH_LVD?JFnY }[PNֲ}McR9=T2[ۆ{r ZαWM<=;%Vr)`T r!@ ?ΖI.C +xע-0-^_*1}~u|Enr)l9}ϱvиМF1nVfZ^$$7NޡYng."2z~=.{%r$T!FG YNVN:)J֗Rr˳.w yk5/g休R³%6i^Itͦ(VNN:vE6l/Mrr@OS[]zdj?磸b>?#Y{OkZ Z!1yH#d O$^}+Ų<܄ޣx+zyd2[(d|oa<R_N"4*|u6l~&-YZsFS>#CKv]姀A$ ہ_gi"6D ³Yn-QDB 7:vfԶ*Աdo߉UC"P5v`xFek) nxaZA-θ$g+P[־(0A0c;Q[y=QBhYȍ3 #'01>T+Yo!fވjZo,@#ܚ qsգAod]ie;ҥ$f($luoF1r2spBŠ@K%F,2+Fa IEXK.+gTyH"9@#UtQJdFWw4-R)W/e<7P=1nKJYa ߁PxvN1栴B˾=sOQKƣ?9\ذ2r?ڥVPTc)Y; .ؘ xA8*Ӽ6"=H⻱;1O ƒ,W9ld瞵H95 )b -nfߺͱG w$~ҋ⮭9*AĶF*-Y$fŔR7goV=ʳ5ב(BdS\nK}{nk6![)@H8V R 8B?_αSώpЙxP$k(n5!nY |[s] 6G]=ۗ;a{#Qܭhxf唹T,S35$2 ^SFX.$M${UP#y=;iwvcQ$ϩVo[bY]H+t?PM`o"!%]f&- On1ưV)Ɋtm) IV<1J/%z}m8OFos}r.Ht@T m9޺6&dL3%^@>E$A=2V4posߌUٻBN7 czܓy-54[%N6jv ~m$%Ca' @UmߥbKy3brU!1Ў5 2 Ċc-ǯ5T⹑ԉJ*9myf ͺ=瓁ӟ¹[]B;v` 䞿L~vC"aQ3wsq{WCK+4I/MأǯlU/K51F{խU[ITB@r{TLX"oj<ܓ5rm.aIcuÃ\YZ䚓<RKy8?U6GmnP&ʣ·-""7(A,XAc2  Ga]\2#4OG9A=~dαb Ċќ >ztur'g &hT[V0 2 ,:;y8ZIN6DW* ?ʷ.Gm8V[ g'?Nk\qJ^3,#HV^Gmqۺ&-A~0 7 {}DqDM ^]۵Q|}AtNnW;QH5yUk<" =iSC/@RX?4s4i<; tĉg E$i|EM'W Cx<շYuČO+_nSI#Nzv%kh˳ &|=5f-PDvQ.8YK5Q4r :YcFP~dh1[^f2C,sQֺUE!mD;ʹ,9q=HX% ~\mncQq]J$T%bC<*YdVm20,FN7MnSCpB -s:5tucYDpU}?:)-؅ z]ʇ2<ꓻ9fydfuwo@+'ܱ2Ͽtͽ3#}0Eaؓvח*yv=L<%0@#?AZ~M@P!ymђxNт85uU!1$bZʕqs]`"l5wkJnһ񪾣1 |ï\ۥKv3 df>V[wf`@\t}?·<l]tI|=zLz#MfCp iv2i3I g<ֵ1Qg$C\X ?3?ڳ>;#h~] jbMNrNV=\n^IU27<²qVeT&RqΝV=+;ZGEHNUIIӍjvB1_zOI-1r2133䚈J'I%y#n>oʙ IˆUۋ8&kucNJϴO25 m+M9E4qBa\#%n>ºI}6B21ZyIfP~kXT,^*_Od$OO> J5 Bq ec{N[tJVw05fCs4`yrL1Aq] 8cc5ZI ]"ClA sֳ(YniYO/rAꑘB]u$#oS`OJøDT%8C̿# >; '[r-!1XyIS_jۆ6$y`ڵ.X_MY0y;,g5LF^Ւh;347Z- U.99=Y5nƝV *#*dgUBc8S\EmwSўÃVީ⒒_Ua'%ǾMҘp^i>^Eбe$9 =e[#H[֤'j2Se坸 =()Oӊվh-(/@gG#8`sz)FNWm%+iW8 -ЎsWTo"Y)''zT`QE]:16QEQE0 ֊MFF(1PsI[ڥEV ER1 z -7x$zKq!_γQΖQR(4} P܊1H2Vr^1rKH(f1MUW4Rp3P[K$< y*8 c79I=Jӽ#օ؃֜rMrɋaZz;P'Ҫ=MZ)98wTO^ni+DzfYp23A@mA|R #g0=r:)!6Td#D=ϭ"6Xiޛ$[h~B0BE$:Ҁp(^o`A9Q?o))667͍+.-!nAVFLpqJ=kSZ+Z#]B aNU]9/R}I>l <bTqQnqj dWO5U1'S9۹tY\w<5 ^\ f9`3gsY*\#8C\Nݜ[Xـ#sU6HΪ]ğS&$*J uuSXpT@U9 W9;z_qڍԧK1>Q~&H.M­f AxxBe'IcAdV ڂEHdd=;}sis&uRXLb #(0$}{sq"H$u H#RiCsGIWgRIMH#VRI#H7ΚEw4v82£hgVJ֋Գ)j'8__kE$%f{2>VzK}$.1`0횂o-?FF?N(=eM{=ds43Z[!Yy: WA( 9՘ ?d:6˺cH6|R~y֯xr+y"E _KX\y#` lPK[[+dA*yRӃ^=6Ț4] JvPK*`*kxhHB*:F09&qDaT$ʁf#0]~cFbͷ} 5jʉmYs+G1˺B#b n8=9M)P' e0 fm;gI0z9#x.bi#$\(W,w։TJۉ˒-tձUP|@ Ǟ1╉td4wP:ϹxQ(\y?cX?L1ù]G?eVQieE>ikK(% X xN9>5ET)>xUIq{Úu;aN%'ק5xy+dr9)8n)]Fr3 - q &qɮKPya9AI~BG~\1\!D.v19+^hd&ODr&u<>\3H%P{t{]@T3L 4̚Kvv=z}ꇁoBlW?^էI𓔬sKFbDuۂw.~uz.틈Q}?3cow0T` l5&M8;ִs=DW-+\ wˌcw9c&4M&69+N:T:sK,1!.pS/QcS!U4].]jZpv dxh!eF w89'V}FH R!V=,})qcmqnqӾsbNI\NQ,۫#Hv9op~cRdHNX|u5ߩJ>wn I<x<2rdU.Njկ-JΤ0j܊}[9C#ckfKX `GAŸ΋;{hYeq${v;j?![* aNG UkxRce^?1OҦi~q T ڹiusiۚ tq  Uǽ zsW, ֮Eois~%T<EPtqZ[a/*&p<{ץ;9D!nz5{!ZBBMaF"gTڒCNY.dL btkԣKTiQB$}3BOO,П" r!]fieq\GnDO *ֿm]9NvenRs,>\&0.~s9ᡊ\kO5Bk)mS߃eMr@4#[m%17$na'AA4c87׊ގR.#c1AX,phT=1Ug5e3NZ_fb*Fb3'r?nk"*,;:sִDmV@'C][ [\=y?sOjɌsƭrn`OJɻ$<,~cj o7lv'9¨$s1ZkeoS7iZğ30=aOj].:q0_ҫKm>atGn"N '>(*.E9oNnnKBnӹb&102=jElC8yJʑ[3¹e $N?1VY7F0$9Ƕ:|K9$۝ŋ>"ns׫ ddX`铣Bl'=OCfbmzu9]LM;"Q3W'8{ai X tZ#؇yT?3s M>NQya $ LO%%[эbaT d;WAqgƶd6%eTă9eh*sMV2 ݂KiDIP3 mʨ$93֭FpA'Z))] LzQ" ǷMHDi żlGc&<\Ep^%cuxB]S.0s.y&ʻ2nnLnݕ\?[i}q*ʓ|>_W,-3*#;x1(gl+YjDb=;Q{2D-’l+͹[,dr6)v^!=N_& d{{ӝݥ(Ys0Iu c5g2(PvO {ƽ̱F 𩔮܅YYYO 8~PcFB mS_<ϸ4}8㹧8-Ew)Yd0$q;XS?$AriI,#^h80riYT9Du#HLԺHM1uB0O cjiXIT w =AR6wK9(p]ʽd'p_jx x4 vQĶ UWgkhEj $$Nc^fQiXcpswI ,p*@ZA8'{QZ+wH 0 I($b@EYF[P~ԅE#i-DZTst` 6gpeu^qPk˖+ћ({-6){`NiOrJT]DLӱek#|r:=E] AR2@ԄSoU̘vUBNɨ]*xl 9$ a8i(I2@r 7|g^$Ugwrф3}^ƒV1eӌ=k=,nV`<.2J`!z_ZS+(VA IkQɫ\GMKQ.1o,Wv@#;<0zS!?:Hdr+VOT9E'NO[X{sbx?BysFMr[h($t&*G {0pN0=ֳ&W 3<U_#?k{-4׼kGq"2`G9)mg]d<{D=<~DҴхrP}v3WJV c@Bu=+[Kadx ?ZֺhߗX݂ѿNz`zbux..-!FwWNF3=ϭkw_O ٠LIF06@7OZ[Pc!'X:cZURѫOǦ?\eYehYzkEKQϒ ;xGl5B0i.r$<+ŗ^-zθ& dGk<"8G@P1wS99T} (k4.J I1<)$ zӴ.}y#$=*quۘdrH_ltVm.%%tJ}綆'/IoL.pAlr=O=zTˆHn\r;z}jOo0G4Cc!~!K]HI!$ӕZo^*Z32 lTE#߉7#MxO8nmdfY ǏASi&+h 3' 9U%7RViA$Єz\]]O,V%,b6C~ےoZidژUFx.99Cެ2BDʥ;~Ux*[^qqǦ֢h4G%CuS-8c4+>z6I#kA:J&K7gI'q=*q}*sqݳuv q*cPdUxx(ůi-4gi<'񲁂FqOJb&,>vӡsV'ܐc5z޶c[v: |O*PVF)2|uKQIu;q܊61$Y_L\}#1vL! zj[iei|Վfi7gnzT| wQq_!wϠڶ4*t$;\nQ)ݑׯzdxq~X] T!Ndy&N#v\&"uJ+rM:-G(UO*I՘]L ޳Fʤ.O`2:O4KE"0 zӑiF/S'JrS* ]&GRI*n*ʙsGϭ{8}_fio--$TRtzɯ!gwy -{N3Z,}QUE0DnOq鞞՗{3s,=sR3ߠ *Ֆt4fK w2I G(U0$|;]/A<;WZ${Cg,ѐs,稫$1.JT!Rv=sP^c(Ux ۲ײZ,ckkZv}N'~ֳ-8fFFN{c^=*@"#3Z*$Eb\qTS9Z[e)H+&?b쓞&bL25WR D6*+,N@m$+riC3gp+;Xi1:0mgkgΡ*xһqiqS(Ց,l+&ʿF3t=bŢȗ I2C$ieܠL@QVm͔wFD:nv$QͧMF#yk7JW6ϽSdLlyr֤rV_@dazz}*λrcs$ rrة[+z4pu@FdSQc9tZl'*Lwg#=k{Am2۷2T\JϺњI2B q?P쟽2/5ᘥP )[7qɶI%]ᶕU 8 ׽ Hn 6)߄<n;W:qȥ"M?A?Zv)N-tqJDqF# -vyH7FY VRIq,d$lJ!#zp>SB^˨Fc}M9)#w" ϺWi[I{ZQCMo5`gh@wX0;tBKwӻ u84̓<{mPa1NH?1Mx./eExpera]_i)_EcBEv 4X[a+v].?+,98\Ѩv.I>q1ר-;AfvHwpÀk:hё$]59koDI#NNN{gY nqߦ}j֡ }kT2#{}k+mȄɶS8R2ܺJ6H߳X&+CH :^,:Y^FTF  rִyB!Ux?w7ITTu4cViJGN?ϵqŮԐw"N%+yM$*?t =kmg<꒙6E}zgZABQ۹4a7-J^o`(bRLsNɧL5I9Xs5R1$'d|{*x6g|N#<L4%v xTŐ?U8!H4p''8drsVz7"ⓕDV# '~Wp_#TIC}9b? "ichݴTųG'+ =:pl!N/}ii&L6 ><\>D#TQF0?Zʎ-e+GBW 39ϷҚqWc&qq$qv?,2YO'6daO*f(朞.3=YI"?ZuԶB=W;~AǸUڤ,e]H « GP6mJVtFWlA*H<#g:K@f>ezj^ \SMSuAF6ֶG4یN[eXfؠ43J.'MGC8ە?) IG5j2Ī`9?p'gDcrr2>Ό\:hrD.nLsGqUm2XV9 QeCsx`Ug{ɾRQD1!ڪ}zƦ-ngmp#9=׸bft|RFX ǦOjxEX9dtfjaĩB inBI1* 08ҕByDdn͹#*e"Hۂa}T2tCuȲ-#U3EUBPb#ϵrӋ8t\o@@=V4[2RY%e;xyRd[C/!A#|t++_"ixF-]ΑpZegLXz2?3隥}:{ņ<+ლt=e~Ou[w @XFls,<+k)cR )XWUe|a+h\dDrZL9~vheHOlj~kȞQq?Kra#8sM+lɒYwJ3E3;xr=TwvA;Zt_*󰈫eC 5[hnΩ5h'J$t4Ik+aA@rr2n$ zJp0QM$NIvwB8*f; ݌'Ԏ՝v!Y)wpy=iׇɍ ,cojRln([0}IqqMU|sHdB#T3R&i/`eCFr[5KYT*B溡'nTc$oϺDXfc =KL$$"۳yeX71Y T.Qr@Y8KC[ec!R;OUӮ#J+{ Ւ-;0rd( sWz#KR6 v֛Kz$p P3 "=m,@m Q'ַS2lINJ%@rqYZ<3I),qj~-Q@;M;\٥P0| Է+ r715VB&I(78d7`7m\w+49*\u$;@Ř$M.;=1)M61`v5ģe^ȡ-}F6F@s{U,p6bc-zJAZ6%6&Kqڣ3j b #?_q}M^ s1Dcr}qgF(*YK)mG*AY놌'cGbrwOCRThW`qMe RAF+5TٛW:A3ȩ@y' dOۚa&2h$(P3H94>mF!-jgZQM t_yqPt1idbjR*䚰ڀ\ڎ&*jSYI4P3M$W3˒]ppM9h(3LXuV6%fǮ⼜v@zTqq;S3JU=148M'4֡BRM6u ڔ괵CQv*2 ޠlC.6\gۧP8IL#GE(6jVn;s\.iZ-?d8W$g\6+5+y5p:2N=D(]90ێzLSv 5aPJ rOzii^M"ª|sZUʷZiܸ>5wʽ :P;:Y dny9]c]OdDn- etHH;YdwCuUy23Otc# דʛ5_Oa$;u. UјNJ䉋y\``,{u96Y#$\`[ÀxegzVqCMx#ܹNex8"p)iϮj}?<6q'1YԩK Kסq<XE 9nz *WBrہSw8xcU H[3t$}k:ԏMq́`*sQYQo].ȴ7n.!KYG%GA&ܑԀ6g-cpKn{RCsۂ[?. q-9ϙbՌq7q}b,U?.P?Zxʅ 0@{~$*Nxԭ416&Ҥ }z:)$!VL]$U7.Lv$vϵW50Vʁp?JǙAD膊(%̶q(fgB8XztucLFrH ·HmwwrG",a u3SV`B#*I Պ8wR8BPu$Y5ȳƪNH傷0qv'ҮD JwE3v7${*v wXVWuFtnG4a oker ^Cs]V<*}OUax-%;Z9p>lFG\\WSQrz@ǀ[س }+ 68&AAz+8fQK* c85rbLyV;*Xކ1MInETCld~fEk r`L=Gjc\çh$JHrHb;>5۳-H %{{da~b*XyVsz.!yHh\gpd}s]fk$ sAFrzGZPP 䃰t6pxC*0j@P3KQO:Cv3 Cs%pN=}jB#8?k,O~r6ߝ28!B:W?)/7-QI?SX؎ۅz*mNK|vUF'OV^*IKb72[=zj*cFsh,Ow@_a èN=k9u6-tFK6tY"mJ+oeo ꦭgI5sd1 H%b 'wT6.ki4 T,:r=Er:.4P9!EQSb= o]7ͪ][,FnFXqӐ=+a,xe#!@Xc8A@[ad.nŤ&mq~+%q {A;x`P?YzxMEq)A$ Ma*Rcyv/iodyP7 vmmn4]cԎ? |ѬqK#nUA<=yjn*C;N dwJ F>q3#S'&Y^6Н]i?J~_0xUt# <g=5wP[?(fۻ,xny.gT+iЪ(?2N4"=0>Q@\dGQ&jK^˗KxdB `w5C%֒M>^`rGLjѨfjw\rOY(Oܷd'a!#F@bFntڠ3N}Tg 퀤zƳu%tGhe2($ymIH@P8w'Hn *JmYŀ$t99ӛgw +@kW' rkȦuksLP0n@8=ACJH^++Mx4yew:hXs=ݟ.3)R?y!qד=ۗ,!PgCb`"9MqV2II.͎ rzUtKFKh I ϥt~É9Uo=8~s%Olccog;GPq=WXE\#VPwxh)"ӃV)8AzRQF6@>`B!V5z ÊvR'=khr@SOڹFJ/V`V ծaxdݵ՛}*A"%#yBLdg>qP][l+*" ʐ^[}]\*m `13}y屎[:UӖvڵ$iARO`{+7ȱR]<=#c3)ʐeOJ%DkM.?VC'Iϵ2@Iaqq?Iӯuӹڎ أ O'j~A79J >\8#ZHob[nth8W>9͸^XKQJd< }k<;J,NqsjYuo+aUGrNqc}ꞣ7<*!"xxRX soeyd#vwpGP?۵t3(]?A\eF }<{WE#i Xd͕qֵ$vI{[dz~c=pGXz^k ^W|8?{Z,u]` `}YZ̒ +"I'Zoգ%ߨŽSJNpa38=O׭ooaWy[q֩lgh1 Ad {U-bƙp-Qs^NV} -u hv N9y[GsS;I>+Zh̋n@5 /$24`U*@#X^W]Έ47VƋ[ `Y:ػF-,.dxр#'#V{019m#^11fkv3DPcpI$E9Fڐr a74h~A~t9ZiEds8o=wH?v:Z$mVQ峹cS 1r}+2{ Vv:(cDNOSO=ETW *G?YF O qڻ!QrZIYv5(2*&2jY8\k'] 'OˀH}?U^U&.e-4|ȄoPǎQآ9r:z=뗛[1#,N0 RPu%cU1#pEH#8Uc מ$W'v8^$VHH@rsx&P9FH? $kC4G; ֳ9K/ ͗4k ^y}a1͞KvSWΓ9˖w Χ{ؙͲ<3֒PSntWDrc6po[Rxɺ"Q5ZOpE X,xl~b*GgyLjFya??:oW5RqtVvnĻ T,gTxba7cG8H$yV#8' X'8䉷f >n>tS -gmoZ[d7e9|Mev=(Y>.1b̀(玵6.$S$+XTB82NOˌw[M(6yf@N9j~/Trۉ$w2sW<Th:]I>,82@8`rxoǿzO)Eu;L5+pD#hg FK#Dezn9XB"R*ǓD KgCvcd$d1s0B#kfR2o<dl;+MRH4;&+y X#ֶdla'jLO'+/͋F6ɵgDxs ~Du'MJ5p9K{Yc/>foS+| 0 ?P:ǿՠh*HÐ?ϭZSzĉ%g'2}1q[)$Wl"WSCy>lisqF &.`&IdQvc|AtYJzI?mg$͆Gd0ap)90<N{:>e@ֹJ\B"u Q9-6fTy-գ1qY0%/q"˜en8'5ipv/[M\+^.}i\qkV]XBnݜ?A됷o&X+oPjưDmc=:_|%<_hlI 29XUb$M[Y kCn11sd=k'_%ivXA=)O!BNrOLVgnZ(yCp:]Vʖ3jɨ㹹XQӫ3}LIjWzRYZ]qHPԊ9cw2nZ\L 6V ?֛y;̙!;B.=սPIT$ĄwnQC3{4mK3=mfUI$"t!ȭ*k/suևxH>T;, {x!q3F UlcvC0&9Ι4!OWPnTl 20|d϶EW6㌜s1j-a\|̧T㹙a2O߃O³؞FC̥;* \"&?yvg>Vem]CӤ3e"Ʀufcr{+N $(QzfY?܆;=gOEY13!O/m)]" V$L9An=jycv6GEQ$e65N6twM$vk9$ݬ\dm Q r=kb7 zw=y8Q]%hn$d;}=1Df8ft%IDx$hI&#+pHwҺ]:颳U5%'M(ƚEbёLˀ+6,Mnb<|AwCRBy7sV|wbwUb.ފj7Eęf$չwzylީZX^2 bAWO:je+]:5y=+[>I\dUqmj7q>V'5%ϕ `+Ue(gXeԜ+KٜG]q«-I"͜׊\Q^‰!#RKpS$=1?g^W*1rvDL#V$hd⡷[H$\ n0]G@r~"ב,{`2Us;(#B+Ȗ.nwkC4-$VEF8;XQ>SdH2(N32GASB}x$Vەa py*_bM1J9n =_RO$]QP*c`j*88ҡwݔ|Þzg:q]>ڴ9Zi,}x>*()y9RףAp%u\ZLi yUT \2NXAp2>0ϽGwnlv`UNrHu=]x8p;,~.FG_ޠf ӟΥF`G UVH>1S淣j\ЖJWV(@((QI:`u@[Ο3kG=I7B݃qW)u5[סe`y9:rjtQpP[D(W?YIxu֢@-q]٘R>|{{SԧTD9]YC) B:wjj SW`QE]ҐRN-1KG^w8ȨvCEʴoK) uQncXs''?Ju*q QNԌp)g 9GaSAhE ;>Y!o4{2$sOM1S})`F ?U8ROk9-6Vr*$w-})H$y֘ ܫHc܁p2=~Hi}d&;OJ2O \e1;zRǐXz֐vI}?1NpGF2j) nݕutW$4f$6 RNsHR_io,A!H' TpVI)HrǾ=x̒%qn11䎜)'71T@8}01~jŰ0DRw$;c/zW5.fcZYldA$(BX~H1 C|SCnu@eu˄cs ҭ(89c4f9&1Xـ֤S ݼ{yrrόוAJ,ܐT{s VI]-ו^xZg!rͷ^T6 ڠ˞e!dRUp1, B BdŰ}IwƧ|Jq5I'4$Ji1n+St1I&2*2,*yoOAU\:u崲slj+kڗ+zK(ZK>d.O$%Q@N#=25 r".H`'O5ZCu:|'9=;W%7Ϊ[MM 1 US#xJ6@,9c?֝4K:zU;2;6?Һg]NlL[b&1W>Zf27aA80{`T<@OZ٭ЮZR7`L ci犂)7 9JCxG#Iu-u$dm|i9緸ެ;\qOjm;aEe vNFHm$}N9R\v*RFtd̲,ؓgq@aշ輼lJn+ÀEH:cjIF2bs1-Krv+r?ҳJ$)E̒.gЎ.d G a[nSZG Uw*RzS.WgȌ-mc(ʿxznxQm8F".ys$p)SǐKso5g_(ᔞsN74'?nm@۶ ;<e;P?~[ofc$)U N2GS>Hǁֽ:J1-NZ,`+6).qJ'.C݇ӊ˻8`x~2YNAKMp\O6wmYc,yL ;r^u.h4zm [HbpD'nwatݚM#FŒtzW)UebĈFH# (;ZƝ9q{W:8SIF _AIfUqڄWh:_hVA =~u[RQiRu7fip N}_VDOqh\C,O'F'U. }mp$~l ?*ვFܵ7#ck`[6Ǐj-C-sd9'>h4$ˀFCm?1]Z[jm<JEV2ꐫE._q#8!ZȐZĂ, ޕi=Ի"pwATj"%+H 8M\)Ӿd왹I!{:G_%$u\F}1Iim+1M} EI ͫ2$,Z@ ?ҺKh0%;lyd=VܮgĶl2nrI8^A6W7ɽ@%T:T^- RiWP)&,Co%GAzҗ3i&DԞI襋+|urAsG$Ns扔*b9gF24P[N"Q35$YβPdy {W+'FA{t$iV+YGĒdCT~irRZ[|5œÀ{DPB+zT;29zfyc%9!%uQs6vzt?ֹ='u%h6B m.=&k^$[G,TUp7WA IoC$Iss竉m#J Pr0G!GiԞLҨr>-$I? RܳnU^N Ti[#>j Q᷎k3)J'؜}xeak+$BX d;qp]jFߴ r"}An*-U ㆅ̊a\ 5f:KKks4Dy'*}#91yC;F Io<(<\*8?ִR'1nF⧰=s=i 4) |d1,NyjA-/yq y=8nƷ3a,`WA8-Kwub1( < 2? 5{ۗ>HR2X G2p NEw$;,dz*/^(`77IrElV-_\ޔkmhԨv~U I'Ok q tf @涖S.NJOΰާTĿ)͍DN%ڤz^N!nv߽p\9 Ta'o@SǫW%ܖ1 c @z¶`ݾ#'ko9=1ze+]Y dq .x",vP$[Fm`7+^ /ŶF~ ӡh W]XGz|P4*Lh6*zU?ҮEӣ6vJ}ߘܥtjniKoJ)uvw6іY~n0&\Ώo%TR̩N~b(I`'c%JM 8 v&RUVYx``;#;J;{&[p9SU'-8 EK0pFuzȶ4Doۅד*r?:} #vn9ͦW1JNdIˬ |}6{rrQS%\>aZ*JYSUj1z:;- ɖayt ILԶXy#[ UBX>AsPi0*=1D$i|AhwO wr?`OL#j7ýI` w:uw 4ET6Zrd:PBSqSڸׁ"F 8??^\ѩ @'q^!#洓Łv#pG3|:KVduZ:[AӑXA6  ϱ+9ϖ@+cٱV>' :0w:ѣ<,l;~`Һ 4=ìaI[?L%Kѳ8ڙ[#kc&$#=O[S2&Z;ع{w$kQI"$Vbzg>ַ挒 #csYe֠gtDX(#Jtu2[yS#ח@%8 ɥxow*#rHa9>aHXX|1#ׁ*z* rXK v%rHr8JDE<ҙT΋PX c,Pn6\lFUy{ $]2Yvh8aY{,ӰJƮӼw4a*v8 \GuP\<;wNno[q$u3 IWbqȡa 1x[A8IZz荸t R2qRD0Tx%m8 - ˒FQ7Cyǯo]̏ۄ3xJURH_;R#P>޿Jr]A P+# Z}eda.(|}ܜn:*RLF 9sB4nڅ9c0AMgI q<$UԜʯrK.{tPCg$qڮA(s0 u4P< dBF2BJZ(I%B((((ԖraJ%6ݭOp@8#ֵ܅}xsac nrSrv8y8dCRӃe?Z~jL 2p}zJVz+;iGZBܰC9v$㷵"j;r)i| 8)i M,^*HNk#ցM Mhx 7#`O`GLf TON9H`/aҦAigҕݽhQ]v$(W yVzA/ni?ZSQ[/WvcRǎxN N#p#?r^HԬCEc qЖx#*:[֜d{I6T_rv\9qz*uxOR)9^JV \$szw^**Tq.)9őF';8zn$gvT0o]nF۴3N%A!8?\:=MJA#VXd@C0=`q%ǂʼ)2N=M,5؎Bӧ4ѶK#I \q ݮk'a7U0) '}lvęcg{S[#Mhdz]N fkD20l+N3Q^o7.F3p(bt=k*zimwbA{/-ȉw=;P}9¬^6W)tf>52+Vd<&dy[c"c "o!2 R}=:[k) "=yc%RJ{#xT\,]~qB'8ެD0Krr: 8 B䓆鞸46(9&$LVs+Ia`8N5bgӶ:q];2$j@P N?CZA]ʴ\&vUÅv{jҹ"9#9k;So9`I=zIhD+ t>X)r&[5I.oN330S uhEQGt>ŌɃNըN授Z̧-a) ǧI Ҭʁ$:g󱱵@Urdu>49.yJIiٔޝ?UhH`\v*3=)Gw9<:=:kldG``׌TW_>#R\ =1Qي+ FCdn~U^E̒G u(s -J*0y!U`(sϹ$q{bBA qq®-1Yqv?+*:em9=?Gyo!ܞP '\r=tX BH? GԮXeMAb8N:zV yv.>2Ec"݅9q5_[{Sᱷ4ZlEYmbsr+JQ0Y\Oݩ-,.Z Ht$Ht`=Ghj"SN#FjmdPBnb2OZIXH9"[T<%jWkv;)U0{,aA7g=v}FTki z2m5$3[6G*1@P:¯Gf%g` π; c&¤ƛV14;!Ad7\!$pvI!gjfjI^6]k(`Zʱڲu@?]F_omzPFeƫm=BZH.c*n1>$jcSdOadonSwA,Q _1bǚ],Am8UMK)Q1E o EmɸLVwB22.p̚ dp jAA<PH;:qRbQ+#Ԍcfd't ڳ{raӸQ=褲Y0lt87Qv0q1޺hIVi:SLyrh~h96.AQOk>A. lZp i`,Sz.-)ca$mafW?p{cҧ[J+bNI|sǥIQPJ*x[x˂|-wo+ a֚3,@] I%^Pq_?`i&9X[ '=:5u~d *? ?;zuf5F7қwV :v<Y"S@%2PBR0}ȩ4An<6M,Q2FA'Kڭ3>c96''+ӯ"d%vu01:D4wM1Ƿ2T>Ƴm4g%1QgUz[G)Qq+#zUojӝd_03nT*o>TIa"M2RmِǰZƥZHʑ#(;eFpaNz kB gk wo=zAs9%&AGi5\H2)fdPq`=קhV4gLzե9 RV4ʓQJyq 7hT9ݐ3qʴeYϾq8U-laТogU+G`g#.F$gہOd5ĦR=1ߡ{{a<0J$.ϔN _요ڊۺC۝P7v|Mh+[H>թ B!r3Qx80=F3$Vv³ JGN+LT\#nP ȭ.[sJf`8 ~#7 QNIYeS0-};V] I I`9HEoaB1=@dְK8&{5@sWk <d1XhY$sov<2Àzjb40c eV^U'c|$*>f*WHėV΄Wv@*Cnc9aF{xlq_5KW;&ŧ}NWe Ơྔw#qo~2?y|\3`>A Q֕Сd#_5H&!#*UVJ6VS3AqrαXnQάn Ef@*@ im&$q ~> I.#yQʤ ާ&jF(:<C[ylIa:]'QIȌ8HqJuls6-q2G1%F<7@>l<9gVyCF_`=9c9I+TLj,MF pڼreq* P*ͷP;BC^! ,%DQ$ 8?/~H^qzNHh @T`:9=y)!Z]nH^pE|H_8ǧ߀?'UPO/ n\bDmd(^Mh4M 77Q#M^SW:{m>ȔhP&̆a !S64D0;$=pGoZ͂%|0譐xܜ6~*Z(YA,[ 3,l(A;+ Ӭn%H@<` '=>5Ki.UKD=*K4Ee |x yۦ;]=H@+%u1I vZ{>Eg&z"MtGI$S*ջVEHm~&]"}PqDp† O={gVU9k~5ݚWj%HǮ96+H\DEpIƙKHp.aG#^=A;Wc !bp`Q┺.^Q5 LT}yrm d 䞠?磌< \B*:L{\ޥ*3w #؞1O!@ $l͏{4Asz xun3D2@N YUp,Ȫ||j&.QxG'<[X>fЉJ19F/dW2 x@I(UK-RHcY7+y =s]5\,xߍZj):bEDcK-1 yMguRQ) 'Xd '[RY1v\Τ[XՍrrxSJUV2YJoy4>Z c߭ZIڥOS3k.{bI6;7p> bu󬰬hpYu5}zMCnFv=+8#K\4");Mr^!Nd*K[gОHǵh7 pE3y_~7;Q]$^<{ٵخt]w 2W pvu([!,x t߉kO1NV79~uzrw4od1Z58 , r=0?<֭\ܘaH '$tH=?FkcL,.Ɍ7ө7m,u2xmT'B C``=/C#& :vMH-oxあciOIs*BE:  O:(>EtȊ$<(%9vkl p ?sihqO(RR t s]\m* m${uC__=Ԓ6I0A]Oݤ s7|9Ue<2e%)\_^{Ѧȹ5yUXtdս,|Y0>! y@qג+[I_QrcþqcJQ́PawA޺/!RBPz9'>5r[RG 򨥵6$[tCaiټr૏!wFƁ]N}85MK6duQYXi:I,[o!]7.O]L2̻Cp:tn$Gڱ2FOiF̞c5KI3 -ncS[&LR5?33ϓ^6/ȱD6D݌އڼ[[ݴ\u0mIhLrMH V߃H8ڄs`-lG٢i5")bǨu*|/[>0k $ДUObx7(7FB0&$ʍ.A4-7#2 y ʸvKS sVn3*QqG-:Hes"5Emwr%H};l_ÿS+}Q:Ɯ3)բ /e̔ur6m] OB.r a\u̎ jGozuIDDv=Otš=Vcky$Bw%hfiOO~Uri. YYF:jr![q17 r w*2XFo3`7?n|Ol!2@<֌ (Q?JPVӨܯA_tqyX6ܨd\㎕R+y|s+'sgNe7r'cc^Rp ey?7QT 5\ۜ+gf@e'@t֑1}YŇדE\kby@ÿ+t*w'bP),@$Q. c"byrU'2,{Y0$g;C~SࣶFp{+Y{sQԕyB[9TM2Y!)1n~Q@>c⧺zs>d[9 B_V+HSfiMy֥h" X`lN6 5HˇlDc )tifv&:Cu\0oC4ДeFen[vIo>.BTU#%'jNUq|q=n@_QUJ<v^NW%ݭvt5k?*8; `"{Tq$k,\IQ EeLgqC:$;g=?-i )W^u4[EpCIРQVKWw (n?ru;|WiWh+Ң3BecasVN0RZ,sW8jH}zCԭ)nEWBV ((((xM8'j6 $gYvۿ~w'֛) qǾ3\a\dֈII]Qe?ÆSa.r~*ҸURsR:",ځ)7d96LՇcc}+W(E.RRl0#*AƙX(GqRVN]E0'==x)⪪R尐 SJkOGQ MtIEF_h5 pC%\KE%I) `QZl!2_8}a m<=*'E E-:ӷz98.1UgQCK}Z\6W8^Y]4(OaC؎9FnHWa9J&*3HppF?J}HpW""'ںo!>H#O)qM^AUpOQPO K(` Qh$ͳFS 8G~BҺr\W4vI.l.?tBJK7-3e#9<y}$f9-Ld`K(oziQ= tm;!$ U#K;,2} ؒ1In޲#yC ϠQ(@IZ5$W9JV~eɯcI!A@t5*"N00)A,@M{Q{rlZBp3Un.!?N* hŗsnzuyK1U.7+>m5=VcPUIq8sjvc&|8;#qS5Co< 1fʒrI9TzЋ$%UWzvBA鞵VVP۹lj(*m9#1Q39,Z⮺&2uk.-㌲0BMZӦw*.:gUB8=}~5["\w'ĂGy(觕bk%I:vF-w6 hՆ[lc4Ё]'5"G$+9ޛ͎O'SI>*DM&#tZ3~b tήFrx\(E)rMj\ݕyHZG @P:'˴r{4łg<~'[8ByjV%Eeu$|l#`?:f uksb2r@^qNro+Fַ&pnؤ,s| i4\RykH>U\6yӌU]y6Vx= 5$m! c?Jpqz2U䓌wpMH2xuR_va=u9=)pNiFF ⤮FmdG p1 ~|վ72^YFO^CNO&JM"+VTxp:!``RqZg*8eWh s'5[Q4rQSW vBAguiUP@apw`ݐ1}(SXC*@$ڹSi.DLmH'L<Ϛ{HSTOj=B bCqG?>X$RY#S׾oz94[ԙ4;hBC Ȭ?c!.V2V+[05@Hc7Aӧ\}kĤF-jeGYd2#w 9$F3\#>\@5V4{dGB?)8g~(IdH,<̃zʤխn:~wwmǖ98'#5R ["o-!Il\sϽjqVIyFz <[IK3Ix@xg޺”,9jV#f !N{1ʯ+(3^p{lJ P[R3,['`Sœx`=}98NS|Cwtٹ#"@v[D,Nsޫژ0>Z UP8C AOYuxzjU2 w2,P`qYcTXX&6l=}p }:SlSU纊& ̓8t֬.a-o?ʤ`w el(y8$srA'S:{N&Wv @vm(s0'ӄ ݪxVoy?ZVL/m5-!l rLG|QIssGV7aA7 z#+KKJ]y ak=IsKs,*~gRUH'ⱌe rږ8=`w1 bT 1?J`8vmdI,y7wcwV09JmI $u]Jd#?6yR8ٻ"䏥s:ί DUJ#!=FJ,B7Qߡof`FǸ%a(('hֹ YUڬdKAژV0̏ #:GI{ë _M=nj,8L\BnCRAeir:{ jH$l>緵i[$b{Wsa9YgifcP~Ry=9,O$dVA8T9LM,wڮu.;HS xjIu$r]H8Y# {}8Ţ[G܂>U>N~Z6Z1 $vP0OOOI&)I26D ?wqn(9ӂ0OޥV1$sQ֚{I7eU!@='lW=o~[.b?%ݵ{Ȋ* $jV'`5j[&6{ݬ89O^UIiT :s=Wx7kCy'.0`\ZNMa&ֻ&p*K-s3ϥwZ%HQ,۝+et[lgl8$1Ow`r ]Ɯu=ҺPfx .9ا9#qqk٣RFMtzÙ[>S(9r; HO2ۈb~r `r}C\1+4k6Vde/r?]Do?w'!F$95^MO{+6֔)b!b M ]ya sߌks ZtDݦSaBV<.㵂xb2b~A#ڵ5)nb<IRTsa{W?mk$VJk$1ѻiьЫ/uVz I/K0>U^ݽ-v ;OAɬIro]ÃbC/V2߽-ZOܥ"}|$vln8ϡ! H- 9rTz; t r e2bGI n+ӭ]3ekxJ.2?wI|L3#9A,qLI*"#?gnX*zz xr{\%!NvB4T}{ǰ5kQllfjɞY~mwoc] #8|CKEVxU1 OϦ+&uF89m\=T^:#]F '#iq1k* Z)KJ9!TrNT?:cr9ZX I=GZxT؈Ǩl#G /Is|*w,&RJ[ҵS(2MEnViFr M#%ԃ(*p9 ׶O3pbIfV?ΪFVLcu5kQD綌M7qLawp;iuddu $)]'繜K$ U8SpK:v+mX|\&6reY_1^rҩX~pr+m219-c롱ߏ&gel\F(X%"_73,$f ހ桸gEfT8?^MZH$Xu9~)/bvYI%tV~ǖIVkȨ`N8\XlџvHb %hG 6;z yVYR@Ld*3xzUF&uӓ;M." 6W==K:dOoXZ$2õWsfdSF8{?]S!R2Hr$sz Z[V$uFy-5i$F4B4fIG$$'[fIY\n]PGӚO&K%cb~0Jd;A$m-gNg޴Mg4sΓ{3J\ngpщU!98[ztܰHq'FEvzil%6O?SHR͵K`ggVIKUeI)+#ʄ0oGv}zjS#(EV ߞսʦʯ$7IL$&ooNVj+IpgmEOQZK rxw*9Me"l]&ݤ%OP %hѕ Trm Ķ+mǜE疀T޵Q9pz,ih3.nwmbկ-e9Wn yh'=?Mi"7)PI47 q'm H'-$ Ng:ݒ ]ǽ]HćqLV/&۳F_A5d/ {Z \}Hf<$TVLY\2"s]4ŞyG&0F1sU?gSOTz6dX )IqM(]H ^CP39g8Dˋ(&68Ig͔TGx{2A<ҳ/݌w!Nص75}ʵ=f+xi#FgwJ5Urc'I\k.2s  %+ש&%";vґ_7ԔVⶢondlˎ植x2JܶFH`1SsDj=kRi \,貾4UX`hΤȢ3|wlqx$-F6=G?ֶs"Dv3Y^ø@V0$}+y:4RDbeiUB.Ho_*qI rM}$tz%`?Rw0<G6PN/pV9<`MeἙLb1kۡYevR;'RoXXp1jq&,V0Dl;~h9(neyM%+Fއm{quPHaTQ.I֖;e.0<瑻oq+*T@_۹\$.I6=g Vq2o hGb̌>f?KhKb22) :f5e%cqU\^GZJkyCCe@P~l 1W\"=g^awN{qtdBH.zlȻpqqZ6CGQxr * QݼR N*}ΏG4S` y%ֻ8aH@01jSy4US`h 0DȐ#$=O2{WHړNzJlK;WN0ד߯991U5Nxf qZE2U;Tc;tϥgst!b~s@uH<^"U'E,;V7mJ៨%}3CSuHD*I9=vR Z$/T ZR*d>SFUTʲ#'Ҹ7&:ed$zkc B sjO+yѣ̈́߷ɦM*iѫȁT*~K{!w,K,3/_SҷgnRW4h8C+2͞=aK+3''Z@<]xʣpgRǺ:B.Gbrw=?Z猩ٺmGRF}TF?{tU8)yYHDgY VV@?j >{X"ZY<8cҵxw[sF]Փ$ʖO=HI8qrzwYW- 9=:vԡΤVv"B3㜜x6FŁ;T}9hVif>[*Vdwo4 ܛR줆9+WVfXRIo|OqstwE 4d!J%EvRnlA.f&@DErw8=GSI.%#|.~U@E9洁ͭZB6Or 82Uҍ;O#==&ѢDUW<@@d ;8*vp70 U,Ic;ڹ9Jkܨ-$0KY>vWmsG_k1==9'>*WB{ n)09*rI@q  %C`rrwf`L*[%bNRA:Ioʦ&P-\eUc4E :h?yX%gIT 'L@ҹ'm1Jw:{PHFQsp! O^OHX>ju.先3#k;p+B7}NJpU$غ: PNO͘0C,X |W+o&h$dک|efA eG@ =zכΛ#$1HRKY2ߑ <ӿJ|fYI+9q/|q~U$lGrUWp{f4sQXOp$|qiJSRFF< YgFV{dp@=E[>{+[.Jj5xaPT @OU3KKp>`a2`22')ϕ;;ж:7 ]㌪(m uocݲ=V,0-IJLe^ {ͥ.bqװR&v9|_)۴iG8^yiq[ ꥶO iqEv@K==H\r3Oκi'7oA}f1G;r XI d-nlyCnLDf9*q{㊼ )f[lR0A-p*q4U;{YTcn!4[oʨlg ʥ,w'?8`rM葂P 6>^sgc{ ./Br}Ip1S:W)SK%ncr 2p_c0Kl>5TG;w8dV䑜T eјԟ+8Lq+H:ђ)!R9tIE -&7:'':x"D0mǹ*>gIYGoʧ U5^MMm-Ԉv8#sp\&$❤,+UP$+GRORs=t~mb~}ۆ02=IR"1q Ī쭘 ^on ҅Uq?8^NN3hi];rH$})FV՘[,10~9#<"<׭}'m-GbT8V=2mi)eRv 93A 08Gn}UY9=6Ć$dV~=py?\Bӓ$ފ7D磎?QY71&U19Ԥw5o#hn + jݸ*.~STHXs'U#RK.;G'pk7[0 |J7@ rO Wia{ 7shVהͲ<4t$Px%'b{WRmu#eg8\-?_C;5QA$7bqx)%pt$GOQQN|{(MI5+[$˓S0+/Rin!%Q $uq*g(mMO6S=&x;KtF"*X㎀V*f΢ht[ps$zUµ5 0OI=L)%iʳ.A~Ћ/~%8r!F>R\;"a48,=gta],ֱnl 6=U!RN\z_41l8ܛc橯-8N_5ו цe@N=rtVIomy7\?UbZ[uǙld8ӥ5C D : cǽ7Qy4iu:+Lh~}:ܹx}\(f P; sO$VNDP H?xcG>%(;Xxj-Cm=A2pqHǨMpXG"ܱ=q.V/*+BS`sj߰Œ y#2,6yEu.A3^,1 z*!X =1\^ Q"+mq Ӓ+Gu )Aigy?AZQ#̒{u6̰H8w%1usnF nLi|8) 1}*H$9D>da_`~}Ц[Yf1)f{ǯ9T1BIں11Փxso~^fDH"s&r/ƵZN|F3} )nΆe =͎pK0n,3{ZxnMd9 ʰU$u涯s3 +dFscl/ y`sUm5HHB =u5>q%+& Oc_ZJsnIF˩bCW5e("$Jq;֒L+9#V*3遁մo!k{qX,2^(edܬxUw@:~ZW.f. ȨЀ@ר5%qNʊT$*-[;IcH$xyi/ϵF7x>GYՠU[w)iֶ0,!>gC`??T&k&"GD!q'Obv %$] ;+r00=1Eʋlʸn q8 {uи.hsIY'qq!#d>)4Sukv#hAU=~\TP8` $~bl5XE9bO?tOm \2¨i;'9΢cʎ(h,~5mllW}IN0kiCce(eQ>{zMs{ֱ1mCON {`Vgv߽kHL=:b[V6aRWu )e O$Obv^G_z|b[MiΥ|-x om{tu[Ie_ά0I#t3I)bn"FFj>!OOsXJ'դ\8֞Go:TnÃc]]INT>gr~ֹWvGlehIv"mіYL<ǐ(b0X=?>xʕbUI=>IdrGFܱnt7-<ƐJa 28@=a97HdsMv619dGCuMqnE FgFr9%I^ol s\yuv@T ci '!6ݸ?3V,w7۶y'#{k>-.ay K<kZTn)r+6&,i*n\>*ެVdbmֳƦpnQ#@GOI Ty]<\ jg3I34IS$BYO9[PVs嬂UAZnI4o"6:U}93fL7L沧6̒%g\$ap޸svѤkؐ)A'?J+KwsT;3=:Wn J+Z[Gya; u?rNZw #'Wk|L@[8n؈|99XFM˕ pM<9eCzvQO">aVd7H$*d?:qՑ+s̰֙Jbv5m9c%NGOҶ/&2]l?{UCkdn}2\OO@qI"R $\v=ESS[ J]TM*#8# v4QO"IB̀1Ċh[.G\mS?GVY3sՏVY&rY]6N$S^{:x3zi{Y ef`6Qֳۺ*V޼ZXv&$x<?R̓so]b~V YQ6D-PIАBlt⢥Ga-ٙ^4PI` yli`9_VDE|v\W/4penC9֞}LQ2*>cV,YU (kgEg[iDvȫOcVÃt$u"G{XvE6ԶjiFcRSXI70]NSUӦ{rc3F"vo|Nxݍ!{!JGOZ1A?Z eekjazQ228:;J H$u Uԯ6T4.ryӥ'J*Gms62'jQ3&@_Zә*1sEDdspyzX+I֖1QTIqKHEDix4$vR4֮vsH'&Ө"hsXRU%QI"ݳ*rOƩZmC]eP >xǭTLU!=oӥq{.?f'juޥg qaHJ'z0#yj#<~Ctĝ^7JOVR ҠXN T7,~NA>`\z3i,O\'Ŧ8mwG5#޿:7AM[ѧb5!lD><ȑp*Z(SKmKESWsZNlv5<ъRNsni}xSxKp*L~G}W;*QpzLL1;y⑹aq_𬽫ku-j` qw5)Qkr:q@T7c:"e=9g%1G#c79'a:~ ՔFI#F $_񬥈7\"@~0GROJa8ۓӉ ikv; Q`n;xHJ!Tg z&qd?O_aU6i$!dmP@8Vk2FRM"1T;$3U9R_.;:vi~F ~eIpU3 Kyj`_ 6v֢ةR`N4B )Ƕ{THe,dR~ovoIQKw-{^{LgŴ?0ǎr9=iR\NZRV(H̹?+v )v$| SZͺ0Jt<]u%%c {,Byt8qjv֌.@ @.s4&f{4Ey* պ*~6e ķ4w;eD8<1]!ٳG*1}?ϭeMi+_1 hh9mrr{T$JabeڤrGaߩZ m2+gOZ_a!}ygjиt>`ޮsǮyp[+kDTb|6'=*e)ё'EMFSq߰ LSH?*H&0D ~W4Jk%ԸuV,vޛ[qjT*#D"!RWwNz:%@!zgcFri~J !S)d`8H *%'#MSg->[bTeq2},c*IlԁQ"<ˮ=iJH#*>'֩5~gӠYJ6 ] ;~?6@҉OXo==kwE:7ҴSu%M2N+ ^~18R*qӟϚ޸p\v%}w s&: cS*xxZu۴q$B3]Co}2oR9p=A1F2mV}sU<{y@?@8?iHp=}Ȯ*-L%3eYT.YY>9u9Ho w4sJ3y2Tzr88j 55y͘NZ!c2#E w#(\YQ &xlz UT#p³ХI~rWV]g!O͎H/Zo 0YI(rB34ݖco,Ep1 *Acp{UUq\~ÖVd~ 8Ӑ07ݵ/l1ڹWVFn"*c\ cs؃ҥVs6Txb#ˑ@0v\G9X2v9W3VXZm&'fxԀ’ONJ!+X `w#ޟn$*D;wSګI*ڥT!Kgrr{"svIrzƺ!47浒@vۼ4ow1u}*!n..-ʥvbaWV/3jVv˖qמI>5Smݱӵ:EnwUT t d!, ʭkZ~{d1Mm~@`#֙xr 1>R Nz!M/EϩjΏ&KsO?Zt-lci u ܯ,(1ZQQpFIގ1},gʏsnlcSĖB')"x"Oc;YFFfzV$/5s$F(I 2⼀28*ys\i;6tgGVܣ7>@? 9DEJ;gp3ڲ|7iO29ZOp%#pV%dC (hZ|"wl)G;\&R!hUJ8APŬ[YA Vdy+1==25wĶ S(!5q'{Ҹaiz剛G|MV{ g4H(0۵Emk[rYWԆHiM^TqΛ ~5sWk-1JՇI[өkGN\*nZB쌧8‘dI= KZ ah|2+ fh tNQ9'gʎ=4KÝEr }ZbX|F܀z}0)b[}VigcE>gcܻIs7p<c%].Rj|tnJRq kg`WRKLg^?U޴B]>Ф(#քRi3a=Svv5 1̀1I:U~>ՍE؃6yn8\;kBTʝX 8{r-6YiYed T`'  #X%Q\<$JN92ե !BKteb7.>i'lM6Ɲ[Fv8aoLwCmd|r 8mܞ{TsIhҊnvc8}U6U`ϸN8z_4ҍe ݞ:u7 k-TIMaT|˩w]EdopҠ{qQ,)rv {*ml5{]79'5dt^Yv` x&IYn!s[L P$&}0Vx=NRC/$y3|w`}Ϲ5JHm|N%G*} ]>63b]X68䎘3[4 ݡ-5%ž^beg;`hI{˷aNw7/bV5ƞ/~u"|‡ g5b7R' P^ذ?ZԶ[#y 98#raE:~⭮4\ `(dgZXd1)C9?#،W=lfԳ"ytۓc{nDK0;v].1eܩsutId%@9#nj젴,q깑|vրn(#A=]> ɩq$ʈAt jNZta^z}B2 !2}qkkd$H301Ҭў铉<m [r1\1{tϹ1M'GiۀSq\w?a`%1eRA}$ujd:fiMȒ4oF>PNvք㏳HǖuvIQc"wI!d GBi5ٮN4\9Q*rn\N>sqo'j3לQkohC7\ʫ-ݪ@F $Eu,p@nx#oFA9 ْ)rLi$rW˫i*F}}OkO._6TY =n}.{xLeIjt׼`^sEiN2655ڼD"o5Vrܼ3U8'ۓϵhY"#R#q'?\E㲎Vb[B7)اj?1fۈb6 ss,*Ų 3|*}\[Gi#>.Hlp:4׺׹u6ݮ$wr1ҧWIʳ!}3ʧ|&'=G֮DbEPO_qk;5dsI+he_+6FSx\ h`.%O1}}A]yuL:,~l \9,lf4l:.z r]uS*G%⫄Rn`@T 3`5[m-w2n|}5^H~ܫ#802 5e[Iq:Pcw8| }>kbHiͱr)rB@N ~}k $$0 lqMңVDBĸP-M{4$e ^AlI+Q#/p,Ԇu?qQ3FȮp~$-,tɤy0D81'&ԻHR8WGM$pG9mϾ+#m^ؐHQp[L' B?320bГֳ,6!IXV?:;y&A$U}~$im+{xevw&2B2FTg/\xvЫIoƬ=ܦa 8l -ѭdެlۚڒe";PN=W72`Np00J*HX19+Ų7${ 9"WІ$yM%Ikb픪}*G`rHB 2Kb}$Jg,0G?Ҝȵw-%[ƑK4B_$m X$1nH֣{y,HZΞ( }q89{w-QV2ad,ѳ!>S_˖=8Gju›ngNAۥPfi!6Indur"Զ-bٹy~U]Y+%,zTXuA1ڳZQaknzڛr^$DI`2ns3Q“#8ߥ3F[UK'oʴ.HaRM uǭ-WaϽ-QI+TgM 4R"RbJd}ǥ\+9gU ju'N$C!h84ފ!qM=RO007t[[Hh#@1ӌ c֊Q?ZER6ydJ"sqZd0GQMK:3!PBnsjU!gFSÆiA+&ઠqŎ}V"XJ C}DJ$HP#d q{ LRx+'PYy% ߧJF{){ӳ^ qQ1aQރEt[[R֖` 7 drCgң:uU^1N jV!IY\is=Nf>Iz`. F4WՎ ehpUA; c#^q 79د$ ~ӧW1u q^knFy3qP\ʖUE'3< զqtb#.8c޹jSirri`uqvzyjA?gNIneAKЀQA\q~>k؅0s~Gn[Չ}3 Psq㚑![#HQM UHSb\N{r A =jTUP 2;gU"9\m9Bɖ<{Lm4#p<mߧMJ* Mۀ)w4g+/cz5i.UX(b1>RiֹNKQdV`[+z uWpXʕ8=_jFa[ۀ9=)*iSqsb!HUK/CE!+ 0;)#"!A=k*Ra2讋dflTav)W\AiPuB>5z$VQ @)F  OPTgO֧5yve%009x$Ȗ<0m{ׯ|W\Ӳ$V^ru%V2@ I9֋0Т)³/"q?'Oo &ϗ-g' ~5Ȕ&rb<|'y#31,;AG 'Jwަy[rg\!} 9=x+fnO;P%U @@qǦq梲[֑b|#֡S71'(T P Zۆ{UicLD 3W?Rc]XˆVp>P <4 wq 붙"6/@#}aU/ pq7˷$tҝUSGڦU 9n1ȿd_ѓ=Vۤk"e-p3 JZ8b bq^f| (Sp!՛5&@'OaTkL('3qsQEv$rfb1v^jq~m1m'n9J.y]hLc n.# ڪ >67[R&'9^9Yr%ںJ#qW4"8Te 3 {VRI.B|-E[o)T6I{58SF_5ҳLZ7’sǰ^cxJ2[a5~f̱ `/$m(U ?OAQu 7φbO 9bE: o*e1mntԽ,k4Izqʤc#H0J (cf%'1UyJYYLX$.I@=LHr˵~nx~%t- 1TssgSQ"q/q䒣8++=[n2xsM9dcO>06^m;&FZI[1\ӕF89v1PR8< MN1\ϝV䎟ܶR dAdH 3~}G_ 8ϧ= ;ʨbzqfەF?BkXK6"UrCnZSaZ+u@/}1`.X ɜȮ!cDGa_n Cdq&o$(Jg=?!צB-䐈@1ڥQZmA :X@u)^,7us pGL~ Wg `W+}o:Euyg J$>犼t&&js(kc[[tJ%IQ4y̒Jۥsץ5,2 .u|ztDELc*(9-#uIۑS.ȬX@rI:VL[V$VA!XaǨW$nye'x7dHg5i_6_ީS̑gwhį#M<4R[i7QSVYEmgA'o]sګ 6ᵊnV3Yc![[$:6 }apl/UCc~I(E`ŒgsZ7q%Ηr29!pI=G֙%U1nzW7k[s+p-jk1듐qIId}m' q`~;頋v tbC bVASqlT4k*3ZN8qzœAl0+5=LD; u'G.s(\#ݮD1Cc C:(9bpc'05/).@ wק&\^taϮcYapFh1i>ӄ9UٽB!R8dV, W]޼7HeܮWF&bLH\Wm;Sh )#[҄%aNb"pz.|uppr} ' WSpF>*HaIU$`/bN?A]4*(~',+#=*2M ŕzt-O[*eei;Yvۜ=V7P SQ/=v:crh ocr56eyl 2dg{Ȯ[VZ[]c2ue<B.]E83%ԞDJpC 3$ꤒTH#;BС>$3LI1;$׵$r2@{j=ghrDV3eQ+cgq{ʤ@϶+j[vjKZcHKGzy.uvK+ct^V5Y#1 :*/W;T:FϷyFU%9>J\2%PJSaxr;=yAM$eva& fa!ϐcfNsՋq,ḍm/'38myS[Iq,G%!Hpr\qV.;TX|,B9%zU[z#\BѨqzǃ^% ;8bP"Lyc?(T6fj;4& 6#y\{s {UHDo;rrO4["`Pb8ʂ5bq"nl9km9mĚpuHPF$p@8Y,kƴP&A NH}gձڥYlO(a2|r #G /nYٚV1ck)ӴL]>%1'#m )jjHnbh R:u+4Rjv##EMAihI&%'#M6|^@P5*9K)y$ag<nI3ai VW3G f]#C)YEHxGZӤe#8ӂIrRr51Cgʤ@ʟcR/,71ńӜdq?suquyos )#BIe`H϶~Yܬ JI(K* ۶\*e+oniTkn-졬KܿCү_.c(F-R;sb 6$.HsT1^LnR{Pm"X w,xEǮEm9TR䪻yl>RGPޕ&w n&en}qZHJ6kVk{31fXt,.p0z:ӱfHJvsfrieH-;Rqӻ/ϵt ! 뻞: ɑV1naܘr@=x*c}vc;hڬNrLu$~l~qۈqJ;N E 8 튊6q9\EGJ_)bіrOZL9|@Qׂs𭧷܎fB}OE 1[<2m6C5"&HH'=Pĭ5g F; `tqX̝T!TwRRQZEMjh&yxo^;d ޷wOA zPi7 p[̏kU1VTɧy9'к)A I$Q㑂j⣈ ޭ#Q~B%܆ýJҼo¼*̠gqTIJ:w Ehk2ͅf~pÑ -n4$<b=Z4,dm6>{*ͼ, ,'}xj 7QS!Ͳ+e;pCZG/r2ۦ) ɏpGzUL[xgN 9)M}UjsU^JGb:r9;cv \$>Q8PFl#8 1Ԕu]Z ?w $dԗvr.!o 0E>U 5q$~2ٰMne9sGs|O$n|7$aqҩ$nW2LBokhJ+HwnڗbS wƜjJ&ju)".rFS]߅/b|1Gd)@P{ƼU]|(|g] A;,@j:'(rw^{RA!G zkSra8F NOk82*[4H -e@$(mWО9'j25c6^+Bq1s+yMqQB'RԴH5+cjAϯ+X,dTF(^HЕ4}ŽӢ^̐4VPxQxf,eugZؾ9Yݙ!y>eX52ad9]Ty3|qN'$G,?tO3: z\ ۻY\eBznEi%U9sUZpR:\\ʫǻRs[Yo28@#TrDt]{-co1Z+%R6Ac*9difB9Y%pzѬy29[in>@'p8zkn, g,2Ǹ0 ƔZ $I''drOW,^SK"H1An3 =HDiWSYZ]ʃRD#c/ZVhwdm'^nX*0I97It)Pn77hPFCR)֌ՂA9MCTԚVLښМsN3pm]*1#XYJ푑=]o ⳬaIG hսN~a`HsW|-o;s^qr1rVuVoAn,B}H$~WR9>]岤 3~Řa@v؊HKPB9_Ug;&e^sR[,{/N;!?3qڱ+ |>Zß?8ȂH)fP>gXa~]+}^[˩YXYڼ HTc#IֱY 6!IHRV|@Q85 o}Z|:t $kThAL$}޽Ti($'ieuF9 -9\sp8Ԣh?iY6&Y 2PWjWj 1R8` x4/u!䁜~pJMQ;%Qfvnnun^qTe'PU|H 9]2q(Մ0U' @5NΕ㰽R]AnFϽ(!N3@`Sx5VR\@A n˿8jXѻV23%7P\1YW.䝣x,`v# #cJ3mi |ݎ'֧D\e**L`ZVVR_:ZwDZZ€K]l#9Mp!<@͜^OJ@ 9LSHdܐGns;($籚"!KmB;rqޢoK>K-+@'sUT"XǹK; )n,Ks˵m, nPOdQMW2r]ѯ-cIC)Ru{dr$F[zRX],,WzAcNpkBGI8i ƧEs'NIӣ6IW v|jI7!MJy0j Hg# }:~!/'Daq$FKm\A9OZFQ/zҹnI"ŠK g<,*Xe('$)w-K>{ETW>[ EcUPnx$ӡ-fSRIV8x3*ձlE!1~=ވ9V8ӸR8hIP>|*IrTI;b n6sqq-m!shE#JH.'?_j2drr|Rϙ$$ #i¸Vaݎ1çP<v|~_ΪG1ufeOCLXrϭ,Q,Q" AJ *VgEvx(~8#RDQK. PHT~\s?(X'#|hVN718&Gj"瞙O~u+6?x;Kk.2_CUySqv4$`&5#n2rXAf 'r#deE)e. ssږ.{#L<9$/U;H OZAe `v\Si GAaWI#s׿_Ɩkula͔K+;H!⹛Ic ̌J#nup{{Vl(ņ >Z'H̎VFPU ۓ+T m#FOJD<:[$OG` g;]jZ+ ZFF XX s8XQ~9jtxg]B Jv#Aٸ^-VI>Eݼ)R9HbGCy9%ia8H܄u x$/9ޱԼ䚹T&uȉwnb#q|#ϵm M;GUXzGRN FY{#p+zU: T[o;wzzR[՛?cGRih"`3z4e- S2N'\ |Ap#8CʞlgT|7g fy\y`lm#4>G.X.tjn^LۥV|32ysO%HT.[$'lrHI `^ԙ`[+ZUpRTfI]wK5HUc g8{\3ݹyƑM:qV5rCm-=8p*孴1:1Q[K<#W;絍bl˖P6c*ʬ0x랣'U[&!^wcb` {qڒ kx杙%b̀-dj%PLaW;Tw9ϥTVf34l$O-Ÿ\Ϲ=G5hmD! NI>kn^U-ǘnr:q宭4ۻǕ&"[ ]Hn3ȸC]wWP|< Xg>/OWHŲ[712#|7,w (@+da}fIq@x<{?Zڛ52)Emq.R>Cn1jHU}iXbһʝ[6H:AQ>y`y>U=n4HHq 2 m8 ?dZL^#*C)EE/Q#ּJ0q&vE%c̈́]8Leq#d^X[GvmR|\2IP@sSYAwy~?<97#ʐ߀#'5EZ 0rwnժ-o [i)9Ep;\U ?S-`Jn@rQADDPTtR+c>يLo,Գd=+~:44p?+=0sE\8 D˰Np**U?4/m)+\yEp&JHA`pp 8צqO1ڀ9\cUq5w4r޿҉Q;Jތ&])#  /J%(\ A>R3w4'?}G} zip9=f+O'dʷX8Ul'YbhsmM)MAݫ ssy拋Ѥ!;*gMAҢD2wwnO][T@57Եˋ#B2IsZ:5?z)xNB]ex%ߗHgۂI=k$qFI1^ e8/aA(/Y 9U^IRT?%cO^c'(V̑KJ@D&!<;֫y3Ŧʬe|>C`KHϱ#smYBL}S+?DӠ ɸ1)(ՓlO[MuX7o錜qrߝѬdR"sA]GZ t#`cqMb\&{h*y$@YN.ͻu̒0pm;K@9'͸zm'K>xJceYBIZ]=xU[k\`sV4I#Lv+(h݆X"=sϧje{ĥEQ;~5XO>6, vOǾ+Il%[НUإ9l9cynSl&$|@<}~P!0sĐ:c>[)kj+,G)e[<Q9K*v92d1+? {_~9]`)$=TVZIhFD$x/מMMwYCid19ӿA<&"0HnNTڢ\} ]TLb-#i߷̥m$cR*ơp.նꀦ1qJتԾ9dj|M'ڔ H!p=rj) `l, @c?Z[LCyvQ\zdn{bXg) yL2p19g*ܒ`-#-}zW&Z&2 Q Xs0S#8QL<. dĄno j#Pdx#<u: E3ʑ. 퓂@޹ʥ~VE:!v K)EpsןGqhͩ^Apae\e >A֭,@I1(F`.{r\ݝɺgDCYX*zd\wS0v$-T-%UU.x`j.-͂FWhLtOJ].G*R09B'm+{(I H˴l2{7hE8UJlM*8B= 9<`3qhE$p; qCsj~DR0Xd-nu'p{Qbnb;GVhL˴uNYRPdCMfNB>X\ OPؖH!,[fvu%c ޵ Ro16f+IǹҬyirrdC+Eg3(cb@q$U(?uamhl5#b.P <]pAT i*݉BѣHvIf?Ɯ] &( 1s֞<2YJmwqߵ*a刣2(H?.GNKx˖]8'*AKdYWQ?=*:!]\q >f:Gkim*H$d '9%Cn`8d)$T)|0He4F"Բझ ^ાurC&RW0X_XG*dy [:qz=B-&O,")mt+kL3''5GIr*C+ipЧ sWSiV0Fl; nYqA FB=id77OZֶ[yW̸;F`c>JKWvIf Y@WPIY*A]ڤ >f| c,cu_o GC&e.bH ju&h#;zQLMZMRfgEw&Hu5/rZ6E"6(Sw,ѵiLt_BFJ|3?ZPf59;X˽cx@vsYT /B)A$uz =+IX^38WOҤ,d+Xqy˼O·4;V2\{]*tJ\̘4r觵lwTy'(khH`gF)Q^GP⚳`c9<ѤR@-f)QL#EGe!T=R+M280:V>K@϶x5kʍ(nmF1scKC4M)%C+;$;F[t_TV܌(#x=d$H> {*g9&679=i~AVӌQ3Sȹqc.TxQ)쌶ybΘI⸫YӺ_)&xb>nHOZlbX\gdM;\PĹbۺ?qI+ZF&YW~%>ps:H_Q3:Bw'BhI*xPqUdu}N:8+$lT=V])u.ItHKzV*Os8UYq. z<{tC&Uc;^;i,!%Edg᳒~T;ǐǾ}Fxޝ%lܞ.=}:~ #0P2kAl gVEa )*>ڛ+BZjGd! M#dN3)El69Gv rCFSӯ+u',zcTR 0N9>LDcz>8MOP) k)XAʂs?vƱ&WH9 NGֹ4.V"ni&B@%{WPkxa|6?G5D| 䃌9'=+9JNL,=3vދWy\d!Y@`x#1$!;uEYFkyb@njVsrV&W $~R}ljTnsT onw\G p%rpA9"JrJny<*ʬ;ˮ@k*Št8?cqUa$I "Vi:N+IUpzګ`Ͽaiٿfp!F{8Ы"zHF*h.~b:OQ<yn4V:q}:UŒhMoA][`gHx%$k. Yp\A?E̓5#vfG*?#=x.eD{ܒs^PU] w0瑎ޝd@GpϸzcsH2rTznjW`;;Օmq=H\s~ݚM&[سP('>zzFgsiyp@Sm!mE<{hq_rくΰqb*%#I,j0xwJHm(wϞ.O9wys<#*pw sm\q0rZ.O:3k{%F*@q\4t#F0VK 8:zW_4>ll99Y:կpȂ?4H?+ x %nQjb%rTuG|H>jүʂ?7L:[S"Gdsj%[`Mm藞pb6o?)۟˞}EvAM6V] /7Db{cqRiliiowD2#9$䜏Zzq[Mpˁk:|1a4Y?wG*Wz)IaSwOtlYҴIaI_?ݩnM" ?xӵ>d1ȟ)72c?J|_?) %UOOG(NxЀc a #+O֬aF9=H5ve5J# w*c bIӮeDePY7/*T J!t c)\/j#V`J'޳KVo5# ~b}${Z+9q"$bpOAqWR19ۋHPfH A!=c#\nWЃ+M$D>T28|ˊ& FrsBL"XșeVݷgsn깭h?vƃFL1 U(>+ U,<cg}9m?&!#nH8JZuyQ6(p4BλQ$!T qz +ldlOA{'Y7+}с׭2Lr73+=Ԩ>XYMl0\1qJ`|tdl*̙<`O\br{JI=0nP\GlTR}s[X WD ֺShbF 2^0sZ=:ZQ$ ُpWaZQ{E5B}nK+<'=!,%Bۻi\sJpb Obp=TܟzсY,*eyws98y˚ogܴ+EnBTG][ѠkU)NU$AՋ{%MR dcuPf 뚵–n&lf&cn C I$E_<6p:;EbRa?li0Y#SFNH}3ZbvQӌT'c]R+xT9o^vI./Azg֤ UNztU]-dVC:~9+KhL3{6:u5CN'A<nF}O]ř-2Y[%I'aAHa{Vd0s=QjmNN,O_5.!,L̿.qYϳx B눵[?1GĄ2++zWJBWvj9.8{J֑[XA@h㎾i[Qy$r(C;ǩ#ַ% (w[G#>kD; ܻd13ńaI:HaKci眜k'j} t-6.188T:^a w@х W0I*Zos @O@?v~5B"'늯o)=tESiJMHU$|bN7mcU>k2#}҇A kdD]h?.9Pzs4*z#좸KtrrW!I'`RsT.a7X"0vY9 zRiu #:EP O$_ziV^甫e\ƁicFN~]sxӗ?4s%a؍HC6[ifDW7$@dA'05 mB3¤y=Aq5sF 0c sp;TU !q-yy(%D;pyh$S0Žy=En)S;0>y˘X8\!?>ujnQwIUkQ1 jd;]ΒI%J#arWn9$ 9I:&H5 9f:rq#֣/F'5qna A`wDG0]dp4)nc , )9~Q[md2m)8矺OJRRfv7QUY#,;9 pWyP]ΰD]$7ꪪKcϳ'cJRq5Js$)4er^¬Or&[z 3攴cp|4P%@1ss TߝBn b}~8TIzWq@z?ִX((;WL?z9RXXJ:b$?4UbVT8^*oLИd1ʁ.Ux }jÿ`f;A8\STQ"Υhǂ{ȧ';ǟL~5%vљrC dn(%wyZ+E 82qw%!CubvSZt ]Ҁeپ:XgH!FƳdB2F gwS*%"AF'8WK̙G]YQ֣{g_//Vu J$u.=q\؊\Wy*9!]?2dg);^. #vZ-2;Jdl *q#2ۡu.>PX0y8jiE)JJ׸H3B,Ğ:fIc9{rܢ}d-(I$Rrxwt}u;9@xUt҃^aR)S%w4ʼn5qaXLrt$uknTkT#a# jjJj֑{J:1 zՉ퓒3ڴJ!6!YKL9ݞyLAn-)'(#vC2rԧ+=i)t9,j+^6N28/Q|?$Oc{U(t.IKJ. ,B=֝çdD p{ δUb3$]QżvoJ: dc-*;)x.q\Ο1f8QVYG]mͼP<A%9]4cw1vZpxǥ#1+5xdǘW=y>E29;G,k59%M$J_V^ooHHs$^g5Tjwb YThfI.x)m-Y85vPsۼBAq`F}GSXVvR "݈uSR0W=VH AJZ0"wEt8"e*$1ĂȤ1Je?vGS%FRFC 2BcOʦSq,J0~3,V!Aݖ%\d|LotI&y1<1g)ԷƎtsm mGDQ~{fP(#j5@ufpAuq Y@N{Tt)c,Z-iϏj嵻 (8uȬ!Jہڰ5;@eiG <+J~EEdyE B‘&d"|]\Eh~TD#&&{Wm):2WVr9 buzU+a41C~۷$Ɗv2\0uB'r8VIjxZT o\^G"6^tf'rFzE-h[k{=)\}:TT4R(ΞGy# )aevی7_ʭOehVh2KmwT{Myl[sv.'`v @Pra`Ab35r<6ҰQq[yX@ oaا)p(tgم\Jݺu! !w]|ɾίG5jv0bi-B2G5nd ^","``zZ]'%dd<1nՏ ȤI"/Z;\ڥlHé;{ڭ+x4[Fltgi nP@u&wc,tH7R[ךY#xaGMRmDI,g턑~`&{OY.G!\õ=L o=; zU+_>c8]"<OZm.wG]KF*qs4wei%yQCUYdo$sXW0F ?(*[ ]ݑQkծP@<+WcZ!O3:I2ܴ%|~ba]>ggcu6Zm=lG|UƆHDء޲9\!j˲FXbx$Y+?EdGŠ(GQA u8FN뜞 1Q秽sb'E gKqb[ӗ4S()N,`jIW.++ب˗RŹt8?0 NqVu剖,M,d $v BϽcS/V+8;yGLP=hc[QSlͻ(̥7NbqA׌xjn9ZN)uz$EOz\LElp`p}MWTgZB6{c֢drP HJS'Ps1JH y98$dJ )慈I5rH X8[2bpO˞ =O$72'{d.J@~*TSH'. 廆DѲT $> ~^ޕS_x'iu?gZb.ޮSt})$Ytqb\?G)ers+o,ʃg8=*Ġ|qמPnfQ%7mr; fsON[jΨ]<2GhX>@br9uʊ;re ɑt%eb*1^qS ?6(%Tcڳ0ln`On>*uQܰu$CwҖȹWE@F}:W%vaQK4H<ҰϧJ}%0wB>^b<ٱu9)B☉W9eb=?ZUՒ_**ڐ,sJP܏Rf(@8>%ԋmTH\ҹSQ7(Qfgڋ(E8]Ǐ~zXb9(3hFUv؏Z-M\)b`yx$qkk[ۅ*B J4滉tYYX ,_Ө5nD1B'*Jp#O"ץ=Ꮆ iK@]yZڭ˘U7GT`ኇDL2 92H1O+'9l=s]9;4^ 1 8YOj;".@c6e t'T,r幍d!Bg1N|}|Y"Y1o2A8^O==4nDۗ!C.3ifXN…~֜FꬩU.1:~$W).>pWZI ԁǩYJ `gf$jJ` _ҽ7V՜-A}xD()´d9cБ)r02A.3øO^jy!*X Ic!v$қ45H??2rNsziYZ$xHq"8,'Զ{#ҭ}0?'K,pDj3*gnqF~?397g$#;7eIY#v. =eP,+I+#v9;Qy 8?^M-4֚{,Brx϶ e.w dO =l\0}M ?y9Ms(qKn /Ρ9#MCr&rdSc+>˻ht!E Tn6sZ6ewq+rzr>^aIVVrd  f^xD$#!\*@J2- ~f1WE(9^f<шFX}EAy:C 2J I'y <?Sf|t>53- !&Zխ_Y2mU%@^fC-1Ƴ<OecVޘZ䓹79Uv-H_ F;sh!ܓD*$dэt*iQjO+1]l%ݼorL(~4X^c`Tw-zWLht2kg$8GHɃ8dn;Ar220?.q~٥ee_s' 9:z=j fP@|}x#=1*iltF)&cGV{]>ljRGx9:UrO8…g,p O_lg5BT⦎(ƌpxzkYQ0m)# lJWhK77J8Ӟ&Z7Fh @ Fw)r,%Frv'=GN)y;W߸@bI3{^H 0Ym9 瓟%hՍxP aٸ=JXJϐO8Xz֫D Y(W9ޝnO@jP.tv3p܏VIo#&H(^*J$÷&(F')"\ԅrJ) KOL;bːz7p3zy|~^rAxQ#4p 8]y <gW%w۰I<`.FyNfͅ@=4 π{=F ݁qں3vq+ʅIh=y]3Ķ#qH#"zmmjA<+jv]n9<Q FBD$FO2@2ܜnU[kfEVynުPL hGrm$?lU<\GxEg}v 1l)1a%_E8*s׷S"cn }: VIdyJmFIⰑ{3`HS>5*iFksjO[F(Uwm89t?E\ʎ"`lg/Ў3᫑=2[$MBg$?iڇX6+}jS5J)߱do[)un3߯R]J{yǭOȶWO~) m\'k`8 _aNIZ瓕D;< I?qvkv-sih+ =qǯ?;TFqpJoYDi\0OEqtùPTF%$ɐ@zsVyu'ieBL 0?+& B6eٗ)#u=Bawn{s$x(A$Ϩ-"I56H[);#VN^q($Fd/pP}A ?3X9[\09h2U|#*8.ٝq .im,rO8Ra#<ޤ;?:HFU{2Tpz 6FYsG6Hc\sp~]邍&:a(0rG'>h:{ fY-uT}@ݻh8{1.šUln 1?Ev5fCwlfMd\ʨ<-c֍ؔ2vK qk"k5!P'9+ qI)F-6̏jiF D pr[z6jɈYn{`\Xp̫яNKXcHʹ,23g:k r~&EcR Ry8%7rN@$gkծLe;hbY;)dK/'x7tE{w)+]骷Y{(iq5}}ws9Y+?SXMDEƶ"}yu3NmwiǵexsMaF#I >=s[\G&FvשaZ2q' +4'̥t0st Кt*kYP:\ 5z  l ,XT=$qЌҴ45Rs+<.v#~c=s$e)S$"[dd+ۀ\~yɧjVi"HKe$+WQvI2)県RþmȪ>k?'eT%YHQXn<2NɌC.&(nE$=v派Tmh6 Lk#ڼ<qWtcmo|@v,LɐFO'=zX'eiwzgOJui(4ax Rr06iɆxyW%p2~CSK%YXV8=p MBi-+c+9Ǟmi6,˒\msL(6௱i94UeWX)1yђxgC2o&(II<z|O1o#Eb:rA?  =ĄHIM \䒳-_GwpssKW;ȠKިs~W. @sjh#;9gY4p"<<޻*oz$CHV e `i36y820&O2N FH~{nWa#5mz+Ey h4A8-a8aX!;r$:`ˮEAq5(url&Nz`JthdˑNrQhFX]Hdn*M=,9v=N"bB@\$ TeЈrIۜzd{߾'w(j7y1[VR6Fxxs֎b(q@lqOxd]>X!ʘ %ğt/ߦF9+mŻK\}\T6Ψ֐_fO;̀#|ǎcx8OQӄNʩ|'h-8V2"FcFwy`mʮx+ w7=_c0ʪA' s&Ol]%,rI*ymKVEI;@BX+a6ZK!-8vc#B{ɎS"HvѴp}:kQW)ۃxWr4:Fw~q>b+\]D"nGLLѬ>[[R3Fg1FX9}Mt[g HQ@v˹*``㑎iQ!#nǓ]S].Y=L]>m3tG8:UV@pLqdm#8fVPT*9s6ڛ| elFG)(t=:@ =* l~Sp~N%cBbP8$c?\U#r^'pǏ+:҅")jCwpScxV sI1Yڄv rG@c|VZfy0eשrV, _89(bޛNYݬ"~c j4UP2l 1Ͻr:EK+44a  G<2UV`[UKGXV-k|s\W,q-ά%R}=>lǽvJF ^LY {sU7N4Uyf<"e ZgaleX# ӮGk,$k)B$w׃؍'2*18n=YTvAū4mj9?ҲXIͣMCI=+FXV|'I3oRJq>[UTݞHmc9L#-5_Fz=>k{IT,, r n|ZfLj,bqgiso֗0̱.dV!8i$<3[*4IpR6F$'R;+ojތ"54,%nqu{du-J6Hb0>"kY#H>B#OW5$Ъ7sl9=OT)ǒ=w1I56;{OzLARG*4sy,*HIGL_\ #_J\L4O,Es$dwz7u~Jp=O>@2N$0UTo_]N12h1p$ӧ%ā!$jA`T3dSN UXA]sFT 2GvHW*1DU:Htx_bӅ檐űV>o&2exO~O5ʩ35z\+zO*[!hWn1Ҭ,Ixǧ=7++}\UVySo9ĺ_THW51UW+b #rFqqU6cXѐ|0|UY^F2>WKDv*sѣVfHݣ LˑAY\n# Zd2`T2A*k탡#_JKv] 㡗Ivʯ&ۍ*G͎bAz;F9#<:~Ma'|۹YWG6ޘgo.%Xr N*|"C'{H^7ӌUFK](̯;5U(iqߑK85r:DW }rV>l.w+,?Lc]_2'mL^iKǖoAQkSFͭQ~tݻx>vE |2⥲gocny$}:X9F6ɫ3խ77k5H0GAuBkj3 WL8B}6Y&J$+ ?Jν*p&0=/^yo79n#Ҽ٭+n|ϖrWZ gu.SQf0oLJśA#ܫdm͟M5|Y~'l,tcKN:q]2E)*'qSWLҞwZ°",d1^ӐO\d_獀8׎Zuͥff,I==GJ%[#. +:Nu3RvW$P;F=i[K"Io֩Q*q@+ƞ<ئ,gv=qֺegIhm:h!{K+ Ž 5pcOM ^yE$ +=y5sӕ-P vX-RFվXrX)tf5EH;cU(fO+x &S v;r.WrFƞBT $'h8q飳%Wq k T{`dd!?U <N74SX8͍sQY.9%{TJK**ăN=+.(YT]TFXpǭl2pӞ<-).軧 lqB.{o%ǞҼ>TN֣Bw>`g̱*U# L]嶁-+,H\oR1`^þ]p>'c8`V]f2c1x޶؅52^-w9J:̬FYZ=+2/_L 즂)hpYgU'<ex:]>Qk1GVC#RàR8d#|beOMI&bea9PqEPdF-[(O2?B袊,)M;?sb[]Jqx5JELLc66XQFX8Pī#L_qoC~ǯ>>8IKsX;&`cKHpK^\SE橻QE0 ( (HJd#:ߡH3!]~35 5Qig׏׍[9m'@aSe?2 1VX=8! t8OyWL*SN$۸H g=\vr(v!",2a\d{TQĨMA6R:>mo=y*qW21Ҫ ]F$ ?ް?z"O[">k#|O;N=?*Hb{}Pq nq˧i.d]߸sMg}A$M.@n?BFwFrUg?{ٴᓀqE!p\B wUh)20A, R׭3zO8_LCn jW8O5ZgT(f7v]ώv1 [uͱl8UzQ5Y>m"݆ VϯMY[.d0q `CX2ĞkH ǧӷAgi".'e%v]/(YvY|-XarI3Nܤ+ Q=<b$cp4]۹icy9R}T6# l:5[˖MbWbjm ,f f72e9\3ְJUcH;nM lR7Jm&!b&ϐ&*"GYn3\ck*NK(У8 `#ҤNp&Am1']8efwszxmÞӭ9y#>KSiDѮPWOBT~ Hnq?:S2ױTTill~2 Z{x-WsdE>^-\g'7x>r5fa z^uFMʠo FH#5*XKV   6צO)&r[?2[DHBr'9;X^jl@H |vbq+@JI}ZI cS+zfT u\Y;mdoq"[cӃoM#6Ϙv)iYd6v庮CtV6\` ˝- ;\RA='?AϿJwT 8TzT0T%OƷNjf+Y#4{)~Ғ`bW^ެA5{c~@@#pk%J 1T}]]+BdJ ";rqP"sb1)~w q\Tʹ}ˀv=u?ʦ2LS;c9s%x5ۙ'uVP[eB`Nhl?ZsajyD IG)昔5܇lķRs :Ӛ cR2 (ޣ޶Ifghё ː:tp=j&)nMvU 9!tW<[KCjii_OEȳmͷ`r\PnG8E)#(+@\gwujP',YyP=N}m7&7$Ek,C92a϶jFp2 Ϧw~&jw>Ö z^g5jd{IFZ4$Ppr99=iXn ד}jQ|xGMjIAl2_I,q^c ?1R[%ǯқh&A O8Ҕ4O:(~nVW] ,bV9 OV>%$;˞q{ǭQmUPK[ pO$g'S\$])k~[z^m봓Ꝑf{,Gso ){]8Sjq׭g)EI-P[8 (c[x|%+ye#kzy{qSnu1вF͓vcZ *m%fU;HBF;|ķ\d=+Z 0$PCkjFUF_5W" UIݑsG>_JIٜI'}9bǀ9n*.;$^|s/r8*I P@fr=>Vkoxh0;_G*N:F8<q{V ZMGi]Y}Go@zՖbtX9Q'ұ^JrqԄH-L1&1L2HbֵuI#LU#2By9.D2#6p29sMNцm|EF:}z dյ9'UII# בOޫ}צG=HEebFBv1qU,4颍i%(<*I$?P3+R*Ӯ)*M?t{xKgےZR23西RAg\hr`WqT+92 ;a{q?9%]#Ӓg9bP.+*̧8`,6[}NsұHY|S @79Z).)R2ةH<9QDg$R'3^dp 㨮vdI<ܩ.P38 s[Nk}(X3?J=_8v 8)(F+-Et4NT*T@a "9dwϥM( A'vp1ML1۞+Rzn)js+BWg"pO*Y=ONGzNQm&MX~v3cn>QXd*Brq!8'p@;B䟩9Bj;]~GLFMXĺK$sP1oWc];ȆIDquG@:~ugɶf_(ۖE' sB+Mq\8M'גn(Q@!T?wqg-Fv\5jmp{}ic#ިIAl9݆l΅uH1g2dJ8ӫO]5 7-fLbA椔Jc&<5 epw˒y9<:ָf^pA`:~4TԬxxF@[aF8 Psi|́~YÖ woۯj%rʖ9oWQ'{?*&Ws=m#7&ji*m(R=P=:[ip+D뱰\N;`~Y5K÷k!ʤMzwQ^Oy1+0F9^`zJ<p໖FKi7yn֐=zcvO C!@fބ |ÀU9v=>8XK2F+cgn{ b+{Xf1Xw9xFcVȱ>Sesש5r᧒BʨZP$xs_y(ŋqR;0sI˛)eNMŋQUɮ!QTB`dsRX@W+#b[c,[fBeN.w#:_Яck< $@u ˻`>}DȬҲFP)xwa|~ߡ"1kys!p?8ey"·A.!: !NZyem!m=8?孔oVBPA՝ /qX_Su,seԷ.eV]/zu=Z0 8Y-ڶQA6Bco,M2XO !~c 9銷ybcxB\ZdoL.?Fxv4n@\9,H'=Uj&XC" +qYJqish͐-xQcxYIŽ:yrX>@_2@d;Xb:F=5>! i1rb!{Z5[IGX"ȥaA\tr-I]jBI)]Cw8_zU(!ıct[qMPUxG}X9nW.Ir @aQ.| Y[$}}ԓ/sa1rN$ URy#hbM9}y rʶ6De93s]1svi4bTyEihp -s;e;lGL}OzI^^2q c=s>X\I(=#v{}*8NHSm'}#ArVCk{BEDb;FTy&;W1lX.̋snQzPz3<<晓ekiQl`n+^"l,9M.6P=NyM?RJ>c3{u&k۱1@*y 1"06̺hZXF&e)sMXsWX{?j-+*EuA8sڽ8o)ܩ>@I\ Z.帝liټc0Hl~]仜Y~'nȅoNL;`*5Ό?ád} &}q4tH AZNF0:`6|B1GjԷ(;XMw8^ s/ƨSsUkDQǸO# $SSxq٥0UrU[g(?{Ϙ 8T)tRvĂE,܂C o895zI-\m#(#iȮMhTGЃV8Z,wJP]P?ҲIT-5U#qj,0 !.ZEoyy$XV2 XϘ2 g:I.US3יCX3mʙ<|񮉃7\;OI'jV ʁդ"^VǶB(穣&uG"|i@U@AN* E&$$ҹSГ۞) tРsXң `t}KoNgb+:#ΌVaU&L0c:V%+ETfl.HF7˂P( P,FzR)d2xR%0uug9Y(E[fU'^F|VR?Y@r͵O DdC@G$]0ZWUr@p?AZ[(u x.T13G:ymإ~N9##Yf%I&qDZ 5e\5 |xɮ^峙_fW'cHAe#YjWD3~l|У̔HvOFzuE)b$?泵9e,J?#u`yH+yrFf`bX;ZuPt_&dCqԷ qUs j4c6NNDEEGʭԚ+fwJJNv0)U7'}=3=ѧTǶEvRQNǻ*}.p֤LL4㑊6\yFQUh(d8 犩UwغQMݳJbRry*h@Zm34G.?j 񜁼-XWn\*rbI6zpy F*OUWL؃fR9+5̡ڤ;D^[Sݛ4nZ&@B ,VϕsŠ{_46F (ӥ!<V,9f+[ErI0yP~Zu;ncq$|K,8;o`r0k.Ng-ԝ>yd P3ҪǏ.)]| J2[vIgy5Iu0qj ͻ$dZ:-$jsE'>T}EWОXRAf'OkhiTcGSDJP0h凌 Q+em(A,-O2z\tJ}g_Qp8d+ D. Ä@Ѥ1#8@q2G2R[Ӟ ѼJNr=Q*ʌ3^1ޡW0ΩV39g|'TTS]JWhXdi|rȌ/vxAbp18FZFp1%w\t9O,q)oT_A#;a_8eAHU #<G=q؀\ɐ ϼc*F8D 5pw>ČWa H#=! jFۛ)jKIGƳE4ӕ"{Hܑ@lv5q.?xd$. 0 'օ%$Fn2B@ WE)sjsCdHv82ԎI#OґhT8e G≜[Oϖ~Znk6*r~7Nh^-.1o[cO!m7lO]eAyE-/dǙO\VpNmNNt3t>sQ7AWT—r47=cR sU?~M <*G9O;Pe3`1ӎj8$Pt+Z9cbX`Di㧥kJ5E꘥q#Qj`KIn㧷S u¼B1௾k:żdH  c# kӑGkRȁpܒ1M8wPwb(#M0?O29UŨ󭿯"[aFN֑U)fa( iH.p Q=HR@:֮qԛq[Ƨa9 ~y;tSo9sFXH[ X䷹5*€!ooķYT|"5Qr=X̙-ܓW(#543djsY-!',r oʹBy{sfV8#o-0$yv9M:lBdN'=y^^#/7zkfVоgk$dg9QV%47?.RFIMfG,Ɛb2H8$=b\[纳,}+ EG{u*y_7qf*$%TFF{Dr+Yrޘf>JV¦Wao%hmf `c :{g*1ٛvl{ V:P1{XijX$`hKM s?xڻ#>c@ @ϰoҥfhBd+*{%e.N1g17k[*IiIu5"=8c˃FXa,\X,14h7rN:^2?UB"<5Fi[F\Hу Ic2 ޶fl ue| nPY>; Jp1򞤜~48d @ػ2= 5Ȫ;sj/VGژbb \c8⣖O--)?2y!iLDžs`'(eku=jGkjۂN[.P:`T)vP R\?A]A휶j{YZoCtT*fJ=Q98gNzTP 2ɎyJC!ہ9 SM/C*屙r"D`>ˎ8=GbhSEsws$ VKIc`U1{sFW5e;w#1Fӛ֜'(L[¢I;v i*3*.~U}( HN=}Y( c!`2v檚 ;tSYH`pAPU;~cǯg9I_cztlK4R[}m!KSzvkvnZE( [z0zCV"l 1 @#ç8XXK/ު3KH.Rv_-x/S9 yoPbLkӿ>O[M]Z7!@ېg+i[gX!$lt Tme'c[xF[i,18=I+GBuž~~UJr=GhJVef;'ZCiku"2.ӏo_zC 1!S~~\g8qZ6a#WN:pVDL9}0b#sp03kdy7#Qsn pk)fe ֠bF"R5gs'9*iI*(VOC`F@ˑlگq-)n!%2`F72p2=z}kq]d@U:.1M_8FM(>:{QsH//$  {皜zFA 'OמFGjTҎmq0 '}514H"3lg=3QZH+y%Tܱyq~u]hG,W"f72GLיJ"vРioysnVS9=r+ͮE,m'˹ف]u]֞fLfu%@;G\jP;푉8X#x+NjM6n,%.Us 7{J\]QN<Ӿwö-b] oF ` y93HMnfgcr=^)Ntںd2sH)o80 CW5ksɩa.In[N =9&O҃ڹqzmbXЀ *Q[ast.y|ħ鸞9uzWjO)ضsƠw跇F#cHHs^rGA?n H*۷8$vlRAp ʌIq~=Zo}ʜRMhqx'h@W7g1/\YwG֡XnܒJ#>UhYtF;FT8ly7/vBE$d"%%*0=;U6..v;qrF;jYj_qޅf=c2v`ҷTA*VNKOcեM$yuXfIQ2 y-ZX^]  +P˹c؊HgksIxQWhS2 G=kEhhф░1I^M4DW;CHB'/Q)Qxe\A}5I#]}zSъ nW/*[d/߻#qq#gj{y ,pG?1Jò̒U*9r"]{w|dkJ.4hXik8PT9@8 X[cP>y;UĸKeVbGɮf[[;٦xcHNUظ]K ϱTڀ8-G*f&#~ O|.4*VP@=h7RmBqow+D&6#wqb␱T>'8Z:"Hy]>sGgUOX Yms&s![v1b݌qҦi\ $*WaM($v\ ;=yŦ)Pߎ9N+4eWe$ `qQ _A,ԑeS8ۦWT݇\bzZȉitpKw1zIcw1±ѸɪVv,(P;2j8)$*W5Ve$PMcs)l529(je9դF2(ʀӿY,wBR۶ kPI+<4nTCqG?8M #7)dk;QKyc}F{ hJHŲebJ$p'M*8'NLf8pGN~j[H)H@f'#ff`z=j R1=P3F RԘ|VOC([`n'?M}ܳ"%ENJ;~<)&m 29f3*(V M<[Y[oSA(Y}aEq'sx]L̗()p>lcI􄌩偌/|ԚE.8}ycFsQW iT@([=*bUH`N#~< 8[q׵6  + î1s TN Jmۘ}J%^l(>bW{ YHo)3#8.r O<GOjڅh *y1lgh׽K#`dN.)L1PN]`&&1U*6^g:Wv-3Fz⡒cP {U+mB+pUw:p@~;Ք2](7LJ)ԍ^plFNBxzx`=IҝD⚵Є(Pm48szӇʸQ̲2m Gq֕Gh{[ jJyQ/`37p=f-́O}+;P K1pɘd+iJN[X *I9'?x}~b$L"ryV#8#m6S% u:WE*1 t.{h?0 V>Ulj=H <3>ڜ\̭yw+;+Xc! 8'!He|9$cJJAc.N9•=I2Rm:覬G`IC; $ƪ붇I Q3QtɮKC1i $cρQj0<}m3b߼tnC}'tڨs}+{k,Á)'8NR:Tۯ>M#C4 H8>?o;:2֎9&-իj[6aiU^e <'O:gs%,Ρ9P 1kQ;&\.:WI8"Fq+kh-y+v Ā4R20 {)TXXN+A;ԽՎWĥU#DGrOӏιERS1\7һ;H`29kՙ BP2ŰHZQ!Jg: Wfø#x?eO;#Q28Ek8*MV?VәG36GLɮpN3RȢEhӁuE%2zKx5#vzi P2 g=YG`-G3D ʓk 870#ieUvH6k?ҤRf\$f}=SؗQi)2SiKm,r!tl5)d'd97ݡI#h1k߸ZrgBm+O. 8O4lBAxZE#{a_z魬>æ9Tziɨ+-ɽ5֐AtaEp].SEsZ}EW`Rw5{ <▎ߐ ZC2Qgf1htR0('8&8zR\/~'!~t1MPkuezTS1Xuf Q4P˓ӞsA%➯PcR!!@ɨ̪r7/ ߩ2qcyf8D \aZ7 N*@5yvUm!؃!q1R'NY@<FzĪ7G +>%VL#&@L`;XqߥS-amA4r6dq_fn>޵ ,+ݲޡqǥ5fe dgG E!FvAzOJZ4pz_3Qrz;H;Kgs:R2/<ۚ[iLyN];`D#ז rs=Zu ':͂?w#U6IW;NЧ)cOMOp  \#Gu|\Nq50RrO\pop#[`|s?<`MD񴎅TzW3V`)9vc'N̂4b18ՀX{81QO'҇AMEYI'6,^cl^G5ZnFO^>>٭+7)Px:d :c©Fd7ލ#:Q:% TFS2\}]##K`0t<'?Z*ӂd8V)2ꠂ;zc}+Sr`cOMx''x,P@ {6Wq8?gZ*PaRj @~-/L<Ж 2 " 1=H,bt;2!We269&5֍ucXa*+F3'z:qڲ_A$a!ۃZq]Z͜cf*G݅QN=}jҢ¶xgޙqx 9;Nѐ9Xs\u)8 f9v)F&K.'Ed331\ 9pskP$ R$(P{5@~xnxǨSڷ0 ~nzJ0澆*rDcGBK&HO#u5$)Y?Ҥ1ƲMKw<:e%{خ}.,8w=*{x%o"\Sje:K 12䬪~]z󩴋a2_,@z䟦q\TŤ^E7#Ja=TG+ +0fU0sN tœTg+w/+F?>`!cz}M2RRO6Eu$8]={ӵ&*0 `UbH|Ȼ'3a{Jn3?#hy 檪-s֬=U4hl-5ګm^QA X{vW]9F;$> 5F? 󼶉ǃV8TrC*g^tȣWn{~#ޒ9~X`:8?zW#DPNxsJA{@z{zt[{$ j0Gv'?Jmt Y+9(bW9R e;89tٍ5ɳ,0,sLf#7 T,-nT9 (䃜{)5PFrMgJæT [p$]­ncʬYɐ G|WWSy8<6R6瞿95is#jL_~5OOdIX_%H]cqPڑPdГM(5R k9Z? ml#ϥtL~O_sX-Ys3;\@G׌}ΙWf^WkȈ{U[ \2U@ā{sK++߭fAAݠ͑W4U֯tҾ}8Fۼg={t "ݙT85Y<[+ #8i_[9v Rc<)$'~UIXôJHJA.Nr8>Bb[x?nW\J$cOZޏ,nש5IjE@ܔ<^r11}0*789@8 "]oc$w~>r-;-!7y"_?ݽḻfHFһ@cٸ\ωYUXI8<7o2O>"I5hQ0#P]MĎPlQE 2 ' 007 ܌gU.`;Kʠ n. m]3N3ҋfAJ 9 p3տ+HNʻlï`?*h.k:Ѓh5%{żEƊLdffOw8qǿ\ <4DnF~?g$} ғepzHJddr2P`F+z௔@0SJ5%ywz2N;.{R1 h% [wR[;XmF\!;Aמ}+ďyVČNz۲7 `7ǀ|Qs~HIjwj =Z7!f r1q폥q!&y^kxڥ…cXrw%}Eue]B%FsoR+omJOrpܸښ&dt>h?-rsmǧ\JQ`Wa!U'< 'МQm1B3r~%:-[YcpTRr哝#RУ%ݮ#F{B9<қ%g$21?9"̏vV׽C;0ԩalr3H!vJCmf &[yk#Aۂ :TŒ0f'GOh-h<]Ԉ.WF' s>'V\̛`]з.LsH{$>< ?`g@23eovnx]K$$G Pį=?:"ծg)cu!s~G]DVMx*F9syK{ yU]?-%̉Ĭ A\Hҩk-0ʅQqל{+DYЛK1bՈ8ӕ'8!ǹj-K&)P~*#ק+?JewA4R6AcrYӎ#I£ )'$[L׾EΟ#.Ñ'aqNz !`[1gic|PNHmzq٣#2Ǹlc9~5$2h\O$02OOJ.aج4e>P"XSýs'ᵶ1fD,08[[g}e-!`t>oJN{.?hy#>{(W K 1ɪd{ ?Q*`ds6 D|~T@GfIq^e+qW[\nyG'+ƊK>\V@NZ&la<NHQmi*W֡~Y=Nj6$O.07o6d?QZrJn.m>EkXL HFOcy8GNUyOCcD^"?Z}Go PȲʪpϱTӵW%΍ Fcglе)n K9!n\ O:]$l ޑ4e ?rŐ\ԍ~,+Nn` bs=*aĜTcX$W|*?.beF2 `ggqV1=@'V.}6ia!c#:g繮vϠQC*ji F!Gr<<Ϯ-/XC6:p~͹|c+"I"CQ,I'rV,zQ5۸6Y N\FVi267N?F<dBv>}+@Q#bSda\C]0UO@vLͨ-Y.c2yZӖfke)<8ܣbcngxFdײ}41'OlNFR" 9OZkpags[Ow)QTmӯɫ"]Yc gPEFq'^;jLnUy^qX@pJ3޺%Sk>7afO2F;Kӱ}qAR3 /ktb#*LjKQT=иQn-# ֪R˸$ IuCby5k2U6GA+zcE±9R0F~ۻ %'s"{{uY0IT#J8.;Iސϰ\\֏)X)I`z0? ۶Ԥdjj첕)&@p,ҡPFqOCWQYI\󁃒>u [K'$OWDJtc~CP*Zz*T0' 77fLuզay.Ȩ:]FGi95u,wfIT}J:&C㚘VAaM+eXX#>2kI*xΟ, #&!@5gK4U,q9SNswNV3+mC4r>3$?NۯP1t$$u"4&JCWCihn&iFpI̧yީ-qyܒ`@+[NҮTD ,k/i}!HĀ7ooG}Yί8UfGCuK~fQI&鶞nŒ0Cy09kpkzZaH$f2-y BU@k6&03g{K%?J؎x!ŚLL2}k/͝!01E \G˜jWRadCw,͸yyS\ ӲK!iH*ۈ8>ՉeIvCooFQepvp㷭Db_a9Q  gֱfw['tMLLVMٞ d˳@@ g ZjG({)~@f?O_L~)B\8m+yZ1q4*2|5.JE  Hn hOp~SvZT-%^FvqӽiR|DC,Ȳ#'gVq82ʖO5aBȱXo72 r=iM)-=(G8QwoJkmP9VS`h㴻ieir9S5)$2$,wd4˹XȄ*,O{V,;=EO2Mj`| d`Z@ޢ%y E/kQEhQE(VPEǛ&s<uFm<V17os)$c։՟#v0>99Ҍ(~ST_Eb)`^h] 5Bː|?KK=ƘHl3չqFShFYQ#xGJʆ?Zlu(I⩛^E!O9=T!FE/! ck[j@WRS61⭴Kh`%X@=pǍZm.nI*!$ŗmG jߞԡn$:@Ǖvy u$M#4lSH?2~WNh` RFUa7H UO29⬣|u'aQ'qB l#`j6̄kEBk":W" [l2l3 ӟQ6Uhԏr5nn!_p =s>F)e^d9x+Nmlv6ݴN3ef@ l,;8 +#OXR\̳ 1.rX{rG[$ `<֪~֏q3=Gl~`E pLҠR_r20p#ڬAZ\c$R39({P19jH0& ;s+EPu3M| m?S欳ǥU:IK&M/&QFv썎AR*ֆ-mU.݀0^սNX$nr=7CoɌ`(ێy"z:,=Z;)njt;k_04R%N}?fߵ;{?sos42Ď>Q:W4d^gVi`syxI4h Yxmuq6#<28r=Uw2q3ִQ+Σi:o0*Jm.1o?=S s%ygI?.=Gե fV :+*mcG[^yr(PѰ96sYi~i8O+FI pIwR:IZiV!kΞLyPp.2G韥Z^{V}S&  jy^:րz{UҍK7TI; `0sUf$`2059l?*κ]vCtu9#8 }VRotrڱ&B"HCn׊ O1#I\x89Q8A4q #$nc~ Sm*KqviH)2v@')G1m a$䁎G'KN]j9_sF!`3z L`UXF=z=GY,1͒ry&U!-mZѓQ8VXiwP1|T)輖&eo4_y|y *$q"w!T8gh6)f F<1KpȌϼHOU5)RfH: FۇR2*6y$y4d +##<4+*B< zl|R~Pm0?N6(mp_q&Vȏ 1\giv:c6Em#lR8,X(]8&m<|"IA׵O2xaPG) ݀uPm5[ %fB@>ǁ|R_6y3OlȓRj6\Y+${UI:$ w9=8&䢚s~Fc .a cS2.töY&vl; R@Ƕkѥԯ9 +pzu5;Kx]( cZҕ>wd{kX'5{i5")! {@9׀+T8U#ԟ5OCKHv7n Mp +ңIxsW[!Հb:2ysӜ~' +Ms]Ga8o3ji݌$:-3=>þhkٹ#\'*~I"] 9eOXL4y }SxQeV$Tb?)ȏws's\k*S5y.=j4p Rahpm>?'`XSpb0G(;t?S4a! P$$ 0q8M.{kL_}ߡsRnZƭ\>,e]F|`pON1xR pzGlPYEKeb`;KIIp8 Ğd8!K~6hnw)sC 4E= !G֑u$N2L$g'-H S}*䯯p4!?( d $}xIW6MaMM^[C]L˄$*\d޵ἷF'μ*_]pOlwv`[=~WxRc9GpsQN֥(~ߌ.eO(6gPCmҳ"~%-|˿t'5Z"4 &,v7w?Zɕ8Yx# GҹⒼoiMimI$\A Ā0~=\=܆VsjC&# H#d~4빠0G:BK!ny#sA}LNH*7?$}};VT\5LAr@9< n^ #pՑHbX{ԏ"XΣoΧ#;AgZFsvtg"f~gPI`ïU9$z"lH?7 9EB74̳aw7 ;pbOx|rbF e7+4\:y]KU2E<#jyy.@z֌I,@$pzk7O)lG;y4ˌ=9gO5^:+ Yc"TI6diG\!bpnCsG^}EhYVْؑ7NUH;9#io'nA1 n&PQB˼/hN8p}+JڲQe(nw1P|,A9\Qi6$/4ȢT GNONըL6 ; %g}v#53snІ5HkO AR IpH߂r]H'l}*ĒH',2o8ϠSQY^{+,KuprʜnZ4z2c²H ͤM(mXy{VQy ҐNX0IYgHfU2!ZultVķLXGU4gbl ##p*:{GH [w<5am%Ogv-iShtpgZ^LM~vKZx 1Qln͟g̷!Fb0_i_+sUE6`|b,85Qm++Cobae̓*жd8`!1HJ܆\!!q 8d#$n|Iky4epi è+2{xMMP N7mjoN^ɷnI]'LH%*=Tᅫ1Jv +{Xm799m&+f.$[hYш[Vz% v'b{ 7de/09GiҡvIY'rJSn,rf:0yTSDCoIJ4F2qqUwQMqw bQ\ٳ5*H sՀuȣXܬfPXJNsNùnKscR.(ޞ%Aa"JǨ5V+M [OY|/wE%c{dTs$tgG>,]2A$;/OʝBA(|3q9\k"N$U^s}M0jgiD"%)cЀ=0MLQIkg2[%v GkOquf2H 29LQ܉WϕNAj}v>Ӳ2ӈ-'o^tENuq5ܰ]`>I??*Lmq"K"~-;X3걪gܖѢ.W6]8ɨM 5ov2DXnFnZ5TLeP涤ىb'1|N6d-GS\\1]D{:[@.X^Eg]qNz3nM"$q ~Z 3<^xumoULMejiZV y-J͕kXH*ǥrzYcO3qb=[]%%Y8Xl.uN*ԟ̉AGTqPy,Ӷ1ک\H%T88\!G n:S͊{bN:nag}CL-!]hm߽rS֠V7X қdyHpFh2#46t!–II{k1;rF >r5=|ҧE{1[RxKL1`cS;T]9p*wR;Ìez.Myb2y"+"K˓:@ pZHג8óeN}**P6*%X|'28R9cs;Yم*(8jq֔S8{o^I8Eݍ+rpySsJerTkKy<4EZUeqE"6}bRS_(%Xl¯~\_Ɛ$pq?Hٲql>Kwvgk5XjUqKW8 6falǙ35%!PAz梤%h;;z1:4(l:Qʜ}iqczc 281޸hݦAJe֨3 Y2C{8*;z`&hEY2v8b[QRM1Vބ+-%i8^;nUC%sӌՋfmL Nca=~&QkM^^LK-JxBGӹHb$ c c8c1<go4Bkr>\O^ϭsFsXԋe A渷1+.Im2 rO9ϱ6a4rB$eLx+Ԥe# J NI\jG,2GI#v]Fnrq"Eqc- ˆ|(IfoiF,gr9vO4oEסArvr yC1 >?* 4䕵v:'h5a`IT5XԌyq\r0SػdnV֥vB[lSrN7gϥZ3iQdccӀ z֊$R 7 _ivi'6,P!YehL)黡9;V+FwIV+q›vrV"qw Jwv׸9b=)tX+0acGV8˹;ؑ,B̨71qަQ$r;bf%?/#8^dß|V[sc~lV}쬲><f=Oq"m62m89JϹGyQH=)LI]d~i#V }q ER6]6TdR3[lPH30|իhHo, PA-ӟf$} CX%fIF/)Htz] \W_^?JZAS , %@~bIXP/1dE,vaR1^2G<܉ HSn@#sҧ U]Z ɴNVVB:xvJ!L ,߸|HOAjًJvYA pOLºg{F@,P[TҮ23pzk>&>Х[=2Hg5oMU2RF`YFHvqڦ1i_U3]oeec>b86q:mn%3T/*$?ӭb;m|L}k9׭F^;儣b; P? ySD`$=ONϠ$].r0,}8St挟?CuYH#?ʒMȍH\~]"Uv xҡԖ+ȏxL* t=)O=C6.e\*"<'ON`# [YFcŘ]JL\Ӝ.._:LNlSg \#Nzd{ےB ֪YhԱ3SjO/9lgh޻#YI=jةv[gGH`r;:`"[ɳ,n̋ʦs$Z> }3N;U]X# +`#+Ry7i/J 24B)$O=ԖR zp?ϥprj1/ZDURWs,wuݘ)e!1`HuU"%T pxpqc,X*Peާvr9ۓS%P<k5Vk3Y67PJlLʔ####x'ۊb 2#>_S"4 'WN~*\B;zlr@FR+~X00:jw{٦w7VJ+|񞞜u>HхW=ݚ^fE+w@A$:wskj=<3U+_ڠt+oʺYDUJ cukWUWnijʆ2м&V_sG|Vȓf32212+>iℂcv|LnkQY-MagWPuv_Ċ<:WMw9{ڭ}$"|Ml?“*ZJ14湕*Qu0K#E2p)b`t mNsPz2fۑ2"B=qYr7VKrҴv=*+Rpy\lԎ+NVC^~U i( # *͹ij3$jr:B|d :Z-I"x ȡӌgRgf ֒3kקS1K֤5I&$HU=nZXK+3.'p\ҝz|Q ʭ5 ûw#C8R,Qx?ΜFORmJi=rOC9"Z- #I#&--Hs*kX24pqݷ&{ Ȁgl̥ @U9~cɬbfO*p#~|̲JdJg҅SbmVf '9%gr2xg1eaSWFZ3iFS$l=}mȊM0'qkCȗ[`mA$B~-!10*VFl@$n9i6kJ1QзXXcQ!RIobj8fv[c1Hav cc;B4gh %xokryw+;{Vwqhn 9c# ~^s $ibH!|jdlpǯ :p4~#{Fid"'zsI Xds2$BXFMhw-6ؖ[Kڙ7#3`2ޜv vQ"F)9!Qi^Ejˊsл .Fs2}/"@L+n%W;3Y)Sj.})G+t>78#=hryH26G3 Ikw u>zsZbZƻ\cݐ~^~TsLch** )޲nx(PĔiA r94Yǜy ‚9Yۻm""+H{6a 1RGdg3J{ٌnC3LKF>n2z=G5KNs yF`=Gz aYvqߩ'XQrXCV̪@9nOABm'62I=;Ma餾l ,I9`Air[*XǕ*mО"T_`{fʡ氵 cp'x B'%Ob:;˛P&d@cӭ^a6Ӷbe3TSi's!%WV_e=_P8+^4,ب>P}~P1*1..#QH%+?v&$0-#=}%nY=KOsI.\3/#.I_c Gq%vw' =[H]S 4{<\+;JDcGv#׾O'Z[JX;[pxzép* IǠIFY%e 8n9?Zc{uS~G[gKgR8kC"sb^{D`,2AOjͼnnlF|ɭ{خ#\Hq :g+ՑUcl˻˖#׎r;Sx2ra]CS[I$EJ~Nz,YddlW=Ã"nrk#λ\XxKk˒,֏$ŷ`)$MXaT\zTҡDq8J*Rl甴.Fskmg3/v3zr?Z߼BCw+8P=럻9.R@w`2s^lܥQu}v1-3lTUج13Pԣt ܒq>թHj*,)1So'is4m:>vԒi&t4R߼m/EݓO[HlD$> A9`yl*9LLn2m`^ITeϨu']1rCJ9oP,sڝyeGp EQ$.j 4IlCAn:X(!#gVw4U-% TyrW;m@?\4N|5m5ҽ7 37G4Ă Hn#^!e!_5v=:O44ɺB\H?(9i[SX7>D8`=r1`xV)K*üH@|sScU71\me+FWםZO6ex6Ɉ|E;sr3aY!Jn@e!'@8{Ȣf+XٸxRQ7$ARHd_X˶c$gߘs^ốaPOL~5pZD|f- \5N^lUk>dѫ3QO2)SԵA=̦?d3 a)*9*@U.M^̇¶fXqW[ܤv+1 s;E>4xAKνKiXk,3X]\RZ#[,&lrsfubP滻 fIfraxрYQvD.ژmfn!XvrH Vt9Li-?UdXdK,w9멡WˊVX ]SbM5fWI*Gç֪wy>Ԛ [c9*;1cgƝaf"9*qyqͺ*rG"H;nPR~b,YoNAQqߩ5օ]B"1xvĿ4U0DIWBjljI j(EHCg 3Ķ  ,d9~yLqaQ!_NO\ՋtX& ߺsk21Z9ϭ>^mXeʒ>O f#d5dcSǭU;{et4yx 7$<7vHzFKp#Vev⤹b@IcaPݒ7|R՘6$-՜zW. ~St،1ؤJ'YYWkp9?T`-m Y3ފ-]KgiUt>ڢ+<ЦȻqNcYIũl4R@sP&($緷 u3$˽P?QXܓ8Ys0ngkKn(%i3x:2q>TY,NXLb+ zPwye2 lc'}1EJy߽D(4ԓ֝QBXƅg;xVJ kQғ1 $))= ?'\*4\H5r0CW# gq ]I\4\Xwte 2('x&jMI8%𼬎<`pO?geP%g :`\{WQY8*b.\u r kjΨ_6AdAx\5Ml_KC9A\95ZWU*E[;y Y;lX9uO^wM7q 1yXD;`R)˙Tt]uH嵙X]U2C} jۦu<9V.I9P@xcҤc3L񢳨HU $SnÒ=vlD`2IOOn8 m̧RSghC6*wϩ{OK Jf|ʰ k+( eidrO,+:A8< 9vsГƔטg*4ۋ",[ DI`Ui⹶MEV@ ޺x7SZ-[U q+"Ȫuַ)WRKHD,8`;]aCi3c#Kcұ&ȒZ3~2mۛ=O5 ܈$FbɌ>_xߥg(toXI`$oh$)A|AҬiE(ۇ,N0xZAK(#LuݔfBj20I4rҾ#QTU9庸LlR0 |~n=>aq$Qhu>cY8]mWQkdeM:鶎Zq  }SGAۆS(#9XV!!a2 Z:qr53.SkU Pqr&7$;-$SwctfG2` 6;rXcbs O q$Frv~`gT72KG͖I]>E(9KRdLO=Y@ls׃+A*1*\>5~ݿ#0^A\qIM>e`yVlZ0'8#y=Q2nFOZi".@\/ԙOp$OQGNkdEŻ`E[>(c:4"W2;l;{}9y]&Yќf #Q@E(7pIrW].h+7\[p"EzG55UW OSRY0;/֜73Ww(ݑmr݉NWx9FہRB+)ST a^0F $\x=qr) Y1y`‚zgӑ{TNN4;!C++`;]UqmgVmo& W F>sDDUPxzTZ5h.T,#@ r@+Q0 OցkW]g Xe?Ra}\,BN:Y|%kq ,It 0xurw،ÜsrMV/~P\7ֹӄ*>_SXNNA {zְsz[cWG]ɭ¸`vF{qW>`fFx*A o=W^A鞆4}BI/mTDJXpeAΌҞsyE4+HmDN1c>HL &C<#կ{ؘTdSډ-ܙQFy'R)IJK1-$2E*R0P>asS\ZXŷEp# \ 7qLu0=j*XyNpn9BNRNW7V :Fs2?RB@݀ā8Mn?kFT_fۏNq8#tȲd'}F \&Ee6ځ24ȠE $GU.HN^pnPq/ݳ)\1RId -b%0R2kgrϵNd*@?jtQԽq4ÏPiEϖf~&%`-x}VLn^T4­4Ǵ&B{@:<ƑN +:S0Zskb8CdR҄aHqDg&'H>\̐4{(WQI߁\RPCOӖ[[ah9r1[~~X|\&I&Tȧn{ۚԱmԖ9==xȭhU9Zփ{S.VzsFl׿t-|b =?g_\I.I1?vA>'늽#}©C+9l<)c1,q`0އ~{ г(iv-z㯶EME+v4nKFgWhD0 $0<߹튒WdmKy='83"xk0Jw:A '_4+)(c{4{nGTqP ֧xNG?fV10 c+*=q5=~C+}q9B+\p\!89j`= f ejS{AQ"pBW#[uXʣdq1ӵv+l` >z*rSF 98&+'{zĠYY3#C]P0=}L9,-wC o#1ҰlTT~&7<4PrXv ' Pqzs΢gʍH*Y`^#p^Ӗ4*޼r9dUi ^@rd.q{sZ֔iJE6[tKz} PW+&@g9qOzE‡,wL4;ʣ q>k[)Mt*vHBF';ޢUwFoZXYpx֫&6#RG۴8WVN IeGGeIV*<=~ZTYZE11XàbO9=Oj+q 1$OưVlҪJZ =ϒ#,OɩOγ}כ QRkm q]Źsir%NqQO3<\7t\Abp*o;=c6ɋ9INRZh{vJex4,xl;7r~ź&w ;p[%y9ٚV&P?@Ղv;)J*ŔIHf(K3NJ{{#{E`7k`7NlLHёqj͵(رO}z{pk=3Nd",2rwA 3ވ *_Ѥ|Lʬ[6{sM8Xn} b%k\`,vNA`nu`ͱ&P̃m 8kT+kkd[c21E8䜐~LdQI`XJѲ2޵II-NKt6ӣ/6qr DaqmUchn^'M\zv=ɱ@ :( <8`I=S;{i&kv۟gVWq7N;;2dw8Rd9!}:~Ğc{u0{~ %XA#Ïz]_n6"djLAA*ҡ,Ap#+[HOnql0?=:rZso~~T$7o8w,Q[(@U1 ߑӅ7O[)YsӃߚ ~[nfMϘvt$`8MB6T0%`Gֲ=Y4[GWvBM"H(u8{KY$VU qGb1]A21R $~=o8rsJյ=Q0[i$љxQn9<ޯivV2iv|q}u 꺌sXLe<9=y& aNx 䁟j/cVdW{ _?rOep_s;G bYC&a<$Ql\e8w+$NQxϦ O`HmT)bg븖!98>eqe$28y{Cu-& L*{iRO3y o{]pݳkKۛuuO~WHۂ sƨo.tR HQvF1[q`r##v{6܄јqЎteʟ1fUr0I=y+ba2 FOas~[&)H*.@A|c٭5| o7/9r򮥹&)5gKUIDR=Zk[k]Z_BnayG窑sֻKq>Z*8'O==kԴ:ĐѢRUpvCןN)MS-5l-~sʲF b;YڜX 5їG@m. brrЅ_lkuKGq1؜(o_QecyŊD ;w;WGݤKpI+(4M̪͒Wd3rXYFYS$NF ~Z[1ETtMe'hӷJ0D!r*%]nNc}u;gNYIGPx+H~˪\ڵqoD] y$\q˶(L2HKxqP zޫ%Jv{q0 #X2IRTScL\r,Zڼ7#3"$z=XRjq\[!|TVBą\qŒ! F%Q&_qJt#9xqN^\34 ^ٵy-> ;w^EiȮ$)~e}ulYNDYi8#W,YK[giq|BAw@ǭi˹8RN0H>?i+rGLcW gTrĩyn?hirv-\\,*ZB:\Ƣ.u-+} ssLVxn&Dr?*u17Lylw N'.k%MۚIo4%\ʌb c7A*ǿor뻹rSX.KeN2:OC?&-2eH?.с¾ guRts1Nܓr{ڱhS%mb3ɩeoN]OIkaO %B)qw'ڹo&iO;}0Y7H%~Ȋ fW. Up3#=D>󹍄|nG>Qn_›V4dw87"%qqhs6̓e8+ F>Omi%\ }$7\}h&"I78-wqޅ4֧AFF c I3?J$`Ȱ69,;IS,β?1MntjPɈ?֒\|zg2N‡eU#۟g^4U $;:uSk($U\3RIϵU KE\V^:x4P v`gȪJyp2 ^szQC憑wx}"YZ,+ky>YKDCkk%fj1 "̠N0A+Dtc``)'9?+ NJ?xҵtvxĎ#ԆFqI.\.M#_1C~oZͿmeHHyih-/c1XFALuVd*ZMJTcOMIs1ilLnaTF}`Y}7da Ŏ$=k;[+͉@3<æ[;qĉA;J܄La%N!E Nk  8=u?+kHiF>P6ZuqOb䗷$ ~]T+{͐'ޟ=b|۾H8PKy,8% =rQk.t3K0@'4Qڈ)AP>~u^q fߘ‚=ycs|19w#VSVvD܉Xe!F}[I^NZLq]i=0GsVEu8U\=b^i(ÎȵH v=79SLՋi2n:}k7q1&ys2mRIlnlY&+fEiBs a\^IMiX&dtFQA}y6 w+]e m|눬XȻ5:>mR3J%Зٍn$;pzd}`JrmjBD֋%dS?('N[K4VJy`?Tyn]>gF zw!>c 0QY9F憛n#XƤzֵi;14lsJeXSkH\{#{vOzSm"]"Y]hTqE\_'ßqfjEٔp Zh!1}MIYZ Fpw\j5JW~CKR9H|ߘѹ9>Yld/NG#!,yVO6;xx~cnZf|6:!V@NVEFջuy`pMU F7!8qG>Bq59RdTh!9ҚA6`SqtjI߅z>ԪտҢ#3XOUR\0-/z\իEuqsRc1QurQjJ1r U=m2;Tw7%& kQYirEV* pJӜVRKl:T7r~ѺPsۧFbc \H@U I4 $yv7 X/ ~%̲o,&c0 0 Ԏb*Mz4-2ỹktLyd c⹋âwLu&Pʪr mhqȞXf9xJ-}XPws,̎!X᱂}: }+ [7ۡy "* 0?R1Ы͡]0!;Ӗe2̒JA-1tw l95yV0tF5"n̕@$cc7g<ߧҧ32H5&^HQ& zj3 X|#S 94d__²UUvP]րIa0G8'rgb\D~cշ5HC#3g& HjR}͞TmSFdzmK4eB(>swqJ-ZZBr1w,U֐X[6A~D#Rd9cym  [ų.ylQdH,LN8DyXa]r lҭ3TA#\!*=1+NW,oX'qׯn*fWʮ8#=iT2.N^15C$$3!%Cgzc^'guo ݑrY|Z) ŵͪ b[ ->cH=zj^ i*̙;1Њ-A$$z9mY-ZCFs ;Sr7 Ĝ`Sʨ|{犊Ta*O=Je4Kȟ3$8Fa \q:MM/=뾝9t3WУe"[ۂ8`L݀>ڠ̙VIS4rNzT9cIs-z7yhE{pcW5I g;>k.RӮIs=j +'.%Q/=*P=wEpP7Ecnׯ>b*J]TLSuzݍb8E?v%u /.ubg)ץ^-znrܴғybs<ꍥfD)!3F?Lpqubܒ̝6$r9*0 byd~9#5u3}i>xOZwʥ-qFVdk!sWmVw d _pӏWs)Jpp=OJt)JHpR+;@"C$^Xԃn*WF8 sCSB+n&6l`W5T"1*;t8#q=?ɫsC&YT Zy&I8' Iv'o@sZ(N"9N(A~+ eIA  BSE$[k >NsP=gw3$8iɂz=iWdSdJk'?O˿Zĩq$*lg.?]4N5qw,;c SZxVc$$0ҬKjC,%RG2I$s ] (#zr4lkK* Ƕ{mA I ?QE 32nlF{}:ՙ dנjiff2ݝs{UUeI9\pO{u_ o WtCGrb0v6ߩXnn0H uYGezBd2-ъ2 eW%)_[E u7|'g{U$hYnP Bq{ʢ5 0Pmw+ IU'@'~}y`A|Y]P1ܰZ8TX2GSYkBN+UB˷*ʖY"q{sq4yPI\rO=inaeIAQNAgۮNGXDH~QOuh:[(ܥe F1:O?C#lt^yjƧ!C ɶ<J}Eq%nLdd3=Ozm$tT0y,2rk5[VUx3A8o_,Qn9$0GԒxZi dpq2Az}+jløD2FI.@:1ҭCY2&H\g$zZ$]zIޱrǘI8ݒ84Z;sJ$S7i+"4Fa\jiI/o+dތF9B%+B{M n tAkUI]FVjWXYUm#<*i9qҤدVHni6`<ϽA1`*p?y$@9/J*#|9W>*ihN-MռrF8 r1«;ȚVrH ȿQ_+BŜ۬6\R՘'$ zRkrMaVJA(HbF82>`͵G;⫻өAgk4@͆9HD|G5bƋ9-j+gp$H<(9kқvV=LOmfSi2#{3Ìpk3:az'8Xy$בڷb ϼ MLYң F4u\tDpFd')RA<@\ {eFHIqê+RJqB# Tzjڮk[A]t*e2>c'ҰK FW6 )$ 0?zto,%Alr0yy-#H(0x{WVZN]M84chuu ʧ.Jl)/At~ҮT@na#m/ًT[ҴVKESo$5]g>Τx3@Oh̒$`;N*}Ak{Uَv׎O堍?r8_q0?*/~x4i甴DN|P[%:mA",URFA$.$sd[iah'*sim-z(Jd\$! p6~~OKÙ:m.VlYY br‚UO3L|o$y |A'=8}*}FtyOѶ3rI,kLDlX݅Uy&N*}:--a1FB*)`$* 2ng,CgqԚx3H#;N:UK٫RЩOYI_*zg'ִȭRE]}˂OA %2!`9=ڳ%EG<7FC:;4er* yH `q%qܿۤ{P$ +v\@֓rDg+q'E,Ѽr^FV2G$"v{w ݚ$dU_kgɵFI]KtV2g?Rx5797>[bA,<h-pC9g{SyCfӑs}'XA@=5cF-7"73fvGt:V|Z I!2- c' w4F,c;@Gsӝ Urvz5@IFq:LŅ˻4Fӕ$uݐIuᤄ<qV̧ʅ#Y0dw8835XsIu*Lblm9zzՒgtB\1<` k˘@29]yOҬ^x4~A`V..2(+;2eBdٜ>*[;)!gvXs$ǵ4Jc<'یZP(DæӸ?^p58ʫ>],PW3ڂx*Nz|ǃnkQ`1ϽZPnCc ~˵p ZZLZV)$WZ<Rnj|Suz--GIKn!qQB Q3YJ.2@?Σ[m # ,)pH(rZK3zɭfJn:;5c@i cW1d @6;z>^}s]<*ꪧn߰QQYZ2Ɲ^F7)~5f?.L"pՄR=9>iU6@RUIjXFF81bİ<7qzVŒsN4-SM/VoQUlsӖDf!XnSUyml>9ONjjT揸cIoԚҤ%x  A \)ɸHqkcV"$(zV3̭c̑y-B{^5uEgIf2r$ }#J 1o*(Ih (^x<=ͣrmcSM-ť!RO!ۀ9N0{eƓ#ިwM*d8'/FKY iFW~Is[K*,oUoO}lG*Y!vjeUy$S-UcIb܎LoGusO<#e^h]r_cPJkf&PT>`STn^&k-H댃}KX< cty@C[ӃU5bR[H,3JgZ3n>.cY F8ہ==r.;C !vZM6ۊD!ި_41@e$gTUY AAS͚~[DZ]\PFSrbi7q4(ܘXm1$`5K@%YXnr bF=Bkkkq5_hb-n2J)C݊Ur/~(bl'ƪj[Ŝ\p?r K}E乺wBUhP{ Oy=0@uB'i.'G@'+MONȻ0*G "ȮIjXi3*sm}X{ּw)†RFYwO4 ĮTn|7 { QD%Y^?ѣןsYw O O'߶28)=_ dC.NXsC,WdKr>o+i4qHGJnRG}+ms>=4<ջy;3‡qdbM@Em읤U2m{VhQMjO_2|-_}k>iN_6lB< F,p=k;6S}+fNZ+fSۻM## bz䪖?18# N;zr;P@{R]$v I泞cw̲B1qu*(+#n+5vn+ѓztpy]G ";VL(˗A0%I1P^^EgvAm-t4RS_"4%*o$`g<Mm**pq=l$4P rxˀ+-.hT,i2xKwu[=7Cet,]W{05qG> ۣF&]ٳ^}F72I ܛX/Cz㷵\&PcIUٴg'²RKFlE->jT(K+rrprH&s ,& C`Tv-n 䲷?1 8NOJ&/(RA8qONo6iKPjtda*fXNv*kg ZKY $=yhhHUFs}N޴m66=y'?4E%sTkKs"[x <n##=Oik/%Pw8QN=륔ED/Ws(b89"{*uYN2gT~?JlFROӷu$ -V9]ey=@<~u<~RwՄ.-2b TPۋ[t2E2Ztw6G4G#:*q17ii׉ܤwssV.a&0Hp8Ȣ/ѝ`q]QNNgEhdY@p;*̠DّҨܳ#I,͜P x0>d̊z:~Ͷ٤y{_fBNIsKHAF8~cxwdOd_+oʟ*G#z R`RN88ZL۰<s^4yZOs2bLM ?Sӭ=Nԫ*n+\W߹Z$m `&*1IBHwn9qAV%Jwv8YNSsw;Xi$2dGZ9\Fg 8L\U &<;t* pԌss{TyppTF;5м]w>qeO|8RHىBJlZҭEJ.=0H1Aj_ NsI0qb\t;'b!q s9ʽn £az@Ēnf5N""reHЊ[Vdw~8PJoO3]#ۭ Ѷ >Uud-ο) X\k5+2r i%J)b3*~Gݞz~"G rob-j2C>W dm>*W.тI܎?X[,+1}gkJU5HU9'sYJ73Q*Ma֮|6?*9UNA`p197vv-E0#]ҴehI(T;7eq,J@Шbݐp0~V`, 2r?\ӣ]ݷ_ЉYe>I?=ۂGl73p q%ԱƦ8T(vrǃzQ29\jngCle:&&>Ѷ3Vs+U%W<*Zt6,b(-n&!&B*7Ry烜sף;2oLT 4:SqCy5Ԅf+b͸| pI:]% "@qۡ9T 2PF?Zt6N Q=h,rqJ<獇̄sӿ[ sWJyLI&,psU* )3r0y=>ҪHYf T128QIQw{~JuKۗJ2Wq$dl`~]UQc=z{ѫݕnI 䌜=ƥ daufieM^it5bצךԱtǖ!%$g37sl`آT_-dgǸY8xVIX0yG=4QMJ::Z gwuQ*2>^~NqJ&g- p֑SH~ޤUu#&"G_n*N6NShipevT3"*土{2>}cV'3#`UiTTe*rEsY:x .ط3b#ǿkFM!8i+4ae7srKȡFeC[[1<$iI1Zk$$Y< #[Ы!Xo>V\^{c?iG9EjtF.J Hc1#J/a>vkgRiĻ#$9;sV"(c2HdzO̰LozsM5/~z?̘ۘm+F 'gsZ䛂+:d|rq}Z+#`*6s큜z3*2prnugwthQ$rp r^ܢ@rF$j ƐqHXp)[Ms]eI݂8 3j;WnrAW*pHO|Yd3fEF쫹'>S">SO- cI6 7gk;kSyXmbF1ګ66VSl#-_G,!Ձ5mJaOs98 ԲԨl-!lK(0ONOQ˸f6pRVx09$n\]EĬ.v!aިi%̄,ܭ=GƴO`hs(mng,w R=iN}om4NK:6I.`kg!iT8G;G-E]+ɑ1=zN5[6re$dzGoJӮ5;,FYQYr2GZʸФҥPy{ pp5hDfp 9s3ǨN4d#A;RJն1ݦvW sǧ=ų"*S!9g=Ni(C?La*KqrScMޕs ky t,eL ~2wqkЍ+ZsR,7Xt*͡ܙ- duk"F3[2U! ׯ>r?`r|-T:jt_.7aŽĊ1X*NSI;Tcp6J٣˴$zNU*'$kÖ;I ^ޙ30^8?ABUP'R (t5gMIkz撹'j\7 .pq=5JLu*Ȅۚہc/{/3ʒ O=ǜИT` n =5OH难"ap$.6m@O?k;mpX9]iEVNN@noq%$QGk!+LFSw:5o%+rJ0xttF|U WvkYjֺg z9'?pqNS2= t NԪc 6rV7%jAT \pscڷTh|Y d70ߚ짌%ct&T5WP!\7̠ 'ҥI0湏k,SC3@rr=hi(>bMK,,,4@Nr1)ld]2y#4ݽF;Gڹg|oB3*1-qW_C29=ȯ>Q<(UaG@+/Oahh._t}jw3i R[`;$V"ID'-sG39U8 ˩jMed=}}};8MʇˁpqLwbᰙ$pI c//6ї[*gRrxd0ZuMqֺe8b 8sk#\{Ž˺:FF_9ѹ<:S)dș2 b6bzJiWtF` +RC4p8x${G6m fH<,$tlSf"bPJNqUkV0nɸ+}{{+أ<wY 0 g8+N>c/p?,{(ٴ iNqlrH-fB0 TbH] 0?.H-=&P"_6t#u|B)Kr bFϩ)$϶qK߼L$1rGz<1Kk5Fi|^X=j+eh.FCL^m75'\5.ms,ʹG8A[ln]6 hgʔY9&+vjh].@f`n^m==+DlmEE&rK e&V1رCsz5O٣yLD{qI8U.ۙ%V?yLİJ6|688ɬ# CkEʹ2,gt2 ˺r:zc;Vou!l$t'qcCWã<Y j&i&g\q9^[%92Z"0ۨXñ;2 Lj䷓ϙ-X p09Ryʂ4,&FGk;O{^fG*bH_|v¡k 14D Z{uXD,h_F2r$Z?J' 1R sz vqM+Fiu~ 9~OMo 弹S;x'U;BsT[b.ﳹ9O0~CuL^^BVaӎx Im%FܟfI#RBYe1Vlmqws'%c% vB;b:u*c͖Hr*mWvr{b]X+\)FpYIQ}G%HKGS%ݤ3J >z8j[wIy^`=zԷkM pw$Em wYu# ֿHVs}Tɫ$q,1P6p]k>F*l]x^;zՀxC# @OLjJP%.YJXE!i 1z?]\ۖp 9'kOM 7C<{cάd9dgU' WѶfrԺ[VĒ4ۖݴR~qڮ%I|ӎG6d챗Y\*k%HP3;r =3Ұ rܥrh!UY#XdQ?YU`ݒX8P$0Y~ޯr6F2sy$`dkܰq֤+B$`y RsUeu XG'Ujmq]uKEbq 8߅=O=pJs8Yo܌Er3OoUdY⊠g= ;Q,e$)<>)0lGeRy$W:PMI.fTeWF x{KE8Ԙ< k[ܤo$:UYlгC!' Xeg{2"Lo6\2FB@:٩&Td+#ԧ\^`FU@'MdncZOMq6V.~U)<(zGh03\>?*} A`@ʌLa^qKP3,ܾ*ɝn_IiO~b@i;B|+cJ|X(" Ln$ ~&_,BYh(Qrԃ=o,r YP9\_q16sw,r6cqO㊹.L1" x(k_JI+gG{pLe;>իol/DHP688,/x]Ĝ;3h7h<*}UWnvEX GRK ִgV\ ?u)s:XiӽFoɈ. GH:(3RbWJPSfbF_Hb?pIY [ϩ)qkf9CDַBI9T«qsbj7dJdk/ }=j/,xEc`'N2qfԝi/GC Z2%]Cm sI'OjR0>nqSq^#shp= 500FлdZ7Z c]mV756y4gN$OsU/ e rNGǮ}ZA9U]p:Jҽ4ӞvD̂qgڣuc'i7Qn;@i`) !?5R'Whq46 qBnď_#)rzM&4mHu[ig˸*1UK&J[emDR+ׂ: PIqcnoo7ŀ'8lAbqI;#׷>8sJmК4 g *BH2N:*Hb#| t?2**[<}l~U2**ێ<3[Ss0ed`!'1JTT ;I-K$NYQ֋W{Rv0D$OU LvlR%)MK2dQAlt.V]( $jn.ny#17#0lWkhε$(3(q?ڹJZ1H'y7YB~j/pwבy0G!r[A?Xe`!קJ(G^ 5&^hi>M :Xݸ}铫F@~T 2s9n"(%qr^E-AX .HxvwXcTXTtqM~y] Ǡ)܅$vP \@99w,=)BF[_k)99{X8nJa#?SAOJ̌TC呒}I-K tIwl#c9 Gd̊Bo 9<h\,Byqg&k් ǻ3dj7+vyY8X}r [ΓCs1< zc~q`d3wHBo&rz9G>0*um}ر#ipU\ۻ޴ ŀrzb/I-$Vc g[FW+^[0IK␁ޣBAV+q0+U(hr١ DPYր[FI;Hznۆ=sW;%A_ -ʜcȯTGE}i ƍLMl`@`XV- ,zAOCx_ksG,ˎ>SIYض'y$#9mH"/9<2dX`! lzʖNGY6{z֜\`qn)bq!R_ӭ<U{yy$>r`g#Zۖrc/uA FLy?ܟL%K(qYml+Չqj 5fkeK)®2O~ovG'|H9c5J7Twiw]<@>$g=tV$DH,9q?CP@'ycY u+NjNZG씬~R^rZs[,k-q9>Za[x;.7cvdiSA$Qc5.iy0nţ5PszoO*ɔu=H[Gj Ep{zUf+o%dS|-#sۨJK6%R}6!y/ΦeC ҩٺGhƒB͏R#Z].gYS~w\{q҈B~e-]Q[2O,W!!3n8R'8 ׌P]I7 5֨ӊq[9H yd', {tm!YH̋\r;lN̲FRH9?1)|d t${%zj 7KHE”r:wV jPmUela· BR9#*b9T5=P[r&P@T"]=zN oۉc}C'hս>4innL{DKpq#8G|^Q4jvmH<qg,ۣo'03tySBe1E1gH,H} kAdٵpqҍ.)b '@܀"]SprzheV}ƅq<Fٻ#KrvH>y{O^sҪxG݊c(Rߓq,3sIR]v9J` BBrOo?sJj.hu@HFGOB:zg4,Gs+Ʊ ;K3GҫolHv o|Cfc3LF\n{gݓGLateGySXJ J~R7nϧڒ{}<"ʌ# gq9秿b7q .G?i7w?i]*/;@<K{V9F2gϽzw,Xè{u[Ic;Gy1Ʊ= ׊_n*}A{~cFU+wG[6ZO9RW1O>=$Ą۸.O!G^7NUf*:(ׯzpW5uTbAueԮ.Ib38gYOqh aA#p:H5jvQDYZ?5A~A=#iQF8G i$»n EbL``dqֿOu$WXg뀹>e2"@r9Si-s^ pC*X nӹ\'xIJnlC EQF@ҧH5;2>Eb4>d.I2xк\lsA]TUsWr3Ԃ9RE:Giuux%UQA_c\^jYbH6̼~R{Wq˻O@$YN8GCPk)vM:pL-GF5T9k{6%w*|!FKg'8K(2 K*Sy) 6Օ~e 2/DknGa< .QhI;rxFXbJ>$.S qL*T=S"ix7NP(A3rp\iv~r\A8sup[PYx-02I:XY|1B(d{ri- ?u#V-rV`O$`t=}n#-M̈Ո#\SKOKH, nNq`u rXH@Xv:ujUlLeۀp{gLѭV'qL̙H%#HZ[縷L$ #ع,3ګ9.K%=X4fX\AT(eWwmi7wXb!bI'Fztw^lK7ck0m򁻁O1]J׏@0r~`09]sU8h_?3^bZa<#T B}A9>Ek^N,J㘂aw%>ۛhQ $SbY͞#2ezzuD3WD>X,^OO厹YҧkIbFsarz^K]8Y"Fr_nS6^L9! z~vm'%ҍGim]_9:T6),;"ƄR1}F۹x >ȗ|'ƪA$UQ0d&9F=>nƦ$cI:ݬ|Jp@sU@r۸UdH|m <׽f_Aƒ[;"oNÐ+^m4F*ݝ;@֪.jՎ~@v2FA;#1qZjP_Io% w|{}vŒDD 2 ۓ+[Ѧ~2ʣt$s\z{TSNΚR՛%~hm Ir>%[Z ܬr8M 幊6O!X`8s]6K3 r~R:9#rsXIdnH) b=jJ#Fݓʯ+5  _*$Ӏjc!E$;]Q* &6V"rL <2H` #>gմYC*LV$`nϱʡw5 9#?AZ<'JWשS$Q1$J<Rj>0YxqHRQբbClTrG#em9V<#glq:̧' >SN/mwxH xlŔOcԓ\mCm'}8暫!LFx^{fU#RIb!70U 2,`)m$@|y'OTKkt,UbbP-V ! f[fTi``r\w9>\ܥqƹ,e\z?MlL\\[}6^F4Qy&< ?JFved¹\eW+)29+F`8qW!>[jZ`*n#$ `Lҹ^8 G%2-cHJl;WCgzV&$F'Ź8Tӽަ/ h_bW[{y Їh<*ivaX@,@inGS@W;u#̲˩n_XPHǭ}%ݛhnұNr29jCJʊdFW2U 55[ٙF*2Ns?#C{#:jCo=/\bfIxRs?YbI̊ 6 彉P=5o}/23 #$ O^Z崎H!I'˱9ʯֈE5•V]2k !LBUU@$V5WPx 0>¬[ȩ2mX{>}s w fd(wz2ZJM4Tor֟Oo4 łWpr1 юH>f:l~YDKue ɵ`Ͻ^B|op.#׎jZsI—"L;Lq*lel#œ*?^A{IJ#:̸ŒkkR !DjBwd=Mcvͳ6qRKG1@!j4=Hr[k$`N2=w6;=9OOt箜d eA ~YzͼJG>j"٤\j˓q &- ăZti6E;Is3},_OjF$mYf.d]ks՛j_~=?'USIRwEo"Zݬ!$ u$I%ȸI;sQ(Qg1^ Cʟ}_9]4h'U,zg"H_{WSGZKehfc\_,{q]3TD'y^GAzU)byz {U+âN7JODEYz?ֱ5c-M+)l18zi)FOA!l3Cpޤc]Q2ryϨSxeԑ}VnX38(ZkDM28̱v 7vz{ҭl  DmpqUnY$py>sWmtW&gM֑ٗ IζTvIsA ML rqZ~7N[ue;pwHoLs !ma۞pqeuŻ/z\*VVʀ/ZFry:sʚ&;bWFr9=9_CXbyM)%%fL,AfPHNu|Tmq`uX&$AtR֨oe6+OSZFyb 9QEnCȼz:>]Ǿ;h9$FPд(o~=iC4.+b59'iM+̔z2+(5 hrr0>ol"]ǘH'=8# y'>Ƈ$<~?(յ#y^Whչ_b?JNAd?)n}Sp@؈FG8aS[#77} Ǔy'F*H_oQǩ1zGT.eFG!}2=ZqyRHU2\x"}nm۰OJ[x(l{ w6D0ܬ@%x~>_rۻ ̇opseç}52/n< I^ˀMn9#?-i?#H޲Qu{K+;Cn 8c-KeaP Tʁ $+`]' g=+.X฽Fb[nH9'y tZF wW0,D,}|@U}B;`+.`z4iT2ORGIЛ=9ZKi Es?h7ԩKY Wi\nM cƵFPQݓ$ܨHF9 BGҤO|/^E,@m 'A6kt0Nj>RbgN3˟gN$؎J S!GZ 73pE[ː;׊SfaRKe}#,. ێjޟnQ% ҭDw=hMKMn +#)_8^zgޠm޸eҟ w`"l7`N=;{ܨT?8I?µҪu^tv. UGY {Rm;Q 2sǭXU:0uVZ+ *1*dTAB],IbObb#۲=juC$13ǧқםN40zԧʺqFE+~n_>In pOY縧E%D\rkjd%JkEG'#]̪ 1}inӑV;2&8>qgR,na1C|ۈyf\2}qlI9<{W| Õkr%.gr=BvuH2xܧ3 xTqI3VXCO >Y>q۽rWk;"G2TҨZ߻<#PK {p8~=ne#b>f'S[V #xO}zQԣ5#Jl`2Eh_Y,(Dԓ#њg#ģcd9qLuןkr,-&P`0*Q1$hINAǩh|H霠nʼnՃUʰ` 3SX$/%=M[뚌3ʝD|G,y21H>[ŗF3H(@?^+Xb,cYd?!X!{&J\2|3\I?襣̦Cq-۬͜/@*G ǽvztPBd7mzg c cܱrc-tuڣʜ89;*M+iVZjZ R0HN' ŹFaQI2 3Ylۛʑo+{ڷx)%NXӔ7W1V=ڹ\;wE "˓9oͳbٍW IV H=ZFM9,pXm̊:9"=c<gMB+$d8V g,GcWka,b28sJ|L'ˉw;S$z F}1WVIs$&|ԡ5*y'Rpry"M?ij8\:s=sC!iɇ蠕#7WOb _<'ތ<)KvLQв!hU ϹO4AG1V!N*ݒTX*SwFjm]ۦ\]aۓ= 1ˎYj$fFc ?5EDSO]M;h_k1Z"!$FZc(s3U/eDPLuRVٝ5yYZ2dېJN=ۭIs"x }6ZR=*S=Iw256BTq~0=3Nwx$O=st\G3S\A@ON1V(2;ry>cOiv SݷVB^i '3[yK8<K3;JlLYK)cZJTWe{AvJ`TjQCRiJ%%+";I$Fl2r8^{`y]NUYy۞ZbT *1aSOb%>Vcd8jRg*pLE!?+qޮ铑Σ4QA,I?Zp3 v+spsTbHݰG8$b3e}Mxlg5[!ǚ:mrIZ6Q2Q$2j@Tg$;uk DPTlBN  y#ҫZZo&Y ~@y1kN2m\HFO5 {d%cY{FSy"L` g>=jE $cAji)W _xZ<*98}I")fpۘ#9T:(m_$5i£cdYbDLL?dtב8#.Fz"w|9u\k[GGVO'QѶPZGd,\p8<oi rgͥJ\dS<6FaUl?i4A *$kА (0>U@Y-فfݏ:sƜ4jMYlƏ%?@}2\3D19@ҵ( Y{Ҫ_Pv+: I9\0y1 g 3\\IqGHݲyOC>>Pxd1#*Kqd>WLJUڣcH]N$>Oa#+לg=gq,Rwc\($֍yѦe_ %QjTҋ%[s8 ; +^aό>0ד9ø6K Uv LVЮ.@voS0RM3IUqWkS#8bn@<0;Lx5!e (bC8b6A]q@pT-.h9sn*y \Y`bOOq:sZZ8d ckԕr;T+7QAMe=1gNA=]·R͕_EC3Jm$Ƞr>_]ܣZ&FEd6:nʪ;==:}wVM! NJlF0KW+91Q 뱷1vGT5eg' Psv2`tI6>S99ֲ.5;(evH ?wN0OQDՋi)6Kome4jŷn x8 ?:jbْ#krs$hRբ]#i FsNF.g1Y[+bb:?Q FF8 J'OƱbqM\d;W:Iz]:ĉ,K.sOdF e.N{'y;Mڭdb +sHYO}tn\H,'~k_ybXV,C!UߥrQ2yF`cY<1K 7dNMi~]UP!,XONBy/tR9yrY.(g5CEs82})N#.[PMڢO,V*<Ӯ QWE$$VFHlR>w"2cq#]/ ЬU/+na0:~5\[~ ZtIV*ѳk:RoҵpXOE/aj4h;So6#ݫ$ԧYe9$!a\m99OZi༂H)$9fA8ح}aD(P[tuZ48S V$yWkI2Kn991Q_$Fq|9twp,u42R,F>l~B{#p:~CV cm#Mcˋ{1)   ^ ȅvfyr:W~͸k ='*:#W)~)uc%G€q}WIqf֑#q_MciB?.Lz zu b2UqJ/T\|"_.+?1TZ=V PA8s׹ǽLl?6H*,Vt8Q ;WFmbA rw+,ᢀCdvoO~^ʷvZ D09q  62S qk2*ZmǨG3 ?^tT''pf;1'vI%(!y뱾[&Ct2X!5n$`SV6dFm.}(X2Waފw8SM݄wڱ?kտp`'c_jɎ,溎IJ,Gi=:]`ϥPc+,O^2t$P_x^2}Xx2/Ѹ\=G[~ g1=khvC6Heo{tmq"xVgD8/89tYmا{YW 9=}*O6[n٨ѡ{w,eÖ=OJ6vUiɍ_kȈfVfb}1;@ֹk~T<{Vm!>H*OC}*u~5jH6ؿ)dXjbNzQSk>͖UY|"~P8OT1zN!_v+Bu?\Uc O=OGQ y0bP}(Q\:#qpGjESnRxrx$u`5Jy„u; WB~Qf'XYdg8Z#Ot%f2k 1W9=*KD*b}v8Zͧu>k'VvVe;]\mG{cGR(-A_j1>/>${REf6(9!Tƥ{]cMn\\$ݰ(>J!E;zddt#Ijf1:p ԰Ap3^ԝp@y[3h;h3,9ۖ~^3Oy]T >l `Z#]o=jXT/ :݅Hݜji$ 2~<6>l01{wV *Un4"Ăp3QoS, /n=? pFs E3 dd?L,,ųq3j,/;ǂ񶤖O*$AǁڡMBL"Ec >!$mП,* >HUx^;W} !>?8@ Jэzs0?a#:?sShXGQopNӏN{v;Ld\d;WDEW#IQ'8J>6Vb脟27ąU!nYF$h8BGJ{WA5z{tJ?40M)(xq?ZܱFK;zD\[ۜHǻ-{Dgs)ݷU{EjkI<,@PX7nWdELxp gu=Jوe6c!8=bU (^py 9\Nk j*-|D"aFy0I8ƕsn\L:wުBnGPƲ1{ I4I晐vvq15)FԺQjW68jRUb_:8Uǵ15 ^6u +ks8ݲb; lS^!qIpiږDrS`ԈcqrHAٓZEf$zNZe1Nʡp%z,ח^<)_3zNڢŻ{ScXRU29cqa[89nmCrn*~rH~N+H%4)ǖNť9Ι+qsDgnCpips]q K1Qp(jboR:ccc֭FqNy$iwr6XU;%ኌ+Zki#. ''P@[wEs_ZswϕFrsrT}1QFFۓR3Zr#bprYm̀bHc9ZpNF]S;@?=8hHYǐͰc8=+tߺcU%~kyh G^zpL$UG8ۥ< Fwn9&8 T d7O. fsmiV"1,lcMiv\L4`pFz/H_b*H9=jȑP*pKWG%*ҋoX)Jp$Zn R)'-c)Oq 7w&T meHBYUu[H31$#}0$xI|OfR &,K+ AP<2w ӭyr5Gl`6R9;-y'=0HsU{H+l^\Ch}*B`P@ #EGb0rsqpOaZ \T:כ+泗-)iLW$g>?J.0%8K@~uspUueCl;$"(2?=iΜcʓprL-_<[8\um޴8՗:(&0B<I9z9Q1A֟_~zV^m 3dm ~V~פ[ li]ڥEu 9`66lt5*Ku !~mmJqQ<[3#lP9$U2iKdQ#=H<{SQ1 qI>xU/8b0B$nYHߎ9'<"UݽnlQ[%ǃU4D m,q\ 1&:ɽ nz${wPHt2[i'i#_(%;d8@qY֩'Ⱥ~i]\_XXF{͋FFݜÂcZ٘ ary汮ڤ2Z6b5p#9ps7uEmP;Nj/~UCOYYTIlsyAzEu0ۖW3"BeTDb0NOS*; ČpZ5iyj ;+ lNIϿk EX{I\jR,K8BUWp בT庻h`2ix''#7E \yı&x=;qzKQoG~=^mnzNJeb6 /%[ >^uy,[yg"@PXI=zjLihS<錀 )~ֶfK,I c5Iou'e5\" *Ilf9#?8^S|ђq+#^9V ,W.NaSp>?βt7˝$,d}9 cs6략\CwjLH1eq=1T-fi],r9V)l7 Ҹ3.Vܵ;w%՗nvAsV屴ghU`GBYagPt9Sx!{I'i axx䎴KFgyNW,f@ڬ g=*95?*hp~`?ss޹B!+Mʑa @%X|nI|F%$`{qCJw)Z0I%ٍUf1G7e4ɳ 1g'g`_}WfԖ( a|8*Q] w+>Bj< ST4ky>ST10  pO~T1n+5QD6%t̎Z(ݙ gN:։i|;~ub9'9&Ì ݷ9lNZZ)Q{SONVRe\Jr1܀3#XZԎ 2̑,TV>cle'W7IEWqKSv`lR,Fpmw FGL׎j۔;B p},2Dhܦ#|ak*T9[oт56\Y {\=k/U&Y!xNxIƟw Ysu3GR9dѩGohrҦ8rc0"rIo~q]R!r oPs=c)9"[jܼɶ0"Qq78{$UpdUdW7fcbᤎ0U QT8'KB4*I 34$e90:iʝތ+=QXFE%r1}<֏٢P h@@ӑX%FWh'nPۏNWxЌcօ[D#81^8 KӨIYkm.4O;&v0$ s`{1v^I3f62V#a\黿z]?(9Qs?p:yAQ92~?eJQR7J-$ dt#W gЃYF$m&q"3 $=qS7ae_0Rc$ Z5&nlvtN/A'fQ\y; ;me!YrȺNn|,ܪƞh3r#jI-GwtSI)f9\uVmhK5e|_lj"_E V\ʾY۲H p~Vi0鱉 x1#}8G#c9O摺I_4qnf!m'sUMB9yʂ98>UFXg-y̪߾!J`dok$X' qw 訡H3wE%sN՘ F[s Z'@L@t՝cC+9B7sAqSimKD*C99R=:ty=k˝4[[v) <zFk1 -22N*ly hČsQTUV3e '*R/F W:;EW5"AG~ D?38'>V i$p@7;0S i\1c".I?iRqzɷY]N!VW?.n&'Ng\Z=Tyg*Z<ə)yAn9w)6hGՓU]3OSH1g+'vFgc,YxĀ1:YXF> ^MZ,.<20Kp2=ZR7ԪRLdqrMq3pȱ6P{խ>H] .v,~5jUG,`˿8c51QCIrGF*jMt Hd#I#,rN%I8$`DAv$5jWh# @AX/Gf `7gMYtrRFP&9 W/v(79="QDڙfSy6ۃ¥NoZ*NR.Fw$MYʷxPXijKc{9zIF2OkkVsѧYIuegR8ѡ,q*qVq%İR F]ͶI 11=+rH# TBڲ'mʑ'>:; RmFЃ(`TnQ;qSxdIv&GKugZ[y;q"5MN.: %TLT*3jC<)nBǒ[\RzRԶ+̍c֪s۱*XKɡv267RTrϺ- (ʬnW3GWB Ug؏-XvO;3b);]l<]| 8eA Y]:|r?0{T <}9$]V9] [ʉ'nj "5hʗA}=A̮ʬ@ʟHp]yoN-)( |My{C`@U/%YB!0>\vNNO9"[c +c'i#ibs~ǡz6Rk$|~\spj7 cN09C1ѷp>ӊ(rWdyeF999ۆ q<~#d(dU'} F9ȋszYqKDSh5%0wp8#Ԩ@jw <J!UaAzkOE }j }X89 ;c#3oT'*jB-¨!;_5`1ĊbI)cNi*y]s|+Ttw-Fh09?ҕ&+ps+. ]mcX$y]=RWfe$h='UҔ7N]L!@_rOPrH+=ə;HA?`cQo<6Vd"w)I.g/r#99jj'F=Ary5ӚJ+C&ݻH:`ю9ZnZ; @zt$I4mq!|UiEjMUyaM=~bmHx1๐zhi26qQr'#vJ$YGFny~?%D]@>׍)e:KDt'f5gڮ88'ӜUPynުy*('pC߯_zTT(@AWNsɺ̙$#*(ARcLף: s֖1OC&NB$5n9~>(B±'.mH`NkrNP]:eu$`zW%ku:׽Lҿi#v6w )mJ\\Enq>yЬ cUⰷW2*|RYZӜb4d 9#vqjYq@ $GS׊=֫$Z_eX~33A(T+R8 棓2.˒={`ZY%T w8JT6۷n2 l)t<2 m 4$ x棖YAl;zDE0 ?ۍ\sʝx%w<ӧV ƺWU s>aZRGߞ `H):|~`z\’rhwBݺKg\:B 3"~T=;cFs]?4xaVԃplIU*H ."̮1FFF޿k̬jG!'Ȕ"Yd V49p1Lmr|rɽH9P=\FCA3RIPF8$x*T:rԵw:+Ƕs⬠ʻY ?!ִFһ0Jv&tFQS~{G+tlgrp?N*c.[+M^I!_1,23f`8k3YiX ]!(c ݄yj( H0rGNھ^O%SyWɩţ\W`8kyJG~aul=d`#*L,Qdd m_i$#zⲧ(nLAB&2k+N?u#`s{e5̱B#V NsTDR9$й-mCr@U`wo?jCom51iNI$a3^Isո۩VضH1;qsW/qYḷusCimO ;1H~uk+L^$w\`6GT[˹-_8#%ᛁ^}孓biVrY?I8ɩZ:[Ny7]KxW!K5mjY<60xqCۈak|ł H =H#JIt`īdF QM)Is$4r$qO5G{y4<́T |a*Cwbڇkƨ N6@yɧf2#4mC(;@~2qUʣ݄ebռ00227 `Tzg-ZF9v9(m(uakm2'=8t⧻b f?Wmu2f~~JiJ1q)#J;xP*r͎M+ gVP6+9>S~J;8|?+Ӌ8r5kGp{*v]Tw֢ROB:V_kM K|nhˍ/kHS|3F# %\'#\ϕ (Vl²r8h㉥LJ@Tmՙ,+zm_|v-پYzførI{RX$QMpy||yg}k[@GRoh*ιl7!*k,4ˉ]gܮV6R99j\X!W"9=A Ӷ~cZ*4JMXuZؤmeyAԨ^䩈gg-ŵL2 P~8uutq"d>X嶆G*X 0O$g5K8mTDEpAJ˞Ԭv*q]0ќެ ;FD =gGdc.-!D1,~Qq_[A,;@$|Ӄ*⠼drLVvo9ȯqZ9=,d^v#ow7၏-h ͘oH@~K A ųawut۵7sFYJ"BGe:#=Ju bH_/rA^b D,:I "$Pyۙe=@*\3-xϯ?8FPE?yz-fR"aZ/$L )#^D5Ep w1dtJ*YF=:j=M#t2I.0Oo𥺹,ŀe_?=4 ,0+NpQKvz-`VD]м͗<`sߟJNI&f⹋vvͽFWag Q݈cd/͝eIX Y*0l؞+.>L 9;7"\26-B%INdAŒǵ:F[[} r#CUtkɔHРA$~u^Q ,Qŀ)޶Z[Mn[D ^|0뚆漎DF}9# dG#~ $N?JOr$R#!UFII6cy^ݛ.b0skd:/9 o^ܕk7 ,ֶ%q-@2\wx5BդU,*/zGүūҔSLVyZ.+HP1dp=iq!$ xcq9ίIO16F'U2 ܻw/]YPn$2'p8 w(IWJN̷t+" @q&L<2[+,Z8IYvrǦ=q"[YEP2F 'Z-13IXptasޢtӊ!Q/EE"9Iun}:TV_g"HU :tybM%(Ǟ#IqH)m+L;"d ƶsEIe{ӝ@iǭQٖ ٷ򪞄f+Mq4 I 7G{z 4Zu T)Ԃr bMma9%Z픝pzu%cSd]З93#oZЧ$r>o^k~ζq `r {dgGŕ)ZFW)ndВx ?\WY\I;q$i#Ŷ3'>8e@zg aK(QӡjĦ5Qo{u$V)@+bIca9չQѤpBɨ-{"r<=krMH$_oڹ%cp9[{¢c9Z݊ewey =z2&YZV92QH : t?oQIT#gAK{"dc'k*ڕ@fbv1]ni&#޶o:6.0O̧$9Eh撳ôgJ+~x{ !}=b1Q\eΒ$6 T,'El*CԓqJ0I=b]p!8V}O׭#KL9ϵWy\D;=ԆlpJRs߂1劵Cj G} {TN8) lrIR2T֒Tt"VHp~NC{ԑϽI+\>GNzF"zDE@'qVr&げ=?GZl9$~|ϽDՊk&fC`iB=~5UѸqV*DD.sV]M@-H hS/7V(8*=A.dg0Nz nF={(X]HwR ={xJ\FE)"g8wVhUj ;MUCy.0AHpl~5Rm/r׽EtyʮQYԪއ,$pqUmC.W9RW'?ooZxF.wiXZeԻ]2㓂qǧgo`A+QI =j#*I9ɪ)yj?iud@ž oN㯭BG~*^mDWܲd=狦P&4\R v՛os<ֻ\~iW# c\z Y3#^ 6uуitcqry=j't7 퓰X:X%+us>x!J2zF |p788tǵeFn򗋿ydtW ZYg[z֨ދERԱ%[s\VaTe`}iR4c".: } @W!Y包6䃞>Vr[\!%Z8Q%w1בEp}1}ַʜ,@*XJYy\r3?  .{V%t- 8ÞHRATUcNyf"v*7JN,`<'kJ3|tfhY}8=EGùc0ӼTm>*JŰwƻZ^kFk $Cɱ8dҽEA7 ?\tBR-2O0jJf2>UN݈Aaw?YmFU$ c8ֲRG9rhO{y3ON PҼ'P#^6NҜ)䷆'Yf`9 3Ϡ=z(+^@##tGd)$q)989<ө df@+8r0`qs޹Sj𕌲{bN ޼OРOgiO`K1j[۩x\+gȸV #$> ֬};{TwpE.p8*c2A##|.3ϭ?|8oήg?)JU{7};<ҮnqgRUkALg=;uXa $SWdQ%dΫrzGҲɵc9*2h|2 NƖѣ4g.A$tU|>d&U͐:#m9X7 N3V/aK3K( z殚4[)I$kqސ>-帎,!|)l|<֌REH*9JxUѫԚby3]Ye)萐R.rG\RQCHp3J=HQ~=*lQb#"RRp5O< ;TJdl;`jS2EztY2j̯x e\FykkrvdUNP7v㜜#R1JyxPu/ͩ:*-vi8T#Q"A<33;L͆V(CňcN;r3H<0/resMtT~zqDʤOn&z։m.:52* S`o2f"`פ2+.\{:L,$$إxe-L7)\G_JO+(tQ#v޷63.g CUd1ԷB5,B~v>欪[zvDše G<F?:[/USj 7v z~TⅤR@1< ֒<@a[X>ˊ!CmF9㌖{VR**kK$i1#N4\FvTiC<`שZaI̝0fʒy};y3/8pf9}CV%p塏?<|1RT41do'I\sybov5!UqOտ:@aPʪXqqS,$=?+ɶiҢ5Ly{BryCs)$=rqIuƉaF+$nrGRI);_Oq72068ެU[bSvR ~t6dm ?+(׌)-Q%.q랤剷*:N::4ՋM |ƹ$fC|*ιTԈ0eEdcTwSH7p=_2yp ņpTzRnRz’[&-RHe%T.޺E*ݻ wIx#Fݾ8$3˺4\=jhՌºߵw]a1$zl (LOn̹FEtk5}zHBbkecvPU&b520cdqjFU 9lJ=,<{|19}+/e93X\͢K[DaU\70{Vp[LwKE#Հq޶AMWTH\lηFש(@Fc_sdPK;8' c FT0Fv-nOXRy9\DLیehtJ^Ґ~+H Zָ9 _YvA;L73  =@9=rA# K+^z:Vݓ8sjDGuJr pY"$106'8zc8쑿,Ą :p8̲OUZ3qJR1+R=hc 70+I8JՒ{sO_z-E&6Vpp{:O8&hRN Wl¤)ha #^'jLu+ut!PQ`֪^k+[knߚ>{9<U3|ǎWZ9$tRmK$c[޵$DO= ;yh9Ir0}×K)%q꽸뺰́5dB<'wp*5e8J]Eܱͥmʾ#Omx PG-I#6Fy2=j[h=@PIls=kg-3~owZMmWf{$\,*& Cy"S!U7zs~u6rJ-g@ =Z4{a}ag (hgQn#y#ZSMţ[YYfR/?xrA;rK%ĪJѠ!9qHǰŷ&|7HGRSl9uMs9'O\vXIL9Q)#$>Sbߕ9X瞠uBͮ.l*m# vzS4xgd "2T=0zz \ۑi% ۑr 8ϧ'ҤZZIy(1+as$`S:a67'Ӵsjiל8iIM-{#׵V)!RT'޺m7 !;u zz~o [z'ӧJF<+vu=O? ޤnfViWf4YC!"suRܼ֫sHNʾ썼m|ZMpMwa&ۢx2+?錀Ijq-ų=̯i wA=GQP[V uN<.ϴ(NVVS(Ծ8]*9$aK))q8ܹev@Nkn侚<xK@nNFs}9}[k$tc.@'{l 3$-{rG^1P\[DY` x>h4m]1,@ UN9%RiXZA$lEny C#( !`\Ϻ+y̥w1ϐ~lsfDD#9Fݔۍ8E5F.ZE+3HAleW輏QIͮߐeŴrKr̲1UQ1T% )) =~ȮH$PJ|^{ڻ;(UNϭ$$w B7Mkrr@g:~UkZH!S24K޴j9/pL=9W̬w*t'ZBSF0w2{J8`_U[p@2e;x b|iqk#L "(>8Y״{mC^єGdRA V1=NMhҵwYxR]J}L[DQ d{cR[HCDbUFrI?ֲ=l&8ĩ*/ϕ nGk{K@3pms İ^ RoNV$ұő6Ș0p idH2`÷P3 Kɔ 2H93Z$6 m3 FOCKRfl``ReA{SB}:~z1~r9nX'4nN1ڨ꽳ʌ(sϿ^Eq?匇P3$gSSx-V+4GʧyAϧ>V7xDn&\ŀ A󍤐;Ʃ<eISo"CNx9?ZɴE0C`|.efvR*՚z-5Em1%AEOGoi#?Yg&Q9ڳ#o+]+(L8~5nd br#f$|L"& (X`g*.s}9Ufe%p+3Jb,)9E2n,?Z± Nьy=>2m[xWeyːq]<#Nr[{l gň^qYxJ@/uyVN rB\c&3%O&D1> ,HۏYTۆ< BӸll~኏"J:s>hf.Ӊ Bz9 JHB UmA Lǩ8Ӕ"FA\`>9s=yle]ZuY,&P`{ڭzrBF9'sZF72ƨt0G9[pNdJjȪ;1!=?:Q S<ǶA{[1Gnw#u"Wi3t<(zW& ;qc& xā ^H'=іUo>P:q[\M;l+[vT;y?uqrB$Ee3"rI8 4ڷ3x#dž`zjsZFXНEϸTztcfg8vt餳\<2DOs}?Nm2fIu5"vFlAv8r_ZɉtG>lc'VՄ!OKO*f lx\QF\Z1Op~=8Qvid "`]>$M.IDDwJٳCY]T? hhM +FA=Be(e1`VEw=$jV9$׊|ܯ"teMZ50|m99?*tDNr[Ӹt7FDiU6}2z{5g FsJ nKiN f?=LUV+ S8cRD2og}I>6W3$ei+HEbp3?3 uP@(o-T($:}kM44I8TY7ĀF]?ZԌ vRMܷsq$u\{hrTg ;I %\Ƣ!Ằ犖e9aYg˹h* ruݑ.ncclsNGUd`{PRg"lڿtn#fy%ܩ*ʠf![r9o1XzҨf;#tdA)t hP+< "*zyϽi6Z1 0=}}k9YL̬Br>kF-g W+$O_2sZ5V0q}L(7Mo~ bxd]c=AӿEb ` p3X^]ow.XI91H|@0zrU՘T"#'YFцzql0ld3*cQc4*.glh+"T;K.Bg5oL"8' q99S@̏Y@]q:p@Gc\M;uHG7XP7|ù;jl^sv2`nێ`UAp=kAҒ&4BN3lM7d1QȩӍǦp*9cG1-z~MӶR2UTRݓ52@#F$ @yoZ"ylPGN3h`puV4t/1V+4/$FUç<?V8>pڠVpDc 7n=y3Z(7IyH[dԛp'ҳ%y|'w;Io$&僃@Si~fe\s v?犣}䊰]4@G`r9x'[Q䳯0I弻ذeRl6t6QFV+ZA$Q]fT8sIgn5 r'G3?PrF?N= Ň|)6S%`i!h/88y )WIi٣kJcCv8H}NĂqہ&?nuiR. $< Mѝ㹋Ԍ\B|UvH퇞a5nh_-0b;SNH@x qQJRZٶ̌,xb>*tPe1皃l1^Iws启A,wl[o<¤NrN&$D z `o-cw!TH5WD)&w* O9yqBD`h⧚..)j5+j&^5@ w`4, ,8 ߶}9+۪rl Ǿ)$|'UԨrSR2nȒͦh$R+x9DZ9QO|Vȭ9ۻ>%19NGvsX8R僿Rjc2y<(8z'/~I\"Bdܜg frG^O:vyU Υ;1gqnuS·SV]Aͥyn,R)/j]<#s˂9~s\`auunIe\|b%Xr1Tc K/tby3f9. wT $~QLUKy|1zu[yOVK(mۦU/cecc5[dF[sճOjv9d ?OK yۿ` ec (s#IHT;22~U}wلs{YONGp; GJPqJ/ƘS?5SrnႶ|X=~p} uQNi_-fzqSrRۢS \`c=O JKmVmuU Uc~8SRR Xvc\=5qrWKI?3v8u,yqP[O;W/l䞇5Y) ц EnnX'?ԧ=sEk3bf 1"=y6Ntڕ;+15^]ԢrAH:H7onq*:jSkw}{n>OKY,qZe©zCF#oWe_*‰|=[/=1ּxuœ91!}?OҹifU̮Nᵁ Hd ,l<'v >1Z>\Jef2ʠcB^׏'nzv$ѡBv:Ӹե`c`Joio.̀$=px8)Vx-#s]|]֖9*R]KMڭQZBǞsVx5BK%X }j9W QS)b}9U[[ *ԡzgh"B,@q}HV$d|+'Ya+B-s`'[s^aX ǹ=y˛ERq/BrPq}GrXcsRIby]PgdŐ89Tȯer9$lU6P*:ae*EB2j+t#Sc&G8jG6U8O^h'`bO$WZ{覓P[2I*Ǵ8sLu5[ӭNc!V?w#U"<6l׉!/+ tF=V.3'KqmO@#?>VJP4-ۿdL-Tdz1n*I3ۡC~gR3끏BOjdK3ȧh<Z  R=6Inm+L7dn89<^Pi\:֞/gkt>@̜)S;9[te"a!1ld5-.nfm+Wi1Ndڪ2TF:4ӡTV`Nqh恀|X6{zX#`OG1ߏ>H%x5$fY.UF3/8=*ޛ .4b_Y 9Pu4t[C e%\|ⵡEWmhߠgm2H쥖A+o+0puxSVxi$M\ۖj8 FRFphòԇE$o |ÁyȫkfUL|p2qg V%R8*;v#+Vܞ'RGlۻtޫMFiZL0qtmu#Ict`9zԑyL]P{1F}k\g{g5y )kx2$Yv`cޭjw0ZA ٷFpN8uEӭՑT猃)G^A)v&خs2BbXy cXڴ.&)cl88錑M$4G8`1Pd[H{sWt34X\A7g$p dܴw6 ӦC 1Ivuپ0H0A!:%њ+O5,Yn9 T8ϥQԾu5ݚI,37Bw9}Ӣdx!3F) gu8=#W%ZyN$~T-yǾ< pɴҧ=:-"5911NGR8=cWMѢyU*98wG}t^s+>:OӮ% if6999\ ޼F#b7`6C4>`p31^֮|Qtsb;;s{Y.[>h c95qi4R$F*T@L-"!.CL1M̓A庁O1OJ2u'tUc=:CeaH|3횱z.YFԌA"2H.pzϽ;H,uV!I,@#[Q\gGE@g(q9 Y |ŷqTvؤz<]|+kw匰BrHnFzw=sPC432~QI:)4 [g'* ~6%R3m 'eZ[Bk[X #dR|َ@t WMINWMOרk|$ ߝMvB1UIhd҈]L0Vluz~q&ecɈ@ǃ7QYW3M'$Ll8 +=Qv/9aGoĂTm+pW'?3dvYG-8@W W䟯eCL]6A;Ep"LeV5I 11[ Hҍqf$' NGNey]p> +KF;g/yz/z2Uji9YMl3*o';F$Q.~?lݾZb c()\aKly9j֍{a bxe6v<:g(B.vo\v*+\y9+89SϷ:e.!RiO}MoBf^s\ 1b 2 ی $&DO-R2r1ɩ@xJ"SIm62ɻi,];E[zfH=(<|R5I"c+>܃Ӝ{DHc27KA9 t=7QxK#c񑑎q\#!% 9S;:yUN]P$k21ecv}Osץ4I|v0 I$ⶵAùRn2YV$o2m1 l `HZGOOzec1ģz|8=)(Z$VrJQ1ʌdL`TژCAou$2E'ۛr r4l;BZXp6ՎS ncsjeo< Ax'pV +y=ȁT Dy Ɏu|dm$k&,1 ~XFԺFP-324x` xǸҩԄI!V;#\.N:~[OЏZJB;o yf'ިZ=Ѫly|CkI I 8aq; ޹>BUn!p@LE{mu/Ka1 8<־oe&)3BFc,K9Qq(o3IZX2Ce1r[kvo9`j= O܅vޒe>RC u0_2@RfsU֚iﱔ]ⷆ.ݶ+(bv8G⣵7 l.#E H샒HϨn.&2 ̀lYmjm8##G&)ؾT |vʒ1*+뛧q$DG7Oч5hi*œ--ǵSu-#I42q+䭤t(hQ2Jv4sOU9n}kZ_ZmB2sf[<W,XکVkp6zh(άTezu->_33|ȻPevjr#7@e'ֳnn!s,,m}.{9], |Bʱ@,1\j5O)0z7JJGIi`Xndm^aq guu/B?*҅]5ZI<@?縪vY#x*\OAZ]{Mdzbu}  9%Er:w/kmΨ 9pW\Xv$?TZ "!)qԹsiYxGӦ[C#8 qzZbPnIڹ#-yplِD> )%ĮamzV8N_:J)ZAR\y[d-IiY\(XTqZ5=9vxv?qn7remVs=$8r+қOYeYq ++C,>IGW#oOw׮ܷDSI|_3a##}+μEܬqāV%bzEE*\ɓU򻣛ncYaǏ<*snQ =8sZ]<~9 `DȔWmFL/&/JɂsŹXP@,F uK;ID,vSs S+>}֦e%fo[2qq'q|U+!МJ o3ڭ]&y3s1C(C vnM)Pyo$ 26ϥ[/(UeA$z %Ī$"/{zAYYyAc\őnWb "zX9bs8 2N@l=io&̐Jf,XY]ﵜU1a#5%D$W{V_;Aoi"m9Dc$ִ˫ {3p12:6T7U?MZlfYtې<@:tH"XDz)/+1,GO[E#*wn2[I|o0DTc)՗"vB= FᑶsόumI989+:(ڮW.>Z,vT =Ǿ?Z9rgMKUԨYZOd e$ Foz{q')JԉռlUT6G\SȤJHcQ0A!F=) `6ҸէF2~Pxjƨ]y\+/ق,Im뎃*di qrOӟzSm2ȋbz)o2G6q\q9}ӸI$V9B.ܴeHJ:. $u5j>>>[vx$qLY&)S~BIr3sڳb3H?puc9з2,XΧZ#Q%q k=I6 <~\}sQ-3#sVB vHnCzQQd=$ǝ=$)JMYywhd"'ucd|0q'A]tgMImY[ؕ@2S#9V(#cHqU,lԔH@ۓg<US*{"D .w 8l19㟭qR䦯Q^q ,K^;pJcb/CןQ]A=%^H'Ge7(#v?Q?j #YSܿur%0An9}Kq*%n@YNF~f@%;YviҶ9Vf5X@cc_OKYXȈM2|ePy<㨮SB70ǚUmPnA?sNscԡgN*I9d*Fa~u~fEUl9SzeCn$ {q;$4a~uKDc4܋LZ^su/-#k8TTH=JGnEg)!?!R?ҮlsXQq ZQj7uiuHm:2 ,~iŧS.xsV;S1]q3xlk}87$mX9wwC*GI3l0zw3ֳx:-|WqN q\ƫinımf Td=Oq "999^29yֶcg 1Qjen!#ϷLW FBѢ6 7 8K$cfl /Ɯmc[٫6јF# 9w{tb9H_ cI֢))ּUr=s#b{3ޫxcV X,x9qVP[Ӂ]y_~D^H]^I +|w,{o-v-nNd uF@督9rXUN1J16"N;Tx#*̌3}=ieef`QyQ[[eCRZef1I]@䟭 `RJAyN*Nfgd.WNucO/o;sG'MS_}fhB 6cF8~uy䂭(/XZUg?e.Ĉ(`w?i*Sf&mst^)6`6%cUVXtew)ds`x?CZw3{s{Uibe':1$msB Rň$ĞDTޜG3od" (N=jk U,Gjr})SU΍ MRю3%s2Hdi,21Fdu+~p?3\R+xOGnKX&hu9~㩭$((=0i(-Бڒ !XTb9%''ad *28H9гy?cfۑu b3HIЫnɻqQRIj|?_axx.Ďrs”QrMZM'Ff$U>Pa^~P=hG_jځ77! ӌ}J,iWRU} g5K*dVo~)&0 ' V;}+*Y,2r܎㌓ViSFb$B9qo}¤.z\<8;`vLll&ڮ0F{z YUP8$*UJ7)Iŧb5FhuS#w:^k2iq 0dǟj,P ñ/5ח$@?NO=N09'GUL~@qUCq[KjDgܹ 䎽G`O-,MbNg9@86,ȑ,3F9>0nO8Ӟ@6)1SԏNg\݋PƉm Uy$3TRT86~Ely:Էkq>ybeQzt٢HmSKKKtmnyb<,SiXʅ`@=ӨVyjKr4si2#RP6Gr9%ɎDF1ݸg#s]tV7@=QL- j%!rpqpEUZ 1bEZ4 #U `-ky:KsǓ~^jQ[++1NyZ) t^5l=O~Uim|T*jD@`rzgOOwɯm\,aI,NqH\2xOQ>fA`a'$۝sR{ -bF… f[)Nh-TgeprAJX`ܖ}GB}zI.4ҲxyЖ# 34dܒi2'm"d;c$|(p33X56w7N4HekƎCSoy'52I1h, E qϧ"\WI'Zҵ9-?;͛ R<}=iB) ؽ͹ގ!pp{~5!ys6Yp~V#$x4j6kXaYx$`n? oEԗhslKvن9{VeVzJc"ηy+'.`.J݁+O֞>UtU !Ϙ@K9QN.}wzT썶p@$rzq+-RYw-ǛV)B:Ҵm O0/`>j6V<@IU{BЃq:Ż dpRq=񆾚=2eQ0NHXA8SZs4QKcE4Bm6O,7ݜh|TM&bp *8#\ KQ-F_s9'i'Ekhp︅K3ynHX6uj][_,NHbd 'N0}Xyf`ڽCӥfG7KC),QpWq-wi9d?2&P:p=^tٚr=zjnqKo4Ɂz\u`D&3wCp`΋R} cYחM+D_V{hRSs\Ò57f%14G.e'<5F0R@$x1d&8E?Ěh1E8|`Vs+3MN{H!YYB`7^>^35*lkIF+caU;´}wn>7zI1}B^p2B!POʿ쁃jމ` P"BbL8Xµ,:h w?VBԜ{ ].ɚI{l*%^*bybSeP瓂?ZB.qz3\1 >$}VHF`.Z|k }=}^4V`rNjY]Ճ[l\T\0+fF`s\%+;)Mj]xi JC.͜c4 pSʒPp;OgP3-@+c<8j}f`RH]ad @קJX&*ˑ#+^խvcS;n\t?tYֻcLDY; }ZXY Ea,ᔓf6l ?2R]F$c8';(۹TچęJRM~=뚾gCi ͻ 2 B 30 אOҳVL3C>U0I7Y%t[BC\e#QN r/K}q"#RL+y>Ƭij}P ]a'k3ЁRԗlq۴[*F80MSndo9mYoS!rvہOZE#P*u.wuoF"Ē, K\aĦ &FcYRt斨VNGSMrwcvzW[6Ô ~/-,$T <퀿S֮ش䣹QЗ$+`jNI7F.M;a;8X~Tw$1V UKw%Hó;-\LP&vnlIxǭ\h.5ͬs"#ڻ, >AQMnQ%*T އMhn8 ʟΧ&FVpPPvRWOR=8 >~Ukk͇P7=3uI#)WC`~Lf92wmaƒsVFwgy bܝJ[oÜ:~kֵiUw8 E fIہǡ9\Hef`zXRjk@|]!c.߿չ jYIf##nq'{C"GܾۜSbf[OYAx#`_)Q^}G?(_aޘgr4FH `jud0vꁛLA<'k?R>f2~v5Jv.P6[(>CCOKEF޳ xҿ̻ }5f%D[uԈ+x9TeÔ3DUiʼehy˟6Ȇvt}3R]#d΄ Aϩ1ybY$H̭Jy0T~5]]ڠ al 'Ϡ !_s[_5gM3F% godbsKBhH۾c#8TVdcVxm۷!3'* ̎8LD2ӌShU?ŜI>SNӢDB,* I>R09ɮ*V"4.\,>+OM9Xl N$ciwJ$k aGNyVymQ6bN-#}jRVs5D]F' uێ*`,Juvar0c=żaO[ѣ:徟mƻBSr8e<"E:4h9;yZMs2cPfg'37̀H@?^ZPۋ X\L}@>>&tOi]\%O>?1.ZW?ʪ*4R[(9U-͞? - ϥaRZ|4pma?|BH%<~Ul 9ힸPZơ ,w1*wu^27t_t!xsT3WJ*I#N_zI-ƙ*F̱'>?Z@go49cu0=Z\i1H P0[C2,l&KS<߁äLyU h`3O:tӽ;=wdO g :?ʯcj`p1X^%O,Pn'u=Է!bsaܪi+WO)`*E6RN6sV' -.GLsEQ`ۻcٸFj1~aQyfo)Qgn>)nPI:Q9lgBc=DUGkyCb>_{F@bW+XWwG7%̐RGLe,A<{bצJeEp.HF@GoZպF"݀ T쌮I_<*r'.5 vI'bnpO8{{,~]f*7nqܞ{Ke% 6ȭ:'bd\4*C 3* r(X&zW~&PZaJG2NcT\4qc1㚊O+k,YJPN8+Vv䜜B+Y#J[kR++yf|@ 6ϧWڧ{yΘPrmv>ʒ׹>Y0ʟ֟l@@ gUaS,w.sQid- d/WnirH\NN۽:#62BNOKrZyMvaY$׷+:r-vœc<ݥ$Ri9*UK]Q } smbޫII9a֑[DvNy㯶gN5jS{HTA4QMP,- ^F 5FMQ !e20Ǹ=} vQGƩ9;p֛%âzx3VFJI% B=hiȈ9pF6'Ӛ(rb污5+m~3R#Y+}z5mFeATp2~+ W(wu'XFܬBJ*ܺ %ĬE\2@z]GXe U`FdLfP}*d=~UbGNzҟE'r̤a&9%Ѹ:gϷb%fdp*Ѡ%-Q8]P'GAz%1}|T g=D1N:J|8S[6zuڲJ[~$9]yO-$ƺ+eV)MlO7Z;MI]z҉H  kb8$`n}?UX&٘<@Nf Y֟5p6nrB;l8'9*¸hc5 `x\Z~6N0Xנ+NMhrZÔHѪ=$@{J"dVP f 1G5jxGHdm.=x:qNiɿ!5n8/V$ C:YwQG٣Qe۞JE9quI.PK=Uo(0xw>OizOJˌsZFqH.dUѓnkm0^sҜHF sV9 JwiXҤa墑*@#F]T0 An{Xo87R.sv%;hrm Օ8i:dR9?Z28 zU*ܢ dɣ>bls0ƤW3ނc֣Dsgq;p=:KO-+:M̒FJ[q'0鏥;@sL)B9׿~pppmZI]QQ' uםyO*ZP,l8A25H-Ǖl( Gx#F}9*{=`-.@$rs'VwZ!-GrKYed0m`r#y%kfMt±KIu9n3nR<kDq8m;얢I-L8vmkƊH&/_<{ּFeR=>;SRU'c rZԑ[BM0B98;s;aiw4"|c$ozt\#FpTcqMq]ԗKtAb/6Q㧟g;w`Jv+/&+7A3rTWgn O ^U[QƵ% PH,2k* $C$.6)`x0ׯ9"3+=?:F9 aN3TՎ.OG<ĐG q5"FPI'+Q9WMf3Hj-%,0`^IUJirǩrQMYFb+!8}}ݤ1Y[w\zg*.c2Cc'޼U]ne HBo1LRA7Tm(Bp$H5 a)sUĸ5MUǵPp1%9͌zqʱiH;ywukU[fKnfB,vw?t˙-M Z}Rd  dc`Y2\Hj;ۛ1HEߜBFqOxeK-9<\=Y%X`\ \$<dwճTS[HR]#_ =2r~Ki E#nHsjӇb }9xa|"!J4h~s[ "v+j8=Q1{tC#՘Vo};H+$AdS5KPVݣRM@xz#oUKÖ(^r8`;M5ܢPgGi'f3R3>C$W̢El^cֹ)s=u.ڍ͟i sl;Ifv$tݸ>$ AǨK6]̛&Bf"4d>EI-dǸ 1uEl"j/o`Jv' $$?tEm[ʐry涻G#miI>eН൶|)i?])U#kX8|Yl4-T;~&;X22O sp3QY)]_Z$qd3Ecq|V ƕDVдS. 8~m':BT'%+ΡR/&r"d̸8lJ8<ް4ˍKXʒdh(uA|S]Y0v% sW;QXxXa*y<̣f50@:G z!qq$2NTI9Rugk'Ƶ})[RI~\U G`Y]f(2sIf8ÛVtZ%"Vi*3`1oNIMz:[&@@xO^EU_a[k3°Xc0FG zGjG}d? c+G)7ƎVTK#kyy1JF9} ^z~sUgO(Y^(Փj=6k'n?y>atVNtaQɘH#RJDJ~\2keH侺dmF?>ɤl*QҬNÑ>hRT JPD.cJ,?2<^o/kfF2mRI }+wy$BI&C9ʒBFGҷ4k4͉U|z3rhӍkjo(crŴ~if7 dqUu?b.[ P/\Oc<$A*o19:]JC-󯜛Ї;y+*4=鿐4Vך,qyɲpN=2kD 1t犞ݭKw/ѦqΣK6>Yػ ˟sTμ\rv3umt:rEPc9?Gq7ڞXZ"fm9݌>}EU %V2>Q_[uy1kvA<{ѕ"KcD!dk*m羁h+[hݑ=ABX#B8CQV69* |i#X ;A [ue qQ+:E8Ǚ:gNztWki$S @s9TWfuMX]jH"4JNzpOKӫ\%TbIـ$1ӯⲜcZSRKۨh-L_V&s} D8oTfayHR*<\ΡvgtebfV\2;yn@jIdX\ Ǎ1U L˿e9A+SkIcQ-U֎2Vi/tc'V-m5iy'5m$Jk3+x96mV+j !- E#kO>f-Ee#Vs',I#8SJP}rQo8o,®rÌG9=%["YűG 3eG&\[G38?2#mJIr_쌨$(7Sl/=84E51Ua<ġ˫1f_2[ҥ,h9p T\r:gH K}M4O9Bwk"FeI9 3GY)s)Jk3. lF=9'`kdo1~QV@ ~"Oqi}tuȋ`z)6*hՏwCֵәY_Bb嬖ټıkL1…IaƩ7~uǗnH# $q|TڼNo^r (e'X:1&"x*$ `k>Yܕ^-K[4;dZ=HU|05(m(ćHyzxͬ1r\ ZFK0GڪoЗ*EPobq~#W c"hJ*p;𣃁ҙe6D[ʅ|njze݄QwM,+Laڜ+nhhP߾Xi8bq|r* #!X+gs9u!!*^֪궳G%CEb GIRPw>gb}-Pی2Om /< nX8,xXP7wfPdִ,&ܛS`.(DO LP< ~bx-?GuGq)Xc♣,W2gP!>`Ic7e0 +@M9^: F2\WnbuDhWn>~gާѭ`  MBʤQn1Ȩ@O:q*ź2eJ `zOZ;YҊII/=c0J>ѕ)+#^xnoT%D 6O$ۂ}kC[,3Tt-k6 ^5lK<0 wnj2iuckW׆yW8ȿs0útpJ:bORAiVBd,y3܎AU5I"aڂCpI5%%]ܩt<ƃkIϧ~*{ƖRg ~ۯY5麎'Hr1d`@19>Ҳ5;#5;,[Ij)pl "$)" |BbgߚaȂ }X.g"H; C_jޞ3&Y_9pB*gv<õ9(~a>Lmd{[u+9c 8˕"McȍhyMٌg'v5ss;LpI^02$arpGҲuVEiݵ1[.d3·ʈ@㞵wk$&)g'DEJ2,c@}NaA ;[ҩ]l*Fu>աs:H ÜZKlf8̻WǯֵRMhy$& :&631(iky2F=?Ƣ/"+>V֦|ͻ2 h.7|4?թ]GA0 DXL*㌞ԅ"l}D'}^4{1K#v3EL *-s*qsKS6f*1=*ۣj $Vن펼֏ćܣS0I=%FuۜʬUx;✐γBwRUƝ57As;]ĸݸ}#V!9++\#@j5M"!RV-;mb0'5Qdx))<UbxIFB+9;ҡBLq6%JTc!/iWΕXIP2F2r3ʪ](R0dH+dmKtVjonv#^:{kynHZ/, $ں+c#ڊi6 (@o\5Bt^8GOj! U Er)@r?+jUb.*z5#>nN-ZO+]drX߆x5a-`vQP`@ekFVkxMbeIn@}>5vyr4[c`$tySZ=0rp ;c{d#֦8%Ue9Pn^i='-/df$h m''88Ї:0-.[Pb޹F1U)T\*l0}ϔ`8!;V>5& 푓(b ˍ$ c_j_"q11$F zsUH.D ҩAڿ0r}m摐D(Wo>|Wdvv$z{nwHl17?Vu_t[Z̋w}'I$5*x+}gΏo @vorvట{?3[R\P&QQ$ /ZXdq Bv}Ȁup$b jW>ҭdkC;~RNy9gB_ 4EHS}ˈi X'R@eϷAZ.u+i P# NCƧC|kXmo4u_G5yu!8qןC%{\vvS hGu vVEVpNH׏JzֿVs\9VZhdJgqߊa<\ϿTðN҈dG#+qk}KV4 $gUPP$F]p$Jn!v=1A;"ފB8rrPe{fɘګ@8CUYT.w$s隣:p 2+2>$$ez+ ˅ϭs~W5nyb@^In%QK*!^ajzqw9Ⳮ-ŽМ% 'q,Ď#4I,]y ypPv<&c732`'nw ?Lf`cXNFxpn){G?U' ݊:v-E7`OOZԢy"ylq~Ju_~q]0oV8y$u}Mԧ8>3mu`U>[@$˅YFA{*XBY$1ei&XpA\2r3Ϧk"{5kuPdGc>Q|7G/:ւU @txYor1 ŤlfB֡>Yjdkݷ,9Uk6^gR1n(灞kXF") Kp\c `znENusR)#561E:&;vװs銵i}-1d –S{V{eT7/tYԗ53鹂ӳ/1.3J%SحG/lM`H8=;V`݄I"HFr$ffY|^U6g'>5VT\*S}ǭmt)cJ@ eFFuǸm{I$ YBXgIy{$f+$f&2Dm1(͏ʁ;RjvwL1ćd$zv ,#Kv1 lwrs=Ea79_mtO3a8'ak-2W2Ѳ7`z{2ܴѲ M ylYN!J224&(W-'kluM+42c$#H9zt+$Jr}Ӂ>V-=2MI:Еʅ;bG^jF)f݊Ʌ<.ws?w"K<#w\F qNq&qlmHJmP2\pz}ZGi%0,mF᷒?*5~dnBcm+3)$uG8mf8.7y%HВV@0q, uQͼ>7wͽO6' N1T; F0 xͥ,Ci$W*v @9鏡C+Ink)x-:y0D>l9; -h@b@HbwKr'{SYe[nH27= ު[vI-噘 n1#ޜgtF*Jfd#"  ֍q,f6%`^u 5[Iey>F#Vʹ)nw\t(6LZf)M+r^1:skr&( F=1׭Y 󷡌(`1M_0ZGVQ's]}^z^"[bt;BWLktFFPIEb(a1p˶LanjKik ,XYZhO,μlq\-=ݕT$[<BbJvG=IA`.g@_`%RNT N~WKyno$IS{Gr0g ~$UȼHM9 i[kR8eIJvXZxYJI#A#9<~zWc᫨-F;V)#VJ ;`:m+;y]eTpV2Ҷr%YGQXč zӏtV&N0}9'5cMqrr:zT*vap|\e'.G qWfʷJ1ð~H}{ <6BH(w3m<1]\P*#eB1 : #Bǭt,SXk[?.n$%IKDj @9ϭ&߼1#ыY7HL\RsƒP\c̅ /l =!xNͺ`0:}#/j~h#*B w$Z K)]ٖX[k߹Af<;Hk^b8 q2xdiE&wg2pߐ6s6#j#5[#vWz ZRiP '\>mÌ~F9&NۂySA%0c$[qO&~V@]PNsub%ԖHfd`0͑)>j}1w#~Ulݥ+XB 2?R{R3b湊,SU>$›b(|ȬG|ү %R׏,qY{+*Q˵lqEP"oʟ)hY|Ĝ 񊬊a1;V'k*IPER$iNzQ&}E^r~|C$7HgO)G$z^ʓХtEfUN+e *F>9#k.x_IB&uyjI j@87*.̋p94P@d})T(YKl(oj :{5XU7tԑB}8i2%Fxjbxձӎs5,LHp.FGN+uunh)hn)UyY[~?AҬ[6ojzgק|Otc{yQ6\?3g 8lQmnrq%8g?+%RAt#5 *3~v^0Nd1:J1lvVG:ԖS|SqQ !0q==C#.NT| jG8+jTW+w-Tx_̠Cwd@qVOZP5ʗl+eѝ,{Oe! 2N=~*q99i nj3J /%k6U:uJnRc;MHV \u=;֋p=;Gq@' G)2^5(yxTČ 7"~#:/pm'o)ާ>4d((P2#;dǽdF> VP$bsZPY2*'*O$j7ncߧbRTtZƒ{u$a;θLB>u%qQy:G%c`3m=5(`H#{YW_CF2q؜kTs'-T-I4~В3˘n JҶdT(f,2zMP[۪lv9#==Ԃ(N8Q~u5ѣso7)0n.q̀!X$`S쮖H1_AUq5LQ_z=~ d{Jy$`^eJnQWV:K6w@'R jk۬p 6zԠ|m''Z┤oB8l9bR@?ZN5#+J=;7pAEjHH@"'rڮd@ϟ .3SM z}g{NnJ(WROqUeh#Pr}3ӜWT΍ʐeSr՚i' s3Ycl9fg?vNiQhw`bH-/WKZIKnr~H+nTټc[Q̟fbhOE?]r1j# x?) ϛA?\կ'mDT^*A[ ;=ȭb,ʨ.TzuBцUkFh^d^Y#Z|h6/:S03LF]U#JFn6(ꖭpm~q}9]]`PFI]1` QB` weJ^4UZV9I-n$ւ4~7ς@B=렴,Ie ;M0[,"$-qsn±[u&,oeXHTdq 9j6ަVRpG5iKC2pN;q2$`l+zkԪcg9`MǶBx`7qѹlּC$TvqO6VXp:ɪr4;6T8ZV$i)nj }@F\y'41K{vcrI8Vp g1i5H(1Z4Sw6s]NYC T6gEyaU'';~ZX1#J+TrBk s`eBW$Ȋ8XoFCHX!@UuU5O^Se+KS>$cQS{q!O:$W(8PݒO}@8S1rD=p?@ǵkXi F#yA|f9rR:Pӳ6}ŷLr&>tsR>?ɤ;>|aۃgs5Jwڃlۤew F#ZVvlI31vÌ5f r"ic\vqֻ!5~$N3JVZZ=:} !Xԅg?ZGoc# (IG5аNKܯ q; ך;lv ۈl8?ZRVurTrYWj/X-k$q Hȸ`p>y^wzȽ,cfulTgi* fTySnnVEqŀUG*Ӄ%M)2aˡBqڶ#Y*M98 ;"U\}&C"8#d MmA#uxܒד8k*W)$z9 Wj(S>kSFTgUgA0bzz*r!K;%s0yayOO$kU,"MHQ17@H{aVj<%';][P L[sǞ1x>X0K;b|3/]7Ql3˜QzԽk{{im'yP vJ]NQsSZ+9&" `'f^ ku1yz9%(ozfg J'1"y$n@  14p0XupJ4=cP' L~eN+CJEdd,1de2:vVʊ)DM9(SӬL̲ &x2Q'ƗsԤf΢VY!ExHNp?}+N`#n:+kb!b2Yӯd}kMm$Eݚ(ԃ(ԞqXї/ԉǫeq  g*u8͔#p,W;s9mh#jVG(`63?SurVa,ES/Mky;4l\=1P"dn3PK,p @x>VE7“k#䑐{{Ӧf:M&El`V7h ,;2+YSΑUA, 7`[z툐pACw".N)&[My_R(Repzc9<ՠٮd)";9,T`zۃA>5S#`’F-sڸVW;Y/gycڳ+*  OMu4E\3-$ /$&L3dp6hLRȲH!ʂ*qUCCEq'OWSVݽsɐrN}g9EF^DfLxr<-?=Ɵ`( \eeaӌvE3p% H8۫sL`1$c*1u1SdwF$3ÜV/-gmpB0\}kkSeeVD@G(R2F q=qu3pbISJʱJWNDfk&% $?((|~9y+IZywHT `GJ7]4.e)3yt+)y(tʤ''pZ˲m8_ڟM@:Wanfw_tlEDZ<b7c9$c t%-_ȝbNm eʢ*zF8Y!]>(-f ¶ǡf nsT0'\I̢0[$vQWN 6kErV2@i{2A '8Emb*?$ ) hkYXgX29k&^ID%efO.'CNCߐMI>PJ춑u-\yc~Nحfam*GEBvqEf[I2JL`+.HS4Hn8dvrr<)Ξw4[KO.62O!`db ,AtPK1 czTvҋyɃ-@ LqZzF\kHa|%W`[pB2Hd5oYK؏OEu 98~u&X )Rd^J8c\v109S sGo![xJ}?R*d"/lBbԣy ð gNK~T]=ESbC+opICơGwA$9*v#PYyሂH$nR޹5;llEi.r&܉7й|WG~=gfa @c>;A4Ӷ`c]ﺹB [."S]m o1~&88r:}N+l軂6lt8;W:DkѣT|UR)xk4S:+6Io,{8#?6~j 2+rpr>]Fʊ(8KlמN=?r4dq\^]U_O]i߱xw|J2 y⵺E1 H8'5MmufVYS%%Pqykε 3ʲ]ҘJl1qG<7}+*M6٥yb7ppzbj1MQb#%9 IG/uV`ɝ(R +nٝ%؊ZF* uqW[6-,ӊAm\sҧI ~wjMd([!QKE8 oOj.#LQ$Y9$0W# ɽ>f\u`&Dv,O}  B"|FyIK=ϥrw"aX#t\$^SؾSRa3?j H-ʄ}ՎK^BˍЏWyRo HT梃2C I)Ž:UG3)9d?ZJS\Y>Z婬#0[#IP7dwitv-BӏZKYKkZo*;p\ k+d(AIS-o5s!U{SI&&uI Sm$&d.}5d̥% -e=91ޢ.2'4_ge[M>}4+'ڳakʽhBw/rVk*=9 `!6$Vn 6cԊVqK{o (3ǾTn/+as1 \pD`>cAB*Br&{W{eB|ʿ#Ʈ]Iщ_s_I,J˄B+M8 =y[Ji4M]ʎ@S! lK&YSH" 8n..h4(̊rF dEV1EdG #cH4Tr_[>)J0p䌁uҤ-[|N僎IzqӟJh7[prA0Kp@N?jPJnenK"i˳,HˆO1>t8j\CJ}?!Vorz8׮xJ[imHU\ sNu#dR&3֥05Ui(ER'1c?zy t}f尮F?.:SAJP$dqI'4:j2kPUB!F9Ojdasޟ,I'R)%eЧ Ь=18߷Ÿ) j's򍣞I6mcd).sGZjw^EN$Ӿ'fjM?q?#n:ja2KwrbJܺv4V!1!,aV꧌gjΛ$1*E|ƍog5U#\@prA_P?"+ӥJJȗjܒytSA鱺y8NЄb]@G#+UD[^HJ2OQfЬjqߏ*{-GV[|y:*k8M|9ӿo#\ *i?>CD\Ȋr_`+A՝t0~Х[=Si}0j"Z*af')cR-+n9?yiIshCٛDN3Vd|3j19)nly{  ,G#.%X{ 1Ƿ޴մ."4,ssL{4?ְu巺nc~n9 NYi q7eL”WgahR=H,lsv΍kWna1Z6 ,9sb=sWrN.#B`.pniU N;W5 ]YV1`r{_<h$f3ЁW I0I?7S4O 咦'N02HgJۏ#ViBn؟NӖDp#1* nO&'zS*nM]r!ٖQ{ޭ;IyN6iI !.H\;8==5#p9;T1106 F@Xid[in,xI#:W/%7M*̥n"smޠG9#c]]OsfPHg`p=㡭xu380@@<㎾5²yє7cb:(KY"yIܨ ԜԎXP3ޫܼ%U!`I#1iߌ+"UGBkwYEBzwמr42(FA?tgյU9 9䘌)7}ϷJTw|-io-D0 6ֶt(U 8V8 eRFcF9~.`l #(Ͷܗͪ15S\MGi ZbGJ< )5Kf2Rwm'^ƻa.y8K˙$Usю;}hXt$p?SRW5jF'O B vB>Sx j ]IjkN$mFI8ɫ\ͪV&BUvxNHdks${1i_*yr&^ҳU=zH1pH1к_2JK7'h?*{[.eٚCrv*[#9ZӴynEpvq#:r~V”ZIݾDTS6܎͕%12sҦVH7$Xǟl`[y-#[ϖ' ^}Oi,vBN00z 8ٻ-E$?Ġ|{VFI:MCFw`ci8=GU=.ݾj{n|Ý\uWiogu]4[I@M9^Q.X\n+?&1Pwt9(uݦ+skvpO[[ZZ G V%pq>?m,h$TRw{RyΪkc2E q?r>YZƅ4rqB-1XzwBC[,q($6ß~4o|RRbŏagFQ!V|ֱ6TY٦-P cܜ{W좳7[ 3XazzMJctPCJ9fzqXZ˧^2!8鏘V0Wtkg9hb%,Bm΍Q•ݞteXڑΥD>c3]0x[Oڡ< -`c[H *D `Q*ns^DQ$˜98#ґ#Ki ~J2#i"lZe AQk"e~mʃ=#rkCŃf<̞s䁑ߩ#4l(II%.ļ0y=j(t{Xʒ34nq f+]3kQxofdσЄs=+rAwHVfUz9R5$/MxvZp9zc'V6s!qqβtv[ F |})t0g?5цT9+4я㨷22H$j0 *zav7mKdmQ“ʁDbA.~\rs'wDk 1!Tz8b}+G:ҋVb4^kk*1Wa_bz[\Ov̛T6+Dwbhml`pGj\[HdV mX`~5evk-Tkfbc+n@W?=&`CHQAH'K~xWYa۸ | 1խNIݰ2Xr5RObOݱYHڋ3BX|G,ǿrGjZuuwʉ1=6g>ܤq[Mm" d+y9B5\)]}i\Mdd5ݭ !d$V=_}qb/L[i7sT3,Q00V2P (w~5q7ٙNC8 2F ϣjU rVEWY-&7t?Cy8MA2])ᏠZ,LHЌl}G@^Բjmiu&Fh6 sq=iȽIЬ`kCtB$vU0V['Ub͸=@ {UW4$I 7l)Y3Z9?2w";>{PŎqy-;L+*`\TV/%tn$i7o㏭.g!B&ᰐp1 +ntS/5+\5u%UTc7+"-.-)cfg-9>n^}x4P\;O4BH Iz;5c^>R6%u[ggʼ Nmo3%_^% KڞiHǘer܆`3irs kG$1}&Uy1,ghA?^ۻ8JL䍭Kc}+H9&-V BDn6;A|:iehn13.IB۽[^,rIK`B㑎1;nKISB@,5S4v%6eGl䄚F`HLI*#BUX`wrr9x.4&'Cp cp}wDA2 ;AUJ#Ij=HbD؊@ϧcj7pkP1p%K2=2P8A&إg@*ƙ%LsTnSr_piݑ^EDdQr(XmN8 6yFA6RHGbl^&ybvkZ㙚Yڡa#;" ><*tuʑ:/AqjΡg0Eonҫ$aY`q>ƩHb+s{CZj}f{M %wϧK1 "Rr~U8r8"7In͕y1製t0" ;/&*v2rOcke Y\i;Lj/&dF}ӎx= y ~(l s\1@+BT0W/ "Kc*!\yݼ{N -s)>[\*of"*Ňd`4BK=bU.g#* a邪xYȘm#d1c>Y(EU2㿙$(2<2rqY X@<~u%Q"M'#vjÒ\ֹ$cL"O@ 皻iy r 8arCk+\y~R\]t02 U={w݇ :#8V|adq;9L-EfFp2qYVjlhY{}Ոu2-{ ġb <OٴP1ۣK3vk:l6,N f>'խxK8.x :їO;-9!b| 3i\ưGoNaQO]ooڵ_P($yϱ wm8|ɩ{PIV,y٧V23X"1īQꪳO\'Ҵ/81 zjFTܿԎ@'kxJ\I+ʐ40Nybuf)Ds'*YI"IlERy̩%fڑ`|᚛QϊO* Zn@\jj*(!IZƗ3N(̭-#f]DI ג8n82,%t#i+3Q2i=LmʲAœ7JF ݴc%hϽƪKg<[ʷY9S?ďI·\l9z2kxeb.8O:HDJW,hѸO@~w;#h0!Ev,~@K1ɖ!~d=q}E{lmc'QT~fIBZ;T; r#2>L9pLH'iP96h#1T۳m@~lw V523.Q448׹gxHJ FIj|eB's^q20嶢r8۽*+󦼣Pqw1SSF|Ϙ[ز9T"3ΤSI I`'>^x7l.IyMEEXM+ܙbzaSI,OEJ$~aN!I p;91st#z(qL?qS\qެT%jFN}<~uũVFVD$@ߑVͼhIYg9:ֹpTs5tkYj%s̥֫Gb:ds#$=<|'R%xx$TeHSO>Նݰ F)YnI"n sz~&|]K,ە utwe [Ns4IEFbю :B#ta*|w>50l:*95WˑaCV#`F۵vʕ]J[ymmjȑ죂ӟ~nԡUn ȊXvݒxqq)JjD"4$6ψc¨9b@O45H#Xc?G|RmN9޼y F) 1MbkXrdƒ> 2 g~?J|Ŕ f$c?ʜK(^Nu߹ˑqP^5ò)+8=p8:քFrq׎j؟1TŒ`<{g2{Bnƅ(o9<<~H&3y,w$`R,LH'O~UJ+cWpl1cŭS+g<o,1.8'd!q%Q$V ln< gi'Bɐ1Quj#VXhwnS9YW]9ZZ\6#i'̠6w =ZsJ%8f]oU?4$>N|GKut{&2[^?Ͻ8@62#>bc-/ry܎A{794؆w,;㑌 hfuU%7@aX34Qi\ՖyTO{ɮ#W@$D;9:#֮\`tuecOTH@ X;Izv12XVƔܯe,7,"G|ÞxpA<;VhHnR`dXn'>iJ-2A,͕ g*KwRgXr46uW5kguL|Av!zzo@P0:sM.aWXIB8_z+>;޶<ݺQv Q`9ϯ֣YH7 6yϵHO5c |C%&TI!]#hl{S۟iۑ_=jG@T`uR9 q.Q#)+05 n zsj{Q+U628?SR:"r? 8iU%b bY›3cw^ڬRORJ6BY@ 12ȌO9RZ}BYՂl y 4^s33d3UL_;pňǷm璑X\J-d4Rqa2$޲+(϶5-Iy'IESԒhIJ<S$'pP;O*OJTSJ쪚{Deed_y{cr_m2 V+qXZvZPغ@.~u&o-2";@?xg> kH6;b:a?xqJD!UCsTc;e00 m6@9kBBŀ?37.#a[sO!$Q X{s];YH=棐q݉y٘چ;09z{W4ZCRGA8#ka r[=`a, + pnF:*mm4"IbE2p8Y{51Ĭc\u 1Z\%Lr+.׎k7pW!;k5Mhc͝&g*񘢓$lsǮ"4 mІ!XNrG$PQk!52e74ŷLZ ۀ=a3S/.ky(s2[2#y0VWݐsan88cI$c#i+:iX?ӏVC?ȌFA=yFUeR+NKwk5 WIR#遟qyI Ťg Is$}VtlA/*@7jܫ%*m # G\{RKI,W2"F!ɜ㎜}3SKuqTS\߀pC0~FI 5\ҖHyJw+A6;КtBq)@sڛq$2#e;\OÃYG ڜGT,.[ll¶UY9Hnzq]pKdݶ! Rpqԅ?JjX\.Yu真ă]MFNiG[ `g8${S#v .[1#j[2z֛"9{WHrr\\mfTA<#8cVҪI IQÓ?[Cӧj ;)j<h1c?<:UyyhdOQ(W $@HU-DOh.]sO§X yo߷[MGBMѕ;>pz?Z+9[:;#V,}ji:H74V%=#wdk6Kq&2vIl1^uOR*qYek48 πF@ L̐p 99-J;ax[͍y) :Um=AucaqbBfb s9ZڊMѴirvGdym n 8<T#%p:?kv134 y0:VFۈ9ZPI^Swf5)./8(V1ث@骲A>^BH Aw(CR@< 1EY>`x߀ >4c7t̋6f2m.`]7y֫_Ewu|2FiFK2#n tI{xcFw+[#N4ālNx%IZƤRIq:  3,SFA=:־G%W;$dGz5nH&jM[ߑX:dsE,݂ؑsʌv֦t׺d"ܶ;ihJ#-WQHI=:U/j2㎜W֗2I5RpЄbsZ2"a zZ4gc%w1>CHd{~+'V--5&̷uW)?0ULNex8$. >nz,V~?R:ui飶 YX"ŵ.Xd0ʶxpF1Ӛ9 mtBW++G'<ҽۡ 8?\޵+ A pz'$~5ΈT~dj7ڧ$l;#^:&yKYFAz}}u9&Ԯ,&XT=x< QmZEH208 }ь66P-ٽyia&?+~$fi8Qoe&T۴km&8眊Zg ЍB28jeqMt$&Ff/T<=7dU<=8.j Mkwqr«j$Q"$EE ?0VĶD"DFYwzgYH} "9\r:Tԩޣ]zt0}ZV"R?G.nchŰbzwqT,q1<݁UcMq$;FߡV5F:ZNVuWʐE2c vry>lޝ6FjRJNI9²r&[Q3_ַlA9'@>k̄ԓ6MAXC"hK2p=?Zʸk[!ĘE`## kP.8RQckI TN^7 {I 4->t">fyǥGk(>ϱGـXOzu;TwޠI]L;X [HH$ xA9dLJR.tmťǺ Wz֋uY9JsϾ{zE\oɕc2[vH]@Vt>銺KЙSjWZu"f(À\~ElZ6-Hof.ceϝ9\r3=G4{Of۲W&Ju<&%Uqz*$ڑ?:yCNpĀ6T5^9Oq`c_9c6u*iY"HdV6`)~cןEo5Ď,*<\d$v#^Hyd#% Oa?(X`;KVI2MkMk#c ]`<>%~U@Xc$+& 8S?x.r=:4)g0Gp Ѫ;CdG5Ns.U.c\J*'zYOx:[ȏz*$g=n}hK#&ty)Ysm\c\hQem$qק3~]"{o7\dqC< fE2B[jF t y F\J8ʛy {r1\ICz ^^ gci21?LuMzڴs۫yg!98_RvKI bFt8ʠv9?FQWWK~Nclzv^j~deCZq6cym*RTV9Fz TV8݄Z#Ē ?l6/iZBD䕉f#qWu@i r#؎z TJ(NlL%5O=+/$dr 0d_)Q`zO1YףVАbK@LaIfR}NxY U_rػm+b "01l޼}1Won%62]#ID YK(#1Tˣ,%ފd.<Nj"ẆO.GOʮgnUB^17sKY%R|b)ks`8⺍3\4O $9ߚt<űdd+Cb>bIuס鶐s [|.HN"fnmu!%#;O=V/9 ,,Q03֠ץA@X/8}8ލ+Sh-[UGraFN}kD5&K6ڣ±˒@=0p}Jw$zW7þ573HFтzte=R4rIGq۴y2sj|BTʐ099.٥5hpGݝ/ʹV!XJcc+,euOnzcBJ"`GK{מj敩\i ]!;aR[x85KZBkyձ d܃}@M=ьTr[ϥRPvs4H0ǵOZCnb\ U )ܘ3AjcUNJ1Z$q8G31wTCtjIRطD1 h:+IjV;|I<n²dxçK=5no_}|Yl,CR>)a'ΐF9`h~ҥԵ4r̓g%X0JǵHc%h"w`M1=X:EI\ Ř<3#,q刎p9䌟M[itxU_`v9}9f[FV$&TlZdYb|˒I^9EXmcO85OOo.)v+m*zrG*&KQ,$v{m&ʲ8ф!iv(#'ګYH.crcw?bx8a3kNebm}7.,lČ3#ZI UʲdVz֨ ;Y1Gz򃎼Sl##WoE\ϸi{赋-$2$nR˧'ҳ)w#KG#$98Aא{p`1 PqqkK%KPxQҊZMd=r4ı mm6OW'h/.gy=qMRⷊ27g{q]fM,H2qsǕ QRgEeq fXZ%x>_uU pw.@9=ϱrCn䍌i!Xvz\\2/?8?C:sDZ>57U$$ƮTҙ#}뢜9s.Wdn20+!)Frk?J/!G2 fϯ~0r=꽬ڪZI27:w7 O<nwA>a6է%̃}u (*ս#kn5Lf.Tݳ4x.NXJ,`U<$!s%>{H<ӥmDnT ֝ѝ|zT.Tn2uX ɻ9yX qC08x))19o~s:Ty{Jij'5b6W3@5-[Q̍$x**D5ʉ8+Ǚ,@Nh=O}w#\vvp$ s.LTUJ9pppNdRy,mCJ[)]2E.NFğ֨I3AzѡWUV;:,V|\'\҃0(6 ފ`F~D`٢}=Fxն U09N?_z*$BղUQrֲTyߴ3>if I[9# ~9mddV#Yry>`5,9y#!1{K.TGU|d %X)lΨTI*ެRK|&1Ax? }GlZvpQAbS4UD^ ȋ PkfKefbUe裸smJ-;)F%ԣHdq1 嚷mr2 ,ĀFݡIV 2D  ʈcHp ^3)h:zvZ{nwxN@{y/T%e>\%,b1ٍ $dtSk)Xa7-<%g2s׮9✩+r8>m+mH?K\KxY`7TT1FӐ0:{MHO7* :1IJVk8+t#9:jYb\ɷ-9U-*\k&i =yc3J5$Iuiccn#jg9 ?6 [X| A,pOӭ"P>CZn;xª#PRV)r=_hr]8.$]꛱!9L{(G%s6:K@,ȡCqOj,k)$^jGQKrxAFPZ<ВN@3ķ2N2Ip tk64&G^Fx#Rч-]*sQr,#8l S*~R@M*lsUw>Qa:>ruNiI\ 5r❍qMs펦gVIvdcaf3Zӟ4ҜZOB^0A+5ƈC6ҡvusK$p 8 x{+צܕ8E>I!=hLM>S{'1,yGSG1sZQ#3P0<ǏaZT(-NВ8QQEX%ԵI,xQJj`BzjHd;1|dk"Hd A8?֔Hg j .{ZhE FyAUS .u-1v[$~~ z&ppjUuT&BiJWԛV*>QI ֢#GSirJ>{qB<ph}tIVj xL^X1W~TH8#WUEGn:USV-ǥ)( cu+}z~)H x+UJ''ߎk*RKW\s{+w CHI<87 Ppr0i"&̅%d7@H?<sV j\#!Nm" l[B Dy+F{c0UJhWiy~^)4⭩Y}늭$m 3U~HQ8=jIJ\̯M=g7.m6U8*Hؼ5 m2I%p;O<ΤfA åtaHF Ȋ <(zG=i:~mm ą{N :S]ޕ H#0:R)F=.bbCm'U3 f<#c~Fɻc:!~=N{~ڤY-R$I#(!RNr:}p36$ ;\㏺8 e^Dw#G"c#v125ϝA.eOq+Ī!8dls߽t:@@{=Lu JCbo ٽ3I#@` ǭu͕鞦I'lIw DYQI)ǠcGŐ\jTG#]XtdG8 e$9FU=*?,.猖zҞӟ.6qT(9' }psV싳8싈6.uZtg9$sMQ e=wF[9hגx;ȡYcieHjd²DL0* 11dR['~Z'F7/1O5ˋ9GF;׏#;ᱝ9X$pVqyX=8pЊh.VHWx7gB=Jgwb˘9>^ڭ2# .8sen~д F'3ιq8IR~GU*ie0v$=ꂽ0ődBp7O%>YU)e<ک-V24RG8Ua6Bj%10h\[[ȣ7dO_Ҩ ({R|Ȼm;:Ԓ]F8Fr '#wm[!u->)Cki%3R$@8ć #6z0"JC6eJq-i;G*9#rxգn}V敎%28=y0,g6q7ceX &\07!x)Y+WVu*IC;!A=ǜ߭UV 2B9??F2) @R[|m]ŏ]oPX\ds0#<7B| bJ@g 8©B6z`سG,$k*ܒqm?Mm}(#~cqIs\f=ƴY#8ea9' ?*ϊgKY.w)c!ìFO1pT6W-dSΝ!!Sa-pДG|I'IEUޒ"HOwC UFvb=\' P";:$i.a+N'!×"H&. pa2JX0pqv2*%;;x8Y:}3Ep[;\ 29+bjh>Xܒhu+E؉ti?>ntHcrL( PFР<~4жvf*]^=[( <09*MB9X- E Y8zYFKEifխN7c=y\&nAP;6Bzu'h`IL~bJCvNr3jޥo$w2&TCrnY[#^\j]-nr I#֬e&%y; `ֳ(R) u1bCH=r=kJ3ǐ'Ǹ5tmsD +1J9,H'pf{-#2` gKM$vN$ns:GV,[{.L2 (k6>(nqQ?CT \wQD0y|) dsV,^hcm$ߴ'q>u:7?#<#zNVi65aPX}UvyXR5ZA:ZI:} ]d1r[.sOO6fd$=@~jQL^v)73e0L]dgڮ+I(1A ;Iw1ZIF1Ƿ~2!MؾrYA3c'=՟4!^-$awwM4WYT:ijei 9 w`MA{qvc %HGvc$mIJIzEBI9OJ覛Pzi%v@ߨNKֵBARUh'mWlM$mI(DpFGC>n[-" 3m$ڬ22x$I;iI4K#m`F #Tu@+rM)cnq;ylmءy=zi$>\1 fbXc''ٕrN׌"rnXv` Q@̰3mGpSr:x3R*,d3q=ZM)4.g[Xm;; 5Gl#Y8 Gt?!a+Yڙ^7\A$9>l pOr:@}*ʔ+9lek{tRpnc.AWNN;~&-D`z5=|.#*˳9z}*0JJŽ0PH+5חX$bŀHcx g@}|8湅tͻyiD`v8/yj4&Op}"#>Y`+tv~gn1H!O1>@q *ޛq4Ѱ!W= ԏ4m\ m8>p:xV9*I6j#rmgfX*Jp/ ߓxew)@Hc?Fvr<2vf2pF=Wڹ{"4o,p ۫$җ*ft "i^"8v2.1ǭzv6M2Čyn!Orz]k̴t{x#o,fp~v2׏jꭱʒKp82Gg96ffk]BU@4B@- s+Kn;XHldr +vWj@St`Y@j%,1BK,qd8j)U䓅͹Ht7V[i ?/V8cWGm=QbBy$8IX"hg&fYknv9xZY1!( X UQ;3[}QW}ՉU#+?E7^9$xgr+rI>EH#00;N['⋋'g ѕB6I)^JbnTQXEAdGxݏd=irM6eUhn|֕%̮{- è܈ sRp9)&T>ThNN<nR6}7%#smUqJG2tE獧8 ^IˁKuo-"Ud@|'ү_n)Q'``mUn]io-y ^B~XݱH;uJ)5%^W2#~,bE>U$;\UN9RZ^-& 9~OV\9|GjQ.&8,m$c-Ӻ;B?h\)x 'ɺ$/2X}8YwQѹ 섮G#06)p,G0;K, ;iNsJ)hc$v$6ۦzR. 1RO9?| lIYd_nqɬ#ʰȖ!8%r;J|h!̢Odmg$a?1TDrj*ĮɖW$U`*y8 G dH,"˰ʑWK;67 0$O-VR:HHڲ5.fɏ,#85\E4&|6g#֤4kOQsB9u>խ%+2c <6ʅDf $+ͨU@ /yEZfHwH09kgQ1;B$/p|7Rg XO<2I{Dg#Q k KkedU\}p] hn=- gXGiKO$LX*2xA5MYn's:=.*)V9$˰iR٧ d|``kjm0ǧbbN9s)YٻZ馹X0$N̕mP*d`#qڤeVIqfr3Um ,r;#]i^,eK5{I}ᕓ`0[HcUs<ٖ;pbE$g#@Yrzr~^$sh:Hf, sҡ>4=kGWH~f*{`wPI2Нn3: mYV<$àW'#jPU{geV CNN;zRT1Lq7zxDd'Ro*:x!};TZz`C;$u:q=V^-J{\>ĩJRZ/Ӛ%*ÑM'׷g|*yrqޝ]t{Igz>Ȳ33+9w&`I@7ypK 8ٞ:*\Ú%_#G 1*}qΙ$.$(@Ueb q9gIVwISkAӄ A~֥L"R%-Vy;!;7'?"fF98 N#5·'?Ql9G i7J^f$m(FqTM]iXW]ȱBςJҥjI#H3!nNw9ϒDӏ4HB+#~$? !#U#lz`wY Geh*I}9?6r-TvS6 יhs=>r8YO֔=1"u?)S喪_Jn[Yrv,gٸ*;sQ1=RǩSuLr9m߀OPԤ o7ЫHy`^I毨Pȹ9gӀW# U+ܨEm˰07zs;HtjpW>'8yΊX2{iNT{i{ 4J(=(&xnsGc ŋ[tIC&3Ԟ鴓ro{ ̔0a^r?1.Cp^07sہK%QB ]ʹK[Q[(4dd}CNk)NI_Vk nNГWy,dh.AdztANXn+;QAf[ c(TO4361tpH̑IQD~d7wGbZ: kS$%gbOŔ~}3W@t.T#ҝ lDCLAS7s'ǓSZE$eĄs#8I4DtD"ǿU`x\yJp)ݳ^VqkcujF\# ?Jdr@?1YڝC_x7 XJ zV)$ͯ)KuWnq|* fHգa#gܐdܼm@HH8lg^9'pSPR܉KS32In(ټ% s=TAG.A8u>ң(BQp& ZZ]zBǷZq\nkSFBD ?Jn'9iŵ} ,_r 81:nf;%X(2i'ǓRs׏^:o"Q #7C<`.pCgR.|ݠi|ϺkJ{-:t؊꧉U?u-JQ\b ̲),~l!*A=m/Ql}+vT?0HX׶wPdw~cs?(sN;B K{]Bڠ}ߔuڛr|i *<Q'` BF m<Ԛ==$Ϙ`䲌g#(Jrt$;O"Yv zGb)nV"@c!s ۯ&s$U1+.to t6BŁH{[8.dfw6#"A991GS jHF(֟neRsŤi &h`̓@?PGa\eTb;NBWF kw14vemn'?J|J-Pu#+m4ˍ"FP0ۓ{~i/gqEf)$na|Ñ}(ZU{ϙJ 세\˗<Ԃ?ZvF;iT&K4hbnO}IP!b6..cIx#~B>VsۓZJ\c]'UEhEYJ`6܂Xd Ǿj%c17q 3]y+aWCUnNG:9ʲbWXmpoj!NNvrӱjZ٪ɱwFs 2^[}T)}I$'А? WAgoF;ݓܐy{ ]FXmYNe >X~-וY-FH V@bďajqG͐?/VQF!z'ۃV F *x" p:JG7FFE!:Gm@TséC6@)7wvNj(ݭU* tA'.ef<ӹ $r}ґuw I]@8q Ȩy ?2LfPjЍH 8f8SۨzT9ʉ#;oa@1c^koWxsd,;A=2yTKVUfxKqo9U sՇw,j+I'*:qk4i>EW۪9.#<5w)R[+ ߺָDcȭܮʤlp$dSzȬZ&b ˪1ջ4viәcA'qakGpy ?<\NIʬV+i`7,2IE==FAoJje %q=0 nj~!6u mlg>'&pl.rG@W'=5Urd Re^F $A_~[GUAT3-#?"&$d72b^yzt$-_4(RXoOMcNM$s2N0js#Dy+9<^MOgl(:@ϨM#JbnάJG̠ЁM:DD e D\a?1'8<ք+]b9`e!%{q]VtKîOlEq\u}{hfVՔ('l mn?eP1'%(`:W|,Bn$J溿䝹v8$czmt㨛{gC:|8kai0m8ϮjڴQUkGB0!3Qlaxp66$gXdQ͓H=I8ǿ4ۙe}a?c~5+9b+nNpz{W4eRቌe5y_jKªT,{c=?:z k{ygL}2wIb6I#{W0:EP#s`g*Ts'vn鮈¼V։ 9ڊx溸4k u >fT?(#Wuѕ d"P߼V,IX^Agsp.>l?͜؜Zgmh:Ktu -"\giQ4BK"ƱJb1A,ďbk[۔@%݄ssvU45$`CsZ'Qpl i%-Vdf\=o)$2C$  'q?)WY/║sA9aXI"F<3Gdv<~Yh!^MPmea'0۹ x5a)SpAS=-nͽeO.61zA<ź}dO*pp:?/QTyiAgȇoXv' #ik2ҁCe rFyŠJl2Y2B'p:TgtXDo`;vvCݞ-;#@񁹔6w$,;Kԑedgo FO< zS"mi|뤌;GsJsUc]-Umv|Ѿ,XdW_4nVli$YmɐM g"5T;UIv0lj[E+kxupIYvSS Z/hg?2;wDrHLp:ӸuF'vrw'CUko*NhPbC#ϡ]-e,3pǭHS]-H ql Jj cbo՜l#Hd>?@}hhַ{2NO*t;LQ],{GpJcW Tќ$(3uTC$Hv>_~qˉM3بT<^u ϶DCr;u#يX0(m nO=;XVɲʙ%iN pxHsӚ}k*<2*c޹k#<@z`˭ǻt K.7ǂN.)-t#gnf5ޣq9ƯK 3ϔn,6q9#Ue,r,=kNiE06 XTǜdg6P@ا=3Y7fhObcRW,Tu$ZDi@rى%\rCJ@&u+$$6G̛IӞx}Mg{1Ev| >p_ebQl2(F}Õf`sU4imHU]8}kEM8s7wTMYywwI"}39e 1Ox;v6R>nG^+v]e,BĻzQY]4VBdPJ0r9q:3׺Ci.k;0@ -v~Ur=57Ym0PŁ p74R?*|7Z";lt\l\{"X&DWfsG㊿ceb[ps rdͣԢUeU}0:4!hbžs+0ԯJ|\x>7:w #Hr͕czr|[-mlfr%˨ ;˂8KGJ#Wp9'ވ#ݚP7TrѢ 2q+IE;(9e{Yc7SS8P9_Sfh2+O_[-ZMU!C0V]VJكIc]hht/fuR8i) K @'yH` ק[;|hθ(i&ӹ̈Q=,AV=3\{ccL9s>agd|`8\&,,mPۆA~[PZI;k6wt) e\\.ii$ e6^t唲s2?9c]\g!_*xaY6$E28t5c)R BzKeKTE7$s2N_ :\-K0qF)<GE#_E+$YJ{zBp$޵^4۳)]$8uqrW00zr6FH'ޤexJ4ܹrP+*wg*9F~ TW_f+!dy0?&Fld{q\V_.J99㷵9 y((+ʰ18Wg:Nڔ.XѰ\G\^m%a#7pxF>aB\nAei!xލ[g JM:R;eC&m㿩1}+UNpXծrۗjǽHQ0?{x>^Gf8:NhswҬt5\ҥM;*eRBj7S GIAwl$Jl ' {wϿJ)8wc#^>ӵX#r#wOsI+{Kl rC;c#$c$tM67)78UvMs\uZՅOK(5&4, #q8e"s:R" owA"} ;wǧֹ\ڃP.H<㎹y 8`Gj12IR\׎ '=5('Uc'K0;F j{rypv`3sOVZ0 %@QH@?'\UWs˙br jgc A䞞) ;r{gR"C!rr0WYU"I 'I#4nq'9ⓣS݅P 12zg=!+"@*t(5&HBNQDrț]]mvkS guUJZN*ZɈyAXԏAiZVYi}ǧ\}*ijc+)),q41MiU|x99:]s#3plqҦmr>ۆn,[j@$đ1+ȮxsBjF%SOji# 29#\)7b%kM+ e9$d:_:2q#v7RHZ>ΤM-UܦI))|ߧ?J 4AVsMfe'=~|KI"7˓;@? 7vVFA%K<v'xj`UQnYnEi *xdiHU1ǹ':gکKyE dnOj[.6@<ǖpn}pj)whjvh4paC~z?H/2rX {=C_ce0;Ԑ{)#=p;՝.L &KrHS[Mzh8]3O[]ee `#<*ܲ. +ŝFɵ8m<Ҭfr zm?()]+>vmy@GnӜ~*:`Gn*+E*#$xI*K&D*+>ytsKY#Ef!A%*C6eA n?GY<Ɠ,O͏˥*w(m %{+S_<:Փ֙` zSV'VQA]ӣO׿O^N#Qq KՃrn[rw);K7RÑU]$rI,"!W7Ҵʇ\7~Vkp^tlsֹBg?KenLF3*uiNV#j 8O=0@Px9U$ qϠ~\]Y][+ PB8V66c^2gW:4{ǾF#~3W5 $78}Q^3"LB+@9wq2iGSocv ?Λ+€oQ{0%ؐ#^%ż $'fqZB>Dr5;H5$ D&r>@vz wq2 .[nuK;[qIS}?٭H;V}ΦfUbD(F Ty qpHJwGSN=kW5rmH H t c5|2S_iFّqu+)XpC}Vvnݴ>ޘ;9BFgh=H#+Jҳ Lu޸o{Ov9Cl9=6MD7yj[8^)3=+KU LNr  ̨`9F>FQ1bVe]'֧5Ư$yb2/x;k $st,:+kFi[nTTڜzelMֽȅYSzzM,NK6<g6d8N4}{uٓu˵Wdl/' G܌}S5-N]/?n}V}Z2BvbFIT;}xUŪ~wpk葍iԠ3 &/Agt8 ]W?+ a{Yn͂(D1H^1Aȭk[5NF.-'0:TuK]oR{Q$bJ qJ .۝ 6t^U'$};:VR,%br3QǥcdRd!۳ v'9E#.b$p Gu F+ʃԁSNpI;jocmLiN$Hb:溛k+h198'Niȥ]*?\Y.]!'ޘjfVD6mI,6MTjܞRmnSmlL;fQG[7#Kh$Qj 4do Qr4,x4`@jH ZhIە<zck.is6dYf&$98񟧥_/"Y;0( c\{n&Bƪ1 s֞V4.2 |9ϧO{U:YG ʰܡpQBxf#Xon<+>٭/hz~}q3nTb#;H<2}j6WRuViJ϶&!y>'i4n$[`b w05Hw,qÚ4#ϸeN6dAǠJ#86Zk(AC,U"/A`$yIMnWLl|c}u'9]z48˂Y%fIݽCr&>jӈ!00@9 Km4[Z Fyo$xu}iH(*GTIlMmS8PnJaO[{h3 ιR+bxZIUPc9#RM2pQ|C2RXt( S8brWozh{[VYn$ǃϮzn,^k{XQ'L2&$p6;i\vm!XpzV~ӟbk3*V2:' GٖB#!N=oq%$qS;pMM)$,ehh\؎kJ;x-HHDlSˁig'*($9i$Ei};'$)pp/#s5"7HGI’!6sSVX0pԔ4-Gu,+G4ԥlTיF&#k %wɐGv>-ޥ2<%%tŰd6ӭlq(c$dJ -m"F~>9ğ|лh|6I8Xw;, t=NkAI[c`fn$1&5!<ކh☳2iU1U=1q,>NHv=iVk%[BYE(sWy4SRVK v20c6I#ku\khlbkt +ryR?hc 圴8.+)$,R,)2G9I#ozB*t٦K3=ԑR! gnsEtondG?ƫYMv%CR@%/%RhRai~#=:۫y'x@[9kFJӌJεHp-wgj>dv$eқ[8#$F 2 d&^CS8;C[˩$1d6bN?dRx ,bg%F;HopzwcJF)&f\}:MFXF+˼TB2ʕ5 ntǺ!Nĸ@N}ld+4"9 E/mG*s?ϵA bK|@~MJRntx Է=ͷ3({f^%CkXOס5!w\|FY-t(#=&U[Ɵl kr#r@A<~bg {fO# 6x݃{UH{hEHv-\ҥtF%6ۆR0r큏l{V2[;PZ2ZݝV7 }?}XrF |+ji @#,Qt{(:'8[P'wD3:mhN1*Ax}+ɩ-1J$@Gdޥw;LApIԎ(Dp #H's" 5Ʌ^\g cwWJsmy6.X0sv;~s37RA$N93YbHynB3 ϊ+@fsZtD%by`+/S3X?J$%;B¹ LY1k2[2I8efӕx-M v6ٻ;A JpϺdaNsd41-Y/ ,[$9vf bbo\-%]Yx;_:Xäˌm.;VUԸK3|`~a2_\R,_&BOJnevMXƹIxbH'vz5uhY;Q' 3Ӱ^䠶$q .wZ nJC zFyx5JM+rݞm+Bg̐`A+-c$p?<]4A#F8 SS3\ id(Sn 6sнۖS- 8 "V uVrg;GҸ=&F!9kdp$NZ4bՌV).v$c|6`2,OЃ[:۫E 2$3Yze%Bn<Ӛ䵵3.ihT[THcfڱ9y~sMX9;1j ( 5IuTS+,BԊRw%зsw;QR%2(vPM5v]Z3$Y$M|'#ė'q`g*z(q֚6oC"+*ӯs96IpXsbξZlukyťFl8!ID0z#qLUejǞU@f[$yܨǠϽZTrʮޱuJܧUJe*#%COAԫ <\2j]H]xbF}3)TJԋ*?BiCx8MLgB9%=9-cgn(#:|5]|13^*9ArC"fQ'#$tޖxB8mʒ0rN3Fht @ c+:)[Yƚވa ';HWq$^rr}Nqa&57;<0C #8xʬP*G#i`E^I0(' F8) >nS+p}mQ2 d1y*\p7 `[rqT9J8qK${Cjr0T?)$|r1۵ 0$O'h'RM;Z\0Q) + SX#$^emF#&#ϥCyF3#@< 3)e㍣kǭ|'iVDwV4k1f"" 뎜wj5ĊfI,GgPeuݻ('gojFhU>$wpEc*U*+?Smcoލob`6F?] m-$G~?/ (*[[8ۋ` 02$`g;@nD2dq?z)\ ՐAkJS.i7`$(;IH{ďwy2Qy 2 qEy(^]-ćrR#銰AN~h$qc[B)%%B#e$pUϽBArcnr uU u.0u'k 3\RbL`XoTP]~zgFLj 1ǧJ 6eR7ECw,qƮeUUl6N8<_XJrMhR0q ?UmA ^Tz:TW> YNYM+#| v^999rqڈ[ۺ $88Ox vYzJ:ڌ/Ցq*z7Δl[[(H+#3t±2gIfU`rN~{fy4h3n&9mEH)8ıFwQqSdNxrGh,$G>sn,9JRgc]_:zIYL|sXZlf%m,3:吷0.98/Nҡ(nilO*]#$aέj$RWp~YL(b9/>˵A$+tes,cꖲU8)y[ Q쮍/&DqZZGBH+r:@$ &O6Gc.KHcf!D '${<ֳi wZҫM_aN9Z֛ UQ^Ʋf r6ܓr9>` cxLkɡG rG6Q;jb劇"Q#onfԛQirj<A]5B.!Qo 1%⹍6[q ,tE D(X7u mVJTy{.,mg[ylqhY'#8s ˻m9f7 7#NY|m$YDG#N=FwiE]ҰM3>vɵDL,O|քha|pG`@ǡ ŝB]X \`=Q[|V,=)%h|-^QV)fq,0K`d|e%e!g@rzZ[Z0!Iz`$:]ԉ%7 > cZiB 1)n~$ $nžTp1jIe09!h^ qȨ(f*rdE,G׿D{to-Գ6q=:Tdi Qz+.by|rԁ}Z6iBX:a͎T\r{gڴ'i_9|Y8B ~ZQr^N-ۡr |񢈢,"0İ>~IYllÃم d}t{Ae4/Sq<Rk v`|q3y{V;;&V)mXXP(HI'={Tډ}pZfv  zj?eĻRprLaG8 y"7I+v4gAޝ=Γ625 hyٴfQZQq5{OoCV۰&{xi7F~*:r88X\k%`\!eV;s؞֔҄vj6֍kQ _kݜ:ڢ;YHF''#֪o}y O#FB9: V=p,q08?5n$EhGv>[e-":NW88=OZ|אA(BTmp~}&FR6U*11b;V-쥅Vk(c&P~T7*MhpIjgZjV+g<˰`I+~aBpxBg[Q՝CzZ.|GDE(PPmH d/Ny49$Rv5gեc.R nVC)Ue ojzxvo!#I!=995 Wb-H%;Jy.?*ǖU.XQZ=Ƴs34VD h[,<GT59eP`\crznn$P6V6 lwٞ?;ߴh1A ϷZS34V&\[Y[t vu 7_Z̳X(摕 <O֮_o݈LdA?C5K2m; ]5Ę|62`0x+PwFӊ.\&it,>'NF;v:Z4LG^H먭-&;ss3H`q'JImvDʒ4~br4빛n uem,#1]9x{X,Ze7,n}H,3GBEpHR]8\ǹ!V4Tv"h2g=Np=]Rֆt-2DaE S8_Pi/cl  GG9֝] ]!x$=\շ4Nlzv\}N1jA~wrȶ 3MwN7p;gڥ-n1l4'Â{s u ˸m[VWXLe',W?M^I !XcBCc ?ҸIGX6pnjPF'e d B1T&b% !'(м>WBFM9S`zFOBZi[$Y#r<*e8 Ez|Y;Mjf8,AvU5K oS ݁tYE⸚Kv2d(90${nWQny88cJ*ВWCOh^pAY"NӴz3N!7h8$pm=W샎EdLemZVa8#iޛL9tTïb=+e8}FvZ< ܔu8-DڕNqg[ck1 2\mLmK/QwȨe_Y$n>cLpK`)zE/1r"kS$H2l:dd띬qך"2>{3vlI9뜎vbϏ$`BScVXmmRoӠϚ;b.,J['TR3 t'~u$pUIf! G4eβ ;@' gR|ZfRUTn#UHY=հҗsJ" fS%NOeү7Gnp{ǿ?Av"Hx?V%CYc![,7v>{~N%M`$r=01UO+J+ǵbI9Һ=ZKfC=^]wt %X'< Q]4$)'.)"OJ#*꣫z+Rk%d]7C'>} &`N[[O,d-|K q\\::pO=N)TS-5_8%l\F%( dwքWPϰ %a}i1>֌2Dby߁Щp'\*ɂW_ǾOS[\Gt[jBYKcTRq}j1,Ʊ9ۑi8N*Wl&ܺq+yY &>^1^TmnOơ)#tnXۏA+Z%$)]r"ˀzO~#8]w;3{ 'WG:^Ռ40+?/jHؙ0F@!1یT 8})PpO .8wX32eHMʙsR5U`t]cVSqvb=1k:N0^¤$c#8Y銺#%nGqD9遜kܱ]9LQcU gXyvtl9ԇltC4 \0 0Q*?݀Tev x?QfD%iϫєk5}).@v@@qu?CqplH)TcQȫB2b c :R$ rٲ̍V >R:ƞ[/9R#GWx "ane3\4*;I|uS遒|mBB!P=R(ՔFy̒)wGFxdkM"t\2`gFzZts-B]7/$tt 1ȮeRPmmY,R{UѢ|0xG Oj"QƁؒHrzw}{LPvpJd; WA}_Qd C?XI^);Vuk|m:*r;X;汭e?~.IqrL2aZW3W`;n:gfPXdm8HJVB 2j*;0a%T|fiYeTD%8w>昲m$c .pH,ĎA^CwWdW-rY X힜UfaQdoyjB~udz1(q߾j%RM&8>UvgܴDDX8$r&-#7@NI*ƪ8∻gbINY X0F{{wFw:dZ푑~I=@~S{kh&E X9]R'Kl"mn gO¢QgeyU $~Ct9>f\bM$KildȦ<~R۱G'+#KK%u2yeX#8 Vʛ9%y=* 88}]˙4W &ې <Jqp'^zg׊Ϟ%eE.NH>S%5qq;WM)T3td]VA< y]6*gMy3,E 4R7 ӭQՍՂ 0k0aGqͱl@1~Z=+ 47ŧwuMxz7lOZ}@JnjAo:$TqNf:6A]?Zu(%w˓[⫷ #5: P9ZCGD]gEd$n3Q,ꤌuz瞕=z& (>{T)_sUULbLߔ9a{Ӽ=Zj7Em$%!=OR\i&䑙Z6UMCddfx+F]_+n`-eyI>h\%uaC(S$+6zW5TCª78mI9]M6Ai\aZDx"R.ܕ!CsklJwȠ-4Mu,Hvd^blo]p[Ш- Ө$Mi1*i{7%.[;HHHF7O=޲c[Ը2`9<Ң`vס K;FP`wr??];NިCw7<Aךud%dOnL]ޝ@W6v\lUhT;8t8HvNJҲIq(Y-4E oʝop~`m@Mex @chՁ 0XJ|KrJ"Y}G>ٕJ#| /'hgV^YN12Cvdx$:y;<Z+V\#۲/@,JeKq:Ue<wmU$uRN@?NAUWr1r=+j0I8ϡPv$_[ bq~Vdw+4"Cf 'rpx\zUcw0S$۶uR9G<ܫLC1Ўy?*cjfL{I*ў_¹MntYu(G_;H-|g&]iVngAFNF=k#\meeT S1j vХbC+ۡ=Sn1=EXWYf(V?Xu n ^a5Շ\Z]%A_}0 9&bUd[: 8>o_Q]DnCsiw3q8Ok/mK[Jax갣- ; / 'O3>IfFpH}z[ZՏ[Ns$9d.ARA~aҺHT΍ՎrOyNO&+VE1$}*NuChzG+\JXdU۱ڻR6y&%YSob 뒜d7a\Ң3厉w5rl5y5,t,L8M+Œp=o yfCd9b;cGP=6`"S#l7z/PNz5S&J$NP;d0+gJ -Z&5{o^K3`IMzcku 3ghK> }nkRIF3EX:cTf@bDaܤ unvqpAYwvK01جoqӞjm%_l!;jqWK 0aY@Jہ;h,"%l.sҴE4211b$z`rzEQ4@cⶅ8N[Uj6Kt 6}>)Z 7)0GT* *;fFHc$ۯ$WrЦqn9?5܎+q>`9L"ZhRG}K3yQTp  ޶2s[ z~9s&ܮGZv!m6Lf r^~;Bƭ83QCo@$FiԾ芤P rxFu;($_/b猀$Nٮ*Ɨ d܈~mێ1q>#TJU-UTqc\n\ &Q0TH +m }N}ACVHF6]#1TzlIy$*u^沟k/z͚vpa(r`Csީr 6#$8窒rwq؃خn|ۉZK5żQ(vu\pNObmՙ8Iob$ Yy#qM v!:e6lG7u9?SYLn$J̿&#chpKWi:8° 7ۑ};nufֺsu"ksnd@^3jKŶK3W83ys,PKs{2EGK/ 9Zŷ[ncY>"g9lazsTsHPNZhΒa+ZF$'#8%--+**rTNγA n$Iqp<ޜe$?61sߠwUY0wdJ2rDI)07a$9msouWi#FaF㿭\m!ѥmqd3d 7 r:V~-zFIkEUng .a*=NToYy(€~cܖc@*߇/4<'Dmq(+=kIŶ <玜UB8)Cmm6S@Fd-vVf($'F-%w$?R)gv{i&c.c>avo) v'CsI`Cms8R䝢huĵU'eB]rXG>YAISL4ہ8%;oL-#.O{s5K)|҂D@d(6 qֶG[Ϗˁ$Rw Ih6S$q+d{tewJ黨=k/G tT<8o'ʓ-6rAMB[ȼUAPǝo|+:˩_O[nAuFr0fl5=%EmU!PG-ȕxf}7cZ{*NowCj/S&o:rM۴pڧmOdC".6DB\8#iAxHҼ nsNMfr_MwIn0젂} 򬢪Ŋ} Tf.<0nOPÅT<6K%,#8Ҥ2#*^2YpPj- kko"dD؍;֯Pw|\E4"eFT 2Skrݵ흔~I | oA>VL"KXЊl#w;i&IcʜgבZO{ܾԹta2Q#q!^p;r_lFi%kc=z 4 QKPp jf)/2Uʫ`gdOge9_ftJϪ6t/r>\`=;A{$2nXv\+sӥqz}JeQP =AuImQrSIDmo.9#x,<{=m)᧕;B1l@'?ʴıωBb?P4V;/ϹU 䑃f^ f&rmD;AOߒ\W%1l8R'<+{Em cc޸RYȅQTJvl= uP&smho%gv & tVdVVxZÑ8zlfR%pC~e+{lL`FФ~kwt]DB";%Nӑ%܈l[d$`nOЁT-L;zm̏`_ nr?,%ck!3@gkʆ?ʮh-B))`x1T$K.ܧ#rj񶖱Q. ] Zs嶢Tǚ 2& w:=Vf8UJc%%A@+sOjPQss+Gi<'9MYIٌmVY||.C2}A G~eԢ;SjRB$3,4jBKx:rN)9ydQ\*p[Ra倳F9 @R hu%Sd M45[%#JK.}y>a4H9@N55Wm8VҩDMZBʊI^}(d{ylWc?d d$x?68рsNsY`xC*#'Ne+lO 1\[ɸ`85J$\ΖO6Y1?}`4ZqRܩ "TNgXX@Baر PCrO> r&>8{LdxVyRnɶ4QȻi[cBK!~Lr*T(X7smnQ`+K$}So$,jRHTT LApy u?3`|·>RsC$E^0Xpe%22IVn8 G(o00Lc29Gk*7|o,pB)ĺs_` KAXc򓟾k7;&*r%{I' cT` )>hijL@ JuF1:{SIJT`qrw`Dg#=ڮF@DmSdU >`hA.weeXF9'944D3'+GotĮGE9zS㹦 xȧːںJUV#<j_)R0ggyQ-yP# ӏU{M/Z)~M,I ~3uRuN~wǧSqUqyNUM08?Z& 2FG뚂q䒬FDlذQ]0YW О)4'9*83!Vcq{k3SIlѢOrg{ˀ0<)RB|x!Rz}0}V4[;l֬cJOww.RH fbİ xP3)pqZLFAMSwlp}Z`kݏ,AOl ֚7$i2{sJ4_IhP"U1ڔĐx~^gmaxz4,V 09\pW_*n{nG4ʎB{Qo m˅g <{ջ-C|`7iЃJu$XrJ#?MqqGIRV.2ҳfXrF\Oȯ5w__rE;y|Ԕlp S#>d29<¬UªN)63Veί `P7pŰJ .$Bib blr:eYԏkZډQW᷁im/$ ㏩zis^NF>] ɼ~2!Liqv~vHb@98B|_#'w9f!u0:1MR c$-J ǷRܗXgr5몑FȔ#uS _KeF5\bsKlh\L9'޴VJK؃y'h]U~b$< JL1?U&TLF ޔ:v8+P^ zT~aihK ~#ڥLH9֪HKȱ0`~f<)]r޽v2.$AÃ'$<0Eg-܄m3#\TuF"E1.\IӊEٙ,p!%g%y9#Zsېxt=zIyyraI<X ׽`=e0dA8߳#@QX-`qڤWD,jTvNzj(HLS>gM= )m(Nw,Hn>-Ԣ%b =+]smhi-¯ Z<]n;pFq5(TSror *29ez?J%;`t`p i25 (ҕfP[r>c6JMt< ~L9?A-jo! le0OzVc(vْU{d+ J5Ezr H912:U8")K!Q9g>ty {%&#/gs-[eHǗ Q8j!oFMwz~4""OP~~NWpO<^ Ͻb2hV77} c̻Ӭn%/4 (aYcR捕M&حjAr$JAF#ïJ幸ΖXuP :l_.;xdQB<$`:}HWbY1B^ˎsҧ&RM!ֳed0# G)gcқeHPgԬ*Nzcڮ*z&)]\Cd#30Apz5q+4l%\aNI8qIʲ[ |\LTcR Wlż+I$OSے1QVaw,Y"ێLSp }=:I|:0;=5vtSza8:yQ]-'I!B?O֗GE>L€8w?\ڷJξ`kJj'Vx#%:kCZu!57;dH1KRK!挴 QucISK"ȱ2#=Ct9OEq/XMpΗ4NƉ͒D }цY1SZCb|O:`cP GΎE_03pzSXo%*Yf` _6sHmFT;fȪYDy+cNXg G5jJ]*~׌w m$dcʢux' t@*`aU6 m+Q1XZʛ#Wq }qǵh Y$w'~UdcyGG|Xr@˒HzIH즤Ip(p rXα3vE/yr{+r"|>Q7wtmqPF>aN2zǩqwB~vGm28B/] 2ʑhWFkėJx'G'9qΒ˔MLLaێ$Qw (i-{G5K1mf^u *FRB6M},1dߍuvT*0"kcY o!vkNsʗ[\u,Wso[#0ہ(iStpbeLSקqEfjwmC*o'?u<>)Hu5{霨m*y Gn橧[V ReXd㏠=o[p.G$`JSX!ٙ(^`=k>_ɵL?)u={UYbKt: [çiԹITR{t6fu)_R5iZۤ1R5?7<~g?fVR{E&sqqeO<;J#HkoeG$fkb.-eyj76|&%FFvӖ-=FTĎI6V{JWtJ! we1.8p+5+d)!o~f#88n }B[AQ6Tyg&+y$e@Px{洦-Logrޞy;nŢ9+Pw^Ton47H2"|2@p?w'$t{dP&r9upNWi&g;p m?+(nⷝ[ٽI]:LO*ۤz(z-+ˋFER g[qqgr)I10UNO0V4qE'&;FyUlt XÞI_-EZ,?FH,#PQ@N0 px7V;B%0[L }pG>Ƽs2ڼVI\>g~XG1I1yoL =`tѦ%5cG2O99 m̠p}Tu-KyIIsd1yy}3avF mmM1'i$;E% ,OOi&W'غ$Ą|. ޜ~UirnpV2뎣[ŨXGTƪEFƪWA[(KWI\Xl]B2I dnGïlSa07W9obj EloV@qs8Hn63aw"+)hm2gq ["[(+vT* <OP;Tz>AA#rHC ,v㚸.IK# 'QI/پd>`2;'~+*m^[\ HFXВ>W3SKvx]m&8-[n@`^i8vpo|E)(xjbZ@˽qw $ /BL|9q5-vK.@#bǧ= ͷ[ 22\jXuHgcAw1#Jʞs91\yLhfzv+eB\`oGҰ# O4fBr8ӄJMdn>Б;@^zJ[]6 {e xS2@JK|s<õL9dڬzjQQjQwR3•=4@,HۜQ?7"GJimt9䈻F_#8#RF 6ѹ#ޠOi9aJ缒zaC+U+{ ДI8ֵb3Z hC]8P2dZX[sN()5)ʕNjrߡз#8Z`DŸ1yj&wC?ozY$pǰ%7QIבa$FjhE;deA5[v*y3kFC's.͇T|k1aV BƤ$OMp2;1d.3S eS*H $VsL[Su/m]\38Hj=8(y96 ߥvngnqH;yzs$7^96wٸc9=ieġX9qPTir0SAq$k7=:>_Ó4-݋#*r@`7O?*rĕxjR>&0j.w!{R*˓J@#jT(1N| 1rI*E'su=ќi]1cvxTjÁfb@S9+\쉒̭.:$ 0EP␟z_ۃ jl Ig\Fzun'izwS滳k"kФ*}ӎj2Q˝韧NE "10}JNz|@*ہۦ~X_ 1TelzvvMtVDs@9UIJ4oQrobe4L.SST{b< [7#5'7d \$2Gupb_<8aR2nJ/$++ֵ9r7"L_z5MaL;y(E(V|J4۟츢SjFC4LW\^c;nU7wʩ+>̼cG[-% XLJG9]+J2_9I;gں{9`GJiT֞eԨ%#;!$t2$K9ڦk[ΣL08Uev5$i H֤MLg 6>_4eGښj5Z!$*liN Es8 pN32c$pO@RT{$9%]rçsl dt#;hT`A(yR! {ZmhLpj,|<~=9R8*zZSHҫKH#aǯ4ۗt'=ʛbeTNHy$Go"O .[< 4.f@o{6 8,T`9ϦO4\bT8=r)РOԷ!IBQܑTS)sjWbXs8@ [TG0e, ;zZ ,\Iɦ1jo?k8FMhuI $.^)=I,stHȻs~^#@HQWv޲aU=.Ʊg7m'joF~0*dh]r+2UiJ)~^9®5+I;EFoJ%`Qck`HxN ެWF;T,pp7+u\8W鶹Cn{=ZQ Wg_8&Iuq!IbrvQ|Gfr9v FA]VB?x0GI'~$k#X,O;c<օnXF28#5Vբ7rlSz'Գɹ$3a wBQZ{5KM y ;0wvaqoVY>F;EB+aW$ gx~u qTQH#t pA~?VV4QjJv=r91ֺȆDsW^{挜`+(ɔ IC UqϿkIl GB:fb9$`֭8|)IjeVN*Zk#ƊSb˝sҲ($|IՉos{˫C2>5d(uMF`F;YF bi&I\%񃞘UW!p6⹫M(KB KPɝK˗.Glު9&3\>=^GqW Dǖ$S>^vuC)6AѶszƭ搕eFI ܍sq\H~ru1+)*p$d4xԬŗk8>T|37| Ӧj"7(qRmԍ~`b#1r>b#J.׋ t#Xc$Um!ɚ]"QUFJ+;^DY^f<>[p:T@B@֘]Np$({D9xZS{r]ެm#YfMvT`O]hi$%m8(I㍠ !.sןҪS$9r??hʫ 0Aj$[t?(-t|V(^[(X_{')HBO\us1@#22C!`rx,:6A^Nq}*Rmi컄Rb[r{sLP8ti m,JqS a2qm s7xgƉcEۂ#ӏ*p}SKb$# ݎwJV)pRA.S<ָ|L^*d qH ұD:B2Iscֹ#٥$o^_!S*e y` ed&Ho3jVX!:9 ӷ{ YePYsx ZrdQrnȣDqFY̙ܨzsj7) :jrOs%UR8><;u}yҰ-# ~X*I@{-m"rgiS^\3O%$;$ bkrY8.FqleHaww|.I$z+ ע6!]¶p Qmu^~VU]yZ7 gvr8Q+Aw5F$1]I-ĈA;^ `:\_dk-p7p~ zs]]ЅVr+z –U>PidB Sm1s.x[|F[(6Ɍm s>Ց$"{yaEVu;Tm=AV P6I豬I|pLpQdG˜u4ɗQ 5*R2w0Ocӓקj5Kyenα; C֢}XZ$U #4C|AhGmO޹ Ze`&Vf 7,s沵9Q?g$|<=)"lc- {7WZFdڪϗ` @?(9 ~jݺe(()$I#ixµnuHuS+~PQx9Ϩx& r e`1?À7H;$;@*C[#TThqB[T 9#VeŌr$㸍1,d+t$kgD($O^@Kp'fiZt=)*1< _B;]KH<*#mCr17+e%\](HXdPH<TbC c"6dESךu)ҵ W 3׮?'ҊnMiܿnF"Fo0;R~\;i& A#'ku Huns-āɕ!KNzp Q"]*\z }'.ir\գs[V1%3ebNy>gӊqu}&r:dc zm=?z=ҭVb6xB[ʞƤ^I#"l1=sӷ甜Eٜ[Z_ʮmB$D"2[81A#ANlF@>!,$;d_mAei_bnLsȧ&ĻEF~fP@c2x*-b)Z0v( z{C--;ݏT6?ΩVֱ.W(3yF89JI98[Td& n$y$Y1}8"&xZo=E#pꇃ:5i%˄"WcI"QW5̞\knmA+4k_5ʎIЌcޱn02C|ҥOJ.zm1ù'sW]+d)_ܜyxC#HrScNpuU] "7s_zנ!648bFIva3#8lXżSnse $HH7FkQfyGo"!vGob2~YVEY-&,l<FNOo:;C2b"¸U0OL`}jܒF`[9c"(Ug@]ZSV97F,ZGg?Pvl8 nگm/gQ<,dqdCgpoRO0LG3ש.Z=bWt[k yLӊ$yY$=~u]#'xgAnG8i~%<uy`}Ia[+Q0۞{d~*{exV }IgX#6gׇ4C @\m3Z*nH١F8~b9;9|I-(0J* 'JHeYOCy򥆪l*̞t' V n ޟ-[%J;}3^uwlByN0;xAOFT53}Xĥtt'wcu!-$Wz˝{kaa";fQm#܏½ WPCHLervcb~95皬\ȟ|*o*ͲA;psMZ84q97Cu[i@K9{U {wy ,B_^M' >WۆV:Vvv݈?w{µcS pCW 2+#[byp^UN2NiJ, NWR;FW +Kgӧ%2Zun$l`WY.~hZ<`ϰF[ZE1g >4jɵqWaQ4~u TpFߜ9ڊq;"Hf@nr1ɨ n;RII?y4al2;j2iJlv 8Y-s`\џjA$y;d~lO\CV-RR8$zN([hK(PsNLHۆ9\RZM;؝l*E.Z5$pHǧvIe-9 ]`8 s}?l)lZ #?Jc3H'P&Be2x1ǹ&i]/ĘIO$+imqF9+MBf)\a }-D¸PJ'Nk7YFJ V7.onF8Tl!l ֠?1wN(f,cleO{WCҶ+ U6F{C\ xˎwqZJmߨ8I,h 7pw`ʼf% +?GB=Ļ(v`݉.0n2X8ּJ07_QrSGQ2:b2m8?Gmv0ڱ=A>-d'nAOcV<&)ZZH5\dÑ  ,JFӊRȧ+2)P?||ǯOjb+v>Y(#J `@X]J1ju)IF-I^1OI73FI#5 fb 03cVIc#HU={zxdZ?йהL;vRû'CSX ׽2Rcǽw*U{6KzyJrGTs̱*x\Mbk6ۢv18 bUkٚӢ۹.n*HDh vO\qӡ";ˍVD{:OXKhՔx_P}kѼ!$5!UF}Q&+|ZԦH3VoP `1_&m܁lj gy.>pr>7~B:jg%J+YA.us1ϧZ)0s]&psӁISRmX˙AN; dQю9*r'q_ JI+z{D cP&A'˹HAb[)Fݣg!E3{di(Rs3~WIFňI;ayR*0xi 35-Iу鑖$RP0 Hb;})H1X))en@'=굲$2W2?>2E$qbXOr3!S Rӱn !jq}^I9 p{je92GSPuqV=jͨ{$\:K(E9,: E,O<]TAxyH9g$eD!GOQu ;?tu q m#c *ccWǸrM,CVi-ɸ ؞OR0v"`xv!gCo{=0 v>܎ޕHÛeūhRun[U)BNzvJm.cdq:PU?z(U-tS {~)u+J,~.wa\9rM nA1tgT"@0Yqgқ,j$|o$zcvj6c56ɴqb>[Ce^bjP,ȪHo̞=+~1[`.1G[`tRaQnr Cippr8#8uDO\UXϜM6RI74BÙO9t*1rK]乞})0>?*xn5f (!A'y<ϸZ#qKt9*$e; ʹպ׃\"s:(Fޚ#i0_ȹ\d8+$#*\F8S,8KxO B5xUXdM8&茫TmS$IpLm_ҮHRH0N dޟ:m;F р bxX[Al2VKz,\CT Ojc'; Td@ujisEc"Qjc#Up1ֺ)*?2jr" Jno8}t.L7> 5r%~ld+N T?eF>`qǷN{niI(3Z,xbpH?Sy,rNUqm'r_p:ҫ[.)IH`Ppwd7^3l`ܯ'%92yr1 huce#["FC;$ wqSY24F!)/>ӭV7w#iS7T0Qq$N i\)dr&+^_88T9Jg6"DðS0N5! srN+,E4-2"E"Hr :zej/70@R,~kH3 H'\ͬo^\4$#Ha߼ߑ'ΟK1b_ןsZ:Wq <:0 0==g/U "3:SA9kl62TV}\hA# ƭQ,=Z*̀LӎGm_duiP,񅐜g8T( Isfv'T*03ߞ. c'v\OL&XFTd{|8&cJ0W]xmbcFX.ֲn5vsc$&~I$z犳iIZOٳ4\랣 GR+ %S7Wj,:bv(-D.$WB @" ++ Ёǡ^%J|;H1?0:_zlQbKhϒHpBpd4*Ci$k<*(X)$` qr sj;byӵܮBxRKjc'RRI6̛PA⽴vH.V>*q9*]`/h*GԟJ}c-.ʹ4Ȑ2{NpC}博3'{EX"e >l;pVy CNpO >Z{4H*;V6#nq`?d[bZm vRI;Iɧ&3uc@52Õu8 ҳme$,6x5zv,I(P v,PASժt蚒V%ވS|x?]MEeNV"\g9{xfc)fi2t b[q㎣Zv$Ο3ldGk"8'y IfYonU^j}kWiux恖a2c*G^\I=?dX0V b#zR#̣÷)^\5savODcc ߂+yAi [f8\R#F?#l㝻l ]lw씩ĭ$ r ߡJ<W/2iqf!0$ArwqO^PuڰAzZ- xPB|Ÿ)U{jTw1|_ - @B((Fb4򒗔L #0;@YY% o, <`>,+qlLp>mŔH浣j9)壒0!UiÅ9>sY(C}IW,؊N~Qzԩf$M7+m,c֥B-%[ig3mH=xfhK:mxO# gdq+v=2b$ zqS7ȔڊF!.`̀>iO|J 6$,a gY#;*K!ۨ$ӡEgoH𑵉a'tBV9L )\skE8`V᷊vIܐ~ypUMꄝfP1YpjЃN7DJ6*3Q\KdKB9<=0QɬT\b݊CKq BHsF0FeW}Nsjwp)e'zb?\ϵE` YвI4<}սm]G7+*ksڪD`00HBv"$*d"eB=-z` G9H36U]B5-j,HJw0\v)s&_Ѧ[YE|m`wA;\ezv,6DEUU8 'B(t1Do/ (r~j SLƨl(0AQSNyb^eϦF$_8.0%saԒwmlʰ%#1#Կ)y'%fWm8X2zqFؠg%'do0H)Pq⳦e6լ%.a)DZi6zف-h~Y\ ϩjK _ it(.<ӹ5&h-FF*˒I$v߮xܠ} ۴frd psRj tX ^{ybͻ:.r8{VQw3 N$﫵Qr@6w =lA/#tҮdI7Do΅>U % 1H.* d"H-IVem kzhM^͛1Q %i".3gHҭgU!fʶ0rOG g?;| JQxxfBE-SͻZNvi&VB>Iz54rF/̏?vMS[$w{Hm ;G$eܜs`{jddm6 g>o=Rn'MY[lhH%b~rjjNiQ$E;w8V6}j,t 3mc°}9=8g bs[] $9(GޕkK޳>`1Dpc֛֑n\KHJO "9.xL {EsNIo)h;RK8Y / p:C^--w z?$!4WbXz\S'paWnO*9,1בX^q JNjSEijSe=-ЕJ{B1÷@? TGGr1)=1Ԛ[I̶LD+*23یʻ( 3 9HT{f*8QmG _4[ˆpPmsϕ7|o~2 X'd J qsR75qۛn[ ّJU  W`iⶂuYIQrs\B'pblmcR|ʭvUY6Ӱe{}i2PՏLHd"n88=+A9';ՎWzI,~qYGX,$dcǦ*rDʌ1,1Ar#g !3_X3ntڸJ%m';Cp_ºv H8tsB+rk*u=#jʒbJLiFdǧ]m`+y7?zsqƭ2K(1i1\;J\1cy{=RgouotZ@Y*ʾƻH.:c]mڸ@VD_D9m㰮cWX=_qߏrAع{򱐃kn[\G\aV;lTRSIr\`cZqH lū DJ-[(O,V9VLwl]~=:QMKRѲ$4T&c62rU6)T[^ބTq$4x`c?JS23ڰRrN&s7`j#3FӪ"L}OUȨHPOj;v\$p SR1igdFQb9Lˀ~j 6Fw֩$b7|9++F@VU9b'TyTLIQw(leUDhIҢpYqY^mwZieiHD2IH䲜U ?,j9"$6I"ra'\;w CReYA\wI#$J%NݣǦ^25,'=.o洊m*$;Kd^;U'؞V2"@# w3;mxl>)E+,wnA֧PJʓ܎Df1$vJ`zsB',r8E9')9^s,[`wPB3NJ kUHzr'nv5 j*fq5eG{Oyʜӳ%i+GF$}*de?zfPG.\}SC)I.Ms a ڣ<􍱙OC=hS@4*߱w1=0+Zta$M?ǓU]Ό1:Շ;YFM8G;.yt\8qsm֩E[:UH;QKn -o8S3cYGY2ʣ9SbMѥ9_rj&0p3L 7F[9¶KK;{n-+jm+u1$R&8y6Rij6ؿfURIF#IW B/,Fѷ=*—rItҮ]Uͷ$O$Pq.2zc%K&2w>Ƭ052Tgp:dXՊVE 9<5K"4X|$qK9gX0OXlw?Q7埖4YĒ_ zM9J`^`aPGB?~yҒ;0H0 op=> yTSRtFWlcnAq}Rʱ̻cX< @3VVe>*MŵyݑMKx1 'q֝KgrX J ,q7v_$~X5V9vQ! Wr? u%NZ#D)ZI|8):{JFʌl&$sOrgi̋f$ Ɩ5R5o$*He'b` }j*ETzRl:(VI 1V0. ГrPj.BhncVf,,zWx5Z rkYsF3)$*Ae V @ҲkP4S.HOc}+@ȑtV>f=>ъI\ʝWfY6T䌎q?Zc^Gl$V͵2NqެT"\f^뚠mbDBYx$Nj+ ' CHxyi mUlQ*Xl_. O~-"4bC6Hpjet/Qya[]0Xc\Myk.tQ,^`i;3W6GG8G<֙+MIFN\=zrOM-(d,(xJ=}t-#kAdd mk32w`ztjͧBatI*J.ؙ[]SMK%%gQ 蠑c~}ml'6`)Sp؊8|r1 z锡*Ojog#0 /Gm%6 72e\9{zUh.!W$rOMxIvóN42A2H(ñFnQjҊXdFdFU`ێ>}]xSV"qN#wp10y-B,{ `& X_rJ.#*ʨzӊrL9go60 @FF3Ԃ2 'Mҭd|7$}݁$>#bt?/_\6=꾅G-*C$Ss|Z.W%"2kmII"Xl:1>kz,V!q,7Fc܂?-cNH$`21MiMB ̰\ϩYnb&X : lKQUr Unt5. 'ݤ5\]YÏ-q-В0=Mq8YYrUճ[u2H*9mɬMVI{o6L[ Fr3MzT^BV3*e #r,_18 qǽl](DD)ߩ̭^Hm;yɼی8"<fcg'<ǵm=DDeUH;'<֠YC 61@=sҝh?QVӺ-*rU1?_ƲmV#? i(7rrx'~w0!OKN H; ":=*Qb)-n (bH\oB<~\StDDuO0$PT1$^Gi7q[]L# ܜ! Ğ}VO,) 02O<}~IR^;j}h4C!%~sʃOr=0zՅSur(EUQoCJi$3rk%eYT̤#FNr{sXڍE5vWte-v$B6b#bT:bbU'ʤܒ0rmkDbT ]N2q TNR@ dz) g5oHs5HcO0a's|źd`_ڮ˰b0{Ry IXގ$a7LNTp)$'Mت;} ĭeݿ#;$OlO%Uev؛BxR 9"d&A"?6ȧ9'/u** oq [x*`9cӺvPKeGD@7r۳׌1FKTEn8ܪ7S>R#@gkA/I#Ӕ?eimT= 23 yG5lb("( 8>c 4Sw]Jq>p*V[xH_2`Σݟj쮞w:kydYZͺ,ُ'd=+_Ry<09h0 @k=6x! DO WK1-MXy06r;V2mM[^d5`[WX/\` 6`u88z8SȒ@QPF:qyu=Giٗ'*I>x~U%$ (!W9$`qM[Y_Zvw{&g3`Fx}}EI+Q!{\_.rꃪ0W]-Vz~6nO9榵II^~c^xhYKHY7)ev5'okf&,:R 9'wqO2K6 2fǩ?(_jr>Fe$e=(kRv)ў5FX `GVP3=jvQ!|r*qi6\/E0,9'o5h2Guۢ#iw K^BH\g#esm Akm4fݼDͷk%X7fD)f<dִwfH#ktޮe(0T4ݮb3LXeu# UG.N! 5iKQNN}jI8 "+$~caRCƫxmBUG̩ tOLqPxhw21׀iКq$]6)D6r7C XְI0i`0=ϾqڬI`g%p (͏S ~FHbݵw:z֪J1'޹6,YY"[rs'˔UnB `6v˾fN|%zZ:hb]$ t\lD3lgyHoRn?Tng6MY!hC 8'$w/YsnwPa;6V)99N3']Ee}k3Ne/O_j'ջRI)h`dE\Z6dq0?;J\t2 ƳBeLsކ̤4ЕURzqsҸ)OY-5.~'Q{*[<3+ ob@sEq"̸xG1Fb w Mj 2ܼi+nAI=+N);<|0p2$Eb[ڬ>,I ;qS\j|Q 2=F{RKyOخ)ʜ撟&N2VKs%t`KʱC<G$4$4H #!O9 jsd9y9$o4*u4M@ A^'wG)m7>mϏ.""߶A.-L%Ă)7qgTvM-Sss8YJ0wОKGrGY Kء)8=N>Xv,Ȋ HS=zFo.WK*8qttC9uc5Idjv#DUTR6c?ϓ[ϕ S"*>AA8X`ځ!o1}j4G/{CaBnԀ8;g˜1ҠSc`^[Z¥F>iQ$ROa+>c$mDBn+IˉZ$qm<ՒBnG,26JH|;vn6Z77 ,sEǽ%RI n3W>팉P!Qlg=H"R0s%HI 28NKwpBB6q:~|bϑE6KҾɣB,aQ+G#&M8=}*XdYPI6OzC䚿F [G1QAz Fxf}8yܓ OcFr:Ֆif$B<<4'ʈH ՗TghV~Vls%fw.c2BX0r O8q~Ur5 PZ!n.Oc5a\Aڵ\uœvy}K+y9H\I,7.y\Sƥ;!i5gS2hvP 緮*ǴA3. "zx*ӧ(ݏ!qUz+0rO֞"_}TN3ޗQOGhꆐNJU$ĚD6Go1~SZMw@p?3luB# ϥ.\+Ө:`W >;U̹?Je]8zITid+XQLW`O.HY 8"7:Rk_. 8ULd/M$߶=?)͒6=zR0iZ͒+cp}qA|1Ӧ(FbPEc7=UUGo;:I'bj1 "v=3QWޣ,ވLR.A23|qnsS1NM X#ڳeyU^7dAɮF".J2rvE3KVX",y~Gn'km(\P p9''z+qrB[ǽ$˳9n6:\i5&eoj |pcG?kXp%đf2T#$hۘBbS8e8ϰ=u ܝLMd3t% ͝rQ\O ح%`A>bǵ46Y{bS勻f*"W\)b̽0yPgx@=}S P=EE_اbg ̱ʳ9ԁ (WL*RnrcgSs~^j|e*MrianItNmW@AvG|Oi^(^ (S@i7ٜʮl`sJ.O'4#LW̬_ZAG? 9p:sXΓ[ǘ @9ҵU.=QMwRndaZ0%iJ\Ӗ%v0G=d "Ќ V5Nd_}$>ƶ \n°;ۚJKJO5Έa8jUQ$UibI$B7֧BoCm PrTJaAgl` )-'T!$Y.\aw^1Ӧ9%tD zZšVU`kM f(c!w24*{#'vr9ڂNJH=r2ViߒI8\=]K riʒFeo__(l#/0A"f$n#rJ c Ki".(<>/gbZfaj#9qn 9IFHwٸDZс~]r9+$[ML%F9A3arG~b۔{\$spO#ATY!E.0r>@H?(NEd`ۏ=8^t%Ͷ/o*&8^" VD,$dlϠhUubF]_^֠ i7j$(Z҂7cQ5,+ik\+F}\\ְ($lPn=rxzٍ#M879:|I%"?fײkU=ibu}P3oHmdmNSֹǴo㹐4Vf@U3B:z⵨kFmKf&Ȁ qUtט"0[=+yNr0zWLg m$g<-&m=늊$R0ANE(ZVL7r n#bYdlg#<YֹpqpW8ҩA!{o89#V8;Rŷ%ӯSkZQlN9-)F+ W۸|޶$lB0yS=~q2/\>,Co!"scP-DK`ڥW*zM^3hpF>xw1`brX؞GR\I&h¨#Y0?0 c~k&R ) 3yZ }IֻK ΨOi̪?w͎NΪ7m&<5$_+ Ns|v]UOnt5i^Ep۰q5udF=,4̫kPXĄ$&1lՐ1ٜ@fbw ż͓~-( `3u>4r lA"0L'Rq9hɑ0-ƴD"yvEkmxD@ 8!2YH=z+*{1H2Ny`ur:D @%g'6ֶYBԩSCRy1W#XɍU5FYgڛO}R/t t7_ڽz7K4\O&bvX3jA?^+_XgUbnMTmoIq38J=qN5yס6bObS|?^gLxf a󦄼*B6*B(6']8)sOx& >a($Ly0ĒsC[{ u?OƤaCe'C*OMJͧ_R$W ~_%Ikc,QN;!݌r}Τ}8+?k6{5D 9lciG}a4X;8-b3 $Fw.$[@r2LEKH0HbJyfyf# @uѤe*OcҴ sƮ1 ׷4*Z͗O;Z%7"SV[TgѰ=}5V(cKTx}jӀQ$"0rO9Qn9a&b7o%S} );jj4;?*=qڦǔg:R>*V =҉~Wd}1ϸ.Jl$R+D{cp BQ]My8X"]Glm;lRH@%I#=?)J]t59NYIErNWrzwun(QUSF^@i e+>)Ֆ^mAɸP)ʁqGWwim|! ssr{6r,% $81yI1;p=21?pSZ+_ϹZNQ{ ;XIE `Xs]aZX0[On̉ )"+# {viGf7c{vxTPG#ifj)Yck`v)Cv`A qZ#"EtېI1$|4 e' Uifg͝Hy<Գs,Mnbg` v4*J"T R )Mk$NMhLSvYe2#HU0;U*2B[3Ion#Y@}f{q$Q YpX 8anI>ި_^Mm]a \;1r sP$NMyG[A$Quwρ@֫E>TJ;G2H*:`so(`T g=nW'du"黭..\yQ.%F3wq׊G$$r6;|AqIc@G(0m,@۴3 {`u3l2e7.%p[F#WR-FɲYn`9d }2 ϦMj5kk!0)+ǂ> 8$Vim#2H%KHi<eʤHyuS8UNJ6A$k>bEKW9oW-G$pzVjc-ȢUscnX}7} dN ̍|rc#IFΛ3pypp?DOA]6iқ$I;[p d pnkjSec 0H#z s@?Sr;hIy m$hB`f`Gc֕)ڕ.C/#m-sqpQ1c`v)b#'9AWߛC!DB:0:}Ty?L* q'w@X~~݅(󫖬mIGq4HUO*NG50YalwYrr:U6O.Y8n,gk/wsZr49xYC:qg3ODTb99ⴒq" `۠-V?cl] u$f*&F `85nCYHۈ~4oFRc"I$I&ւ{9e*8`F$vM3z4 |yHDkץg֟ms;r=9 9<ٮ#ٹؐ `)ԄTzgرsL(ZI$Q4vf2 x;r: t5S&٠-Q e$+ZX]B&K[aD7Iϸu5gO8ďw0p8=rLaɿR5GNI61/w}=^h E*W3bZ1q4FR[q Հ̋ II2TA5+>u2b#VwKۆ1ۑ\5r|"\4lUcx#%x5U$y"9D($qǽsr[3GnlgpN{{]MČ_1 TvCG”/ ӯmE`w$n`:#]N[XahLv?w]W嵚m3" m*yr3oDdihWkh##)yprwm#Ue+e-QŁ4m@ UvVd[V_2C0{{+2mFbN;?⋥Fv.cHᲒL3do9!zdV2$m4+mrt$R.-i$xfHc?xrG5&.e҈cTw'uzh$ԮlzԺE-$&ܫv;W&84& GQAs}lHmgYʾHU {`+ YYm& ńR0;p7mV=skY…Y$l$a#eGogss+$N6#+LN)oKt Q2PnxVU$]%5E-&F] rPryZJ%TBLY3'h~yO,L^ OXH$ّZ78QӸ q\rm+FgG$hXm WiO+PQh˪ :j>q<9vڀ秶j[۫y>?G>rKw]Tzz)N k7RYU N;CR@e@e Uc_[s$ȁWS}ZDžF<0xcQҺ֩jD,dRӐtHcqqVYO5 TnB Fj24bq5A;rgמ#^G i-ҤHU=ӀҴ^hA-Kju5̗y,Եi Y,qK[,V3AʜRpms2bΑpݠQ4r x 3nz!4y09snk1%VGRdRX2wU|oĬU& xϩ?ҨӘo)|-Sy.Z9"ʤ+ǨY;7ДHdpCuG]85NUX OYH#'y?>x6>u?ҞbAp]~sz\gjQzGrH72U0C)S`6mm-$+ NIeOn)l}7o4#SǩOi2$Xb˷<"-,E:h aNߔ f#޳A tHj[ R$;Y9 I8c٪iKs1~q;pzTCdַ-cȬc^AXZDw%Iᩖp>R'UPۼcbݝQ6yUckng{̍+2+ x?#Sj&_3F.gH6~4պ ͽ fHI(Wl%q&wuݚ*\"I!x%ߎǧ'}9- ğߠS\3۱P6G \"4#3zFU2XHd٨!)^Ȏ.FӽNzdXm#<ׯiR#OS.IERn&3.9lcpBx^?,p1b>:uJeue qPjrhD?|XEM˞3U%)+]o*2K2[BRxY8Oץ*GTE$>]u2qmeRw@#XXEh+99SLҝ&yiKsK.m!-#Æ=T0#*۸WZVK&!G&.y9bQ&kgkAH_1Q޸1,mA]VQF|;TqȆ |zՖF'l%f2I>uW\GAߩU':qq$&F9Rcr?LԶ9Xu QRL Tn>aִIȖ(Ѳ#a#~uVEt,J.3Wz⢲݂BzDέXDbXB9TŶ-U~\0⮽8ӊiLn$nP9RJ!BrVYvFU8l튘`/xAQN='&},9Ef,"M8Jtqpӱ J[޷*^mȔ 3x {695`сAv`{Փ>W̥$oa,ݞ!&CRӡR R@1Aa3rH󃓜_<oہ7 S*PZY ?5:UMތWcqMqrK(d.\gZx$D!YL[c2Ge`$F6j lBswSX}OٰWnHX4s#h+S׏ҫ-R:'+F88F%QF9n'jHavO8?UE*QM9J&Q᐀I[LzTh|89=%V6G4La?+F>GݸJB#HZXy+z里7Ak fcرqԼ}zPB/^; nܺr1ڦ0䊊ߪ׶҉r3\8f^0H` sF[h3P Fk|#jsMZ_"LĖn8I4TfT 2zQ UH$UMЕcӂ{k37d@nb^2oF>Mq;ds @brKWWpm#oI d1aOcګE66L60~Uf'֢URi\% 1[ [ty뚾$C#Mwlu 'ޞNRK}N|\ϔҲF(%{W 3zacP:VD|:TW,XUFrиY |3`E#𩗮IE5j᳎@wI*I`H5Oё뻞?:O, rsҫ*I9''<2`R1#jHNS)ŭtao~cqTH\n;I횒1GCP90F} 7;rDb]ʣrmzrnq sUl6Ā96 Q#;1_A>*)%K2$F?1mw#!M`UL mI'F1zӢmvhv`OLgBՄJ&p7qHRrQۆX'< 8cJ>)句tP6ِӛM&@xprT9c s$p}"WJ.Fvu_ c =;k ѵ޻AY6[k% r眂Yy݂gc+ Z웱y8Զpg fW;alnr~l:T&Q:^dSۼ ¹nׂUmV%wH ,TB"?AtH+9A9G>d `YԫNPqNAcuػ̬ Nprq>-&2 ff#\KeiJx=sV#b`r8=Z:SN:"NFΨ{׮N$$$U6$`z;+#s&yc=Sɭ_rH9~4;S6"vCgD6Uarr{jUvQz62;д6>aq$* ϸ9yc숻w8#9?-YT5@K^u)wHqQvG=[Ԝ9ǥeHԝ(zЂys[V1=Ȫ-3Xf FB"@#731ZԢQ+'tg4%bd oNn!B#R:)Ѭ wKWxw%nF3+\uR!/{,XZF[ʗ5s`#Ց= `6̊H=1^9Q5SWf~h-f0`0=3U]mr}9Ļc-&pBdW2'5R6ITQYioPJ<ÏÚ7+o: <ǩU Ѣkdv9اkJzъ9!;7c,q2$6 +o8z \)LI,L 6<`5gjD9 d}yҪ+TcU9ٗp^yIcS!H `A>T KI6M|G2q;ֈ pK?=0hcj訉ͷ==Ȭb־j3שjͤcb+nvc7 g8U 6\22]FO9U)nH{N0Gv4z x2ጣpH#^M2Qշ)Y[(pN<LH]ePkyDqwzc5 D⵾l4ޛfpE9?8e\@$oQd 'jζu h% E㈰e<@[(n< /v'+bc+S9 tSD+Ʋ/CH؞=sPF[gbey-usLZE= 8][wPl|c zHN0+f6lcQ3xݿ4@ǎ~ԋ冃M=^wKhT!fm.xt_AQEUw,NXo65F|31ẃJ|Ihq|VZYn[Wn@=`31X{ 6w9]{{TG#p2;=jTH[q˶/*O݌7C\GYqFY RrTq g*8< Hʳ!;gor[V͜ђpљ>G -s㏭Z{"wGʙHݑڳⴑnWPhvB=2G?-Z)FJ9y/V2]rqNOr*yxpE4!\ c3x>a%4Ke;h7W%r&pz;eUKrYoK {h䑛ܹ[0y?gibHv+aGwL㚻5+5+SMN:הZ\JF](Iյoj.FW%\0#5?Hʌܯ =w_D(G%JXݑz` ~4]{F+5Ŵ0?*:>GQ&Ώ!eJ)۸sߡz֥".r6>s." 38"Q=Ab 9Uu.:oWYU$/RHgLfM@ xl]vwOY4"#eq1W< ܎F)c'2bthwmdQ A< ־74 Klpy=@$HVG'ynF@[v+Ӹ|(`(JJVFTjjzC^K(ȡ<\`ad0ǭ ov+!WcYr|F[ }y !<4n|gⴥ!6m+4 #~FyMjE1Gn>ʪjK* HFqSn2TlFKe܎qOLV=`a̓s*PZfDt+pb3J->ynB\2OOAUtJ҂5u-ؖ[]cy'*>eaxTy}۵8l9 㓮UN r 5HOI/fJv 7޴=Ay9|/e%$?/QJi$x+ӣGjW+F,jAhR`#$;>թi}SHWwgr3f[_jF6; 1F 0r*IWQŬE&†S\ZƬjK_]m. FTςI+jkmm$xIYVጐ;Uks1ZTf+`((fQ WsjыV`U_ʰSH^jbmAX3Sb6q1)*  />o rZuG {}#zVwxc'FF{?Z59mVA#v2\0#'箋VWtjǵ( vx_-;l_͌R, " Sps&(-p7xFʪPrqϭdhY;+W&>SqĖi^F2] jˣ28I!ΕF c-5qV~_eYd!nR[q qj(lLc]M!Om\Q&9^`F sʵⷎ+Hn^3%$VMzڱGeߓ'E8|:ui%9KU;B…Qb?޳IĤ}*F0Y>c6Kv$|*ĂRq$l2ro8 aNK6]FvTr*BC~e<ʤ򧻖_.Ue{"DPޠ)QRj)rC="mVepC6I<ʒHo9d%W`@_B0!Aa RW!>y^NhRZn/fӻcC2;ULlV8H:~u`T (>$zEc""_+M"hȝdbN8\ʋi$hl`,23jAUAX0Gn# rsޡ P0~Jl%e楩#;1}b0[wLlm.BT/,=tZoZ#j#_bhR>b/MHd 嘸2 q47j,VάD& >an#򬧞w&FsjFR/4Q' &`Wo2M˒q}*{;VhiR6R$ cڥԭŸW8SQ[96ӌgxhƻ}]5)\#)P`1fϡIJY:[GS;c)qҥ2ݮ׌G7V ͕o8Hz~^jfu}4*_E1H8Psۿ+0KwAy\GLu5ꑡ-y=H%&+b-@ֲF&;)-@Gaª}iݹFJڒѕ^FYT#*1ڊ+v9eҊLZ{^0z@OdW)m͹H)Hq\5Zv9n=>XR'F1 pd#!ˑp)ϿOҬ@! JaӾqie%wl zzN{ɷ%B $`d0'*x9TvrU;no]%16@lv51tNh)T⑅!eT6랧ӥZO1Qwcvj2<d^*IY+\3b8$t?B,%qNazG`w:qM]ɷ8OL~%.{t0P[qKq0sɻ'f:Zv;+8; ~u:UH?v[v"u!SЂ`t,h<󊺵'RZ%+F@P?Fx#6hԜ{ӑHⲧ twW#e y9n<7Áג3R@6Qr&RD`e@%\9DR { 0a8%&sӞiI8Q95 5|BQŒn9ďrN+&Vf+iYN{MmH4Aɉd~Ux"S-+6ܜgW7*RJѿu*k]U1N͌8P[N 皵ywAw/F#To^n[(Rqa*m|0'F:tIJ7吓 $v6pb$p?YYYD?ȨEʧ$u9sPMUxRO4!%`sS"0!=:WR术e%ܓNs\j%9n8sO<)$+^h;_C6'8Mf+4&a#/WvrԲHCJ|VmY`qDX ZU)4 FP国{=$mn?0ԶK0H(Pq֝ǯ9OVqiB up(9 Kvl=~ H>n]~#OKus_`~|N۸9sjͣ `iRu|fczu50 AxҐ~zzԐ%E\|>j*˞1djWֲXuӕ.a st$c*z@UG!zA@ N;v4%)6PH9> =`8^*zۀM >S4[$lX`dZY-imwqt+H` IG9Eu9'Ԗi䳹nfm玜Be;O͜z`jII#`r2I gx_iSFCH#el҉bR 唁 7Vh̷ m;x\~~LN>n/|$9M^E O?NǃU~,wie N>Vo1Ո628?\Vѩ>_2gxb( VAnvJ&dee%s«EǸl(z:UIŊl{cR圠L]̛JuA,;qJQi/K4J">{VDu̓BX]QJv9Od'`y?ΰ7Mh:PÎ>^n& h\x*(Q9LQ x냏ֺԽת9ZZb3Ed9g+8G.edٍ`Ijc[@dmc(3בV$l%[q^0r?ʰۓlNsr[=;:D+H܌{re`qTZ|#XT*8Tk5̭˖v/ɻR8T8ٛ85eyGn7c+ɺ)F ' rÃ~fQȚJR.iÅhJ r:;VG#g8ɍxU$9ldspy>g }W~?ޥH=K4Nl ~\' x°]:+dRek7)O6Ҵ\RIUdUenGAҗZYax]va%*Tfެi$:$RTMǥa/19?{5"*Aw[u.!Y2+U`q {Q\D@;|$mp9yN*I5KstCe@ݟIZin BmZ=s8j)u6,͸.+Ph2]d8 vֱOt[ ӯN.$FEp!fz5bʹ^:~,\THWP*E@E`pwn$G[c_ӿ$\Tzjr>mY X2$l8jطAxUidLÀFrb_:8<<:Tml){F+ݙ(rU<bgc K8 =FX?A@s]P 1sjm>!;JK4Su\ھM)r>Y %H[h402FGSב:Vnn` ̒L:|y 폭Q"u6|?r2cc,$ܬ@;|q{ \Yw!\prNzjV%*H !#gd玤sVt!bB@ ; mp1{${}F9⚄6DNSBVa!݂2;zy55܆;uY:ȼF u9T9(6]f/]k{Da//uQKMaYX\Os~KƝQ#@ kF/"nL#Bpx=sӊ{e##|3֥u).mL\,ńx!aLzr݆B>♫<;4rW> DQ?73k eźm(>v4VJ>QiO Ijo5TW0PL"jgv9wTK#"IUF3^zU ^T(RSN磭5,BǯOZ,R)A/'gn٬ ;ˋk,Ue*d3z1= _Kd9xX23#S9Z[FU[Hr$dR%vGӬeKpeq$⥴-WƠ #E9}SCuxh}Aq׽vC*2SNۅc;Fާ9m{Er-}pGOQX<0}1h^ghřX0WL}+9֩V/T}L}JeŬ$!݆cSi!/c=G, fk9ayG%FONϦ s*`?g( ~_hj2ܶS<˺iwp}Ho+5On'b "nʽAf'Oy|$*!.Pp{ڈ(Se.=Y)''Ǹַ)$#%*B@@܁$\@e=08dPG%QW$أm;z'd5;obk5JN Em o6iLLȮIʐq@s&[M [J$p3wmV\:/$9~bk6G'$v;R-cvЩ{n|ݒW=lުIr, %9$=@=WkR{ekyHn,uon. pb;2dmc#5rݯwCA+ܼq1ܠ6?Zf Xʳ;RyQ0iDގ ,T1= )4I,-9fTaz٭}*-lG۹Xx,[^0JŒ910'Yyxep˼rH~\M*꬝yV6jnsi~f09 x⤼yMCytS<)4-w5ǜI#e(O9֪Omde Fm=S6*WE_1#]B3: 9$tZn.^\+Ƿ`t͑ұ>bf!, ,;q:}H9so*ss85yZPrZ/v3뛡$_|@{dfa4loyj5C)|[̂\!$7a[4Y3$͓7n:RTЦZn:A51j-Qj##VZEȶEs(-{9#Ȭ!;QAn1<Ԑj2qHr'w@x#8M)] q+(v('G@ 늫.v( 0HbC-.gD* ː@9=N+7Qbƞ`pX: .r2ڊeU*c+ ϔ Q"3Jn$V4K8.m(ZD]b S=5͕ư-%r7Ҵz͸bͦVYb_Xgv>cI[8'iFVRx.u8mԍ!CGV.!]RXN}%Y-qHwzsKs6vIUCG5I&Hv\·!W 3]{X_1HQOP@8z!Nٺf2 r\$"7 "p$pqעpDITّ@-I3K4DX'ܮ⺹EϚЯ(3Gc޲U,osZqV:Yc99:a 3! bI㞽;U=='egq;ۭZk-g%~f1\sጮyũsZFB9;i (e6Y_&Klw*sϊ$eF"s! aH'52Irݘ!6өAh,pNA=O˨ f(ʂ~vNVέqZ$4) ୑=:DvCTHpp,䃅\zֻⒺ0QطdmB/zX8!NtRĐw hdʛyP4˘) 19#nT\1u6C'˶4#9$Sny*1ISIqK ÷qUY"^$تqaN~ovZ6`$s%B8?7K`$vh:Ƿs@9IǞҲ9}OT%{GFORJ|h;doI'9~5 g.R61c'tDQC#6 3=xgOMW3%}2P4eV ~=jIVi-I\VnV6+ksI*OYWWMd AO\7s}KVNȢ@:0B?#tbG-X2Hd |lq3O2 ĹC1#s:{wL$xVG#$$~`~uh-401b(O%c O_i9OG5ٺ'K)prO;`ciFi6 ]q>|Ac4|?U&HIc9 BIt;)gs)jq24J]xetDk3?QEs*Ge#U?5^g8 ק[Blć*JLG7dsdUmI\s[Rj(_Rݪ[00|gNhᕼW*|<,qDB^kӯbHJuAh A4 bTU\'`ηRF(.1Ga5+[w#M#68h^] t Ifd![VijH!B!?rfy(FH51e`v/?^%~eKKDldf\vLqd b\qZdbe?a:qCPm2iFݯz "{.07UWiUhaIm $=o˕-ЎnWs7p;֥2]S7J˶hٚ2۳=1VڄTny<j¬,: W9ɠo+0|sQ͒QnΪ!GA]5Ņ #9hy]fڪ02̧>_ID+u#o_ESRT[x!&BL%g?uob,*'Zw('>dtS:{WJŸ%nr?z@G26Y*Q뎟ʛt|i?xd:J@œe޳RjU-o#EX=EfqMx"]e7 O89]0չU` j$m f.IWmjΛ+$lj{m J\*R=@QYg+OƼTiiJl}ΧIlEPByi, `LnvەMr7W77yX2rwF sd 8b aI0>cp9=ݝbnr pSg| |@_'֞ xe$g*}&0sI SLv;=)06֜P"(GJw_ PVL`9ϾjDgj墮E-dhc#♴x8ή7N}jwl# E?h]pE+iRxQIvqMzLuzJl@ ڄjzcq^m%7-{*k:;Gӑ<@b :["x+r1qƻ-9MrOB'##ԩ03)n8QpG$cp`r%jpn.hUHш'h*D` ҏQ[i(HVOQ#<{I_;G9 122ǾxLCʎ#~3~bAȥ|ȌdG/386~RC#Vfct`ӯN,*䰑AV~PڣgU~rC8U*Ԕ)ϛK-ȇncuΙmn$x fO.ᶤgaO3{UawATvTzgL[ s:`*!pPis?m )b[$0?:_%X:p/מFV1zc)$ ņqiQXǘZR;/ZؕrOLqG$ph|Vz5"]ޗk=;ۓ恙Cd7vV1A Dޣg nLyWbp?SN q#qߚJ* H]=o+ 9QNON2$y^LλN Vj~twrm2>pW$2A>dEܐT('?MGIb*aBt\f)8R娚jLXv k3WwDCiqLb7q~+EA#;PqVsT W#?|B8Saﯩtbr4,{w'i<۽L% !W wim9Nq<ֱO9FsALt#4[y"mjd\{jf6@ $d~<{݋yJi${U:ynGe גyGahSVQqx#hNݮ!*aP* S'~`N:w굹j"!$V3UȬJ˷o ,IWP=1"yU9vdg$΍wiv q>*ՌG-ʾbk-U^JݍN1ImJHN1Ig }0*,i :g0w16Zq~Q[tVVEbFAp9uqx= S[e跠w1U꾘?wu}m*7- V/sxB3sNwy;i : lu!U/=g4Asoo'c2d:c<Ϊ08*R 2'?/AE&՛0\9mBaKgA3lAg'RsNӦ0G,"aib@9%r$o#6獰q'=B(^0Z!0aI}qXͩIG0b "/IdfB7A+'Ӛ~mY?\ ,1qֲZ[JQu #+r1-Dֶ-4f<(,Sϱ3]R]X@E69ua]*^}.B8p:--k{xeB3ϱ9L6m폯~B6LGKS 3ᷳdۂ׃] RB 3@@A'Ilp{R[\GrcfR6#}FK}y `f=Y<n!y >^i+;ob[YyYȍeobI c:vV\J"_&8z0=s*'х3" f߆8M՜+_Vf qʐOW4e-ОTߺD> Eylm ;" /8*ĥé ѨHDeVtԑ ]ǘRF17RӮ<RIW u'ZrX%BAw[b'; ?\[kDHSH~Uzu]R_9$9}({d6kNI:Ta'c$\X^ IdvBB.[h*:󚱣X\H$mؿCaxF%s$v(u͌zZ!=|'VTpF3u\f!dGH \L8$n}f^=1Y4kYZ2SpUOκVx("G 񌑏 ;ҵL_Rv ,ݽNqD+qkGk149D LGG[7s_,V~EIvw\y\zUv6KkHVX#ZzvsAoxU tU. q6+nr 1}7&WN门ͨ=ʹЙ%E23ӶsªHu -`w.~g'uYZXjrn'R,sڑym*T5]<'qL#-(XHL`[O[UxH@1>lZٳI%k'ķѼ,~WQ'>ne[C5 "(+J r|th8X&1! _q$O~V΅c<8S .UMΤ$N~??"XC D;Ȓ1-,$%lA\n6}I^D3lR+(~N[ޖ;.X.W YOz5]c-!+!f 3sUqTTw1% NjtLIM ({OsYY@f{y-x Tݐjܗ(,y# d Jlm{]K 2Bb5"UC&(;լ$APUl *'$)ۘ-.)\t? nJ}HAmrr12*;kw0+KL^Am0yȆ8 Ger탊%HaRs6c d֚CDIꍻ?2ni"f #%H8V5VK`]ZE 0A +˓Iym"KrH_.A!{8ʥx/wXaG _v#YVFvuf2Nv^6K- jGA\~z~a=P}eTKDہOʳwV'FbѝO.?Ʃi/$F6!9GnȟHV}xBC'y$cf2wQz5|>\oaOke%qo`\]j!K']%3/ݐ Uwq7 ޣ Gl*H"s0#WogCݙKH2sr1Ys$rZQf.k'JwC#cv#}ݤ՛X\fjeE$0s5 -z 3K*jU_-FRyݒIe ~ 7gwhztrAI#|TPǐRG!;=HiCJ"Q9?κ6hR$T׭r~֠4A=koOiȝhbT0?ki1eG]RZfe v ze"HZ6]ד߽m,m$HX 㝹W ŪFIl54Tk⮗DD@!= 5PVud`G?MX>Uɸx v$T:rl&@1z͙J6fCɊ 0߸c%W;/Lv&I$A1=qEA2=TbKV]98D&K9 p`ݜfar{T׾LL@pIʬ.MUC8>RjMJ'3w[9| $;WS ylrsE_]^۟Z 'cӡӿ$MyTd$ӗ-'׷'SHcs#u\16%\m]cFj&]UʩYK)h 짐~z!@ ޮmTVQtݙ[{ RXߍGx-DyюZT,8f1S )ՏbQޯ9\6R'KcC5-&eBg<߱W*YpH'5hŭR6Ev @d j2`EaV<`.FqQZ,8gc,L֎|Z/(Agv80 vnexmA5NUMx$g^ժZ=%\#9u Ȼd`}1VԎĜm1޺q8T FZo-TɁ-'W#ǯAM`sVmيH}: qWFRP gߧGkU rB/?M1e?& Nc8st=I Q S[k1㱠zb$珦jyUwʣg 8%O#ґ dipzQϴ28=@?pku.xҽ!Rsϥ!fUɧ$I%R>fⷔU[ j.⪼gnZC oPC\np8ϵnD%#޳ l-݊GSŋú:)RVٙvW5Ԓ21ظb{uoa ym{thf[u$&ջXʵfۉ=F8 :Z9ICpzV*3rZ¼}ܯsI|)cqR5 ΙA_yA8Cޓ7 T[@9"u$_@j«|ğ™$1eP@^{p\PF@OEs;[ )F9'ZXŸKn摧wd-V8^s?*[P^pퟺ{_ƫpZ7 Di >8 pXW ȹSܹ-n`sw#y]p:INߑnڤlB*qZΣM!.]%U(sY9A9V1Ps'X%`F #!2ȨnYp**NN\iMY\*oOƇ-X2J0?N{ӢQ2@'9zG!e@K< T"c aձyx9<U#ieDbDZٖ"\xe=)v>1R@UcTF2Ӛ8)G_&mC30W]/qE> DH'eH"`~]d`iep:~y?aVf2bք8 P1ӵTZ/ F:8ɫp6AigU9&AVI rqC0'<&IfIՌ!-[F# >IbzԊ~Rrj~zF|SFG5l*< уqgJ76:+ddl 69PV&GoݸvE䲀x­@R۲ɍVU둑W=z5 #H4.®>2q$Ԛs"opv?ΞЎKwq; G4 zS#8 q[ZޥIV00:g? [)]Lg(_(֘qEw}}*0䋔o67u;U9UN3Υ*n¨qJXmݻ =*dXlo1ҳOJ21y~86t"g_Z!h<>= 859IŮ[Ú22鑫p{Tm0XHq'ZF\Fp3UKzDPAǓbѩ1a4ԛC(U]$ Bgd HŇ'5o,Nn&Y.dE\1f}(1h5#~ nH^v/vnPn#k=9dU&hѱv#9ӊ4)cM=JF[q]Z[OwCP <3U%GVCJ)Xs']2mwSK!&Sr$8PBFaER%;ԫ`rkRU-<W%)xIpSe5 ZY:-[ -; lzg[Il1}*w2R;* 뎵J+sB'̚O*W`T!q*D/0}N? Kf)>[2>_Ҭ;a0OOҸJK0쥊{w"+y4VNBHYI$360},:֦U @A.q{8ηE[l}#?ZƓjn]HFFfP|ʱlLt+tvˀ} %'Ni4SR(bS825yTmETN}GN(7|񙐀w I=y$}AA'R,}-`Ī^1{}OMe;EۋwH@~uFtnVVNO989D|WH8=MjXFU+sdIdVݷozTQ97AŽo%Ӂf݇W89*Z0N;uʜ[w)kQLks F yEGR F=r ][6g94nN``;\D/U!؎s\Ҕew;vAAHqJeLgN ^YLMq088= }k2ťOu1$q>Ab_qy'>*GwV2|_b@+|*}&8rOq77 v}1rD'8ƒU*@8GN*[b h8}G3]e^rsԜAN*-i@^4{zW%(/!ڋݛmgo "?:,u TcdFҼ7p# 7ִYKHf̬֢ei(nU#$@j2#Bm۲v1P]hz8zՙ- F> RsWϹ)5n~Y&PdI2z8>[Po<`ǭWMy-MJ˃3V"5VnBDj.swYUk'Xd[y@J$(qfq 𣁴Ǔ=q[:-]Hʊ ʐTp0A9E{}\ֵ;dO9*2Ax)ood*HryۉˍZD62yH,M8luVmM[ٱcVO7jfCSk>;z~Hj 8ԌaɍJcrsyUi#*IF$r3ja7.FMOopb\ahA +BRf@)p䜎upFk H̑$e xDpX9u e[PwOL?+/>˙=8FKR 3 :ۓ8\*~^Mg->_Sy s֟LYY$ ܞ:p$ҤدPm cojv4Qзq:ʍ~S+uW6Ь-I"79V~u4UIFXS ڲwIR'MA-5mO1ƒǒ: 栊h#"D7l\9w?JugBM*0bCqV,eD'%dtf8*qQk:ܣd(&!i,~@xEO vlGYw$FэǜuU=I0@ 9$uT7tjYQ@;*=t =,&tXbDѺF<0'v@;ؚҬ9n!@=zNo. awzSjwf%V*rXukrt.IPKVibR<ė&gBjT<:̎"f೘6pxe[mYym%`:})Z>A8D6]\Gᐂz֫_LoZ,@}~^ ^sV y(aTgeROMNFWRX a0Sv0 O3{:9'bݶrѴo(BNރ 9R 9&]2V;n+5މ<=Gy] 1A9zkͭWu#&f2 2n8SrV(m70^LJ۞.cpV#r[0-pZGoo`85V61;HBs[X"=1z}ֲH*ų{g!Y9׬M[q2 >Qԏs Lsث!C ׁsEohY+ c}0 L*sC#FoXE-h䂼7=Tw9銂}FnYeTX 8ǶqY4%MzvRW`a%#\FS30=CjcSeܥJ]u:i+[yo|w\98XVZDRᜌ؎;Vah#p"Rd8C v$5 32Ĺy-l68'}MX@^dDNQ,_Zxo &;;t[MVKGA qZә7m0#ݸ8eUٺJ)n7*^VNnR6Ќ$:+RrT 5ɍ{u`v"[B'&#C$\y#Jt:aIܜ>byߏʷ5e[VdZՄTJ3Ԗқ\Ǩ$ıb'~PtI-ʭϓ.[c@.Gl7'[yaR_!XB0F1sUJ^F3rhm3G-)qVFOs9i [DmCҵfHKav8ǯ? cQ"w-FP~eܑڰr~'=˚ҖYm fhzߏ;*c'қAuBv,H su=>[;EWdAG^?:] G0,1IP˹y<FJ֜&ҧtu+s_j\% ,$]-`ѼU">c M0H$ȓ*>c:c)DZz,%Ve98v$c4NtzY~%;ɛ>ͮefNsdY)ܐI$7ĨW5Z䷑a|>mO֨&FU3 ܙn@=k>M5K{p U,A6sǦ*Ɖ1.!8'T{Y]LV<@$'#TdI<%ac2wdqO=EvԲkSMطc w0528mbN0={:[wC4[$:O 3[=*my%S{,I3f~_k8+n8]7 =0c'C.9$ҧԶO+[۰DD#8\U ]̆(uTWycw9c%]E}MJN&vq)!V O=*%ţ/?urq/*y*I/[]$22 TK{`QiqpupnVo1P d='5^0+ju,  gkPыq,Ӳ79`!6p\1if|U"]֭s#(U ܟU~Tx*өSC %2i)vLID2H<V;J_C$l nW?)HV=U`;&U miq\'W%"옉$*z+l]'/fM(%{3N{ v<6y$bR9%E˳lOLU eiUr c'A;7Hy  6X)XKl.K8 ޟֹwaZtl 1'求˸k2>}X)ʠh GLu\1c} tVJF-ܖ x۞ZΜeVI-ȯ.Ԙ k-ՍF$ :[T "9AOzοks/gtە=s[EBQK"$vܟj<2f)b߁ SW5hPIڹ֤K UDEfP$/ {V梴f\2/X,7ۥ:1ql'H$bu dmcj*CnLpd:6>[mnCfP: J2|͗ʺHx<(*ce$7^ΠiMF`ev֍V}[T9~VD&Uypzړi'i%rė`ۚQ7oG~?QI\MD1Wi8r>+HOWT}E"<٣䢒Tx6bWcgvn33kīN]~t涖kixB|}iGi-] PdEEubGOoY:|r@r;dB:EhD8y0{UHɦTcy `ret'}g dx\u^_h,_,ya1JA>qf Ɓd R4ȝ+RN7Hֱy%|±wk<m[i=j(VA#VNQ1XLK缃,1N["Q4lvXYwi"7. 崀;}nfNAkI5kBmݔ Keb;Bᕥ2 #>ˈdf|Ӱpϥfz2=Z$$ gXh>Jлػs~'q"Ș,~8|6RVf]NFSZF KK)Yܧm&REԱ$:~'1J=x OQU5B?QIt>6xpCqzе»? Tɱ)f9Use]HN3UO_ %* P&oP3tyvpdn}8V wc8#l;V8isO̪';h~v|jIXde^2OInma_g9*}HKKYH9"\;I!C/Q𩋏,Q릜-&JKF1E!KcqOPac$NHH#?wE&I);ݿZqUXUbudFy灑`ޭK rsC{~+աec M׭WVP~ɩ"cnMt+r-̥**+ULHzs/7#JX£: @ӟҹoU7íSN1ֆ 3{J.V ͒zϥ3rzš7cH҄y2G=9?YpL.s|'+uM}՚œ4&&Xagץe_^}̖H±ppOvRKY| a;~3J$x Rz\}i7a3|:dv{;vd>WP@֢j1jXc @']8jUi8̫TSd=~;j&9$zAsja)=qI&O)ipx5KM dOC[%;ԄLP3Pv #=+*)% E65{rsQ17z2JQ!G* &.ˎ9>jK!vU+aM2|t+OUŧNr$d{cΧ/y\䚱=HU'$9 Ӧ1B$<~Xe]PB⾣g8>zxJ) H!HZlP)IUlUscUJ #(AOΜI lI?N}JZ[٩T)pNG^AV.' >㷰qUNJJ؊sIՂ8Q ``ý2ùuPC/ $fYH'9,qe \s[ΔI-n#wMAw YaV9l-m!y&TE >֫woyB[<.-8zRJ=*LFrzOTfZSf\|3Щ4ud!b"1ě?5ݍFi^VU)*{9%N)%C$͈sٲI?@?VdFɴEY8?\VW ̗mrC2( R$}Oƺ+4[h33$=Ҧj);=Mb2u[GqjBwn `DZZrA݈,w=z~UmT r95p)A%6-.ͩcgwiW(%N3Cc9Xq&rKPz-zjdiXH=9?JKYo!gcU`yҒ a cq^e`'8DZmy SPaA`W$㜁iqYs3z}*jrŦ nEuZm$ϟ+dH'<|-+,`L\`5-yϴg<=9(~J9Va/SݍMuӹt%4+S1F.28ֺ)-e|c?Q M]CȒ4He #8#7(@MyGpIH ztʕQfM"nidvb̼ 1DZSOo,ND+"2J3RvFqi*Yr8#cʒZѓ041ͻB|{ ~Ss*I-[xbH;   l6v+F#e|7ONqUB'b~\n j-KV3ڬG#+!Xz::9)=bGx">J=sފWN=87+/} V+ϰW5ZD)q! X؂~oR=ZkaZ吨V$?{Yn#m&\ę 78ܧPoW+#qZ}[ӬeK$;HHջ땲5A+\׆%OLU -iMtz̭2l'#VRQ毩FY'_ebaN1-8"M\޸p98m{gFM']HaJ--/l#݌=ʏҤ,M[D"2>"d1:}Ak+V\qK0q68VX]y A97y%Hg[i^`ʪ D*89Fr;jj.Xp1ka ]I 9XmY*d?(qe#p3f;.&B2 Ϩb3ȭ'FLG>N3oz|4w,S}&%\t@㱬O753,3~B' }{ Dv%Đg~Ax$T [^I|))[! vPiEjG̝QaG/<軘c cA|JӳKwIF"cK*S ׷'~Y`kpn#.7'b\ ÂN= \mY') ;,g+iT2/U/gyq!*M>D Үd #n$sӌ~b4I"YT.G8bXpÞZdnF!,1c҆ќg0]JP*sO֫0DުAaO#8׵Etc+fY[-8ny/%yTI4i=HI$|N>4iWG4!U!4ib4^k[;rϹ6os$0B1Wr~ H9MwhVfi)NcF=MQn!lV0yduU##\6]L~ElAT /ڮ o@ T#2}ɬcV3nAak]\; (sRhjE3I(q6Bv8#ϵI43IÌ"Hҋǵ@F!b 38#MC_diB(H',O|YmFiSIjBcge!O=>xEK$1T 88J1[?zj( q)( r>l0$mnT'n۲Mo.6c`ܱ$q,n]eTG$#비}{HgFmBX"Vĥ 2yМFvzKO瞅UO uW˲I.PlˏA޺ 6Kxc@xPCO1dt+&-f1 rIGv}5g/Fs)mj4MYc38 clWI 29qXkM@6`rVnO)A l3qÇ.yS?sj $ﵳߩ zD0!i 3POktn&xw$G9?7IjJD+K2qxYq[:C>cﻚbZ![bymGgkO ֌,kBA9FU{ѵ2/*wLڠqkK&m5"V%dw\4.!pN;_-mkesQexA1mbb}* "[;eWu,cHǶ 9os +3ddNX.Am˞+Uw,GjIr\!LSj=~OnzsW3]IlQW`\Ӝ|X߽f,` d91)\p2?pTێƥı+ y&HUB ԫO=kԮ.&oF>eW6-z.'7iFYCVU㟻ֳu+4{ vy"`)C)?;}Ep}g''i2X9Բɜ`( uJMWfO=^7ѝv t毴j#G=OҪ,K Teq ZNQnBJ\/%Xa wnz2GZcM(tE9R1iDm61c&[9++Qj{$$#fa+ 3?xz6mn$?\uo2HB\e>c"I ǝ6; #)$6̨!\cԁ[or*KS%l|U&I3-0P|I?&gوHT.pd-Ի͂!28<ֱۄ"!n(5q " uyy ~ь)n]D1fHf1<YSEuxSӝhFq'wM2oY-Pw7[VS4wL%YKEks,y_A9VQ1H뜻m#;\ƹZiExm0Bx瓜Fjw\\4+X|,{ U[X"KvN GU<yZUE29,;vQYӍ濣++Tckg[t<[,\a\:Gκ0٭VT z@{6)u(qHv&R7,7AS* v+evΏ{Gxl>iw2󵤬g9RW )?)j])CpҠf+\TWZ:$Ϙ =8ɢ%PʊHT=@Z4sBQT 7TI Ȟ`e3SjcͳV Y\ѻ)bB=:UFC=S($U'L~|7i'٢FXos>ݻL #@B;IwҜ-v A7(!#. rb}hE #)b? <T6n]|Uk-**$q4) VQ" ݛ<#.ߥ^,4~P 'XD*3"gN3׫wR΍@7l=ʦWC?t V vW# A EyW ' Ӡ 5ݢh̄6zml HNژIjME/8TBPVpI,OnepI٥Y3򂣽ViHAڝs˶yU$0U$kbw#; j$I3Ĉ#j} $8X6U \#9,2vi<&2%9'|֎ՓԶ5Kٲr4ث-޽i2~@\44a&ubwrnk#K)WCMiB_*A)ˊFI6'5Zr1vkE8--j'E"3' c碒T.2Ga!Q$[nbĝ=k*yE>r ϗBzkPE ݒ 8_jьɣc-JQ22=iJw#=jHER*`Um#|V}> n vNxԱ US[w2r}?Jl3@c%TDޗ%IR@6ɤhQd: XYASSr9oK 'GňƲJsz46qrmdH5gcV4wIēm p+9#3Vhvf>e#q?J޲R*?Z=8ʢcZ䅖Eq]{0(`\1ݿ3ZZlPʬ0q4@g?|!%a J vT!@*%V0$\z󻎸Kϯ\\^Kvk"'qEN9^xG2byXo X?)8lzl?Cq2#3OZmȊ+gs"=yI`M%`!Z`x?TB2wZ[t9%rޢP ']#M^dm`R_ OcֆvӼ8#?Ͻ:ۿRKmVepl|'>V-w`OҩV,bƋOBy},AT;r*%NBggb`q{?C >c#w+twr*b+@@$zpH 4FgV A:8| #ҸQM_O4OqrWG8"CIKDKHT*S*A'*.WܦH9|~5]^Yn Gc`}ޤ#򨯚",~}Z4E<4"Tc~,DMeW8Xa">Jg{w$gSՁdcJ8o/!pwҚ$PJi9UʽىU lgZ$Wԝzi3h778ŒQGj# ;yd~5NRA$jRܵo<',}3)m̍,Ȋm9z5ȅ=*G@SIҢT{655(BjE qN^Il )gmj?{EF)j6]"p!sQDrRm^ eePQpjVT!q 6?#qUIZ?3Xr+V x#}YRzu'ڲX"P d cqOVPNqӏѤ&ђ N0u5[I*PsktXP(ap dLG^D%p'5Տ+XguuFeQ7eaw/g8j\`c\ q :fYKlzk:RIDS$,ČUCMu< PpsϮE^2yDD]q遟)th*:gji?vּIn#\Ƨܪg9 #,*̠e*/]s19b*6j΢i'uc#1ʥG2 8<ϿְN\]5 U9WSXjP7AYQF0s;Hv[.㷠 Qsmu˭ FyH1q[{6X^UT[@atTQQC.ȷaF2q^*s5Vn2ß1OӭK$DAlTN*ݣDB6ۂ1:~`mI,I\8 0VGj7IؤFMQ."\IR7eOnkүktsB tʎY .rpgW5 2Hg郞ߝhFgjw0Y)_ȍpCß{׋<4vtBqY>8$;cVw:-{S,qfiaR!¶#lSZWŸ4Ȥw0OJ#Q+3F%<N,A0n0ϭe8)_RqwQd$F“^cݣKd`$<>fm*"(P$ >OU$;O9|QA@rHz֢5tym cNm)O[ă}~ZHk,Om󍼱=i@diP[› ۲I0$}rz)*0oڛ({'Dwx,A:piܲ(0_'VPCn #F8=Aq#d@CG U ݘTD6LX#F%|ޜzGjKu4.VcǦq4[ ̤ȀŽ[V(8;s~9$޹q*nJ{!'i#DR] d>-ĀA#H@$Zwq c],6'*nGSp!Z2v0==qjIjT VnVDMp-N+2Holȑ0;w 9O҉G"FO??^W 1a0+U9#SJېdD (6Xv^GykuE23,LkQ(QṞXѶSj)"PU8^+)b}[EdM{4Ѻob> c^64 AX0iQ n9\ IrI} pl NWop\X n<z{u l~]p90}zdV"2Wp6˹H~̫K>?#Xэ.Dڹ]-〛c1Q,AjLK3>̴rt`j)E>K̈́aAc?f[ػ#ѱ=VJvFAhuG8ec)=7 -vX\*HTnpUݝ'0%vevӅ";dr20# R]foxaB;z߀9tdHbdS"8n,G>>b~Z1}s% Wd^6`6s۵v+eHt7kE[xq$Js2oQz Rc9N}궕g'I&g ub}MJMGըy/jQ[,)t q~Tyvi7i=GN[4XwF q/*>Uؒ~Q9=e~(Ũβ8eG#ҬDh" 3HyQ,h=c21NÐsn+YtHؐ >*KQJJQq*YD3LwǦIӷE>rYQNhrI`VhJ2y8=JR&OSv '$N([Pƻn؈V`$0"Մ2[Ad\ϜqȞJSx'";p"#lf[܇âeNǰ>.&6z\[hFp2CF@=juw'A^@s}+mW<+VH8?ʟbo䲨kgJKc/hn+R9fElmjK7DB<vY:w24qKUٵˁF~SQ[C|"vnxUnnDY\6!P7n@b} nwH[DΥwjnz]Oګx8i!ܔ!4xHlOQN``/4 rLFsߎ I7$Ĭ "1!=O#?r2oDlWB$u'j#+Jvʗ!i -e !PIܳ}o[V vv{Vdڬ$`$܌EaOLU&87(ۛ ѹuf]M)hRdh!,,[|"aOnfƇ,W(YB~vdOojN"[r`@<ߏjȲn%ho<""(ns=0*M$M5 N9<Ҋ!GN&Q5HRJc_/.}Zh̒޲gҦv'af 1mlۊPo]m<#1"Huؠt Ymm, a3K<'ą19Upar[}S3Cg>MG͡}YiX#Ȓ ^Ƴn=On@ǂKdG `tRP 9=1N]%az^9R3ЎsSQkݏ̵z-d\J`TA轾R (`r \Ctr1߅ip{HW fOM !͎>ϸ_^&=:I201w~\ʂdaK鏙 XZ;9*2t+ j˧]q"B!#v<Nwrk )ץrV׶Nm#XHQ?CO[XX f` q\VrJ׻՛Y[)/l/'ޛn\@pf=O p8znnR;G{q-geWmQ#fRn7Hwjn 4jj/4$F!XB_Zm: ;N>1 )eGϻ9`Tu(P2**niksjfI@[`Sk+|8#'Ќ63Iw_sB*2ݔGP b8rOTW}NKpg$}jܝ"۷Zeƪ(q$Ң^K$`Av9NLiZy-<1E~cȑUs{y?I!(.};Y`_HCb@s&q=jKZHĊw$ O$}}<"FNPCI[_R?{FSGU9'cjI{&$ $B2Ie꿭Gy[ܤV+wFFۉ+p fUEJW~9s05f0gRѴv9WU4agK=:kʭ.xn#:=wK"BLzPnhh4m\ ӂ:U[-#5!Z'S9 8ijlv˖c7pR '8LKi@LČH9;A>8V452F7!f>fc6}OTd-ei" dTb>Zirԙj(&Ynӯ5rXoZ}I[ X`K|j3od8rNќdp=idtfp9ZV25eWcZQϜ~*710\.1 ~%{܆@_0InsT\K$|Acլ).[)4tMVpЀq2PןQU s4ú + s\{Oh.uHn[hy+0f1J@8Xȹc킠|QJ)R-кxR]2f ʧ;=lY>қd:p (=K$DY}8\`NOug2J$C€F|a6Ky-nPr T˫a=H8y#~R}95tJp:0 zŖ9u r0lyr=k>e=24JB2*9][ťyӅyw~dTs$q=*m;B:a˃eIܚ装le\8c}ڣs|JKOu$A@ACz=j˜&Kb('RߴxYF uA}j/}Eqj$sa˙[h?g݊:iq[<;SNk"nrA(%Xd=y:ΡH>cdXx<ϽrJUdMĮqjշWP{fϖF|VM HHd*X Qﮊyc 00znǕYJGp99w*p|R6W>Ypq+*dS>ۉmɐ] hgU0xW衟x>=ʶJv,dY\ɧՇѼ LIF8Ү2J \([)DZI Ieu Io<2YsyL "I' GܓRtEAҡBT9Y1i%!h|A'q9n㯨E#nYAݟ5u,M1 ?zB<WEVDs_Tg,(~ٰ &bTHqӑP2ћ\HmO?98nIW4hBl6cIg)feXnPFtV\wrWW*)CBhy F T@˱( 9K׊ܿ㾵 i%2,`y;5մ*m!*5CnıKqjeWr;>׭eNQIsЂYR‚V{Spb48(zz{nn.>T%@'ҪSL2gesfq Hi{չ/nef-> jֱ9C?yºm&BVܹ4$Vq`vO_ޱݳ岤t$F ̓˲6 \׊qh#O:y\HNR8>IkIrɏdq2o FXy!T,(~?t-qd=ʉ[.A#=kE)هyS/ R_E$v~dnӡ#WxGҦd(W!Ioakuco@+w\OcR}*jB4ۺ0 z_ 6%фǕrxs*O!oqWM٘6m rٗʜji%+Myۗnbm9Uml_*K1jıL>!ieiD\g8QOq&XL V}+e۰HDdM gD\3## CVQv.PfM "?HEC? tَ9:v1q>ߎxD!8LWoܫ2@ɪP 8-35ql]yFsӮ{U˝gVQo@sFAOTHTTvƋw|q''X zi)>iNy;hm;ux{>Jk`sJQPnW}nQ+`%Oa֚c#"q򡑱v&2f:cu$XyダOzU>W\݊ Ej;Wn8II=:)q{c϶jKe F yry8<7WWSIkk^v;N1Nqڵm02prrwohvʩ-Lt$.˟)Br zukDԷ걖 ԟNGZvDpvÎzmHl㶎(J}ȥgkYFHcĖkATbAvU~SWakT8k،y%dw Gj3H8=y7{""Ċ0H H<xQYZd_%|!L*0pmv&8jB/'89,㎴#mI\/SO³x|Ue6AJ $gӃXQ*[rXal $uHoʃp@<택5LdrY62SX%U{NF 'VlR@;p@ҸTnMf]֦1ïƭaH)]`OVf+o,b%xqU-Do Fspx5&$.F`?ƹ+Be;8'iۭSk6oݴt jqs2EIrpOAOTFNѽ/f IhZ@ΟyN^~c9 4,%y>s286иK^M6fAs?*$h٘0 㓟D2 zk5]t?Y|s⤼R'G,=^+@`0$ zYQ I7Vk]ݜ7LOhp;UWpj(>ךw5)r~uޱ$ @Zyȇh$q\ԍp~ ;B*{cb洎]v z{Ttv'En&YmI'_ƶRI- L>HRG A$-"ܫ1R1|WA4q6ѵO9. ^ aWw:vE9ܤ?\bF ;qjF(!ҞYpr $zpqȩ?q4g-u]ItX0K}*"qywh d?/|zSL#S⫚U--ZnWbR8xF*Cc-F}J C#y nGYH'ڇJso2+B&@֖$ c.o+{i`A#zq7+ۆ"HY<(:tVkW:v<9:MYq # #֣bXmV "TE =]bd8u$!a<}i[/*vzH#eGN*yϵziF񶾿'{W[[]%<{S2艼ןZm29>ڞ0  9ּU%}v6I㼵_0$A#]^66TztPU`2O'>#UZE"djXy;J e]D}#$jTug֕ѤܬN\q:P.>Y\p$QYۮ?W51,{U@@˸+N sПQ}_ QݽQ~ҪhЭy"VA/<̜d55m#,(c?'T|5kkl2 p<{޶/ڴ|2OoʼӃnW:*M%Dc?xlZ4L+F{?Uy;`Ji)L٣E =³S\&Qѫ: rj=.Z+ġNDP`F3OUk0l]iO'Tk:z/ds6.ހw3$MޔwF3I!YH94T$7\Z7$֥G01x8` VnEjB;-ڟ׽\ICo `3 #=㷶iT⹯nt(_k|&ق }N=i ,qs{;S-A%"#!8tMm,0DH˪.B՜d̟F.ncE}Yfv~uOF,~o4n$p;g*73"ۉ7O:d}zz$| ԧ&/SAZDv82QW:Xv'A۩gz *^0c d^9ҪPJjShYMz9\ds˫6i卆ڹF{ *,@![px$⪥n[Khq2;k4mVIzo gR!(=O~]ՓOλG lt Jжb )Cqx?:֧Jۘ{mMjpzc%QzHזDo-N8TLw50H#iOչކJYmX]EnjOo֨k&(;"]-zZ}ռ C'lrx>TĤC1lr ~)fyKI?ϭkN.zIowqhF0;gUoetPAl Izi2J24͘1/K2z6}p:w 7Du˵ېىa)䁞P85jvUâ9$zVudMFF(~MfI$b(N$8Sc<;yDΚm-k2yen3w46+Z_͇ j΋YE$B;Rp;+bIaEX'NC!,Tc `uG՝ɊH[k4fETH?6ޟLA\ny. o 8J?wb~]>/#2('; ͔eLWm*:acKgU;=N ssmX-w?xQ^H+&c^_gUYVBb9d`Cd9;sۭG$bUئH.sO|uu;at8$Psҧܬ) GێFv4E  Xٙw8=Gv$iujJKnoc5~.bSFF0Rz+Sw5V96ՀZj%?#=.XZ$qܔ`zcU!?]dX7X)]R[!87Ey"IXMg+''­â=˟2VKǖ-ˀy*F0x=+K$ԕ-R yQUv[nRF•R>bkZ᭭#H-/H +9UnMOXq Lp*m+Hl$\)4 pNN}zg537)UnUxf@FAw*u,Zm2nT&++(+|k:kB a6s*!fYbp}j3w@#?;֕rJlNFGQOzin8lrIfӿjIE=KqLDN[i  ;ާ9sE* yTmR0PqZBIm,*vlHrs?]Ki(.bYL;>\d]t/ѻֺT,?DQ!nmkvjyխWUY Y쑁{Vܶ[ɵ6*.Fz\H-'> 03ЌLjU7PIc5" 2ApsVRr"(%hn! @Y68/.O.Q߰9 C壦?)gu ˨C8WD2aPg->5g4ps3(A=8;r}++n70?Lm./N˕{.ӕI 1y57c([ѝtٖWll\~Ub bK+3aq]'FbFNy'I#dXbI r3j$FU@0z*v3ՃӾDݚ5YB$9 x48Be1GC\,[$x.D{#$ey0=z4Y/4KX| 3erk)!N^j!;g<(8n*+BH{uHݺC0,8#x.q }h4j\('%qMO`[=DC$IMF1g8$Щz= 7k$CLQ$e!X8i'}+KOs! ;>֐Iߔ3ܶ;]g̅Gp>uS%UY{({b.}L@2cJ <ι˫X!Xye!]xe'dIF}1`jjY™I|Qzs).] h&`K- H?B=j-fH%2 rO aO%ڔ+$@\m -O=r̞yEG?,esjiZY;VmܭFI#?#jl7~Yw#%NxV ?)dn}k"k$7;d_0TN.D,o/.ω4qq n] CF1 ߘt6dIJ@Iʲ]- cmezPG:jpS=0=+L{Tfit5 ,7$瞴K[xLWRG,2@[0,Sܣm%fgdUc1' 1q\ N;IfX+<+EX>X>ew{OjRo]GRf2ȁ 4Ucaw""u>b"C,,Gl/շ"Gc2X[E serŸ##>{e-PHn!ʁ >]d4P zq-cMKcz{\.*4fiZ^"E) vr래ܛSh%vyxt+iH!` _~Q9Ƕ+Jwe(E]]~d26JT`q3ۡ -X~PWnܵ>!m25;(fAtWN'=zkWd a+<~l7RJNҼCt[ @}đB'y?T!ц;Dp <i\[YHx XT\UVP)m3BSQNUYD~sNr=?ujI @T2pGc]<^O=ċr`Jίq_re"#s~R{ՙh.#Jp nP9 L`:נ2D#0stӠ^}\Osu%RAW\ڹo/ lYspJ_,62XPp'ՙa-t9YvhPP"IY#X2kzu$g(_Ryv9nmcR2)ɼyelEmi#Z@AsLqY:W 68$ ONNI$d?*A cqs5v2 MNɩybx2|nqTרּtk6yXHt8+;KlcT,N|уq1MH)wkm&/ =g㧵Ev)"H=RG w-w|vUc݋A*#'zWlShxpAy`dNy#ފRI}g[ʞk\r]9Nӵh@m]F6 ]¦rxR8"0 +֖H^>.ZQ䍖QJċ\vV&MzTpTp~=X#ֽz5)JlqvA*F R;S k)Ԕ^_b ,'T濯wxn/Ie <ZT[XI؋VrjL¤!zFy51Ev+A {CW2|`Xz(.[?jY[``=M.+jsկۀRzΖY]=}:SμgHUH,ZI;Dw^E*Tzz UBr2{54#^T') FWv`%\ 4L'){D%$K 8=TҞ q*)~rr@4rZ (vb*IΛl\y69;zqֳZ2uVR۴Fɕ FS֬>Vh ?^\ z0"BbNI{ LV#fW2;=9YJSTt>cKs2?KxrJgp9FUc`s[փqRoQE%W@$sOQNXSqLP:0O}?* ԧc0HķݒG?+劺.IQ\(}2ݟvUqu?Z|ϴpA8[JNo<ܮ́$ufPˁ?ʧPLI=3LuE/@{utcvǨQ[\őf؂6T+dzvF7nzdxMtUv")nbOTyaX1c{VBUQޕaVݰ99J(;WrrѣY7@SG=M8?(MRsGX `z*f!ݞy)BեfVQM;:h%GyϥH]KI)LL $tE,T POG}D}|v^GQBTR3HcO^it޿.W8#'?6)EbPߊ^y*͔hwJd |èMa i1-g3*8ҩng,[rsZUJQdR\EEb0H>Z\s?ȡ#?4^O*+,}zdzʟ,D9ZZD[ԡӭX`嶖:`sÂ%ZT\8>p85E\*F1 `ןz|RQZaY9rArNNi#''^>&s?}vEF_DXD&كl9գ+ ۵&6 # %wgLJF[<): A)N|v*3l^8<:,_Mci'whӀ0+x7UBZ1zRf~yX@e#rz?v#;F~\zk3g=v8}UnHXUd!H$0y?m+ݵ˓\C9!W8-&^dϓTwO9r78F;OM|F"yr8I'汔/iF:qVF [VmO|'jFT>֭;u1CԟY$*h͹"tV?S:MM:kH<$,+Fg]8(aUBVnVh+(eW,GCVC0.H!'XTsVz4N䒶Θ,H<_IlGa^zOlVU(q=j儐I TrM(G$`U7K mvTB ([9=5{[s%ys\1Vn*FmM%FXUV9C{UsA%Zg=MydHDŽ2၃X+q"fbN GU9+WY\ۻ;jNA!?ZYTM&38v9>1A E(I'fJs:G6 9 %q[.^SmhyϿ2wQ @>H jQ^KV%t5F`w`#fgyrAܫ c#8Cқ-𸼅C"rcԏ~%aёB1~l:*ki& ϴRi ݂xE ͸F5+p'NI/E6$;A'|TE#4PF@c:*)4kx 6*JZ"" +Vm"dyRUbH!oy9HVsToRXrb~׵u6: 4]ZTnvo4[IԢA8rV QoR=e$}gpv3ۑo뻇HxY.@ݓqp=Xn\n*jn(/AdPK<΋cG>fbILd)Pcy}mOQ[Ƭ N6h/Ē;*ؖԎث$es9P9 a%mD4th; 8<SiwK"o&R \{g r֚J6 d;z= RVY5.Q#{z̰'M,r͸q'zu)Ew`CcfOzrsֲܐ:qZfBK8HLn SL"T+ QI IfKbS-O9 clSQ-I%J:ަ SVar􉣹{s)"00$:uxd\9Q{gԎՋ]I(HNs9$\J߰15w ~\W=5~ΊR*>nIl6$?9˂B׿Z&9[NY2V6Yv2zt{inF$8uUl~=3V}K:x c>k9So E}tmX~Aɀ[c1-涐Nشgrpw1?LV΁0X#v)*,N28u,]ȌM{Rf 値)24F-.exye,@$9whwaa4 䃟mkr) yC`j'${ǚ]?ut#5ƠgG+FXQc_oċo)(b==+Srg܌Jn,F9T#'T׮e1CEHS{1nTft{kecAS-b sT=f)TYjr8\JYYa7S0a2y"8xϡ)oXZ<ӯ,e[Yy%C1㚯\A2HovT)\WȞ ܥ"sT>XF[h.e[<$ XK1+= ڝhR5 d1okRZ%k}|אHx$dk){%+.*˵;l`pHڪ[Ի E[SLIo{m,0xB>M^HoJѴ[kk } 2|pB W7<;P² px*W񭶁n4ʦ*$}dz9*Wr %d-e=Ѹ-HP99" Jfy T0c0~`@՛y*Jʲ&yJpuNvpKo%0bϧ,O*q[rfÖ$oRww#oZ[Z. H'<3t0zt"y>rKyA&Xm=Lh,a(ߒH 7?WOrevO͑.Ǐ2l$k7CTM1b01y L yhlї?6Ћd .I%1\;<>|H)tUB8 a%szFB+v sӊ4x|ѭr41GNW'vFLFpY} #8 өZ \抴JrX-h lK>.09}+9$1 at1$k;L4umCYt-G,{;vɸ`n Sg`XQަVWبs/u?ͺ }j.ʺÕ,ؖPx DA¾S vrH;CsbbJ% D9P h >TN~)~eO$*6ܠAg5rגZGpPNW;vKg5Y5,9c$yܶwǭl\0]˵XA9/{biX{8mFP6]g$ 1ZZEWL^#r#srz`f눢VΑRs0s9OEO-O01^󓵿9OTIdEGsm7mm|.`I*Zh⻸Պx ֮W-"Ǝ\H2v_,۶:Zm5#v}Kgc5sMx.?-QGPqnzSo&}0eIu z~* 7r7o, o*4x#?vUH,EHب dGX:r.-mL27 pjvw(b#b3l=+r3[1l< d~F=k>FTMPqTrFslS>ZX,'{zwc%M92k]M][m%m1NH9zﶙ,LP760CgkA VdH٤S,|ݗ$*L8g1텝==\۠kvb]$\c ]a+{tIu&ݜ}|iWG$iH\]FqPiIu?EIԓr67_<{4+oiOR}Vtdr~6+#®J<|6 r2: B𫲳IpF*=mĸEv| ).H\yQ`)΁3, \zQmIdp#)&FN3JC$rd~Um4bE[&Ec ?ŎUY-R$P&x񥷉#3Ipˑ{`tUTBI&Pwq;j$a)Y6Mo,Avڋ N Wh!Ybv%H%S!,L9 *ȇr=A-Gt@I2qrHSG4f-Lsϰ}*m)7$yP- BqѪF¸3\$+uϿz{R6,FPaqrz%wyAe#<Yj%yd J;1j<ғ5*Z/]4ibcR2 Qq6ۿ.LaK0iV+[zHO9}PZZs(@SҹxJ<|i. Ӗ#ֲuisI(*v`"+dHý˿ypDhR]GmpT`hs$!ٍH)"$rj}Lf (U}bnB7>VwQA[pU8e#HB sUϯ,YTeKhA6S&W3+ ly>j- p;TJ,ȞWf/4m bDt2⥙p,hFUiG,΃ c)Z(ry=+G5;mHd$zvvºly$Q#t+IϯҟzԊ^+31`皚(Ze0=mVi<$bM)Uy-ŧ",;]Ē As1 ]Pjǚ]vDLnŔ[l\\ԫoͲn9¢RV8yCzq<ƪQW`bF w[`I1EA9(FLݯaPsWX]Qr003fczXvc֓MY#YnY̛\$Q銏JPp{ .yjT:#ǦjK;(Z[:6wnl~o\Vf،sȍO)'%Vm[X NyÎ Ib¬aCO7<;x`2msVRmkuܰp?ZA:oe,2qJP_,B>ZR$ṱ?Ú x۳ A'vS%%.~s}ӝ8>gwب=SD!!N c'4F]z~G+s|XsM$f]DWq b*X<1(TS瞾v$t(xQI3GgMsrZ.R8do gߵH I/r1dm_d`bǵjCs*$8ac\ft1l0 dc&jRisoĦBD1griΈIn a׌{I,"l\qh̕B9㷭WY{#]cޤ,#8ASF 8#rƚRɪ+Ij["eۜ*'*Of̧R𒗵J{+5K 2Ml"82rp?:I.'9HpWi#d:}IF&;l]EWb7|Â).]"ڹGԔhJM&dH/~m#$zE; b^3g/d8r'Zydظ{1x̀pzp=k?Pxb6/ 6~L9rVdm?SZqrvX [pu*AҒ9 m >ԪvxYBҘ3 A$~Z:[S.`;d{Ӧ!x 2H;g(n:Sd7M“8J-B[ Og#,WPNqr*!QcpNXV1pϒyp+Jm$Њ̑%p?*p)# 8|;pIkjp-,Dܴ+ۯ$KjDpKu<ҟ1ͫc3PC2L+O޹=V.b<`'9$9枪a^O>Ú]̱ƹl~K Km\ݑ.(њ<Q\g>z+ko3,rǠIn?QUYIV&a]灜׵ZF$#h1ҸgYթδ[*--ļC$9G|n n1КfK[C??]51)A t[0\j)-g9^`?٫і ϲJNh.s-GQ^lT/qy۷POIi۲9lF{ qV Op\cwҨVRO۶+76`Eo8ƥ5\dl[XTq׭dvfLx,+{$R pHݐs10!Ins"+Kv!yJ@lt_ -#'5 #|p[{(!@۩X]Q0RxR 珧NWzd:H&6?}fDx]1cT<\DNp89{uVܲ!81NZKvMU$j22&р9ןSuɩ8FrjmCJEWcmH']ivL~Zr{5\יJxhʆœmqmDm v{u#5eqY%͕֩F1g9nAUTdn}*H#U. )Q>FM͓HێztwU1zsagNQv3rF2޼㙛cd)'~ )I+{X|J?g>4U&pr p{#=j0blBH>P{# m\i˓ N01Ǻ=hJZEiod`"iPrgv}{ּ ,hvŌ+6:r@Xr٘3 !3o$H}BϦ3 d\A,ĜĞq#4d_h ?^*R2z6E{k4̒d#s5zR-!s)Q4h%QX*iaOt@Y7zv(TW=hg9- @&Anbp݁3KFVFShA*:sIm!x*T LJ^M[T\+[s`XZig9ZI> ?ocں 0Wn\ ^#+XǴVrǛ%wR$;brH'?ANX7 y$C1Mk*rxqZJ*hf8eIrJ?lSEsD; Fk݋>rg` pԴ5. 4WIO#MrNẇУ-奣–/ IU'դaBOC{EH[fh&.¨`sSF}|]#B)1/*yPCĽJe9a=6;EkcLLX9'A x RE)SKjő\(nI$֛airmnv~I9 {{,beݸԷ-pkh(b .<Oj AJ4䮌L.!>У9.+q$S+2ǵX'خ8G<1з>ŸW)]102_o¥I\zTG0}cko8eSeKNi!K}DݘTF ]DZ,ȫ46S[6X+#o|ϴVeHF'(tcwЧwrN61w;=9ljOӤL=twGKyadi;VH7#d(2X9krRT)űҪKe 22Aف .eAc7e'-8'=ΤV89n"SvI`ɤik;B!%X0d%`6O=kLH!4'#,X|<:V(OCXgy,O3n M/=CV''#-;^q|\(Vh qT{_C$AY9h[4i?"yN~k/buI# 'y?J1@ۈEfnWf$8Imd2]\ZT!1ǡSqN»:ɡ1ȋg8A9t ׭5k+ rd(yHdž8 09J͔rID )1PH k~dwV (8  8Q}m亅Ui~Wź@5Ξ>֬ޱIA r3e3On\.OL47q5̬wQ-˒>OZXv X+rG;*8>VW؉Euc-& nb*/̄|=]D3>}+#4S0 \#gsI'&1c! oPsƳmhKI`tܨ(<fRO#Gi #q#\َyu @dN<$עgMtMV01p>CǚꬣN&iܫs hyM珨#JG^+|evob2\G*ISwTn>Tķ ZV-:K(qTopP`kx%tM})6 éTIp-+>`eWqRBbb;p1ަjb(RH #[5%2N%RVwLzϐP6n18)t[Bg+2,;w( Kq74qg Bc%x24R+ts_#Z"ӹḡ JQl&:[?R[^o%81lW_*aR/ӌuiH <ףM)BZ+Z-C0:n9lpH@r>}N0$!ўp{g޹UNTeœ-wO3*m'SPKr*1q9xzۡwF"b\:2TcEi\4!mROWʟNSNIRhd9-rbJ$PG+vhm]C`+#9f 8qjs0 TҪsVm|#9:t1x8ݕcrKcJ+#?Cs x)TqM;zV a^ې)9DSD(+ǡt9{#dzte`ݣ8=s?*k8Igv,dl$c#8j:O|UyrRH6gsO٤Վ-MmAL(}imǜpW hUC:U$qp*T\!Q3,qZ<u+C('+0*QNj7#8D@!qjʘf$<YnƬ9A *QRN($8Z)2Y bzrrMO*22ܾGp jJk][芲iIn7%G%AO@SMeܟ4YCg/AoƯIۉRIz+'zNR&fU);zG#m#!kzIEbGToR$pGAVlsݿK7 k`iJnbs8Xb;vaہ@?أ)guҜLNBҪGh1a` i])f89RҨc%&C@`qӌc;pzf@^2>VSsoγ/II;"u~zЌBRWf)vI]*db"%o\c94KI$[  0Ѝ☪Ȍ85|TQBK9Z%H PA9N}*E1(&`IX;Fr9jkl` =$ 1^$~J']2hJ6$ӿJ&[$qtDždck֤ 3`Mj7wʀ0}qIp[goRj}P6ܓ{(̫#c~N:C[X]cH;'r>l8TԳ ?+ o?ε)Nd1,n:{#: bgP1`/Euy- pX20q߯Zsαu$zW?jY&' 8<ա'x9OeTvɒ >Čd{V3;ì'( I9[1Do>'z{yZҼ6(Iʞq둊d<2 ݞԐr2W|kirw=kn^ E-XF\u9 aspq_J|>ٶ;@?3=~<>~F-dsY$6fG ,>^8?iEVAd2sOLS-ԎyJVа"_+kxb6tLLt*aȯk NR~'$9iЦj.:S[b]}*;dw%@=[:42tۜ1?׏$Jc~%mC,jnS?E$"(|3?;c7an,4g169?,ӨԤtKsrV";mvsi=aX72;8<ǥh]d h*O#Q(&Y6+UP,+ $8P%̱LDH%@HiX JO;ca=aάFrlJ+L+.cxYw/'&C7ҐP rkI"h7O^DQ@l +rA=}SZŚYb2㳩Fxҽl,e`:cޠZ22z'0 # ڍ̖bhKPNsng準gP*N1?)ڲLW%[J$PYN tO\f/T";n۽d znrXpvLl vt3E‘Gl8- ;Ocg~46Ar * WB@{VIxs*m H'EQ De*d䝪9}O5VnIݛ-K$*xc301SҹR+WmL~LR39Xur;~ԖRk"վM:I@1  W'c_;K΋I\jky_X8iVV0vb=ĞfmrBJ. k ].١#m kT/d)H27*?\7 žYH?A9*ݔX_pC`sYBJ.շG69}vd9=_.$+qj݅b`\ڧ;n!q8 GOqԚlq wqߦ@naZǛFrJqRQQS#tTZ4bbݝO]Le[P\ed,{,rJ戳pO8hԏ2W)$r#4J!t@A ϸ*6)P[=~5o} @7vZRC+# צ=Y$Q6QX1OLt51eJv%iW%OR?YMjfp~gx#A ]x~#ʒM_c+(X%I yFT?>n$*nͰOBA'Q`y 9G ;1+hw!ۅ.W3VJJ%hVHS)By%G9UkZhlE~UXL<#Mg=GAj浰X =sOcYqw ɬ"5Gt`+}Iz]9VyL̩:P4 腥.Je``dB>kKR+Kvf-983Ldg Dy# Iv;p@[an.3V 1?tOkTo`V9?x}z,b2BnH I2pY*M]'pG"kos*3MхJ}:ջVYZf [0[Ԗm7!I!A# cֹkgp;`7V't$[]EͬJ$ҡR&\b$|ǒ~lzUC͙_ifL9rj+]VP )Q|.y*ʜT{I8ke'uuTDt8PoNZݿ ^}Rw`/^=k [m4l^^ܷ_Sʎf!圬l9$;ʦQVK#K$r(d\lܟyբsmy~H{{t@6]Y[Y)-HGj}=ܫw,Q8!{O>u̗=u4Q.^!LѳL$cEX -WDJzzg'&$I$v)cMQՠRHX#UV7)P=ߝFw]:9txUA>Lgru6hr7$r)9I$I'jlؖPPgU9KhUGyAỐAcJMvhM#=<")-Hyʉ*.X^9*f,\F0;XœAkڠҬn/%f>aWf9| BS|h( )I7PB z^٤Ypű϶j[ 3®p#p >=;]d$'8Y rft1@4 `6m;2*ޫSýݣY&qc1CHd\.؍ u=:wy,qoYca%9\nw/'&[7ɥ,;PmԜgXRKy"e)"IH? YeH'qSS#󭋋X"s;ny.m-m$SpLQ!Gq9;3SMPÂ[>Y(#ܜu5r-DE`9~oo"MnFxUn♫Hn(8$91ٔ%bâJYCɰv Ӓ?CXЈ,\$7 8;pFGS~v0Z1]Âl@5Z>mʡX k.U]z~-7FHgQLT:G1Y\Hxc7(g9=ӎjpLқ'+9г~ެ[ 5I7P9B=znsXvO'~cֹs BY^r@?Mk2]!Uz:gu+\ěaXn'd0+F6ۨNR^Ħ˨>m2Ƿs1@Ֆ3yPlr ׃۵W;hO lpCjDgkdDimB•b*cրM\]sc@zդ͒DvIJAz{Tq%Qam H!(ޠ\0so Ͼ*=դ5ISԗy3 $,Bp'N MEY!O ]Gۖu ]3culcUeiXF1g8_NKH2-zm-O&d) Q,yKUT!?0+,`{UZI%tg{}KP6 ںa٦/>XqwrCq"a?ں-#ALW &qgAUgV$u:&%Say,04L8aKpzvU nw^%m{{$$}ҭT@<_ƨKyϳ,P['sҵSfeB9m4vdC8TL.89yZY$Xn!/'Ө5a5-K/$##95q2&:Ml,x(W.⬡ WW(#F!8نAPp?xf&z{j){qoDZl&#hϦ=MT/L:*`?QqO&6qj_;N!s'yք {qsQF%˛8 & b*XdGXfXY~Vbu?u.3h$1O> y*{]yb.5; b4c9#+ ?#&[[bX\eu$KX굹Qȭ^C-7G9`NCQHRH!ɑ'OLcM}rP XyNzDʖkqoo=JmV [fܐw!p7A-ֵ|=*L5O7Ve{WW2zWYyKM>U8r;>‹e T<+{uf]4u89h+JTYnxXӛNZS4U@ 8%=\V?'LTY'>sn0p{ь[NBc,Ҡ,0,blfMySAi0ƷQ̤<_8>Z0q IKz¬и+ZȉU{W汚hU(v2ajq-yHB=GҩmXqaZބIߠŕvMp7J`ܴz**y8 G|3we+pZ$1Z,J8!X 7jBIqJL~՛/"]?)$rJ>kYT̍Ӥ}I2Fy&$  Ò:ɴ*2Rݾz*3pEh1-f<=jA튂/=V`c^N FH"X3#sH/9W'؍YKFj׷b}r1NnOZηW$ܭv? uլRM]{̆SBc r@'Bjk%b2njg>6}eJSH;<{֥~lo ;U.Gt0 42>s#oe#s[E$ztRyܩ'vUe9ɛH@$eI >BϕBnjp+0i:.m˂ͻ}L޽XS)ϼ}UI8䓓1#ol1P;0䑎1S7*n%F$TqL[nyFez֫5CMЂȔ FOOJiv@"NHSciB c~5i(9#*Q# ZJO%:+~# *jU0Xj59L1X$`r\n"Ab98ЛfSs[Ə*RS4`9>_X7mpH1ңs֤qSM.n֖ ]B|mcQ3;l׃q zT\2:je e&.ՃHAf6{vcm+^NTt.bF8O\3ڝ@9MDU_,.䤵]?KA2%U 8QcQ{s(EUi$1?jGFJXZ8O'<ӃEɭc4eR|boXC1N1G-FU=z~*Q9I2N3FLuPۖJ[%nb9H9< Hks?>)c k1&9OpU(ni'E%iN듊蟰gQW3ؓolBQ5.j24=AMWdewrT3^74TgmM H,{ǷZu Hau}8})n/Rq㎽{K ;͕Jm>=:Zѭ:pm6bBN9';Byޯ9c=l8Ӄ֫hǀKc}(͇܇/hck.H 縪 w1OhWW#n+uo}kr=RNNHpx83bnFb#Pp}8'f0w͵NPV.g&iK胿An{sWMs&vԎ%S *c-ЂǞ08U˔7(<ԝw }fEpr!eqc Wv)C[282J!F{U`wo+P3Y^\uION+b!%:jM(ej <j@-qUAj]#J*בQcpjK}Q,"M5L g` @뚩sڐc!̙ ʶ 8!n3:)-;Rm۽ e%3=F*MĆ?Tffen^DўATSjV$i$qya$u4cʏ,ZMZ- } &23 T/nۓ"emUW=$˶}]y;#H$ Swc9~i +yPrOJȔBR::ġaA•JvI^ĜT!LBd8e\݊»sH/RawPHi 5Ud`Z9e}Ȩ2IOu/3s/xy5lE܁ۏrѣ]ƞےE-8^q? ms;~w'~ӵ֭ܩY\4j`zI€G'-|2}‚zYtKy'<z*Ŭ1[ =|3%G֤ⶥ4^J;Xy(131vPdgx#j^SD.$;yq @~ΆGe r{|4x9W5OYkHD͐1>9}1R|8A_)$gv:8'OC[;$,$ҹaQ8$v)pV$Ӽ:ofmyvOb=01Ros38=}I$Q+NtA&fѣJJ)8#>5} W TIPʦ8HcvFv;Bsҫ>N^E+Jwz s"ŋxWc&tLw?\w2TPCO=cޙ:F1{kqGC\ťĻ̾HB '\㯽m$⡽ejiFHfTrI#X|E$ODZWbנޥy/"nfNʰVI*\)w++K~\֬Բ-cT9 A=ؚ\]GRG*Xw)S#i$;eM܊g#2~2xOZW]::nm-&Cc9PAV*Ut@땍3995^-R4HZ6ubAs-' ;{U]GW"{xS qlsjś y] cu+aL?Z4ב\[`Ĺ)>&.HEy\5>VH:`};Wf"_!N1#?6ߡm̨WAK~QЃ0{֔ܝnbpM[ʢHxc~M_ca`L=V={n[RO$=)u-sTMF\ 1,GvLECz!%frt޽DWʅT:˷kk崌QN%Nd[9vc{sSݬV` "G P:d V-RE,H8=W=*2'Ql4Z&vpIyI @@@^@U,OLP5}9 Bc$ל~Uz) q!#:JUڱ:qR3^$Od+q<=}ϥfj2>Mq@C!$©$rxof`҅ ζY.g#ezδdIt;)6ld{x]]YIYuaq$~;:vHp*̠8'$dq=IƖ`K#J#b/͙yr:Vev6SJmm6>@ Ɂ=OTs$q NO X̊Bo u-*Gd`0vp{`c>Zs~^序iBI\50*X8 DSd[krHy,r=xj59Zeb[8)zG@"[yUnR9SsQh>B cu(nRO+zÚfEO0"2`49=pq𮉤kˈ3/.6vdd)v[n+.qw~:vts6}YDtA]g;H2s F$͵d>gˎIwn2o"F$vR{(dV,DO@|x ߌfޣ澽IO.)`=841'ǂ'V I/]v)2 hYj]3'cqJ2M&p6` r=Me8Dv ϝܬ > 񚹧ivs9c/0|<'' +jmF'͹\J\=+e) `*^=~t``U[l.#8M`FTH2Z$|nFۖ*qaֳ庀Y[;ZM O`[M;]~mߞbydic-gGBsvSЊ%&A#FZͫ]]ڼQF| -61В@đZ4":#Ѱ d7Cn:/+Ȍ.Vӥk6b96 93^=VVultKb _A ݳDGA wz'UVٵ,Cy7 F~`p sYs}ʿbdZ٧+p# ,vx5̎<;PpA:fj"kvJ) 9 m#:Q*F܎y%T֗?3$=<[N ,'naE UNFL Fp:[h+I|т yQ18G9`K$F2ʣW\|Đx=ITvH'Sd[,}x `#r0*#yّHTWeYh3tc7 F>QUxmnZa ˎBcFAʝj#:ɵaON/p6w?,Fqʠ'y%m>ȒGV)s8#CQ}@y-y d뜓|K ȸXcVo/i`=s;Jtf]ABZH U'F}ENL Rd.+DԒ9pGr+ I1JWd) `%ĐJ2FBx;h]xٖlc,ujfX1 #c:  y+.?;OsnwW)8$q*wb1VK.#2:n  \$qiEyVy9ֶ:"M.|) IC9^򐂄bٮJjF%J,=mߨ)Z/Vinixc`v(B'xBX?ϾYJ 9n n9P~lO| &H#lNI)9r4z]!}9bn a@=׊ot"C\yn& *;K|66zuZf,i"f. ^Ph]<֗ d]HwGz㌧9^[;%dki?km,ɼ :=+#rp$|\yfp !N|ʼnǵ[Km5ĊDf`#'89Z˖NP^FRQ,D)qݸ2` 0N{UX ϶9V(˅T|Xu"\6wgތ1gfݒN8sՑʗ雠w2ebwO=xМWиԺ5Vx,.^= ՝dyO* *.v98șMua (`ϸEe'rE&@ x϶kX(}A&ڼ )Fvw~ҹ5Yc)C(}(x%sץth&IV''bjN>zxW-. 3;8<(Q<F;8't9`yڻ%—hw;]BT`^UbC SA?\#vVT0y9#;ևmr!)$np-}zfBGDf*0##s6{ `~ȵI_#9I AEUW,.n/GJTjS,^[<s 2N;V T<%HØE]蠞j[fX7.cC/⬪o w$NIڭj]K6dq'st ̴(Di&!NSH!2">C]f6w(mBNgԕ$"$nǯ=/%͆vHݒq#҈m$i#`9?¡y AսĒpjO7*SP`f08]C%M+:fP(# Yc]Ą #=ִ!’V;I qSR6WQ|1$xBF6-~~Ua,JG3y_z4$a;Ք9ӊhye*ŎGcUB1N^KVHV`r'6V)c 9R1XZjGU6v%Z?.Urv S==ݧИ'%1a^2%RO)3Ixd* ?*5 qgGHҔd,CF%kw",1=MEc@O8 g+iwFG]ʨ\g]b E,SښӹJ+cb !f@;B~OP1@: --n iCgyaTz_sǘF12?S\n9.T@[ $fqN2M>Wio2,S6Tb `~IL-`sJ%ZErxG8H$׸Fp6'˪냃P &Ԏ64 ;Șq<cJ$Ks$wSM7$Wٳ[w·B*Kw ?`<ݳ?yӣ $IU5#1OM<Z[40۬LsSUsԐJp~B4$SrV) .,O=CzeWb TXp*Ď <_8 fzrJ(a`n?SKLu?ϵ"S%Oo]62CoS?ZR\؛-0ig>#߀I?(8b)8# P#Agg g'z{G5+XRFzT›bx@$r3oJSVQMJ_qi8(T,0Ii"V% ߟJTr_bm*hG/! I#F^`RWf*Q<5$@?(2k7M_#60\.W*H T;R\H_9vHdu`xs=k)Pս QN7etX>$֕0+C _¢T"$隗]1A]ߨۖ*9!˯*$0Pc|gX1ycsR$I I".ђNI*1|Ѿ+]rL #`y{FO}Xsu34&!Il8* `F ڹm_^EGL@+ӃgGʧQYtwaR;5e*AJ<x^ j_v؃᱊UGF] ^~VH1RezP<*Han0~5Ʌd)@_c5'r"፫/J h]2nnN01VNW/f*5HR/-c`U2Qgy**uޢ$: d`dYtel;ROZU5A"qp3I[4sj#XSb5Vʍ p^[1 Q!w<㚂Y$E5Wqҋ;X2 y9rdI^IDԞ4&LA"X+1rZ=pVP{ UZܶ~G3MFȪҫ63lnpΡ{d?ϭC=M!;@-UtA#þH]]* :hs2ƨX+"e[u`==:etD8!x>{ETX) cSO?uԩ.fЗUF61-? (/#c bmⶂ8#@'nxMpc qY۩q0dd-K [^rY2̐ `T$; {lR޽k L%MUSdIzi\g_wQ[RvHbnbge2HpҪP>[n)'50tuG.6HFjcJM'ͷjVqV"Q,kYrH5lb(UOM0X*yV'5B29fQE-BHeP o\f=豗'AӮO>ʱ *F JjG&]L?L^jht\cf[vvs푊y `$\ɩ]-A0&'9RS1$o\F~k1߾̢5[ )K3K*Ozt?G[c5,0ll`C}QS2 *-br ynpK!):MQi*L%EPU#ӽ\&Y`19鑀=1Yb]t; ~fb+t8Z^XcJoMH틵`pH hwN{1 IqPTv@ WRJh TipXn8?AuzIoۗ*\d ̒,1AA`CcQTNU]M7ed\K{;*EE 7:/ak(GvIuΫiQZtH˝ۛ#ja) #y2ڛ4UmdbH"㞇$VL# z0nFqV3;bq=0?*)R\umMVdei%@p#|T\sXHO6U{ȖD^HֵVSж}yh%3E(R1sZu'2+qGJܝǰ$BnP\~щ/u{ʓko5pܲ lܘ#򾇑;U@Œ#b1={ZE2`I+\K#ژ>?0}kjM5rZ셕 9wqu#\zȲX/H9U~Uul`.oJ̷BYa۸66nE Zl240D&n7e6YuK hi\[$vD,k; 6ݣg` mvsn*:qU~Uf8 z1RM)BL18#==EkI322?QZ(s]7 M|yJ!*K6I ~汼Esq1h֊q"BƝR8pG<#2| 5\C  7tk7UV[Yc ZZ[9D5QUOׯJOkq#y-  VKYeio.7(^x8頥J-jQ $v)`$p8mnu@ S w7(eD7%q}zqokVYOU!0i脭۩m㍤ ۣE$`ߏtH-PBMxUqk*;"%a#>N9DF4jF.39siuZ^8fA$d cK$I#d UG^s]d߾0N>>Uk_Ό}9WCZ~mJô91yr!"F [q1޴xrl+H$}k4 #i-Yb_pǁyi3(y\ӡUE9"h$6 ?c]FӤQ4ۀ rz֚dXċ<=4s` $1"-st%A=fZ؜:]ݬkfSR%9b lE[,x<3GEa&ݢ}%A-Ǫwy \'N7`5CZ[}JW:O9e뎣\Gy$?, {]Gȴ IF3QԠDrGo|΢O|֛v9V&İ%fs‘Y~o `u/Ȏ8$g|SZCm7 V'? WQ' d9Ϧi[Y)S@-Ff|y22_y#7p3֭EDE1>Ǡ5i3U'ʙ(&K$ŶQ1I#w Sr Ŧ(\A 6yPT2sL}:uqI#5|[vrd2@\0d#\RéImiu*ZI/XH .[w_ *j7X5ʒ4wH;<+ 5h$e"j A3=uXKYYGsbp~Rw_zѬZ/Z1H$)܌zgO9TW$ ƣ0A{wV&SxgF;zckvrNcS,R*'}íL!c>Q,yqC0sĀG> moeh1 9r{` }Z҆I綹dBlf]b#Z mʓf6V#lභۃk<2XʠuF$H؝ZEM͵sxh7HfP8K&.{ۑڭJ3**!/tp\m*BsK[ĚIȊ$@A\K<#Ȗ-ՑF]7' c5XĊѝʄݶ=})b9c6廵-PE] B+['Y%i(?#$ tkם< VGUۑg#sM>NF HX"7v3*aC`. Hb#8"q[ 7n˿;2=AEީi\@n_ njܐ)ӄfcd Gj>Yy~f#6֖$F12@;}9.?uUv_4gm|?=EgLJM8v N ?+J W@ x9e,{!KHY.>0XF20zB&77ksՑRW' LY-6.Qק>9:vK_MJíIř"<$<=j!#nR0zf幛- !* +Qn{kD򼈷G-9S̞$kK̈rLQG2<(nP3'Te.-.'S 3L#r$;ONnn9\I\`՘!Ǘܬ(׿S.i;=!e+H$X" ;͸sk,k,rM˩( =_>v<_r>Vr_U:DZAjC.chw*} zJ+]/&y#&YeLwu=YΉGqo;'`zs]f" TOɅ =Aj$ 4/$ SlN29lO/ӃH[ klU1T S\.Nje#!R cp3TFܬU6F78'qm\ #$yn#P?٥c]c c⤒YN5U 8`E=$1RC|WE'9s6Cqi췉[HC1#s);cRUeA?/˻ޝ0貵!MN1޳/2#QE aUg!;Gty .Q73r&́[ @!PVk\[kyll6G52] "7HWu AQDV+E-Գ;66{`2µ-T-hʌ7>ona xC lXrx~LvM#"U(Ny,A?8ͷa%bhn&Lr l.?j4NǶ1Iu-2#L۴=EYkp=pGD*As$49_*$l8'rjin(jӞ~d[Zd+U;q{`kukY"?u`YS{ƬɦH,j7nR~ocNJ60Hna&se<|ޣWJE\;I].-L x G7&Yv|O:nQ;%,{Y`1P.GYƣtC;7y$"O?LwgvP냞~!ۙQKwd?jX|nCTҭw\I͐12&sǯY!cPD bzzRMugj%RPn*RQn2\KoeldK{,"1x+; c)$y36>÷Ғhg[UwccZ- r$x%]fd`DĀ#*|Mgi, Ǹ9~% ;BHOa8׻w5\F>v `{ R8Bc {. ^D&;ʃۯ,´+]@cҺ9ٙ5+#OD[ F0ʹ\3]NĖP fiɬO ˖k?"`w} DwR6-ͥ'}YTen$~вߐJ?3plbm^Yxaby4jZղhUXPy.G8Qthʎ*G cըzްh-b8sl*Ǩ8C/Y)hy鵩7`У y(v4L͇~_TqCguKWF-Z1/|khYI9Ng":gO$o A4fm4"'` I)rqM."-2Er[j9>ΐHWh2ZkJ孆-h婶Sgo8;~e`\)xIZCT:%G#: qqXУ5R-rWC?vkmT"!VMdOZ֒?2'F8  @n*'wQШT\~HT3\eĪDxǘz~>yj<]Nip'§S8rlN(ucx#ԑH9ayPrGj|0c-[q5)L(NA+j+S/&.TLp'=@NB܂9U ҋR IǾsM7mߴD4Bw'H?LR%urij(d۲r>EU#J[V1˩E E@I-ч?G~ى0㑝c{pwqS,Y}#/9Gyai.ydzT3m#mby_`0)(=}i[h#qcSKэeyNI Xci,{❴G:99T7Ѽ(i 8R*6r]3m鑻w$}*x9i2!H+6pOniOiءr}+56'ba +Nb`Q@ =s^jBHҽg'@ztn2 V;o!8@2A'fvW6'*2rnnW'rugOg<3da!T?T}YNц_۽TT䛺B,x.9-`sk?IKU [ah {sցu2#N+*>ܞ?*qqHFk1?{z fƗcDTό#=?*T jD`GM.v*"Dk֤K؂ gR wޤ)a=l)Tg)Kt9X=vɞHI$3^i}­ UJ*( 0Lpnʤy[($z 6p7>i}jC*CTr,2zJxTh:.z$۪= x8+U4"wHZD, S[P$v`T4|T.[,v>y_کKNrbx,ݭ ":s"1C*f:jVږHUvᘞ:#ۆF$tB3czn2,1L(A)s5L#I8*:Rv ̠2n0du*8ұk%UN۲W9 XW ӞU y";) ӷ2ʨlKgVx8 쥖Tk2s^-%Z0:H4TQ( fE!FHʽeN+2R@i*<@uY p¨6Y,RL#R{LFe5*kBI6̷7Q[1*Hw*2 $1+8 /fp =Z>Eʁ!9֨5Bqna̘,FA+\ѪKA]rh^fԯpٔ7xһwac_JӠ+w F hÜx=z֤ P )ldU!'$sVo(qLV@NppqO Fvr)lg<x21cZM-Ò]F;YJI;>p̏J;@=،>U$ na:Z\̾R͇P2985%3:)NE_TH% `38=jh"-wlӿҫHrO98}1]e- $1>cIY|61dW/'2Xt#ӏMѴl-\.,IO-uaRLHo,r{qҨ#0GԚc͘&IǠsvp3Y0psJ1젴:30E7; $:N1W:fES @zӭ"ĥ8s&cG,,;cafŝ$L.(V9$UP\+@/ž׊ xG@U@jyp5nX kcryVj{]a",-H|U0nLOnmk+Vo@1qA58Y:3I'%~o"'Q"[[%gq?Zuiޤp9hZ!#X'zҭG *7w| gaWFQAJj2Jz?8ep ֘DV-Yx?Xi, +mFBrqL3/<. u<)OR.f^oLzS'۟T"(V[ܣ g `rޕRO+x#l$~ga_x҈_0]@Z#^K+* U :󑟭\yI"[IdCt7I$l@"0 _yq"܀XmvaA8?P2Oǡ`y8J60d%m}*Qu:b3"SO^9˖wF,KyшX $}HOZ3DT*v'åIn.xd рzSd`)}X9PONQ4Qm4zc+wdQIm 4XJpzdЌg=ޕGH_zA%B5 QRxCU*mˠ4-]HbE1FhR ^Ui2ܴ2E!ޠチRKp,ci`TDjozk׿G49G42DIrA'n[nm upA {} PRpFDhTFO>=1SF.?60TSgkVzsޕ+]nͧ(gun%.>̪$FY;5n-J;HQd7>[`8O&3~"<2׌( $J+:{k>R?ekSP-te ʎl?L)r&]BM=Rs' y󎵗s#H֭BdzLSM. $Uwa?2 ϐSVL2a eHa땬2i[l?(Iha/<O_zG~$X|;*RHJ)' 1ɬ[5b ^ȼ{`*8ݸY,, ڭHHh96Np[?) ssA- gmb p p8E4nd ^KGrEϨ0<@NY4k@}EGG)!Fp3G{fQWɺx2o,%m\G [z$wk1CwARv߷''Ҵݯ]Z8f00 ?) 3ZN"}fgxF; lLH~4$1[ 9sz=<\H[j0ݒn;Ft+Ypy\b_=Y/Dq2pNs+Te!JKGKj3hb CgN;޲Ԯ;[X+m?LS4. s$d}EREwffeR*Q9>5mP6pD>^cR̀2? s[1\FAm0 p~S%o Z2n,q` T~ְ\ +6n;cs«%˯~ns5%Sq*2U\?B1$՛HXN:fW| 3[wEKaѷqN \[+j Jk[VX>fӖU+>ȬԵe.O>i sM 3"Iw(){5M=qYG8]p;5AsKqwi ʎ2Q#r7cqֵ9,Yhxďʦ/7'] N#e7T7d9鎇ZsXM&$62(egNƱU4VV"(oRyUoWT#/T2#. (@' >Tt&` X27# uHw\F 91'~ʶ-J*:no"-IDm'5,O"#ޙce$Y3(?m595T7Ӏ1`,qjWa2ڦҊ-p3W ! J~B8=A֟[Ite Ṿ#=NOJ}QG|% )0XBN$@k7 )9TzQR" %_>`Lva<Кմ͐F'3c+'v5[\FMÅ6:((tyOd.' FaO v-lmAu3BOSҡ hI+,2\^{Ki&HL!rw)zm'=+.LI, _n3r+etPKr6i|ϛ3 YdRӧ2ukKZF3b'LcjSư qݬ3 e * r3޵[giqG*$N$ c" 9j͚3 XV+=&խ@ iFsֲbСy#EheaeЌpN1koR[a9LzU;vo-!,mAVZTf-Ē#173Xw;j# rD_;vP-QI2RGKI*2J8`ZʚJnM1.DfdcX܋k#K2l@1 6?kOœְ}ltA&DQ%%id+ A8=ZF\Fgx&#=xxguHLęr [6b@D\a:Qu$?Y/CrEt{HB*+-;Q.XL%1aIo,h,~NSbO'郏´"P}EF >e9ARjh-;1B 䃏z{9kyN.K՘:6~j'pBݏ5#Z%&=7wkIn F{Jf+wj3uw3M&NPbבEQ2ɟ ͻ)&FQۤ;dEQ:URN7h&Rs3I +++!+ >Sqjky~[|2͝o4Y`UeGtX1ɭz dXbݱ U Ձ!O5^{ƒ&fSsci8tVEȑ)ʱ>[[&$|H[EDȯP>Y[{]ў_q4($Le]b:lƳd'2nT`sVgy`WOq"S%{~uE^hI-9XGҪ+K\$Qe.Tz2ȱ2sQ11`H#<{Q 6Xh0lKD߼,˷xc%1Q/ZeDgC=?*A] L-L(3ɕȑAWWf?h:&i'>fn#ir"Ov9>t`'U#+Pp#Ԩ5%NsQH /F̐pqJ^張RۘdQ[7u31)>*f։̐o Fwp}1cUHkڳ!h v$c5݉ IJ_3ʦ-[2ZȐ7Ē0qʰe(ww8!kEDKYv30/=OCLؑ;f >^BqVBG/kjIe^ĝSxz}M`ḈoaL<|8Z:5- -/S&ܷ#hV @/O#nMV?).r3j@FW{Vԡ>eKܫ# `?0)p3JvS\1J@1J%:})H2Oh)s;j cH I䟭;i*}q{qaH*T6;t9}+:uSwZmPKdm]yV&`p˂zT8QS|z q,)7PGO+ p}{S*69\\,v(F| =%zWoD,I8Jm0=\ W<g2]ZdʽZIFXqsU$+*im9,ίZ8xrCv:pw{#UP2l+i$ہuNqp?ΦU N;T,Q`3ױ*p2p\?˂+/+cy8qb鰲JNxG8Ey,3YF2Me ԚIVXi< ĨϷ#_yȡN7gp}NcV`ך졁u%vdF_sPLwFrcqkhD(^+0SZ[ȕiWhJOSu4EirΠzPCdsPElR10G9ib1Ir(+^_]=ɂi\x81RmrĬZ\ddPCg #ZG eLr2#.ͽJA}W*dr.;^jđvWCn rTm8ǯN<"% kRռ8!2zzVu\Ltb9>qZBG ''>a@Pp!AZ5kT_4mmXpãd@d4@\rI?Cҭ4HnF{UFйM+|=pse\ ;D'˷.z3WO5JdSЎj7+[c(2a}D[<JIb~3[>Y⛌(m#U4iٰB|dg7u]3I*2xf <F&1|~jE25˳ )u0qI:0>ƮU[s%+(;t|ch$ϯ\vUp8Qk3O#72-į"6鄙b=ZD&o;v'<~k:oDY>2IH=}Rl-˹y 2R =i,񳭺?v傷#jPbpC( HgN.m a@;:sxfm"ZS^lȒD8<O6HK&0\tV´ S:GH|y;ӎhI(1,ʲFT%$s8jԮ@1I]|[,V%Mm02 e9W 8RyqۦG8qVf7Xơs̃ iZOtby7nzQӜ6XRȪcH>*KkYfG.d\4WNcpEvE5,4,$l$708o/:6!H̫ݺL$㪌Rk6;{S%$8 AcI+]o#+|Iv d[XByݺ%**%2hw%x0٢;6[\]vK?yx z`d6 Y*IwRpFG\}EihqJq,ewjLr} i)!%{Ej mʫFByϵtk[Jo#b+}ZS{' YHبñ qBCyq<фf%t+t^3~FYXH/܌0U}$uXor\Kpz;q$0󮥸vX%7aOW ѡU'pnP:{+U//wSHqǧ<%=$,X?62B ;L3c%Qa!_9Cy*FI;A!qHtĂ2͟Sz]v(uh,7FR?6aN6ofi{XM&pH#5RcHʏ,s;NA qqUvCfidh®l `jГס1Ջ;^# csǰjGQ.Yàeo*QԊ@"πKTɔi0Q?* }=M6Qar`cl r謻ml\!9rBT&2TXrjap."q m2kHDbɻ. +o\ I#WlwdsȢ")ū&"%l̈́Pwy<}F"R\IUssAy  KUk`iӷ8sqx+egmю1#nwԛ4 ֑aيHUw(xjk0atׁ2l]yXP ַ(%b~n w<庞ٔncHvGReBRGAo4"Fh09.9vu< @3`\+=֞ѦRX9$qE0drX2˂C w1}YEbs0tV*J##o j}"A),NH!s_sWV.mE'g(.<k2Dpn^:SR]M*šd^ub>R^V qK=$Fe9'#E Upu*GLU^+k!_h6uKݎ n %ءRH' RÞj]*o̳~wnW9_|Xj  #+OJ-ɗҴ 4;R|@5y$į}_w$2*fw`C1`ĊYn57`M=Qӱ5k+5̺LHiyk.z]dxi6I pO)E_nSff0g(Cw>SG%%K.yby7yYtCûPOzX3K8~Yyg需9:e{3g]["\;G5]4wPwhvE߾p a]}]fBL73yb9;,HˎxK)ё>@, ne%GLjT9*F͔(j+˱l/ H ʬAR^X}*,oUg}ghp0OҢtL$$F3|'G5`F;X%X 撤SҴ\N.fela ˕bY+N@>$$ڄ.L(a%b>`c'h<ϱf,rO^8i"EلulHۓxoJt$ZL˷MH#BUg$q=:=kWHk[,Gnvw-J-pI"1r*3[ike$ȍ6]ɹ>uU9ֳcJ1wg?E%0Wfb_8*v,nUr$lBsհ@:ueiB?6 ȇ9'ۏƻ="K4-CAA~"P徣#-;bP7 dHx]N[  XNA?Î=ry%|1d 9Yjsf˺A8lz}zqhߓ9n&H`ݔiAX,l:ߑa)z6I Yaہw+Yt 0Rn5i ȖqS*>ᏸHSF^Pѽ[;($s15$Ygp%bI݃kDY'C=[%X@XQǶkfUŢQN ă}HN Vj3Qb[ԟL#k6w4Gm@Hg.izDSW\OKy;ec$- ]ǟJKԻ.c.$j{FHEc%9JRJ󤰶*\,ErOZŅ")2Iv;p}*'ky.-ݠȱl`FYK7iFʜZw qu"H(F:9 wh[DC"h ջk"b+Hrw0N{yB"%$͹U9QJtt nM&]O2Cv/Q8#֢b*Y`y!?$nlW:6I edخ@c֮`%0bw$̿Fe{B`,b20LVY-=>)E`ag#ޤf`U S$8?.=0}^t۔5VLrkii4Yd s: X'B {Vߧְ&|-:Lt֧h-m[(qQeiɺMǽT}2~8)%-*-5y %1e`XI=5nRu1_Jȱu[ʂC~*y [I*S=yrKX#N(g$S}>QmԷ I m$)DihE#p68T!FF2 P2I6Sm!"v#0dm*mpIcPgHvߡV_>8ij"qg銘$d .dWD@ۣbF܂0Uӥişkn0p?E9\9cyrGZ G0.dk{X NRzt3pX/M!`c%ؐa nnv/ Sb}kmۗ-&MayRd ]~D=\Ȱ$+OqS4qWcH?PU&H2i'Ǧ3ZTnL6oq 3ʵIdU:=:})|&9&B2A_HH@-xZA !xgV} ̤6 8㜳œm$cG!c(/_|)p>Wvg@mیJ'Cb[Q=nzrpx^_6쑭:wס3A=~>G;HpGzݙn;&F$p '0h!v*wG\Uap(p'B#0v`Y2zRĨȸuTj*Z$JQWrq=ț3 =q$y*͎[~HiK``qM]0jȞ0B8c*فSӜ|1^+5eZVfӜs(f\AR[  K=ұNsvN9D.ђxWiFewaqԠ^^i:F9'D`8<~T#*av{P,[TK1榭.Uw*2&9b;sGuΥr8ϥH U*yS.Bni"&q?@iSVwב\vI_K3y`g` '}+`sc)ubH?&V0gVirA)I7́BGOgI4y.xq~5d N;L ;5 3UJMNPe(9dV99晵w| 5&]4 Ӣ3qH9]),zgJ0X7G4[CXTj4iɴ4c69=sT36OpR[k&W=);qϧJŸkW)is1Wc H"Q`t21֣D7OەVHKP@c di$h3jgo8A 15! ^m) v"*8ǧJ6đi<$c'TE/M!2 S*(28%%z}:zPV[=^Rfd8:S-K`Ҍ#rrp?#wU#ޑpv 'oWirnKv\FPB*[FVfX69?·!UTnbU[p?֪1$?suAl\@)w|Ap1:Y0v1'*:VA]<6MfSsh9c?ʩQƲt%71ck6\Ixteّd*1>J;w!ӡt܇ )$ShdvcwX~U%Q4A)T*YtY$1啊m\ci=Osכ7}ΧV0*Ya,kniKɄʠdZ#.ݿ{) 8dWwSÄs<IɊUt,8? vH7H~Eݑ{T/?f9rqOXd-%Fn娦кq8nc;*y&"B@۸6iTP+ԞH1u&dTI=`$H0[<9nG_cK,һaFjDQ7cz5*5lirĬ_8>`(TRgpS-Ԣr@ӯUhSMGVG,hox=*ЃWs`"HoDI(e$sVqh#$9_q^}LƥhFc_bw<{-v/8'S!b?E#ogҤ(\Try杧>6IHV-q UTUr( x Llap$Ԣ 㓷3uZG`PEM`q>ҙ[f[`T;HMSJ/ryҋl,FA_1U4 ̠8lݴNԥ [r?{UU"՝|x?Ϲꫣ Au5df%f9R6RX.̫9S I׆;RJd%3Ez>ϙ}g(e˹ RyGR)/)d/$?}dgml9^?tnP`@PGo\ֹ*ros.RkjULG+gYf׷ 3Ri.Ri؄ĜzwHZ-#lWqֵƬEoZjqnS$gˑg8#EӼ1w-ߙ{'G *7)l7*OrN?]n>VZ{ Z55~n9n<[#sz֞2֖*TҊ+8 v5eͺ3=C;#$rR*=?rz™5V$n\)\dSX||ڱVIrOay;5k, ?(qz灟¯\Y=pAsЂI6L֤'!,{j7tɤb31O˒1MFIthw8#?7O^3h۵\y/˜@ 8֑ѲRLlY p@ȩ]Uq`zbt4mv3tTp˃5N:pc(KPH.e Xqvg=OOƲaI R`,&r^G\ y<'',ucR&J k<#cjZp@rL6-Ҫ]Q)lqr9#5n5$uBDZ89SFm]軏7u 8.`w0~͟qWm/#c[M/5l̔Nܓ^LK%K2mb$2A*2萼7IeeOG5cb͵#}Z)-KE,29*v[щ\)!=xSh|KqAsjiq:7Vę`NTrUV g[y]ŕ`8wȝp'Y@u -B(^0|ƌWu8sR\9[io7I!H 1:[$KdV%!/~}*i]B(5"(`@ rXqU$ 6 *xW\asV.U%=>ZFp7 cCg>٬Z)19I1Fܞbs*+@X7̑)A 7/^I,lwQſ.!3V[e)9B[/7%u%Iϣ[^P4d ,Πx p ȪH%yH F#jS[+ c #S< ^ oprAmuzXq,U~Fޝ{vD-+:m'Գ_5Bfye (|pIvtXd)˕-!QIzsKv\n%6HEX\s}~Fn:*IozלLgSF*IRwHm2,j V| 9LCp jb!v'So-g2l;SFvkyt9=>3l^{i2U# >>hZϖ)\[.?sfVCqRD(јFX1cҬۋ GFp!yy}u *? 9@Hk'^&,k$] e-$ ߂8ji]S`^O5(̎񿍪XUmrJNbNU6 t 2}*涭9+KmYѢNLhPO==֩\/fy67|y9?sҡ2AVnwr>Uk`Gh2*m;Or96IOu*-JZhD9V/F׶i<w~RA݁ ?h5EܣX/ CrEI/` N8 vjIieV%p@etZ}M]!VX[*wGj=Ж;B!RH'\tjv\CX;#3D[ ,1"@U9S\Qu s7gتIpr++D6P)<|֤E[[ُm-7! kKcqKqk5ĮY06-V PEQ!O#ӧOlR>er+:m"$c[F4ʐȫyJ2# Nc G13'+aIU#o$;"i$.@ kuЗ4=m; (PI}HToa* *?u$At$) $l9''W:7p@2QKIU]軷r=V3, X- R0>P1+z CKqldUUttioIDp`I8X坃++["]K]zyc3[b. ۔}+p9XFźcrT 2O`)E9Tzl̛$ӢYX}ÅXQMEJ:*QһvBD2˱Ȥӽk]^I2+Y)iQ'kNIxYGq,,k#9A?hj34F$9#³溁92Ja%J<8-ۭyrOUZK$׍0tNYH?YzI,_j^X\fa u[QyI"n~5^smsoA+4te3O^'R -EYĸ٤HA A Q;Bdr \(k:yTs9X#26_;3R$!!d+c |lRt.m4S]H*3OB8@.A/ A;%G}rM9c2JdC vyZ!9#0$ҧ$~c~ovZe.'An5Fe剣`s=j(!#y`P'n9M T<;M'4KB;'j\*ҁos@1q϶x8[yhu_$1$gw&ke1cܻ`>M;eb7[~Mef@‚x#*%b՛:m`D1e*˴mjŕl1I?c% -&G(j{gx"ڌP䏐:Y:,k!wPZ\E$2FF@ѩ rke +* 8?AXѓմ`0#,z/81@ϸ-JI+˵AœIj|3,1.E}+'DͭnFfs0SF6IO@%3WJ'Q󖈯Tl@3yLwj嫹&,cp iUCܟwHeN{j$ĹfA{BM2lr@f ɪ7qpr@ngc?%( '5:Aǘd@M֔4M.TAu?]%0:g*i|>Ya ѨJB;F8%=ΐ!B]:UkW%Sj%2dOSPJsL|b=ۏek #f%~l&nO7i{kV,Zb?֥8b89!6ލ0Y[VO1#?*dViZ9 Y$HBGx*EbRF`RgLU!(rSֶtNS+3|ǍګVB\ggdV39*@ـj5;";n Ǐw\QYv廼:0+?f\]WP0wq{ZdԘ)Xd+ׇ\V9Jݘd6p'tٕXjd}qVۋJ@U1s>3I14HP0n$^8[T\5otfȡ~c)WA@\cLqԞGEzUkʒN VsB"А1 A8 I,rp$~5)מG8ݽ]\sPc: ~!} G;I(>YNOELi_bIVM!uT+,ϸl$qj_5d|;HƐmʨ55-zrY+ېxzqRLap9JI#eЮ@a# '#3YFQib4l0rO\?DX)ہg_1FЫuBHz-7(p͏sQߺǧS4~*9"&Fcu|Ҧ-/8jXrBO M8+NCdTb$2YAuOj=@J HwZJMl=:0QBI8zVIɈx8U€OMM$eS5PjN>@Jv2YpQP^M_##\7IhW30 X >UN6$SJ\sUO6 9TR@񿞅I~+(bZ_\ՒP2kRw"}n DX; ؜ x`%tn`=A_Zy$䲗X.1=_{ 砮:+J8_dmDf*  }*ޣiRM$u\xEgipvQ2PNOrF%WhޕݜcޜT1sߚQJoEy܊TI1C|r*E5A8l(~\q]c=bC~Xw/i7\f8m}OSfɴH gSC}#Dcp#篱}.f;KGid;u5&Cen@jGr tj+ mh1A;ؖ$؜0nZ"I#;w(>+6Y9<%8 Ux鶲UTif؟Z%%a̫zcٯ2zM䎘ьUޤS%-0hԃO*I挼*!!k&G"y jLlJssr.i+Uh1`S uϹ>1?)Tf)@rtr)QzdjSFy\8瓃ER0Sp#$>߇SUA C.7ve,QKW0}^DStpH3+&]ܤ@1IIP#§=S!WC!pp3׫9̈=/r/ɀ(X /53^,*q*Yx?Z% !rIl8[4ԞdAVH$y"n8 _zЕG0<!DUL`/l})‚ַ,@9f;wg)gV67dޥIeR!pԉYNIZFSi}R)eT,|:g֮Us0:?9b@vʟ|z3'7=\BC1lc>ޕOݡu޸$r5+1T泥ּVqԅ# Y[^̐C 0H̹8Mi,a0WTՋ[P"]"P:X?ηe2y,OQnOͷ8mO y\)l˥EHy**O3*D+tcLeIG(NA9z=:nUF3:JJSJ9c:M/ɿCnNF='q槝q2{i%c#馎0ŘPI:$`۞Kpțvk)[N`ʶ`$s~'kei 2ː0{#Uդ AsUmEqSp~aQdգ&XRY3b`sli* {{Hl7EY@V$O횫|Go(xjwк:E.>͘ʧ,6r=Tԭf U68<*H]ߜnF:}Giggvgb:(9V0Rn1m,γE .P2,g`ucjՎ!B' '9)4uvG표dWL)SSI7XW1d `?ܓ5;y>߭G3Wu V} )FWG?t<7th"D 0;sW)K  Ao3uH'i0P]鐼m[.jPY۬Х_E1ƒ %,]*5EmM-g;[<2",*ޝ֢gai 18v1wf-T2M>;?tqID)Qף$}YV}:'s<܅?V՜*1 p74t75TH r9 [tfyD @S#/@q]G;U,!`s`q}*K8j]H6QqLc܎Otʴs"| bW v/hWy^tS 1m r{u&I;)8xܰ y*F%H_`[=zbVx&mX/9qYHʦ'ʴ-0#i!w[֡{Hb&C݄%pr)m{D)#kd.3ET EJ]f6 sNpW =,Z{չfe,e:7F9_fYk (o ݏҨ[O$v–|l_(m 1?xDsI ]|`!. i+^ߧȄS޴d#p~l񬫉}Ԇc =TOR>c1*THF*̩wf BO iv*F>\ )@MlrD] +^Gz}^ţ.P䪻=W`OJ,rH*>n~ >զg"09Rr8Q9˔U@{ՍB y,dO24eVt۽Ȫ2DbH)tʩsCZq : 3DUh^(K溚A20^@F*xu'# Ru5WZ8#2k#k nWn'򤴙5?y4,k}^XcUC!_NI#kz}+MǍduE͐ws8VVK.6E 1c?>UX+8#6?(m}Dދɻ!e9U֝3dGb p1ǭQ .Eܛmȑ]-"OrІf@?| mp cA<Pz;]^Hⷲ2OuWerpYHI浟RY2/+ܑ qGvC7 n8V;nWiTƱ8du\ӒRQ(=Q eԞ^A)X7+8'jD/!0W1sc}+xga!d)%яs#tƒ kO"%lX1>N)IHn-\_kg2[JHA$qqW+%\I,Q>cI޻bbd8㎘zGXل8P,b694U4@ĽQ3B#&8Sĕ`rI>$DG,aQy]?yrI=QLFB@oZ4?(2pXdt*2I we䃟nH##FyyxeRe!.01F}HBcy&[uBHXnO*WEsisqx0E#q\ Ǡ5^V$Ŋ7i5nGio{4@':֬kk%I c`K)y=zV2^,QrQ`Y"Υ1 aۭto޶HM/خhK.K(,6zO̹D ă'6}I] 6ZV{gH؉cK`?j.sӜ#jһbT|oV{e'ۊ VbD^`e.6e@CtYJThx$H>ަh ʟ$6Debv2,$ {ѥXKlI|?SZrW7g&Yjzw>S \L^19ߧoQU}bU2G8Ps9ݏC҂x$2 P/SNX% FH<ǸRC>'1ƓKm H*a[<0x:&!%`ޤ:eۇX7%mtV$$ss#8 IEp{sYqՁh?*ejAW)3rn,<瞘U]l"`x6@8=jvGvqǃӥU`afRx`$~o- FlƲtdBMҀ=ڮӸ=*hXYcXPf,5Y VWRci0)P{84#2G$qJW*yI<>ȹvVS$`C`v]ZIxvN dey'JMc89)h;Ak ŭ2 ~bRJNe(2Le[QlpÒqǧ١܍ )Ir!Au֛-BWM,mL*Q)s"(cG/I*=P =$.'RH A8ڪP@NJٚGD6@v{S2Kp I|e!pWVc̗+G#iշe!P{Ժ0dNZd\ƀ)dxҳJ F1=ӞfDLtĮf~r7m R>k-HSo-H)s<(E[rc>X)5455/q=n<F2]ҝϵ^m qLwS4{rM)G(R g4;ǧ*qҎ\Ҟ6"i= zRN:Sd؈YrM9HXc;=@", *#9ȦLةNk{f֣ErFA)֪Hl:} X3Ooz8s> pvDN~X;s8' i'fp͒G:~tO MB֓Ħۋ4T2`H6U7`08+y9ji.vqMJ;?unAB]q'V Nj-AsrܭpA#rCԜn}*y pB8':W|,@>.Vf`O%,>'ԘQLj` [x\Z!ܥA=*Q\n d g(IHfyY\ r=1Vr {F1ZcaH>@@Z'%UeH+rn *ZU"V"$LÝ&`'x𧋟A9Z) #8lW v>"S^N+zʳl.e\Z3*ORQ s˃66 ǷJREM m9?ZUo}I߻К% 6>'֪^)6.A >nz5nb̿;N:=3{hք LەUGp@? 2+BtF u?ZŊ,j :t?Vx)P*JYUnӹ m=J!\k3#?\uʱDctpKx=l*&e:b#9hjn͝c!RQJ-̴EnFݚYNz`:#rDwP}8#ߊִJ,RQzche\2UR t(č O'ҋkpH|H\|px탑ϥtj>fRz&"0ݶk>va cv xS*-;\j^,4a*+N>-29$b%rάyİm''y{Kf ;Pq*JI\}?^.U˫s/vj3Uc#r#D3n(Qٹ<랿ZRH[Qwܓb8= @{rX3I 21rvБrĎƟښqHޱt{w 1Q)$w;G]kb5ܤA:8FdTDUdqV|5VqQ ; t!Q ;R1I ms +IqRW"owWQH8'k1o{b~ r{p?U=F%1#q8bJ3w]XjT6ԲޜzS9W81z q֠rqϧS(8MeFX$gRA@~,d:~͛Rʲ[e.[kD9E[vt/RGWTo<6>R;F^MU =ى33,B?*%(y_,r\϶KS 7Mufx~' ܞ?>Ÿ-PD^>P6X\,nX%;Jl Ÿ8\^gɿ\ņb|T|w+B1Sۣ-u ߌu瞦rvdFDÀq{:s4#u*q3I!8F#zO2y 0*ޡmlb0 `6ҪOzn\q8u\^FyNKSKTD}JԻ 8o9A9*c Xq_kNwKJA* HⵘV~PVcI(+ܪTU7=)Vt.,OզBʆ0.A׌~>j1IĶE"EQdJtgy+cjQUnQy,2[~Qz? ˸1?5]^B01~Srqmd"Ƹ.6?_3kʢeG-۩8pFGn=i- +< }ӌ`t"ҝ]/(D^fe$(@<=vUUhՀ +j0IKB[˨#OLO<198Rs=>\,I$JK`tH˽>b9=B4aeg7P[c`7c ;ƛԖT9$v쑢2›Z@ G#VB$]FqU$[9,jx*j6ɀ9}N 7c t]Q'37+{ gʝǒcʧJ(BI$i":AB95jF΀8i q8#eGT~B1F1t{b@Ug}|~UusDJ q43?Jm< ;W?1\?W,bڹm\%7x(6zmOmmU =?*$5L1{wTdrm#֔`]ZQFI8:ߥA%Ղʬq: p?QjR5Gv'Uᶕ>u!PIQ 泌&T ں&ˋHp?5n`~< xn"G9vXöW#d5J1R2ܥi\NLcd,.p3WEP7䖔Ǧ)4ZM{R{.gd@mm 'Ui:d⧂Q `ΥjR!$qY:v %q1$+Tvߧ('WN>|g'C2JJ8ۊXnDRʐ1QFgۤ"1 n'#ZRhT_2ܜLO,2eQUPq>ʳO:Kvb*HGNJo}4b\DÆ\` sufY:/dm;3t.o併 HQǯCUmVM̲4se\VCH$5yz1R1)ea±mTN" gF$`VLmrNAN'nuw(sQvQ]6*HΌ<+oU囦ܶҟ;G(K庛Mڗ5(I~Rd3eKeoo% 1To:siqrq$@F!H$׌tB] R$2) (9,BOCj;Hmwb92YWaP5ݝw(aDCI@[q#>ٮUVIO#lc?sO>fNAa0I)Fwa i06j/Ǒur[p۾Ȏ։dN,zzndF-)vqs*1M?e̴$YU$pH>SsRwZz7ju2EJgK2]q˴sԂ;sh.dΑsu5_X<֞\ CCp̥ш K%ٻ -U$n{xkOM7Kyӵ8Pb_n[~s;yޞbT'Ҧ%In8 $6Õ8`95<;DG>n1ǨE%b]RkmAPZp T%t,QA*fbH6#g(EdF.Χ~AXW{2s p9ϟLIbIqZG&I1.\G:([HU$IҳRT֥D1V!I@0BAǟP}*}NTX I*ƥW*U>X9B(Q85]s\В?ۘYI>}@ǵs\IŻ(U $ 9N,;7G#Lf&ClIꩼ x=cGٮv~Mi^\\ǬIbllWrG B=jKߴj'g~ :Ғw$85qxe'.cq^wmA57 ˤrVHZggFD 'ۜRO Hd J9$7FFOKHFolw XO#Ƕ+?Nͥ#gGXOk+.qhг$m;ycu="Xm$0cee>Ss^p vOS2} G Z,eÓxe{n(¸ T#%\6B {Qo% ʨ6嶆e ͟id10vryv*i8MY;kbVe/6T y=,ֲJ q! 򏽍ǒ)xA|zc=eeǜ0i[)_឵TVJ7on̥<B:9WHsd,yq;8[%fÃ`j`R¦s<3Iw6R~5ʭ-EQ- ^9gt6$B"c*&:&*{ӣt"a?1";9QT*݂+m;1[ٜ* y*BֱGbWX.x1RkyOB9`7tӡ7cVY1i?Qx-9ݹ_ lu1LrJU00K3v/#e91q#=@9c*L-$J7r۝Iz]ѽydnUp(BGq]t\E'"}b#Ƞ= }Mlcre 6Ѐ,_.UzVP٣<"UY A3QS:qWe.hDΖ\Dae**ki.0e^2h'ӹSSkfY pmĞGhLZdPnƅ0;1;{>J2C5Hm$:Fw 1wpzouq $gpX z=;R2P$}Br?k3KuɑG`[HbI/iM|WHxDRi<' }Es~ Kkxi( Ͼ~nPPYWFf-Ps2X$)lmS4օ+[K'lBP=}:{ޫMv5u2@`ؒs[zhUYo\ƾP#?AT"#Ђ"7ۆ׫s*[iMiVMM#WFXh%aڦYnmFuV_~MݨC|I%@$70Ja+]F59D(9䞧UgNw"}M+L]/HFt;# #Fӏ`k=C2l\%==맶?eh&( PeoPԏ x(&@#ϽqJrsq{ĴZ˲toPV= wlzqtv5Ȍ́$SRŀ#+HռߞM`,A8Gh۷4:TyqűFRPNM7 I^"F!4q O vclzlvkID9rb{;JenG^8nAdTy2+: )FycF>hf>\aUsXy{myr8"B%A S@#uEqIWm9Uy$=*AڹZpB7LbR< Y>S0}O1Tm8erb-w3V{S͊gB#,@}|Kfy[n鵗 =sqJllӑY3>9)'/lFb0#4P[d529 PsR_Co,1?NuT {VF6kh<:PdK.XQO[BuX-jq ekE=-.Tm '+BvFHs$@=XҤ92 Oܓ{JiG]jPڶwqϦ6ƥv{^-*.T|i #^p6}I>~Uc2EWU!?O5bEpA*Xi¶򜜏+%}2J0?0v%U7 ?swv ?g XjR0zӚoS>w[˸2@tVM ֲ:w<6AqȨDkq%n褒@Qߒߕ>8.E݌ZP\+܁~TwZ$¤GIp)`AI^9b&`1ӡ "b҃򫞤5x]*$XNr[%ʵ1Q'R r2=!f A}Jgp&BQdl^:o7qI,awO?JݟB9^gX0qr'Ml=62#'׭e}y ˲P"]{տM4ej4[ZRoVIw+fRKeHs24 ';E9M9wdzVؒLXQ ~oz&"`U}FG $hebˈ^G%2;@kO&[t9hXCߏL,wL>#KH|RECZH ,j8A\Qmлs,QT{tۊK;s $;U2܌7l n88Bn[ã3&J-peosTi)r쪃 b_io/9 h6n?VVSU1)k"ąҊQ[a~?=wdya=j I*"/D F˶U{6 wiQ'4FV^VԉLup *-EeFv#cE?mcK# K>CR*@+KgR?U>r̊_deS 4)K$qPwMr8I*[W%q N**8k rNwaNvLY7%UX7[p?"cyo20vr5C;:03AXy<y$\ؘI[UiM^XzrR`09ni$vq{b%q4 6p+8TںܩUzfɖ|BcHHrPHAu<?i-e|m>c=};U5:[3+ېn sߊuBK0};qj0Ķ3ӁSY0W$/q 9FM=3XR9'drA|> %VRI9W8l@nc_3}}0O©DrӒ==G\y^#959\,׿h)$g=:|#ʞU171G9\AI&ʲGl򳤠)P^rsKI$h'rm)"QFCu^*\c4Jvvܑg\[p+Ÿb2Ar[yvA1 t49;Ti5;NH {87OY-mbFRTs5oˎmb?gPNpӚ{oY0cY>R3p?.ֲ~%}ؿRij7(ǎNHH15$ g䍧$~#?LE#T%Q0yd\ǾsUk 0_!yp>aӻ5lۖhFfw p9qSyWHI-œ=jj+3w]pVヒGœ.=ѣoo.8GYlzgxY2Gq@ŧtGjث9oIM\E?3bU䃜Ւ퓐=I=*LH\A\`8$u?AQג!@<ݺ  YJ̹U` Y{ҲV@ʹ`Ny ۦMmH:MjZlpOڠ6m='~iNNRVcCAt-E9+7=kJRoT$[mcÍ쫐9%v38BbktNANFG<IjL>P)#>W aJ:gerԱQd5f3(r'Ҝ OqXr)kC}9֭DI\UG#3\kIua7S5鉎Uܯ1y-\BI Џʭ[[c;w69+IF[n]ʯv7ّd$?ҟpo`Cx޹uĐ[β<6ʐ9\q5jI]riѕ]MGQGbGʩwۧN]Atr]N*y'xTo#ΣpY6 l9#K`KbkԪlG{! <dhS!+8u#amSnU{9_2ITZ-PURTP~i1jش>'8#_V78*ĎRH(1==sq\՚r+1F#;gE-?>,QU<#tON#[p /ghIduRۈAs˕J=JmDkce' rϧ5GD{" 0;A#$~XB8!GSp~4VHT$KgU:qQ6qwhl*; rTy鎝ݥsmN4靃?sbF?=$Ys"1ʐ0r ݜ\l}Pݑ13,aFW+֜λ.lgi.eJۯQGvRXa%F B}2(VpІcP`屸rIX,*>[19$HgRF3 s$GMYib6 ʕ 28=iFpqTg,A8s۶?Jqz2ŕIWnsN.d|g5bwNhꖬ¤}b};*(7\»RSZK,gޢM^!A42 Q{D0%$ u,㴑T\,6ڲ=UL,Q&xpq`O`]\JG#Ĉ:82}\&]C >2se8'6 f9͐: F+ c֨nI 1)f.[V[4" 䁻pT 0v#xXc+uRJ{,hcwV˜d| @nH%[nvҧp'qPz 4)$d(=29żOw}vee;Jez߉q5Ms2Rfnk+7o4T•C$& ⺺ xsc* G8uR,hB*!,Cϵc\L`D+DL;LkyN4o'6paSUbHxnn+3)2ye]p=A{N%!d`]ʜc.,ps}dp7aq@Yz͞m/y)KtDgukP-vau8ۜjtMpE 7\G1#C?y^Uc<4՞8Ռs?3 p eb0p;g5]0Rcti29(E,14qA]1Sj?oItnMrOP; ʖh|ıs#3I _4e{$ޥL+8ELܫ\\G*gHVɴɻR\I(UD*J3bxO2Äl`HFvɲ4NȟȒĊN; nzmfS[xfUi.a(D-/>IOy * 8e;G֪#μ6ۤʂ[IV6n%fs+WN*+y X}lLn:ɋMrq3H#aN=: jiȥ^q={֔`dvF iV$Ryz"^-PήA I!g}Э k5kc&N'PUT86H+sS^IZʒ[ J-dcTO5K d1UU:9?Zh9&V.2Ȝ1턑Q 8#MGUvLȸTE.#ug1Gp |LiLXod$F@YI`I1Css06v dϒml ck4~aG6D|x'=yI[R"8I]K42d`@B@|n#1؜TVA )BX*"vʅH0ſ*Ϻ%x LnQszsʵNȅRQtz|r;Z+rU@ۏetIThQй) Q()j[bU,L͔7ʹ>E-R.hdX7: ~ gX1)rx;pxWJ֬U `A9W3#[kg"X++qɷ_IqNB4tA<>f#Ts#<'8.&Feke߷fYG٥?gVTo_Wh%)d8pG'\'+}K\ &i%1Y 8϶1Ɩɍ+9a1ry O46dc exaqc K\7#aAbY ㏥djnEh\9SN𭸣溹,Ń:8ʧ\X BV,VcdLZ๚8IG*@Zՠ$gX72 >7 o,eHd[^YH$п0es)-؈kk%#,'Sҫi"Hϴ*]>w/ʄ D$ީJu-F8`[v㞠 4Pⴴ4lݎIN-!&tliW2ۈة*rj߈mf[6fT zkyYVeFr>ջ5ċnr9r:=3)GsZmd7q}儑YxPg(Ē\ˍ=@OJlH!&QRTJۣ- G vӧǙoU$Vb!1޹Hsq4ܜ۩ c@K6qw[!0[l&aTW7ōH+5k@zlfSD $pAuO`Ѯb()1ګګ-BlA\qԳīv8 zv'hDI*1u9Sn~ fIbs7L{{+a(ߑf8G;rɜ`{zս:ͥՆ8֯W@-"bO9SO#LJ~6mR8H5CY%(_^8ʦY[Igh2⩭k nfPX6} >-sK;:('8t\by;Xy>W,A8 )8F&{F8IxֳķRJ1'zF2)p[Ggg8a>< 3 uK*(?29l~XǧdPL69W.ʼn#JiGF,KtC#1;~lmסV@PMRަGPWp\xډw:ȬUNy}~{k!0X?. cyB ==י(X莩c ^Mfa]͕$bX`AdſQ*v |C0bĀy9'NGV.!? UԒ7m(#óу _ORL$A' =n'VyX*I*ٍ خxYί,}J=1Y&r}Sh sV(EŹP;[dM`r{N*7hJe``&F= # H*FX£"$i0UKtN(ʾx\Gw2wZlX=3߽=SQ3]'(#(by>R*ܑ+%O*ăz~.w628Q8)ԌaZIlEA'TK5@ usۃRW@T#۵UiT'i/ ?ICfҫmW%|2`Gr^Ti mFNӓXQR 1f6v@0sXJIY]-q` cww4ސ8'NCdVޭ+E`ee@~c0euʸp8ГYI˕3noWe`;5C@''O>O:=:f;7c%sZK)RHՏ ߒ0HgZΖ ZܳUw^pqۑ۵i~5KD5vD#Q*\y0&1T =x֪v<E9GeG| ba#8d\)U,AFV,Lfё49QG 22  5qVbFܑ*&W8%:1NH` !VJ@#$V\|6=7 D4*#!vuϯlZ,ݓU@U }.QWcyr Lm{g6=ʨ wsⶊ-*JWV5ew2I;rq?ϥKo DۤPÞY" If0![Iֶ1F3$e'ӷ(:nTgr̴929?f;(R$9O̓区G'BHZ9oݢ@)qz=p+/oRN"ݤ4|n#V`IV*`+:K$$y #PLM_(Pn#& 3M+ G۽F(A$dqMBGO_^q7NRV`=꾢3lӳ篥nZEc+ vfR.z{w5+#B@Gh\8TrMssYIb;qm- g0(ڍ7"hSRBW3;A)r8oÊֲ 8Fzp*E-yJ3q%Jڥ]-$Hy'ZF29Zn~Y ;b+ێ9 İ8Nʚosj2+G 1H!!YyjPD{!cc榼Kɒȥs6{߷BG i͒?2 pH=ұ.-,RHmAr:{UKgt";Fp 2HGZ\=J<}Vi~1ҪTP|BK2Gޜ,ܬfWFҡu,N~>J* sJ[(ZkʈXsҳ/uX{m2};s+kBIbK"/tF`0s⢮`i/<,׺Jw [<~W+{tYm~Xq>r Z<0x)xUl.>'y{bY8u}W!=+%Nzu’QK:>Hyeo8>uoLc\˅_ʑK@lcq+TVrNR Ye!Ur0G fmzr?Α> j#*ѫ;=UETrN*+YT"8*xz~uVHn7?6Fs;{o ҆sߡ %*73}^s'ֹVZjĮ>E{ӯL~뙥[\Dt< -ڸE aBjZF Fy?,ܩ'1V[rbwcKn@#X¦$^MLng 0ۮ*HQ \iYFL.[!Psta;ePGzs1Uůe xfS Շ2zc׌ҧNr Y;O ģ]Ó'o*8VxYYY=H\Dn(C:cM\Ƚ{H%kQFI5bQ<+8 ц;B#u6z68PK`/V FqnkpZ5I|6TkMiOf8xDn 3Ǿ\`Wo;C`=O`1ǵ`]It"@#Gwz(zݸyLtP>"MQ>eqH%@$,l.@d8 ;?f0]Bl,*9ItR)n6EEݏb8hȉSJkL+(D6ssGi†2u>O^j,L!@; c>M!ɤvkϰ\]!`1e:x^+GQ,ib9#T#%'E?p,wUWdm4 6;`zIp9u+Yl[xZy1rG>Y׶1\]\_2a7v(<&6[Iyt2_ W`un+vd1D% '$*#x>kܫ_eU.FMȐ|1)9#_Pӣ ƙ ǃy-S֦ w@$ P]QCd㧥Ok E6(3%>a?.sQ\y8,̄] I9 Zƒʱ̫3 < AVR[F*@t ڶ!D +X.`Ҳmh0cjVt $fi M6ʊC cJ2iv} CJ61ʟsX I*AUR f`B BJaSդc*]HLNRpJè^q!'>})? 3:l/Yʨ7t%TzUR^:jl՚o\I,R4v̈́Y$8=GZ-/,,09NkOOӚ8Y+!$";1Ey2FHܡPO|c5̜Ur Kew%[;|L _9l@Hz Ps+gWR[ ~Fo'o</| ٤ɦG2cV +'q+Ө=Ew?-U,Hb$ ,.#F?e`7X!#qKeӑ)&Yr9Ce%۾ۈXW?a)-uF{;cjԷ8nlL 0vx;ztF J <`ʏ5`]CEٸy@:{R^!%ȝCR0~q"bdWWQ&ɏiR1Uyȭ*yelm?5fX㑷ݏ6Izӫ9E(JZ<>fVO0g XppG$3֗7s&D$%#;zZ2KggXW sK PdXՋVޟ4I+ /A$SJDlaq#ﺪZ.GxiA #'$jܼ ;pqvٞI T;Uft1>ԢRܶ@${?:"}@Ѕ Wbbfe )Kcp|$"XarOE#"Ztp *ñ5IL2'J(n҂U$c G-!Y\`<VjS{to4(e1m^?Ze]wzbܿr` >ZU_nU9|ʮͩ Ǚ3`'UmcEG2ɻbHĝ}TF~ *RRUx})CUVmrK 0$YWj؟ Vou7xM|`?#T_bKMؒ%ߖGUFѰ "OB@2X4 ElP;tEh'NɌ̑InY9%9WS `f$z}PZܠVJ ;3(0y$i5lbG hDЩ$SW584hf3n9M8  HYvڄ=8bv vڴFb uGT=Ai"9ܱ!fdIr>.OFݮyi"\ n~|V87pI";z#:Ͽ:6dFĈ+$:g⣹I_8嶦BF㌀k͜ds=6kAWW\v/4gt`C.p7?CTǐ~YfD*ynD$WoCYV=pV7ڡ9U*d,6'賌R""mN?&SG*k3Q[KI4`AZב=\+$CߩOcTV[itIPB O tE:zG$A Ds&B8 q"x(Y,UВsi B,v1I֬s,F:,'=ܚٱk,/`mzVqiةEYdHD!6ާ\Y-<~uuڧ~Te̱H@@ji-̱yylbG ;v 9[DE;I tV!`*''\K*dʆy'H]MJKORF?ko60Ucz덧8Z:9&ћy`wH` =f (K)oO֍KbHV F1}3jl^TeT0p6A6uv(U=†ecG;rFW^pt9O6KU+joDV@YH V~7GD(K{*zˡs}w-BJ.a.;FzsU [f{QK~aEF´ *OEpGh>0m(M'򪋪vRwjCpA *Ci()YP".ÖjۢCYFCbTH=;cuu_h81#5k̗"Vkeidƒ+?LquRr''U<<#,c3棶]ʦU|w.1EdBOXH?"l^G8$H7:pg bk 4\ݸav%ڲ+=ȕ`S+CACUgvS$; cu6mt3oamo9[x\mncQ̕B$NTz-I4pvsN@ 3+dd8 pJJQ+q%H^xHg.F9kkkW OQ\8Ny\ ]sU`OsWQq޻>7yG##ƦUKݱ*XHve zdVfd?w܎}GeKv"ܫ˓w$Tڅϟi,DIG!S>hdBVv5|=3Y!yd{q]ŌW:;#rőA$9(9$"bdl`7vt oss(0'?ݮZGE>Nt :28 "zl!+ i.#,]ێ/#ѭhfWydfAt<̓ßں-BO1c# >D]+7ftjDm"Gvg5kZ(TUr0_5˫"0k啛&"-yTM9(hdZ.~epp~aK#u;B/R%їa cAm:1"I")SzSv%'v?*/ނKr0:QUoHeEQ\rySw*d9*:\`{Eat2$ ;6cY?5$rTy'ک^1ɌƁkNT܃ҹ%Ɍ9t`CHw`z\UDmA{oƧڒ c'0:5*N)8M[#i bepzM$pny:QXWj|4_?fշ oJA߄ OEt&E _|?MViQ:~4#Ct)Ih.V erT5RUDm;^^3[Xm8pg玙?&:ŤETxqK_sx'~xǷTa7o qI-deȪx?BV92: VgemڮF6 NZ׿t[ON'$"! sʬ`LF废~N;?ZY$ƻ$8{ӖDeA짞U_S JO^ك&;x䃌q)2d;g\H߈2Cr79ED*qCcG8V0I{ƢaxGPIZܠVz>f(X\ea P x< ]F=p|+!dͷZⱼFz?I?L0 w7 v2(%ZoS[%Mݚ Nni c:WmèT #Q8ko˶S&輲Cp)_$I722b ǦTUWY%qu t_(yqFg{sTp]*X1?*b7Uv 0 <.WnN ?ΰMBwopmYH8Rs, FO$H^Vt-q/R'8CڵvVebꑘn9=ճ+V h< rd$ ߎUIy.c!yrzOQS;܂(ŁDY"xo GG,TOrMX"]\`1H=O&@TqGrʲ|A= ke4,)\{%2L;Nr@8M+4Ge I|*+y%>ka!>kVR[tLVdr(.HxVGj~,݌{ L,K"nN@{5:QSmJh{G`fQO"UF1=js![8y`sw~-::]֓Pq['.\}) _,esp1~Ǹ Æ+52! &q!=ϵp*2hi䚎Z FQOZ}?7${z~޳eG^ `ǹ;Œ OG=;Ң*WIlW$u''zvHm]sT0g`A:Wh8J1+Ӭ.vˈ˕dVCoAGqZUe'ܖ`0}@ & ^(Jl:e$@Yq`~b7%g99=t,!ȅ\:c?*)Ψ@=S2Dj!fʟ{MH}DMPTImؒF;~P=ZѹAsґ $|>M+%] 򞣯\WJQOޗb;$3]j-#,Fz FB8ùlo⸶+;>W!ժlHyZMFx=1]۾xX܉nu7`ޤ޳m !JbAPzd +(XXCTG,9?0?wbM yxC9g%ZwΘF ^}:? c`W8mbf8\n'@6E8-F줆qלsZpWȮԩt17tyQÐ*(I*ePp2HU[1ٳ}zupdbXr#Ig{)l.[ErsT0 g=A(Pʹ+{ Z'ao>SMZ#WklJù?=G[9?5"KG+Jݒ1PE]]6lN3U)AF:v$Ojc`JnUHm/mh?^pq^΂PG+:IF{RQ#nK`sNrr:9m؁dt+!H Ozgp?g-cWFUNsµ#}Lǀ͞1 6axHb ~iJ)5Gi4 &;фaߡ95q,b#[pxٵv$Z[dpi.aW7>bzzV|f+Y#\uGzִ6Ao #U* 4x8NvV*Dc\7q>'.;Zkui˦6d|9ɢB 7&06 sBkҦ"\yU\ .xQ{Wbxі%FIOY'9N} 8>VUm}f(R({\bn R|o, 3@݁'YKwb"+2wАO?H gW?ҲqWOC-7hehdo1H$90ߝh$~G|`#v0cl ێѻ8m7 鞌11]PQ瓿B[Ȋ/]69խX,>pI d($ǽh~lQƛ }+;Vf ϔvH OZWM~BusE45˅2.lg#?}y&xڄrF#HXy^O-=.8vp6Wz`QQRۇTq\}x9GCDt4[ 񴤠`޹i4I :ɪPѾ̓@g_䉦(FqMVfhe:1#FWwY(mP lAcGrO@CLY09tY6݃t$~#ַvjKisZOZ;4eUv2~=3X7בSAv+4Pn9,ې8Z&Xܮ@ {W;ҸWHHmbN@-tUkSrɶE6\A*CnaycC+ )C<y$`c޹7I%2 xQwv{`0?%W1qQ9[*5"pM@FӀqҹZ91 āT -m 6w9\yNkE Y-{CT'w8 S ́Sp;Ǡ-+CʒfQ BFXr3yh BmO랹U9$v!ն"4x m șvnB˃֦d:tND#Hfar6pZW"FR"y**aZKFcqQC0䏧J䚝F>,=͸nZ+HQT61ذ8'ޫ߸8E#F}B{ػ7Œ?$j<iv4(ebxݸ>ڶP݋M]ۥIl9C!$ab9e򄑴c0X/R`p?[8 FC+.GQuXҘ-[!VՊ6P63"^Ӈ+SԆz=K=О:s6?*e༳f`Ll|38oz4Լ LinLG   w&!2mlB@u[{^q1 +MVSFppSކݭC9y!,aNIdd1ڰ 5‚ݾ֦,J v?1jVR]D$ X3bp~ԌYD6+KZpl%wIJIؑQ4tvrZڮb<\&ng00nnr:{t{MOMQIp\G"nhwd@#<LqYe]OB\I)鍧[2ɾ8"$l00:h7.eQm6ѣm~^9_Z_Xd` 0s:==Y $vtz}8VJızNⳜ5uR{+VVc.y\['9*~PrH-4+{WnѮ܌}ZڷTH]@SOZO=cP_8К!;l+}Xvⵧ(\*OSvn5K;$$Hۚ|75^ 2:D=^[ v2>nXUjJ;CmnO'ks5se{"M^,y%we!UpfuF<Faw)R@ Ė>[b) %Ny,qv)M.N-s#QH͚nq}e >W{Y bT H?A[~56O0k7 (=cՋެX{o 5Кq2o([h|;0RÌSMaO̥ ! U_@~5n &0B[c*Ϳv% sҒYxHaD1NxzwZWt†qbB,pю4iXWOXٛrJ$P[I^ԩ?9F2K#֚|hTy7ۑԜE>EeHvpn}j6KA%S*,nyCFYタ1݆H%qmnon#k$GlpFߦVkۅ) I1q4f{/"3|+_oMť.[h %-r˼F2H;g#[_69$TpaFB] =: ; $0[ I(`8\U ^!9R]{0ufʗ:r!cbQ+3*/a*:@܍ЎOC\X$( ح:gLcRxm&#x3U17d횪7M a/#˵&90ˁy86:<(i!18 泮lHTaJe'FeN1&ˌS59e_,BD*_!FMvmI,,F:sЉPC+D<,n uEGeە Oj]i#cNwcU&Eu8?1)s1_G*%7~g3w +2\!eSj?D 3h$ 'b%{XVV{Yb!f郞9q$ݩ/=j)$r\,L_*uߡ 4o.0ҙ ڌz}xȟ"sHJgوmeY q'j%v2Z+"dkJM-73f]byC!ၮ=J(R8 cfUIǫb -$"%}#iH*-d 3!srcj bv DoݟN$}jI/`1ysM 2rsTuݑݶG|p_ W?wz" ZXq}.ist)˙X~uX TjL|SpgNJa3mnfu۳銎D,эă5"2R]D$2\-¬olrkFy^9e,Z9 AOUj%60fݸ#H$8 F:`4DB3ImNH*),"8l ydba* b{UO ڬ|*${Q13|x+6GL)#nLl߼U qx۪]X\γI[D 82t`:ŻYXU;?tSܻTՓFnR@9WO9C"GwsU9e.ѓLoCH'k?{bj+qҋ0.tQB‚yǽ<xQh7OynmMA4lT}+09^H." J<`r 6eO$FGS:t?t-ԑAj;{jP}GLו n-Y.UԴܗy.cDEgh V@IYqJhHQ]H9x'TMEKI{v'9iN\sr'{PF;xQ~I!L=z県UrBȫ܏umˈ! )?3c\]9E,eH7l䃜尼HԜUTv$nv8ێ_ʯ{FQ.XmΗ%nHԀ I !FX>- 2O=Zeզ'w=-kv/Acqg(83Kd\[#@&i Jmï1}jEJKEj[  635`!OcqLib@ї4%ĎqpjLR\8ڳNev쁹E };Β|X$:f$z]zwД# Z3Rn6'D=i \c9{/ ,cĎGzNF7# 9hH:lzu*^MCw3 Op54\$ROyx^޶Ll:?5;vwzUe.g#=*()Y#yX(NqAƮXIW<n̨\5Nuaiܰ?\w#>mЪ9NPȇqSڣᤑT.Gnu)Fȓ26}zT~B@-[hQ/~KA$ɀlsxo$+/ fU9B1߱Ҕ̭Cn4V$+H!FߙRxRCh@==3+%\;8#ipx$uvATY]Cf ;c9?Z_3mEY+2kHnbr0:QD<働9'Җ8gғ*;!t늂vnѥr1`zHڴ,T7l)۶p屗c8;f`eI,y$m?P hi A$ZJnWp9>}مFϊE@s cq׵fJVll@0Aٸ[}rˆT'8stpϸR@pO\SI(5+ȯ(h+wT(%GsGqZEg}jFK s2QMI r*^NJH!>+;FT,XIGU.<ǹ>б[0 ϗTon#čoeN;sV/uhҬy##ҊwJ蚗q-\<בӳtښ %[-S>`<WaO1H8 qӜv]DPTFF8'k&c\> (}R%xbIA=HEoLEuˑxtU+#sm"a]c q=OUJ߼:RQ2hDSBD'gA#D6?(u?ʋ(b}ɟqueaqڴKإR IU|\yjU/Da!-A3&xQ+!~xIkj"3$;'80I:$οd qX9V玾 _>3Ȯօ3MyPDLğUcx\[|a@ gIȏcl##Z-pڪ3L nyltEG4سƧsL@N)Yzˀ^[ߏWmi;1ugr 󑑃*IJIY  1xRx4+8X| Qj)I%N2Ű;׏R.RZrC,P@F;p n̳ۙ8lcF84)!́>S,qyHϦN|t+7-s1'ȅ`]pN1֌H`9Ȧ c^ݹOr:TLpqJ=5ݢv1f vdAI/%,BFN1%]7p䟔1f;픶h%?Etۛ[uʚNM(Bbx6pG#9NK9L}Mc׽vb_3p&tg\,!c aOe'>5寛a,>a2dejI`#|19Pr$jѾ;#1 NFyzYk=l25cnOkz8'=I-!M("%9?ThjW%*n+nE,Ta}~u_Yeo%Yv68Ԗ3h8ϥ:BK39')RpݧfDgcd6ܩGM6R?xj2>c.7^@qzcmvxDH<b*s媁B:vM "H HPA qsI:`ey\λRSxgnx<+ps<[Vi<&'B8q#&gk OGkZuROtVÆuN@%~Sz~uGW+={`R89'h^H핮7)>^Oj>W ̾Cgb{s֟ưu@_P̟6ddZh$ʱRgÁWxQ- 嶏І Y9#:$mUV<ϧZ4T彐_5s@>BQcߚb-8}z{WR=q]0qdՂU2GA5wt$$2+`d0$b 39*Ttp9c?_aZ:gނq{*c@n+G2#liddВq݋ ~~.0OGp*k| UiN#<Fk E-YtLI- 9duZor2zGEdX#y;v%TsI$cZ}i6{Jn1@ HȹZjXn@]dviwl<xE`0"jr[bzq)G+ v2G/ez9ӠaR2Ƌl)w|O$f\$G$㞙 KnYo?)~{dmm.$q.)=+vJ;@dgh)mpہ)@9'ӯJS*r8?έ ~fvWz4+'3Ub,9136C3"G F? ~5b%i cisDUJvE p1&4uh\eCg#ýZ`df*6SMTW2|IRzUR* .ń*3z*9I @Z PyXcl$.TaL$sӑVBr{q$c`H?*i3KV@ܹzq5HÖ)G3-$Rhn%ʄmQ}Kd}9,$[b܃ГU& i(Ip+*ǒ{:mb_$/!P9<5ήu>[s7bŏa]Ll`V-eyo06c=]IK'R eK#P\Q,fڡgnH)P̵Dʄ7~͠~?XM4b)fu-ͺc cMI'cpB>JrX(L{vv5D;+xH9Ԋkc~SQm.y$ ~<*x[8^:JAimbp~9-Lk7%Sؐp1CPOk^ $ t߽w 9>9ǵk@#ء-Oo^' Ga#߰kɈ>f` K0` usJToٜז8s x㞞J7-V#tFL \Km`E@$PW$8-zZٚcKJd w;AL*҄]Nݙt,%hڛ~PN2ZѰH7~~l+i0~rwdǨ k 8>^QSGTK:rMk22ːNPqߞˬL ?l#Өu\YVq@?q[֌%/_S7Qq_sc"8t?dGn^kv Bˁc Itk[Nrq 2*A zTH-^ExhUI EDfnΨ>G并&78 9q<]T'elPᱴdzulc<4Nt`\Q5(R#zE^n-LoAҥt3҅vc3f?1-h#Cqq︑F*:1=H *rX|`'81ғsΚJG[|\ccC̛Mr%$c*M/3'vr=ǭk䊿T\;ŵ)w (E82921N-, i`A`>s$ v=Ec ާjn|bU˖H`Z%l-9$AY£oI~:J..Ze>oJ+G.z}.tEW2ͭ-,a$8N2\g˚NkVwnXxӧ] Ο-ܻ&  dpy qbaPYU"PweG8_SU)J2RU"2VI% .BeU!g+:9TڬZUId@y$V@'gi&ٵ>l:ϭIVn]5tꠟ0sJ$`'wG3W~HͣN$TQ ėvC+ǧ3Q6H n¨g'f jۚ(Ҿw5n)|;Q! 9F.HR\W.-;Gn-vWzR}3zfbe2,$9MtgcvSq<Ju=K$7jY4C>HOج)t 'z1GWX̰FTidP! ܎ XUH(61څg|Ҟas qy)g>r}ۙ iy{$r@$La@ާHx(BQN{IlH,[ݐt 3\Ud64\? S؍! $Z+J\RК]n;ZD̤D0~:zz[]]h΁rwuBsް-bxͰa|썺`?pN[?kڈ>n0W% (CӕSvr;w{q޴^]FD;#|79>!m6.fr*t# un#͐$LwQ9}qU5'䮼i5NGm`Zpڵ#Tmrc9:=1򘤖EE+(!Adt:d0N6Ժ jKI+>G  y㩭*M$9K'_D$dpy9c34YMv铎ŜqefeRwP3BAVTaKn-fv]c c>fBFHV$z")%JY񰷊٬nAϮ1i5WQss$pYm%Xi4ɡ|;ю#aʌp985b7jm&?,C;l2';,@I<2.Xq=iɥ"/!0M0sH70I~PN;YOJ4a|lA zvǧ9*p NV.Br֜ݖ?yz7/$|q'FOlbC̉ (K_qV#QV,BhNG݅U8$y!aP|v"30rqgJ3QTMkj^]x 6Wqv:2YIOՋ!b0X"l uq\Jo#y\ƍ D*R..--XA y [wo|im>}AM.K; c`!p ,׎OZ6Mڞy[DQn5> {x2=w*D ݴ)O25w:}xUw0vޭٺL.m/1;Oқ|Qif+G u NOUc)8C0CkuSt t}VTV-ʝ];rD>_z8sZP`Ewh0_ d j $̡c1v I:Jۤ AWVqkf҅oA"DN\16 _RJ*J6XR87S5د:FqgGϩ4]"$E1%|DfRsϵt:q%x2<bhKKe̎$w7^Uw cR撱v =%wWd&ʎ >#ELK 8M:[k:a$aءWz1Z$rUUYvvԫ$ЗaXϧJ2F9=OPqȈ>eS6ۍDD7wF&?yKy:G#6PGaMbV pBQJyȸ*  ZS{L9Fr7;č,r6zm!Wg=Woud9AlzVu͜IT30$u'ũm(&32y&0S_HЕo"8WޒȘĥ*R"^-o r 1[RqgOo3FXg}H|#ľZ h@9534bwko]rs֩m Q;-J*~gM9KCHnt" #$ʛ[|=3AvVc1zg֛$%q^1Ҙ$<ɑ3/y}*;LSa#@CB:sN;ps ]~׌jMuGw+tܳcq$Rzc8Zdd1`rvr $nLW*Yi,@!Rp>t2+^9XE!9bijF'0a}jg`w#P8/s̊6zeZ J.Υn`  k{ۙ]=ܦG9둎CB+Gz `gYҩK!X5ddf4f7 Nptۀ=EB`(A+)CN12{Q;#*p$ 89<Ϋļ 3A kcF+iKqە rm\ъ6vr0DlgN*ihP XrO4iZ#,a`u=R[L%YVMѹR9WnVJwzgN F` s3SU=>QZѬ/%HI\i?:#$AQ3, :fE;1ir[|;!a3 7q}Pp!œ7{Tu$x`2Ҁ͍)j~uRp܃{uBmud*ICʗ2;YPS ^N" ݉l|$ʽjΔ;"[mc\y9Da)$䞸S#VOd"B9>k;"9\mȯԼHNv9?)ϯc603>B.eR\*3`g>?&Hz⴦+} tUZ\<-nx|~֬ibGx_13tG\jL!pc"e8ZH58񫥇uଋ5{58`]$ֶ l$9#iu.C!7z5<X@J7:dvδX.~>cMB7zKA[xf2ynY|(s]!P*0?XyVAC6~K36#(M-N#ޒbF]n}693D|瞠~=OZHˠ7Czu~5] #nBB[kL7r "8 Trq>%Sl$:?6׽ ^6+h1 1ϡ*\y .$g9%"&X.2W#<:gylWv㊱ xWgj/ʕ(b`1h䙈v?2i-};jg]4 iB}FĊnb`ÓUns aX9=cKcYMVFf+Drv6z`*Qh.g8O?N:6{,bnj2L}?$jF3$SzONZ5ԀJ9DZ\ʦ2-8,+:̃1?SZmlb\ƴۃ~C$qI%T}y9mzs qNe`΀__mFjzi%f >9?wR:d ܆ܬ1 9Gd c 3\rCU4p :; O ?NtvHnFh| Owu;%XQǷL\J"h)]w=2?=Nhڜy}ԩڔ{!i3I=Dy!+#\|v 'vA-5ܠ( :{vG̒eΣղ)$#`fW'=x>sOd $0a!*Cw~fldXaDgj72?ʎ Tܤ[S!hwͤ. NJz"ihZ ݰ%s}--.do5]crCg= tQ’ɬn򖈪Ҍ#ȷ"1(Qc wb1Ӛ<_Q:7Q*#D*kc+lԔ\eb0Thܼ1bm8kV ʠI1TgiG,I܁ʜ]{t[o prHuB!r ?>vAcg^<8왥;J`A jp;Vm'<%^(\d1_N8\c!ry8kè Mr+d+c^:zԪuixI$ 5YǗrۗ+ʓJm "0;I,r9=x2`>яzPZ\䐦*!OfROߢ9ۍ&T%C7+F *5ro#ŽH9ڦ^to6w"'VLwR<ٓ4qͰ&7*).:(ݕ,+>%1,1 ӵ '!spFGWK?fyxWN⤼ƱF y$w#o^$NbM%Գ%=͖sV'ɷՇ wg$( `W6=Ơ {T6BBnߴs,F;;Vԯ k rːP 1 gّ(,u+Hԗ6Τm@ʃ`yE>I#ZX-O$p?Ⱦo<{-w 򀃷qni%-X-č,{cҝyYh5康ʆPoC;rT|EQd?sؒJ-GiꨜAo)L-^ {UX&fb%PL#;PDYb8a{ԋymqk7VR t}B'$wv*N6p*l㝜d~U;u"Υc<1GGPy5yaܛ,G 7cQ7޾3Z3Y$%b# mzhsDNb0>Qt ~laU;g رW;'UMCGfi ;̛J$D\==sץfmLRYT: d`ԟtnQT&sn: Qմӥkgy.f.RO@G=:Qq3 9k1yʞ`VozӴFx久%D$gl`5Zx,t+7*^6U-m&8F3p`AZw}v#x#"<'w6lgڪI_6Br=çZ-v#G @HAX{5+=J$!a,N;j2(F­cwSr8tCo$U+!|9 J}G ΄Q-É9ZRx7uZը[ ۙ1# (>}Xz֖f_-m&?1)9c0+6{Y=n 0`BqU2%Z۲Iu *{0k-(+n[rz'r=Mml19W'Ny5wVWS ' z޺H 0`ONta#FaϘXa]$.RR=nCi7 e?u99<IM=ϛnı!ߒNC`kQOJ FOc~; ږ 6I;=㞇ӵli c6CO;sߚ,'q䍛 Ob6vJ:Y`2{T*.9$}J/'rUJ8.s|p|3Kh-F2 *_KFyo"fG2{UaK #?{?&{ϖi8څGB -8ۖfMF5X.Bk`v j_*hĮTb2y ROd(KJH8ʹʇ+ 1xVGweܩO\lµdqFV3H.X,yP8I0 i%"dn@[7yvS™Q^U3*M]O \Ն:|ɴ~nD` 3&U8=2N8RQeJ PeF:sAϨz<6]G"lAeMxihm</*S"$+K@XqVW nw+r[ܣgrۋ1R.r\p>U =ƛ$,b@=dlS ڐݓgy6bPQ|ֲܤڗϣ[֋47spYCOʹ#oR䟲0ZB rFӑ3*EBۉ8X_c-mfynbYK<[:dBFRgq,TpCa6SWHb Q񤶳m Gb=. uKP1=:J=<ˌ_(Y +B*(Bk&"*m$OӢy+h&2[?ZmzV(iߥmJK^!O- N%8k^X^[| /cTI-ǓidlERvy HOT)NO^1T:nPq/C\w,ql]|y| $z*oe,n:ֵ8#Y2;:gm'.K8ZhfQ x;wp>c8jƾ׋dC(0K#TyWh_FѠC(*Z%,^c?(S&h9$r@ʭ#jꤧJQrMzC6(?xV.=ߙža#];hti$]XjlI4;9OEݑlIo0=dWh|nW wTվӒ,{Kq)i?^*Ik_ c^OȥH cJB%K`|qx$p{K{)c(կG~*Vm#/E.wE2Iթe%rfP!v6R74V)H=XYR4KgI w jME%&5i^K [k;s+F]he2gkF[sרrg4LB#&r9>uWvXda#=k6s5)N7FpLsTl@*'(ҧ+\4&7vy#(;@5r0yRC+Hx~Le$vqԍd(jҟA;nRzz*t*o~ڛ.K'ERycQbv8|RLVD" pSԎ7P)#?As7m0 N;c;9'M_ŵ{hF.&kƎUU6x8"-xM>bSU]ei.kxX$UǓk\.F1MapplT"M ݃V7 ca]Fk4ұ: r2  $.@ J?:aAuuΨ0AqNay3X]Ј>&TÜU[92pq*iHn$~\!]\TeG\Q|"hT$b0rIKܭ}"ٌrO=u>o0 WA~FJ@,0 `q{כ6uPj2_Ob1vqG3z1#ڪX1PJ؂82cwcD2( q #qB\WP8=ǿ^%ͩ%RM$2  d8 A=֦kΔ\^8kFxD_3N)6͇z5ԨsIF#']*~cmB ;g:1@gWonJKCIGҲӒʸ d嶖sTװbl-m@YwFr@`Gb09ADl!J ~~E Ko,gdTۮ9N;~Xk^ӫϤD"HA0 :qiУ1U,J؀w)5xzΥǙeCN^Ђ62m98(QQU =*u*d1|dgӠ5R-_c6iY_3p=>㊎hC)mpI^=j36`D$Kql4:~N qָOI|9EYx=#Q׭m2g;%X) =''Z@[ʌzM*4+ߐTf>.#tς3Xy{nI96:"A,:ǰ8-@bvA=G}P76H۝ wLl ʬi ҩ̛}9ֳo.$y$gbʁ2Apn3]uq+hE:zC41UN0ݝϧֶ4rs_6W"%P29b2h۲2ydes\Ty[WV{FUUa˄'@]"fsI(ޣiSvKcsv=;ӍW$fӓW!2^!ʶqLu4b>W$sb91F`;JwۧtEJ׭_5[qT5+BTjt(ʇrB'@²K۩eUwᓟíN&RF XSo Soh& FOĐ0K GfWT)*:&mɓ|0H O'$z$RWhgi4UxY$q9rSޯ!.8a{Ҝۂ8?2?6`Xݤ` A"ȹ8Fۨ뙤Mk:wt@mB:t,|"|G#؏ΥW5e 2X'ӷTH(VId\TNf▍ԛR3`9 [DKtV93Y<$qI$.v qW #hS(wTbr+&QCZ+HFi$Sla`AY6ܨPG-?$iU`r}X0+MA}~sn YHuXvOQ9M+M!'wȡ\gDS!(FA߅1eLahKiws?^Q$ELfe+*{6ces6qv8=9j$V`q, 9qj4y\kݽrqI'!UJv A. fU#`DZ[qq$Q[K{m$>~,g3Jr|)u-OU&EĜZMG#8rŋ:`zB !h*=?N X6Ӏ9QVad9#?!Qy֪v3ۓrD~7}*ʷ !e;p gw##NO!(׿cG,`nݛzx&I^!X5/ cYIIi4^Q1Kg,*w}ޭ wG/#9 O]0G 424|vsRnMdwujPCrn0?`z:nu;W9>ҹm_LIK6*e;I'NV AYOl9µ3eJ\RGIrgMTnfg'z ¢[%$7]@n䴊8&Kne\VS12&´zg;HǠ)y0Or1Fm qx5. UU7W3r}ǒT2sHI~A1[z5e\_mtk#y2!R#P)%TK.#l|ҲH7OlVsgޔNW~[ĢHԳU Y$?%JOۯWn&eHcXI?SQuj/,!T ,eArz x?QkG|wdىwu,1% e`q;H8 viUf% C:~f•?QW5[9ZۂD #l_$8.}ڗJҚK[vx$rN7nʩ=]B,sm/wȻwj˜cfhv@a,g1h4* pH IEQ,彁vM[Fd$ m rOR<$H^r jx&YUJxۅSUFTR`0GZ8E>n{'I3yJ24g~mFkL#P<1ި؇ӒSci'm= $g'.jiF݀]žxX}k:rN\K-Ya4@+2`y p@ڂ5yGؓZEK 1GeV+pTm$TqZi$,{m $צ:nߡ*nmr@[#o 9*d[A>Mgȕ< \`9=ᅡiHRlcۧNAƧ4:w*N5Dhe$,0ezsZV}}0?5WJX@pS׮)inC"C p[' ˷'i'>?J8}=*GYbxXܫ3?% zrG^o_QwMb׉͍ sW%8!%]W?@i4(dDpzN%K!F![8$cl*ƌ63+=88۷p} }JICRK+qkq4h# ێ: )t #F#-xH#Kx`H \Jѿ9((oHÌ(<}*i{NjE5Ē|$$>KuDX(R`zM2,6žYB|Fs??zUr`KH2n*qjQsk{),V7 fWbdڤnl8U[S,TI=B$`֭şFˀ6H`3Ҙh#2T3^=8+n_dFI-U\b²A-FH>T,q~WPbV[vr >u\yStT9&kFLeˀr3l :;EB=bb2ÒrNq,ከ 6OkBT0DZҰ8׺4Uʷ+ v*NyFAUT\xIFB O4˫kVwgc`cV.G$.\crjnj NZ}J؀Ϋ!Ý{ekZIBlNHu ޲ZW6X|$zt<{U6[[3B(@[ną&"V9T>@eX\0y'< `c]A4~X1O%Ip- oraъO1(K荖Դ3M"\Fwc,pvN9$>d&zFF ]?60U[T6p\(c'?t|,gfwI'Yk!2@$Qy*ܵ[* ~P$q\֢=ک$hM\'ǎᦝ rpA1QA5̒ h#̙h?ݸwҵPM^&qoPdX8Cr@ li: =9#h[ZN~p|ָWN_RpzCXLԔSsrj8,㽍 r& xnSga1E9#TɨE(GgD(Q>cFSay'WhXEiԛ+݇, ncާ6]]yPL ~^,8"cE(*eN*%{YHġKeXgk r\͎M:_ A+mB2 F38"%IHrc;pG=(o%JJ Vpy9U ͱ!1@2@=郓j-|̹[\XfʠO* X{jWV{[.Gۨ޶ >k]Bdl.+ŽAz|+/Vf8!a,`q 'tkRL2ɰ/Jy'hSǷu&dǪm8ƕ^{fld!Q'$ZΖ!\}C Ð0RjVtsi4S28t̛YI8y۳ #lq?sVQ؂;fX.&6dʠ t5涍-Ng|+-uJp>`@ TH_JF}B|8/0>ac9u:T\ѬRL,4p<2 sGOzZݥQ"d]k6}I$,r Ŕ.`ϊᇝrEnXd0_s@ue"TCNf2R# Q >MV)-mD xms%zY-]ZDrXpy=:CobAU'ؚ8eoV9e<'N@F{CwQI;@2$bAGƫy8g3 7)f@s=l⾑0łn é=wZ禒26Aodb@a$|K>rx@>Z$EWVCIϰ7-p^5si&qP3!'::]ldx'Qkܴ̍7I17p V*P}=1PޙfY|e}* #j->-⹕g7*YQJ0jߋiL(+~ޜSmj6ֆF&g@T1ܞwoujR m2,?ÜgPY ,` Wg9bm dBo1lit528.t5cҢ°GqR yG$=~*m,H#Ï֮t/$Oǒ:RXD`K!@ >kjU_*]L̞fk*^B ƅ88&A%/bI#sQ%)!pÜ hhIw]0x=VU>wjY9H$ \"Ϡǥ[@yVSr##h-ȑ@F_9dxM.gW!3Ƿ.u ֽO=,ŌAˑVOT8 p'>JKy,MC!")ԀXzUF$xUv\H^E%N7+Ju{[ XT;lI$Ӯ1]Gn(.w2ʣ2Oof[kU<9gDR8s]MC ^Gn[;N2}c\Z#eg#YaϚ#zt}k8cb]9[hU;xыO'IUG(8'ӨZVN%VWʐ$=j+]IuH31.Hcix H$ Y/_Iw:jQ%yl!ġoCZ)Q3}v@ 1%TC)Ÿ*+Ϩz1m ȯ_lƢc6B ӇRM DE#,0O+Er\~nGiY]ȶv*cpqY8E{ԫ$$77dTh.'…DH/Y`GzY!E%Y7u0!Hw*2\gҦ3.uwqjɓ稪7)F`\{ԭU4 OHIC&pPTyU#9LD I jԷ)G#:{ts[, f*7 Pq]2,Sۦ~wuKrDtR}T-͆y(CXjUtm ?:[1)bq Lf\3:gd{NO'LOQv2 ~n^:9N;HVH2ʸo=+*ӔSVK .޲gH#8?0(*֭hv4ٮVpqҊEh˥;XŖx$3Hvn?hQG=vDهjdR.eu77] F;VR-yhm+Q鱛f5x נxas\  q $8%H1rH'ZuF+H@8P F lDPA[sU'yAf=:wSV[1Rom xфO3#i!g?ZU!VqTFp5ױ |rߑi.\:H! ;2A#}nY`vɏ-~nۻ_5cʶKeIO {tlRč%B%Tr*m[t_3*X㏧󫦔*ݙM]?MVI#FpI$;}}I.%ّ5jV5p; d۞%iIp /Ul+#R|mZ kl|GI?{f1}qOIWf # w~cml1c* %qiNnJj7lP|7}jcIԲ (4d9y­O<*MF<.=F @@T?֛s6ϗ#~wS S' ^)A \R';= R+̙9$U3&! QV'cyDOc? m%rHh|I-;ON)Z't$mVek#tjg0zUTϦ˸:9] OS֢HhU< ҹN:ZI,EXIq``R_K2H"TpylEYef]I 2Q!&g pqkhӎ\Hdb Hgǵ%Y%TNO~5^UYDC(Teu*Tw\S-^c#3"6NXTr۔%fe ^|2FWu.b <ө<{՝jQ 2{6o 5BE!1$m r9!Y{JMΙdd LQ>{S'1IGp8;HFxǩ=XHdXg~@ی珧ZLoYī,8ʺ9-3orR "6sG.ݠN{~MZ3c c8ፋ㒇@{Hљ#uem8kM.P:eeaE#II'9)me)i<@=xsޛtZ]/$"|Nrfj{Uuũ*Cc9\Aq喒Z#Yfac#r?Ҫһ Wy=:qS亍Y@Ȭ $q'CP.deF1qVRa&ؘ`'.G_W|ࡏ#i-H;I .q{veR/Aч"rs|/'!5=Rd CKscnՌҋPmr>^x:r0GTFqƦsX4K^[Ux-đݐ? 37䜟07  txc)v ߷(Rile7}I0'=jvm N>uXQUIvù^ vΒj1385b%OojPẁMI3IfGRئ/wn>9jN%PK^]Y_$%cj-A;A:H7(Kř_p!K=zZt.> ]whNR.T% j1hL\Y>9ּ)$ݽ?Sq\GUbQfr'tcH[br|Ė=zslYwO1C!m?}*gW`\H$|>:jISrqdFe͏1ޡǧNfc`*`Gh1|(NI =:c<kfD@9?uRkZ?6 mƁ.iQ 8;3XZ7"3j~=k/eN/Re+t഑PAMކGYV>ҹ1psr2Â̻ =3-$Κ|Epd~ɮUMiqE[s>Gwt>*eT z>ңWhF zb)TtgqԎK{ŐHqU}Rp1Ĺ4R8q;\Kܪ@3\Ȫ! A1\KoCxKa7!&( ct/G\dҠ>bdrHA}#[,Lv1͆;cf]\:k r s8ݚsvM]JnN6رa yfɹPB#< _x ~G\{ӽd ݯ11<~xϡLSX+,JBcHǵ8{r( 9L*QU ~NNMU֠w p:OjKsq Tf qӏj`HK*YWsI#C+pB3qR m''nv{d)O@9Pndԗ3Wt[FX"6>X}H]X+*7LyReA"Y}~SӠMB;52qVOlӀZsgf8eVHq&: @8P* 5n/ |".uhwSSq7i,^NFUx+yVE$2><N~p!,1 9+d0i1Oι3ӯ"+FDSkeWMd,ppnzpb Po9'= P1ܛG~HN?YAo:o ''q\gރFWHȼc FnN1$>?q[-,0;8#ӱ*y4`+':#=JxYJM$s9$;X3txc2HH9jac P89'/"&W czi1[oN9[{TslʾY ۏ 29$RJ|؋RRfZ vPK:ǪCPd2M4P v #oUsGNab.Bܬm;;FFۜS[SnW&59YnГN|3#TfcI;c%T~psW'XmLO; `} Z#C j?JTr[.<1ĭ#A2p}{pOY4B^ zYYUgRˉ-!cQm~_SGJV䴏$7p[< d94⌴Xme<Ri<ߑ$D@ t{mZڥqT{t4GM#2ȀHB@pp ^@lghGt4E ۜmw+[d%qOYi- B]B*NseGM>șF53I!w؎ұme2E}bpc, ӂ}kbQv."Mg60q9̭2<h0T|(@rBvß_jYζֆXp6@,@VrN:iR_3;\'{y4DGN~3)4 yA`8_'*ƛb~s SleTpԅ Y#Ȭv:eF#~NQ4QF/mDCckWe;GnuZ_-|eG$wʭM$6l-P`94 LYm 9F+ݶ@.> #ҋ}>eݤTF@;I^[ڭpnky(!v[Veo%*g J덥3TW LCle6ySwo\X$pH\FAN)}*X4P7S n2Qǰ=Γcqe+HMxTPb4IMk4It%Oq_Wi!mnC8] .PF=jmm .\94ť ʱk0jrnOމ%m&<6yHtE2(.rX2 !zp*K+$)Ba}gG~m7R0#SnF+\{^Eׅlsm*$j{2na8lO)rEimffm3A8֓Yk( JU3g)={Y3^[)7V l) =kF \Vg9%&4-ADf ̀+r܁\R)c>[##fj1$,$63;ÃTub;h"̈I/̥I>Ok2a|IbR8Rr=&o}cdHlcg9#4%k.;WÒ͖ GӰ`So,qed!|o|ԟFI1f B anrxsSIw,qby@bHY?4w8AF8sY0]>y:O $V|%lHm}60# O*MAFGt]:𽼿,Ŀ#)!F:{L$̒ʠg WaR8= Q{D%KixRŗ:*-83yh ?*m@ GCĝ;T. 1|ەTŖBKOGQ}9N~E袒!CQڱ"m㞣8Zs<Ʋ1YAw9Ckg sm)#1GNv'95 JO9W`V#9'oO:˝s'j9H-+NÏA|]E P{P&{h *ăY@b^ѩ 15+*Њ\l")u++9LB~m,1;@ǽC&fI*ȳH@*ѐОxH"gz?xI]ʂ=Go\֔5Cyn K+4x,HM3:V {{#Gm34&+f9hĒT`8`R(Xb{@; l0ꤎ5!x(kԆcTخ2vbgǯrj%-ԐR9,`:uUMKeyh 361XzP1ʑ\xgޕ )JlC,LƝU:;Aϸy۳-Sm!q;zzWsӢ&@+Vu+o.HO-O ` ºTTD;䴚Eyr砹su%G0y׹j* Br8''QRZQGPrq\9!)j6夕MH#t8q}O&k!2$O\NHg(gI$gB@`0:CکXۣ4 #E!Sc{U8^&'a`t88j Gp!2T'QJwRrz&4G,>M΄y-GvU^(t)<}sP<[wu#Sd=JFj =:d8 &Ռ.{f0U3 oʨ4"8̙NW;EI$q@Ҩ 5{n2 ܏ҷR[\{4)K#ӐkbӚm1V*YZu[a] [Ԗ8-!,c_0C09~L*SQEdVL2 ]Y&H*J[$'#O16 <<=E[rKWG#pG/hyJ͖ucB+BnNr+UHŒĖG+QS1`J͐޹}D<";qՌ[rQE -kgl\uHPH(Fd#9V/=UrJ#GՖ 62p*1uAM[hD&Kɸia#1̤Ycn~Q鞕弯thI#@GɷnGmr: Pf)O&)-=EHZC"U8\~MO]˜"f 85NțVxiaTA򒧦OM DۢvDL*YL܏J{;D;csE2A#&Q1AȾK{2*=EAr49hXgk[AAٚ7ec1G5n>YpUA$2".* VkyRDyzJ!Q;"ndePTKI!JWnA'=jXw$Q 8;fFӧy:<{N"ĒCmx1ЏTl]_&Q*LL;\Ӝe=M?O-wdҺ] զ`V] k" uX bؾr:g L+5ˍƴ֬WX|(8pq+ZD%23-jwp L+ބCKp+ȥvGv5>%#sToi*H\m8~]Gvrx^F-JGm$H䞣B%-)l02A=zX"BTm=:g qUqssI+tErp{c9¹[J.2i#2i &5b6yU׎?.5ʼgG8g<!2NsăTK<76h9WЌ|x5ja(KRլU̍&+7dqvM&c$y"YDV- y8|+ {֔_emguwGui;yw9;ιvHwVj"n9%<27=qYNSnfEQOs1Z2ynlrtGjI/fy"Lòbq?:5>X rIin6 Q@뚶^YWh-dءukly°26W83~%) ,F9$c<:}|C!sǽ)P'X \ f@ EEz%U 7C}?JxGgf|m<~`dݐc Ҍ s;}?.P|nS>߮? U#k7lSӃ*a%+&uС&._ʆSa]ރ=?T[8 2x$?N1׿X_@rh?qE*:",b=[?.za*x\V^2(<.@IxZV!(PǓ|$N_]ZWCT9 VCpOOڭJ#PXpTS"D*g(N54]R\TUnt|"pw>qBXלfe]zGלUg^qB.4JXFdNGN=jyC#d$( 6rF?*%e",*ӃҜrG"';?qֱQkR[lFRRU8˸9v;qL3+3XuGюw ֵc%%_"^^ݚL^I6#FF=sۊC\|qo&k{5Kdn<2Ij+a,2h2xYMYKM&Gor tDsB|GA׊ki 7f'k_Ρ`H!2J~yOOήIs `ˌeNaQ(3Ȩ^H|;*;vhP] >"FEcQOo.JɨRr'E) [X'qIxdo(VYYH?Jt.]F|CL=~n=5m؎KeTp<{Ո좆Ii~UdF`gPj)d!d?jQ\я;fƪ$!H=9޴$p|z8Rk %HZ1c087Oknu"WWhwvڪ_Be\F:ֲ}FKd-Ӑtk;9Bvp iJ^{5Čl\>*EWFgUmzQ,Uٸc# ֭薫Lm?+g>w)X;Y=sVeim*;TEp rJo*}2),d1?+3d#?giJTw[GLt$B8"IsuF5"]LE6HU]=:{-5. WzSݧ$Jā0un#zꢡ*6[ydݔ#4g?u~>Sc;iAp取cp8G#H#!${sƲ^rM=^ߨ+[F#HHbR+^Ijv-o&YXa`>.cOGp!2^yB;Hc ]FYqHX,_%ڹ~d/#(Vd\ Oj8%d*;7ä~0c cUG2Դ/ڬ҂򲃜HGZ $wrBH*!$ }bN4Iԝ2(Kpx$W(mfKD69ܥXa@ jt3 ;N>$^{[rQHeL =>liR۪N.IV  8,2brGҜOldW%Cci)L6#h( p# 3󑞃% 眹bjEiyE WR9cL}B/oy#d(ggɫ:)HHvbҪvk\YeQvPs? TN]Z݀*XX Q[(QE OP=Em"ſ2;P jS{G<^HntXe`6OCNSAcul%ulJAO+.QC Q$ng%yqM."ƪ3 Ssw#4Knk[i?)%a{Ų79Vfs")#܏Οt-R7ztD8?\ɪ".Μܬ|ۙvp>"2"8QBۀĜQ!ibr>bT0#)Qvyuu\7C*}$"l`2m9>V{@_;7)ܙ3"wH } l9AbV69@CԟQj$J}bq~VK̶qЎQ{``;i;Gc[ В}cq 82-Q]>mCv@8?Ͽg5Cb#${wa9|gB,p {f+&Q8u,hc<q-ķǘdrVLXˍ̽953.@RÆdwG4{G+e3i$|]LR$^E\ۻICc9Hӹbprx?T/,*ea"I`8MEӃp7ܴp3ׯ~mv}ČZ3Ps#.;KMkӉ1&,JmϵTڴ7gE$-X\g7-13^O^8cڂ˱pk1ơ̖f邪ʌ2(~\xiV;}(apFq=8'Q(m&$D|%>aR'ڹYkyM[rwsÎp{U[/C]ҶIEUys^Ə-2\'֩9FecCi{+%3m!Ӧr95,EB2V8i ƳLbXJb9Н#gymdYsI)l6|rweߑOO|T+ynTMSg+$fAЛ)vA瞞,Rў'X S۩JYqʋ1;wQYrZcA$wf#0 I'OaR+TlIQ*S ==E[},B!m#YN89I۩B6w8u!Nr$CֶFL$K T seJӻf o>S(ll;y[WQ32")gn~`Nh8'"Q޻ލi["Bp"]bYv~S¯OQU[Imѓ Q#s}L"'xc'\~N? ճ7tǘXggE'kuts)$3$&qǂۯ?ҙ=GlH8w>H+s%BF' 1B t'K1$]Cd $HBCcy ƱܼoJAYۏknXe ȼ3H[ژg+*rt8U_itkh셯nXDvIP\,qԚH+sU%73s倣cF #ִ].S5UR0ycF85/s # W?3HQkCUܝ1yAXW[hnIcv&Fb $ tGm^FmfY o{*$l ájCH_ h527P5ip e*}k^I+ItlЭrܞX^¨4wZXc\. 9c1ןJ= ^cnN Pymo/;$eXUuQrcS"i I.FNG^02] Am#8Q*q$jbޤ<b6+@R o$>陖@71@=$k"[9WqG~䥴q);qƵ操ש2bDd 3'ܗG6/'u>cTuĈV[X{{0egtG"TH\D bW]Mgd E(]ԀYw.qՎ+B+pK料 0GOiRR/0P <\u 0Tɵ̫OԎK Yƒ ޛ6hg+o\o#?}p*sݗ$S s׮RqStܸ8UiVg2p082I^rQCBw1}}ױC,E,-[G r :54A:60t8Vkr;=Z͕W0"Or**|7vTgUVi4wjVQBp zNc}BV/73mIa11]g4D'#iĀ512:7,L=H㨬W%b_k.#<Si#9'隽isr}0Ex٬2,)'l+޺"*ck8AG!01B>Q]6 F7dK.C ky`~2nJ9;o+[O(e.UPgI1;U_ɎJ5.mx.PscFQ-1U*O\ rqMN<%]Qcҥ,dk|QcqvgU۰*sI롎4 4жQ)>($!1k-ۉ$ g>GTt.o[dDwd;b#K4%Ɍ)<ְR嗙^mp'&3F6}ϔpO@zrzqj"y :PJqH5G* 5qod쮱;*Wa W%9vsi5S-k$^%K힣 ͳx6w`X dJOҡԵ/:k{Y. 0cnwZmOu+ѽX2zH˛W뱩+Z@*ɽT"Gz!jW>TE\0~p}8x Z%03W &GNǞQbH&ud8!'?CִA2ܭ0mme| dy[33Z[ʹ6y"mErAsav'R͍ϮqvFgö,ֵa vd][%$1Rr\s'`nayûl`%r u"QgO0 jӢ)R)b7 e*U;e- gNZ<ɷx< N9?Z$8dM Fp1@GM]!XnmKd}vG4{n'l|A8'F)ȧ&*j]5l|T2(Vz bsW`t,,YA#oǿn=)S:]l& chdSc'%+,A#(۔N*6A^Bj{|+db}#S,@ ԠU8*@8UzKh洎1:Cx}o-H>6JC\.=={VT%]mHȅՕB3~N i$1 2|~ajKmK='hՁ9V cu'@Xr ޵uhۏHq!1,r@>\n\$QRI'pO q*̀MIܹpP2V%culm%v|=d}ON/wr܋[C o#NxI۾ b\39s`csҳ[e\3Rz5>u%BZb!ɍBs5)J] MQBfUby*pCOXCmydU' g䟥giQ]\b3pc 9\@ߝtnF! 񁐙^O'tԤ$2^ft~\WY<-.`O@Xʨ\ԹKHቤ2asԜD2}bv1$.;oL㯽Owf{.Õ@0IdQMJU^G5R8b9a<~Q$a6GY4+&rwòIn"X !v8Mj⸂IedCUOLLZF..ɏk 'Z=o;;_ڹztm*)6O3;\ؖt2z՝q#R. cqjսW4iRC9VP16NIn#mN}*Ykْ;0V(b d6JAmo$yL\6[OCȷw^?W TuN"n&dO#/.~e^H&4Ov3ΖS)2#S**4`ke_ sdc>y;5ڥ!y,ĎqןJK=+363oQ9we[-RYC ~s Xp3L-j≮?*=ʗP>Β@^Q]ǂ= TCWٔQEVYAYX|V"[p |F=z \o3"^O%x̪&u>rhӔ:1b6n9Ic`Kp+ w!Q?JeDJ}+SiYy#汧7+Z\ϝ%hKP ?:eP!&UsUk6>T;8lX!1#@0`G֝J6W){åi) m+!JΞ5U"nB~fynt?nY6lQNi=VIJƽ (K`,V3>[Q6בib;:V8P^tŸ#vnT-B1^2OO՘` h"ıw7Eiib *a7qJс-m<B d|YE2"'*#iSnNȹ8?M rWFn+^q<[RC HI84Akv&IIx% YKI!X Ɍ6 *Ŋe6y!d8Xy~;4.K]o9G IsJU$ KrǛTdY2puekSnܡf?Q*,RolÔ bMZg|LbAesДCe2nĂ9"WUnNz+6l(㢞+*9 v{M+u3.doolY{&_BOkˈƠmKxR'#]ZT1D^(Cd aDIqS=G+ݮi%cC%R" @ֶέ$lTN};Ao[FpF3sWKceD7*Fq6ke15+kyV)N\_7 $^TE)z塔{T'LR"gyVܬHTqSh!gr; oKJ<׶)ZWb21׷la Kp8Wc1T-pcVw#n1N? C q+V?Y.ʹmQ/tDM*;Uxgֱ4Fl#1r`9yy䕌L9,*ņ>$#@`ʯ9# M4:lIyV8]mlw 玼zZvm J푆0@;L ,yL: ݟx9=fL6JY 8N56 ;ŭ-PeAZ6 8<I'&x8جvON\J].I*#FAQxa+k#%8J@ ~Pm,:{us1;VNV,$;Y[F&p{ER=8+.(?;}qqT[jX¿ Wtх"AӠfAjl[O9WuTOdsZd,% oe<('{y:;tvr)m8>ɬW4S#@n t$cf DK@ ?[Yc@RU?lYB2Z좟4.'!y !V݅ q?J6ЈI-zҙ+ ccLT+{,Nۇ09*{I(n/{ T-i$ n7:. m ddXafy$A{r-Fxk&%kJRzś }B=ZYUL0^$K'ţ<u+@Zh\cs33=Pj` ؃oW#$J%yZKX~jG3Hp Ӑ:%41˳F~q޶ms.I*[3UV#pr2:Ҵ8+ng ˝b֬Y-ٰ?C~i؛\qzʡҭ%y&b!}:*DB @uRJ77er+Tr,u!,gî+ u HM R lFq3Uu X 4! q[}j!F%۟N{ 9]۲7TJABscIWj1حZ. ߌfn/$Ifs4#u9T67r luO_C]%""SJp3~itsh4ƵS,eYemZ墂8bXO!GGgA=U,s¨sZ6WlZiYTxϹ~U'-v ))0]ım͹{TZ1$,op7*Jzfd_,d|x쿝k#!IpXpzvYQe{frSI(hՋHAeiI3I 從'̍Ws/9?ґ^9T/x8;[A_].1ƌ'&XEkM1PBo *O$[qv*htVUXsZ[\(#vl:aOq)V޻ӐptzֻP(׏TE`73C$?]w[K+zuz-E1.G'zf@md}[rrBYObAPUrNY,D}22>G\yFwWBٝb~GBHLr쓓|-d6pdާfqN[$"UdR݀lsћ[krs&660=G\Jy6 f[`Q$-K A{֕7ffei m1JNWZiSVW-C"ʧ"2r0~DԮou”2G# ㊿4K'FX1}}EX Ih@6Ln\u{J5c$խc(SN9KD`pp9`cť3$k,A'q0?!PqO_lGۜYy9pPR3QG;*Nϒ"+8YgUC`|ނ ĒC|\1DTJ«oRz4ކtXc-}$%g_&6X ԏ}*KdH7A .4 _Cm۹\6r7O7\W+@0T3yGSm+ۿI6@b`ήQGӎ"ⶍ4ᶟe"8DytT9VgN}Nk{4 xwBsO##f5#A#{gZRm9t.Vh+,bM~=xT:\ 9=(P;juS۽f? Wq$l#;{rOMg>iZ4.+!@?ՕҪ8~1v;;ƾh爂 `\>HR啿vw<tQñ2& Sӎ+xաtTA\{ȩZ-%="^UIm6F롶kmqbh > $M5ԋW $~\v?;ԥ)|+&Ҋy'ޝ[f\lts iI:t?Kež|A\^\ qTmUQar2K+!Av>^ %^}}ՅTanރ&׭# bGC #ƭnVփJB:I$X?֣V((#=? HJC``<"t!10q,aL=%DAC(;t+@?xcҚ' kZ;5Us2[a2`gۜ/s7ii3!ر*;.W֙lKP99P+"C%rL0Q6Wk͜RNvf,lE2\|\}/Q'$(~NKmqC5dy$@)~$mSVbG8{ PK3vrKBXSsrbzm,ד\2`0s9=dI\ֲԂʈzخ Lo(#ye:~攽Q&PUH M ⮫$pwA~\ӕ;r8 r8]V7,F6?^?Z$ʞ=NJΜDŠ1$fb@ہ+;뛷y'd ܠzG'!\}u-n≢is.KwƱy7-YЦ̭&{ck،J|ۇ^  XTg䑐erGlT"DT0W ~19$[(Wldcfg~Rg.m,^<pr3TzɂxxXa9˜?9فQF{9U;V"4xtwڮ>LH?tu`RLs N,B¨P>'KgSu6NgwBb0ks (Y9c;tVVYqq$<\ {l+n6bpZ]RE@ ɽP se#yDm;Ip[;O˜SlQrm !nc!~n2zXLrz(Z c,qN  {~:90[wdw+i]WIRNҾ:8\HB0 ''_r{ͫ$WVu*7*߮jKsx#L ^B 㷵cm+F}cq<`}),W<1Պ8 tsQ&ވPIΆ;xEą B#ZlDEf=}HqX=(AIc>Tp8Gxe4G#)N+EZ\I+o]˘Z6;9dǧ½HPU `k?{.`~\q15n:ŭ\rLS[t5c$sU"*50AY`O^3&wu@ŗ 0I,AB݈a Pp{TSJFs(U9_ s?JsU<&$F`I42iym*O B𥑺.Ls2S(N}@Mg>5/'i܀ˎA(ʕnC,fm%33OQ+Aek*63`>rt\_in-Q!w.$~Un˱{v<Fc I=9*ET➺淞7FP[tU2GU,n-ܪLg,[t` >"At}G\=aCLY)cY ʧ+I*Ŝv43Ib\Nw3 H׌测P^}i4%K٠KWB&Ae r{gh_i*[vأf͐PHvoj4;oPNGՀX4yʳ9iSQ!?*isjilZYl@>]Y@"L/ӹO*2\GIYտr={W+x`[8Nsz3[cUt*FYv #*ǃ֯yd 8QP5ҌWFѣ[Š2n!<^E#  I<nlTr>ERI\d J-iA`Jn]vzo<>d16˝ņTR:`q9Ga41D [NIG\9--ڡ]T>L6\ [A#X1'A5rQdqWw-uѬq0@uP0ZrLrl&"Y`ݷ8sOኆlcb9C `V9 2V\:4쮤`??z^4u ͥ5cI`U[(8@A"W،pOA`tiH|vfN? SeI#L vCO\G\rjߩwM a,7*%V(!7˞AE-嚫]["f]fD #_S]+H[̓P3W2?e,ҺNѱ.'MB$ĝ;/F9sZf8Pu}.%$qy2Yؗx'+7<Jl1Ydxٝa TqЅz9U(J/$oThI3\yTR1Vc2$<=;Kki %*fdxq㞵Oo${aܮ3ӨVݳ8 Vn"G͝DR ۔㯮3]be9l'9i$z ¿5Է1gHVv PKBjcs>fi $Y<]^AU7HʅC(Fp|.*+X")-hîzezR܋Ic NIA֋S>f["ڰe!&W8"\bhq9=*c8,#HsБQ\[BZiYar?SR{Z=*$w 1S|g ⷎ{fWB0622H:Tټ,c#;z^3Dx.jU&õ0O]Oɿ9$ qՋ+fYU cle噀洴)/Z3l~P0\+,H$yR0ʀ[%u<` *M}d0vYD(H፤q(h *w7cn+H!H[t?``qޱ/AEedc~_n_4\4&Q)\bDF33|ޤf!(pҩstʟdJ3(_Mi[u4dC5pHQӌ7t#`,!7~Ҷu ,f٤%g985'sy$I^?=y-lxnjŅ­ėE]ȫFqW=B3q 7{wΨUd7c'>=3mmQ{˳ UlH&a.&R<|9ϯ*.d6,vړTH q>jDqC6il^v㑊$v1A{q |l#/u1~Iv8?˥k % M;lPxIF"VHl^c$z?ix>[uY]ː-OZRS}gJЎqGkp"gA­jMTmޟ)sTcj%UVOrIWWtOUvǜvoxgqEj6j+sj!*?{}:sR]#rG[]H&F8_TnJ+:¯azՏ4a}ܤIExy}HnXzti6̥%7 N{tֺXY6 T[gOcަI]= )նuKpс܊Z29 ֑d xTŹ$/1p}>jj.m#DVu 9'5B%,;NOs T❬Θ4"Ay,.UrիC5W2Y 7)\r=Ěd)YTl:)p9Zӌy%6ܞԋD%Po}xXa24.W8zңKm>Ic-\-#lc&~$肋b5dEPr=bG1>Pܑu[\ 5B+Hӵ^8‡V*Į$WtgF *({),?jx/[ʔ&d g>?GOYJ[w'8Ƕ9j(ƪױQAec(4v6ۧoz<`AXVE8=iݹ7K;Bԃ2:zұBȪO E7 Pir.x'frI]NI3xazҪ!|d+q gQG$3y.XӧYhpD,:lqȧH K #yPG1Qۥ?TFeؾ\|U^GQ]K<6$+.0p?_ĊS̔0fV|Eg99+-wI[ &vS*~1p?SF jʗ+}n巷ecPdIo Uв[,F1p}EkOI{)\P+ˉ'`.a[ٹeEb!6׷*o{T[I/+1ܜTVfZ6{9{Ey"jB0X>%3z1_֒cOޱ6#N xS9=+MFM.4`Vlw;W'$8szן$JU%Y8=U yXvbJ=@P4^_X-P0%G\lc`pNiU9jUiGV,XT \pӀv>MjE$0Č?(tM:Xm5 g'k"7ZAl~ص{HT 9ۂ?0nX ]ҒY@wJ(cMF̀m`s\(!rz~\TiaJ#;s;8D䜜z5C\X% e$qp*pbHnl~fo` 5w- )ocJ($D0Į$`3+CM1_\ b;dE`R^̐@98f4)ӦtJ9֔"*wۯ@=-G9K%#C?VI?}QdVb8$yazխ.-34SĈۦ0=yuksw;!N4hYB6 2AWN3UXS rTxdc@*nޮX8TG5GwGUYQNH`NyTE!.~b bYF :t&Rn'Ҥ{Щ6%QcFN{qְsrR{~%*I !8B@|_)#oЎOL o9g!Щ'zg=i"O% 9qWsp#݉D N滼 `FBp8FFk@4c̭ W8 q8>m&IXIO2nLjQrzb U`$:uķ(M* w<'OPgMMDޤ'&Ow;eIg`p&Fb_5f$ng1)Mdn@>8Z4Rw* @V>6oQAvs;K;2l0C:}r;4+Pd['LrA5&mf-%%L\9;Uhд@tӷtsIͮnQIr 1zxU}^48$Ã'sڥCRC(ng>{[l v9xhOXPm2m*(袡nI?\SyjY $*fNJdCj|T?1ӥ&-XSb 2sl*ZKTm̻er0=OvZ-%d W5/R~QA͍`S!TYXr#At)wh].H;Y1Ԝu6< bS+ϡ9$ q!wfR+{")._&r\(D,F ^q'ߜu[rΑe v9F#$r3ȫRS@gֆV"\#?ZS(jeh]+ <+˅̌ 3z ֡{ֶlJ;$ Qum,<23\qny-bӳԧn&EcR0 =QyRi b Ƞ 98m`uRv^sUUF2p/uHJάۼwSow3GnX Bz~ A;'IP@9l`ei6!S3i`~8f̱5CY@]@lڪ Tj 2Z3|$bA#L A8YDsXWI1C,c6@Ď <v4fS$`J9qJ)9hU[*Z ~ 2Z`P2y(oǽwaH8#@$}A~8eDwGV^?!$18>kH9JLi'.fILW'fqqYB۷T{N:U[KdumTsV<($t$OkV=>_bn(m`@pR%Zq,k;h!˯,Dbg@WU;:[uPS'u8Z3+b!*pcOA9銫$/pʃJ#5qεdƒI4ʐǜrT9)r6W[m`$XVZyBFeR_n3t-pTNW&} +WjFI$/5gw 8-F@<柩_%$L.I| b7u:Ʊc#u,kq0ħhf+U$ ˴H5;xs֝8AaKE$uӖD z2zu|haI11皻MX;~fb:#5:g(2'sF'9a.K^]M-wj -c qݷ(k[]Aq;3>v=1Rvܼ3J%* I#2wA|;ǘ DpIz⒓eBM=NcZ:<+F T )l .`[;89>YG^)b]Y~`z` s`/2}*軌]IE(r>jz62:{㊉.\MJwM$ ! ?ZY Lm(I>Њfny.e9 :qK?&IF,LM 90樮trf+J,m/C"$'O+Ե#3Bp>ypOOZc}- Ɏvs$C*d\ӹ8QW.h%EOBO=1XInD F qncڲY-qݰ3O$z5ӹC)1zu}!4B ܎rxm͜M4?gL[r8#/ҳ1,HHЫP~RN}H$TyNm. BN1XuSXXn_!L?#W4{cr<~˺[y#b&yqӧhdt\KM\g Z`icEtvF,׍F,nFՖcicsN~ֳM ~B|1rzfjo#"ɀpLwEssd/l e wJxx3Yr\I2}&deP,G$mxb)᡹W2FکN$V:I2yQE {US7`#d 8$u}:k[IyAx~ 9zj)jIe*ːI.˸1ݪߓ3yVɶWP,]w1*.hUD/f^$6ݿꮨMƢd)%YZ11u,\N$IbMc)/ ɔ #2{ kN%rE\$(ݹ ï~tˑ%@bY3p ``*ԮU FKh,Y$ur?Tdm<$FyM3]m$CFnu /*`pĺ2F,&V@ż(Pf'il.5p4xFwLI3@R|b֢jweKTnw|Jf=5*0X+̀ӊ,1IчŽun|q4P+l#G$Δ5}smOI-7\ʭsqΡ8o+zo\n%>S069=y歟8y%pύ t+ӍBh9e 7bQ5/:᳇"V#!OEǭnҳƖEnH#yOzj,mlwd*H]FB1>3Z"{3,ѭ}CcgW&ճ"IH#dg}4;u쏾HE! >_Qx%FFWrk7+Xz%$ Gu>%ՙxıg{UlnmOώNdgKۋWb^lyf5Jq݄u@@6H?" yM@rOF{R_C:]yIG S.=?*Y&1m =}Zmݒ[eB4j3?x{Tڭaq)S+Y49 `~J=;&ᐿN[n5dW'r>TWn.`@GS O2<Sdj;o8NZT$nm{erk`;!W#hX@ۆOXLח"9_(N9gAEr5Ұ{T3Զ]̈#{-f霁mmp7!\zޭs4x6k떼! x\coڽBi}r2:W7`[ ҝs”Y0MsJ3YslQ4ܘ +|5wb8TG@+鍠hT!5ZKtBqGREjjGQteULq~!F1|ۺ+ԵQ$rœzkX%5a-sYxe*i`tWY9$r=覒؆s4,&c9rIb;c?J0'b_INj_X#>1FB2kGMNIFG8Gʪ>Ufiu cmܷVԫ 2 =qTV4TQVg+EӝtKt&цsc~k*[kn VTv&2:s[qGgj!EVaH9ln?:Ϟu9+$aG=?x}*}Ӫm4(Gړ`p`H= ;^t!#I2p9<PV#j@\ @þ8,Օgy?Vw70Iin %>c N1U'>Y۝嶌`淪_,VB^϶qW/gՙeJ7 >U'@()ܮ =W^Urq+t4Chg,e1:4v B=;sZ 1<{~)Up}xR]Ͱ U8,2GGjwvC !\Uxr9>rTr rűeHm;A'mլY]3,p0}OBѤRB r*yش,*=?Ȥ⧥ᄜ܆D[K6@1U#{X7D{2$8-'rjKXx풠BHAw9Y,YTm x r@Yr6{&\f7WXaϯZ|sI¢2ܫryA rqXVkKd]MF?B}+:ֹtTe,ZЮ;ƒlp4Y:"Na'ϧj8Y1ـ.u8Cgb]}^B|p?0QL9|?*#:T-B 8.-\4rH@%~I`jCi@Ǡǥe%go(͸= ''~ms&URխ b(w!b$ KԌ[6.%`7'ߕ,2Bp:gM6ob5S2ۿc*ne2Cpn{?TkpUiw`I@učG2w;~5rHE#yL.OJ֤VNPV-͏Nay(QB9Sb=s,QķO^+XmU9`̱J)tsW-jj6M!U^FEݰ^!$n%2?ASNZ ]);q26֦${o͌JJ:8*̓nn_rt zsV擨lDBl ~gq)F2Vc}JW#g $swGog}Nʧb8W:=WmVrx }Ч㎽+sBkiv j.Ӄ]4a&t>x|v Pr\ dܳ`Ы[ R^3LУO9ǙR:"9@ri9i]ܬdހͅ$u ˜>jWC*<6Ipb7(##bR&lsp +#]ag) z;w,1 ?>F ɒ>NI@ 9?HIpsBb F?yrN=?O*hNl99p?:V==95"IЊ3 6$B?NkRm*nuVhDp}&BrffȏS& l鹏lTU8j\#,3P@1[|;<@ e(~,Y쉂e`@95o{U- 7Qg#O+nA'b0.;~u{JG`A#c5GQK)0.pY0TcI,GY-bYr [R)E9!IICmp8R8 )%І8x'AXaRDW#^*+)<3F& 9 N}0 ^b r99E+ԟ$YI:k{{˵A7Frӟyk .1xPv<(7!]Uw[⡖i.4L+ |> i)5xܭ+vR ~X y@rO1U&Ӥ+6̳ d Aӊ*>!9r[;]tv5v˵4ψTR?|>$[T,7$qL[{xXeؙ\fW6wIt h}OO=*+GܕUIr̾218+!+$QH,2sqf8:{-íĊrsКjIw' zSF}'4ЊPi9GR->a"ˌ,w3TI,a>aR6 z ?]$0G5귰5ĺz[ʲHvI!A 7cΪIꍨR5:@(P1= +&'Kh7!]#p0ðqZ_FU*`OCUZu.@pqi _dnW2,2ȾIh"' z %O#kQRf}-™V*(15n[I6d Tq-ԒK67HoHϾHCY-+v^x(f[YG 4G!ʷJFF).ά>dAgWy1>WЈKXDY#1Q bWvRq<⧼pY'W H#wu"Bڲy*PHI9!JuYn+CySFr^*kV6>gq椪rgc[n7x k9d)%uГ>4Y5{7Pfp89'H> P#ʤ qMwA LַaKk3xI\(sש})4M=E#aEjf)ճ`qU9bbHiTcO<%r}QIgQɍˇ`r͌lRWBUz*eU<1QKm ȹW 8 *X\ǡm&tkkȸbkIòs nm+Dmj=2$i$iP2oJR(P: ėɸ2"Xoc| ɤȱl\o=x~ޕU"Յ"WCʆPH @gۥ.s%i≝,$``l(ZH8 6 o1ҙf2X ܦ. ڧvZWf\^[!2&QV BKl҉.+NU KR1YFxC~iE ܮok;1+$mX`~`q5mFv0A$s߭ l,ϝ/;'c G@ipΊTy` 1~ժJbe̶ mOBjQpNZN$u5QmY.FPHX(rXҶ$hIK$`sֹV)Y^%QǗ@7^Hש,dsڦ$60ݴ'`QXfl# 1>[mxRN6+ kI  rTxLTxXPlhOpr?,idSq*H9Wl o*s P-rIq)Jb6؟sUUӌj73śX#r rOoqSZ]q3iA_JIn^Y&5c3jVK{$#a3ƩI&ȖFKySc\|PB;K%+E'gίj uq|y|3qA*Ss@$`!n9NO.y$Rȿ"Nqr*)'3EĀ81Kme9Ӄ$}h-fأx+'~~L+Pd1k4(1͜6J=njo!2 t?1xqFg:G W$_w{" ;.Y P@(l.##ǴO sV-D+܌?R+y@9g 3TqzڜBiA`4s$ ن駼iB1c2g@A ީ2PwO "zf-1Hݟ%j7gf?( } 涠uWP'0{K ŗj0#]i44٤d&9.(nwFI$mD3Fw'jȧk0(cGΒ5Ϙg}+7.XhD1N $!bIyfAS4{2pЏAQ1]㪮3uS%Ft#i>5RWJM%f/ TT3^\lŔi$+:E]Ň8_ʩE)'ph73L &cGU {Vk-I?0SmɹY2ḰЏZԴYJNJ6TZwU-?G겮=e,bZdcqSDhAe9I3̌ J1p\%ƷfPJү鶣HKr)<(hYTS1<ͷf 6݉9hNI4][G]ۏPrGan?6䏭s0)eA<MnOmo'p@g$~UFW%eVl<3׿JmM La;)M F6$LTŷACƦ;2L2###_zT]=D{n8P8 -I$bF <`mAr˷5Mv)Zγ5rNOIV<G Hԍ(98'h`Au`ax=Ic11X<b9sҦw&<;x?qJ_koSG,@y n*y?{ts[Hݤi Dza;z} #]w5+sןʶ%Qd$=3qp@_\X+٩(8$#'a[-|UUOLo&<&\׵HD`'s\>ƚNw~@Y$t&;m'5Ur͎'r=ª8jb_h)-"k)m#9U"’Nc<~5f@yqEO꺓^Ʈ6I Yγ)p#F۷@жxNGE'O%ԝ7:B =NU*snn UbIfل^glgz#bR1nN 5U%)[ B`Z 8$X!VS>ޔFT(댓D,1zTUsLV[?VSX;Z/8yQ墝ʍ'x]]7rGUfdeWva>EeY.c@l~O<7?fr2.hsMSF\Œn-^eFqV#a0#H\یVHgJ9 U}Zѯ#)o,H .⻎1zc5NNcn^+iۂt6֑ aqUt14Vv zO쌷hܪ>bsG֑NRV֩v^w۽ةSuJt.fB@ _T Oa]TϚ;u9mYגJ$$c%qG'SP> ֺqYV'pRHQ㱸I+'?Z!^\jRikț @\<¾wpHʤ|I{ n}Upz jިߺMyCpYK$n^O&#"(1`c$άQɨ^j/@ ydY%y;P{WCkkp!Y3rG3W*x;WTh G@pAzsm9%yd']C = >@dO1p''lSe.Z\y|G~kTnZᝤD&"O1Ҝ:gq}), . pz~DE,#'?JnTL_2^0~^Xs{WE"(;fny+POSB0w}Hĉ0̢L6g=r?Jm/v+x{5ĶI"FmI c?^$syh%y#1aAUAEꉫ~%xD$2~a=@iVb͆': J6n.YUq;ׅ'RLۋ|h$w9r{XƧMӺB;)EeMR6nM FEu sc;K+;*ŃٮQ#n!px]F&]Lȴ?nO30TuK!Xei@\=GhF,1o"CQj! Nqnr{s[ffmeܩB 1֟E2 >~p:Jfm%}5,mfw``w瞆:FKug֞H\z㎿֡.#$xݎ3Ork9Ka吼(2=sҙ#fy3Q-aUmyEzdtU(ϗN9poEfSj3扂ʲLS,c%ONt5le\X3*Qo!!H}x"Pڢ{Rj+[UARv?IfRyN1~<,pH^j9"-qӀpH':4oJM2.HacpO;esf>9 gLTV12 #uQm\ $iB7\v~g͉ <1Ts!B.vR3ʳ"HfH`d's׵W"S{T\edEfJ.I'l7_mv$ylh@ܾ)*0 !_i?)j/ݒ=IYE8QB1(uvѴv7PK8eoKd"M0<5qyσ!yqޱ<5su4i%#:0fG>ȭ+;.ry8qSƻ1^+Q[gcϭ_jC=n U;ZUPNW8=s֫:ZwzX:R|4(\c4@ RS_߁Z֤BSԎ5¨TcB4a FA$d82G4敎ўqՍ3d2:=B8`Bg*m<};5 ,12kA"3JU_x8(/ rL9#1 H?(hml1}pjӨߔm>ԐY$ vGJeCH99c3O% n;ƥNJ.IJ Qd$~Ҙ)P!=V^0;0P6'9V;*HF  Ǡ2$+YIM;1%Jfh+iP1{t确VN4Z۱>pv=i#gsq:cdhaHB ?AN_-bH 7_V+w]E#>A/30I럧#uI 9ccݷr s{T[w.r(x>ӚƺZ#n`wLc& *>]_fvSnQWea`[6ĪD`*Iw4kٿLS6Kq?+kY.eV$*9WtbHfg,ϴ? #lG[h ?ccm'?x#8MoYg`1k2JӴ HT8 { Ñ8.V"%DI)̀I scP&P7^A5F%Mu[p |=ƯYe'' M*q֑u0q~vILXl0QAgSK.)"}A(=2y+v9:nl~69#sKsrE,m:zgjF^z{Y)_K@/׷vPF9JE=k?LOBa៛,39?*D**A,A=kQ۱ozZ!ч6ڜ|p:,HcDb2Nc#[3mV#dڮFd RS2wFr~܋Y vz9Y/yLΏ,wgb}ǭc~רD'લ?:`kIχ+ >M1%FCF=IGdwF* w0ܽͬ1K ߴ `&-`HFG9#y̿kK;IfB(c*ꇀ1c*s5ç1 );:ܐ;S4m^X X2љ"n^W;J*[ gZO7]5Ė7Ƙ|sy^j[+/. v n_38x mN5I\V' qq ]@‚7GBNJ$* vpoAOPp _M:<̤.@8BC$1K|-CJTK=Kefxm'VHw oHzd0&F8*אI$S$<̒;pr1so#6Bb5mM۲obh]aybWt2!}ک=qÄSb{Jg?\U7R$틂@U=?Z0I,V$&3YF\d0YjkM:A(v<;o#p>}*Y<7Uv-Kr 8kMb'E1{e[?1 NI;gI$ry1fRuhkn47p2JV(VF# q]pȩml82%1Nk'\=B1@)H?iU$9ĸoFn<1ñ$5[Vt$yeW:V;O˜ Ju#Ǘ;:e%e;?DK3!tgE*OIh--,I*1ƞY`m8V#ZbN$Gc`a8֨Afm|>>Qw֍MƏM(٘X`ϱJsj͂Kf4ysH8<ԫ5p[Nndnd*Ob{ ]Eƿ'1sܟs$^TwdQ6ccұWЩMIaDkxb$m",#2g*pÜ]?'W[ukq/# E\F=hn!T P 0:ӠU'},d91nL˚7H`  `T[,7qy!%1%99'Zi,hvTp1p^>UNgy3&T sp -|,|uBE6xn[IddW#U?)<Ok6;>I# es,+vwcOokKRc%No8Fw'[|k {)U 'ڱm"Y)3tCg`˖vyY*Qw6FփoT6BwcT_&0`,G{c­_ZAhh),Jq>!ϰ#?Jù )>u)\Y%ԏMWy8PgV]6<6G;8%cu^rIk9b% JjW;4!O39#$Rx4eA M:STKHRIy|(#Cݿ٬h(wۼAS,Cܩ̓Ӧho`!1H!(>%i"lduxc{VKkC'X2 p:TC4hHVģ9F2+RZdW )/aBr*TI];l͝H |U'#f~4/P5I2* *BmZəm wIBe_HG?]ƍ;yH'98>E9ZHdc(t#Ay541̐yX?k:m(%x+C*\ž\/݆@$3G\ֹxm!bEkoVi$r ,$3q}k U25S'F?/}1N#`~ԍ$E#X\ YG|*󵗁"=2I`gu_$vƬQkdCvvLÒ5&t#hWL X@p:,N%bhVl5b/9SXԟN3wQ >d%y={)l7qJhH0G8YW7S>r$q=펕wHCJU'v \W.ƛF)7,+K ϟIs<>ǿNR5'Q߼uV6&Af.R%-GT//uGWT` sqk]RR #뷌w>QT摱#hq<߽R:-%xm?a9~[3,AԺgZ0KpyV2`$sUC]^ĵ&w"JZhTAE_'#9 F){sUcΩ:+Nq<% p2AG|sVh2RM2ىEw Aoʐz~v;TF 'toB7 WҵyY$sk̲ 'aԊص9whn^if[F]#iN_λDbpDU7>ZѦS5;{s{JD>xt7 ]@qI$1XɫRid9ygS.et:&-ą@ݾ>bpz[kJ)V#N5Eb7 \8̇hHNTc{7|̨I`,rd!9q2JqGJ38=>WkԕD̬ct(RzTrI~2}M9тG;wNqnqU=JYt"B62c̾KM8;jrOʹ@٬n =GL_ƹ'fu#|wqK++Fa[o]yO T`;`ܪUA3U-6TL2xӯ5ԕ+ҚK#u-(dV#Y7q377C2*YjR2J'$z{c8zB#$"|~`,QgZVmLH<b(9#zu$=jy Ὂ~㞭W/r!<[d}+uPBG.zs՚bynr: gOjҮeT:Hŗpt#G,ME_eeik#vȢ,vQ;!7WR;qޑT+Ȳ̀K u{T.{("KUUG积*wЛ)7!acv23 M>O5m]/4O;}TSƪ;\XyuJqV\Zrt2H ݘ 0H=Y@?s?.==ֱۤX΄|Cg#RYWvS+F͓y廻88;@kJ[cFCwֲi"j^&UA90Rxv.|H2}R^ SRhL( żOSTf)i͓9󟨣Z?3rcߦev;r @W$x?Zu"g.[[-B2*[88eCϒ&8=XmNy*QmL&8b%*$S(gq=VFy|EP8Vpx=jiGiq%B 7:N]rԵ3~ײppNݡJּbkkUI lS#lVܖC+ԕ$UgITKQV1Ucw2,zs_ª_A?)e9 }9yҷ$;ٝ yjFW$nAo "Yr<ǭNs%a7*8ڠS|dɻ$⬣8*jJA@\;O nOYEo`Xg%rTIpq`M[DXN8_,6E(I)'!sڠƤg4˔uUe%NnF=yDfTk?hh Ƽn$tʑԳoDf`;N~xRIng2]<$z >:}t@aT]8^FZ6kq$;cB^%(´'n`>vE2&20Dx9N3'g<=XQuݚ3j4W7s֯yuHkPyh LH pA9"kxq=Vt+SYOeh[+P?zcVmQ8$Iԧ\rJP:~4Ӛi֜s]TݽԌ؀L)If֒Qu$gƑ*zH{5+Mϐi+\Ve^T`4oȹAڜH,\;^ZAzT2ͻLĴ;w\{uB3JUPT?ZMSCOR){y'6 G̭~7߸+#?֏-IW,Ig9Xṧ_5_)pAGқrf.ʤh?{9qJEd 1'>'e$"y(rrKMёGa̟e:vT_lW=+EJ_vT$E*0njRY(Vr`[o\HT,6nj8$7P:~|R\@6H8)Tm>W;*#3f-{FYSʉdag?7)Yh5*,yp .enA1X737Gh101At \U9vO1V͍ѳaNZFIrG/n@pWJwcLfUa}8=lklkMsFS(_`2rA6q+Fd{/diTHFX6r~[sN8n`$m(_@?ϯ"~dYH}ݐny{}M1CpH fʬ6ckO{F|ݡ 9> Sybh`~DSQI<3Jg+K#5>c5\3dolapGtvYWWq%9"5 @8`tv޵uU"xT3sn5R&m@ﴶl0qE_NWѕmby"MI{xd*n HT)^xgihkI|l10BFT֢y$P;+$$8/iJľ^FSԱYV A4agIyn\bk2ٓ›O2yʳ1'%E,7M*>eUb`r1($9$FQ!6\*’G *[BѼj2vzgwl֮Nlf: ghOy eB3a~4(vY$]A!7(pjpS3Ҕb1bu $ ӉY"@9䟛^'=NX,lחD\!a(d1M۽T&%D,ʀ y'Goj,* {TNN/º4ϐA *r:S2Z\$bHyoyz+M]_@iLL`y' TmwRO70Q)J(R-ٛ#Α_af$A8b'qӯSF)ծUm@-#"tjC8SdlO{8 2.BkcvȬӔrip$1'5h.$rLP[=s]5ՑY5,UZ̋iwbIF'횱$w1EU9;$֋q$wwqqC֥ӋZQ5"Gd jQ>bSvF 2dcwִ%X+<ʪ+;m==qt5-,{fPbE:T*rw[IKDh1v#g'Lq7@w1"KIT|Xl#FxIrsEi(U㬬 ̐z͏}2;dQMjؗx\bK.h*Welj+뒱;0hќ*I`Z$dFz~` y^Â6yGsQyyo42WU$xtd <2mbV_U/@M&Dn,d0xjvQ2n 8cV j`x.cWPw2ǠXխmcWS9;B188904gۋ4H8**GhqY :“'W8Z+?k]Y[–ÍM̄ u](w{g`Vq}FHꆧwLi3bGszsNeL ae; ʪ;uEP Е݂r }qLΝ-R r烁Uh5*Wahe''4f!҉`Dl_,BG$Ьfg [q*8 $ 㑎h+135k}n mw nٲ(F +zZ)E+mL `i#+FF¹3(G^ekuBBBPW8S]2ni*ઈrpp٭>XbK@mf1T:T9ifdMo<@J lslTcfۇ ~D5{ e7J#vXO<͎O-+nf*GLFqwL,"OsO'?NkKMo#+UK=4P kO>wGoB$l`mdFK6;R-^I^D'kA*x>ma9=+bYb!EԞ:+I M8`0\ /=AZ-?(x(6[fR؍r]";fIW {1ROKq$AT1p[+G_$ pHl+U卤cv2qs+_ qҝep/-V-L z~ulB )LL 'F"* ܨq=Z[gHN o=BOJN󡷚+ݔ0Ak+Kap@|J\0dN !|i f񻏧#z# KFg)%mȇQ%UžI |Ts)ڷc6K (Xi$@1uJJF$J"ʸwwW.iKyg3c'U:~V mYvFpe4* *+kكʅ {ǖ5;b#;{*([uw(JO%F:vc%)jX_/`påThciR+\G, _?g8?ϥGb' DAOV5ڄ3s&%q\BH*0#Jʾ3+f&,u9<4j݊A$^8eRkhmo;`=lUNv5XK3ĞjȑO^T|4#ֳ.Z+11C*w|0zzNa*5}zU;6` pIgҴgin8,8mYÐZJf&F%< f,m9U_"Ca_n ՏUC#GiM35RܖCs$Fls7'ޗq(h p0r `ҥIiDN-`ӄYć+8e8wr_6ɭdsFelO!W;E*}S,Q0Fg?xEXK;hsLnF99S _w:<:.?ҩ!V91DR$ VaR&r:ӜA`fФ \%)5<yRy~VݭSOԇKUIdHG sƽQVs 6:k{F~`GOO! !hؾA zbZ̗Cj vql]ԜZHmYmQy_ýzDk ezgVl qR7R*d$בMt0ӡ b"I,)#Fj/'\6мI9b90o6"(A`qc H$c .1~&nIB!7HId }FX$ 9#>s$)zfĘq*r:pzQde'Vqӱ^AwƐo|. 9P@=[I- %_ab#xA*ECDҤdݖC p;n97SnI1c1g<t c֝(H"<ǿWUfh^ A39.-o+dA'iI+ubkdOH@g-'ޯMveF88pq{zV7k~į/u{ )7"X})'ʼA”k Tj>Y^ 5A4+TgN[^A'AҴG!s͹X0U(|:q^ӳҺ0:rKX3icÅ`NGY0*I_-QOZd .#Ppg)yi{?0AkJ2!X¸gfl'*=F+Jb[W'=Fo~O5v;+!jܜR8U>ԗ04 㲤RsO$iF,Kt򙙲\ ] 8#o?JG}u4>|6+3,M;cXOr;Wt5[pFDqN;~f.8?n]HhF#2Y(>[c'ӨIcc#:(F= ~Fj}feP|_<.d;}FU*P ov)6GUH\lsYZu%tw 򬀩^^'<վơf gS`&v=AjamVpfDR͌>(eؼd8_O~22CJR6nXq$T mk?aN{5ЇR[eJ3ݓ_U.$1 ;P%$) ybǞ@ ҮlseFB;}?+(CT|]DoSĆI٤iL@U9'Nqנ[Jb8~⧰۸FۮGб$vN~Y3,8yaAS9kKZ;SuFHRF АFHjW1 [$>V'v0cV\*C,l@ m8< GgJ͚2lʎ0KcִSS Hc+LOT>`;~֭q,l"ByȬf= q+nE2X0: {mSP֢##c$ga߀IǵµQ:YYK mmN0@@25Zgi/%K$$Id% I blppzjġ$x,YXqe%lX| 8 V0$ҥP(ngza#OwgI.BR֗8 RW,U쌃Қ1ȧ.q oftg=yJG<~12k7' q9R)$gIąQ tL+w'97n8={Ji2.nx2+L;>ф|ǹ;9rUR޾f֚ 0:5U}KB;fX䑌$`bFddA=EM1bqSu.Z:nxO֝:m_bS؜# ;X1##@'>|w$Χg,S02:қdHsTXUb?+7cP˸@H)XJ= Qtrm*6bsӔS~̫2 (%H%sj9U>,dd=68Wܬ}7^ztV-DUAnraάe6Dג' -zd' CRđ ׁV\%ll$XJbhՀB'@ zqnr{hh>;y.Di ̣gZCm7d2&$~Vͻ)kU8L %6E%yp;Q-֝˳'Qdep#߯Jxև!Q#kh8#zDnh$)s6N1۟^ةY#omĘ ZqJt[zTmqMd?wƣ.$VV ;!L. / ebp@@1feLw"W{0R qPx5R"eWI?62NOAWHrTEE4HD3 8j&gdrrď#|U9L|/2:^=y#4{4 $zq?O;EBgŁ܂%yu5nGm$qȟ J,FpqОG%n R@q*$Hc1&RHO,&quyp.Hv`{W58sއMWɬLױ!Iwʸg!b){''$.v '1{P6r*ԒGPq#ڳeI4U'89UHdD'2Y淴'ɀ~I\5˥G3,0dPijOb-IU@,z.<8ѧܒ^H'i=sJnqwƴu)fݶa~d^Țl1lR 5_lK/!7टs&rDHktl;Vqnwn%FT>E ԐjwhcW[ IO͆1IN`fH]:8+?:fhawzN1e(>;g0 T$܃@W*NVB*{X]9.!E8\ A$!/ewn@r$>UjY-ߗC>m/b<A*G:"%-Ƿ] Z] {qʞx끏^jM^2#._ۏ956[c%08)*169Vk$ͨEIa5%֟ehE7sƹ>hQ$݇2o#j`C] R)>fDBCp8R'8InHrGE$ 8L'S =+֣ swo^ YRw!8\x rrR:Rc{0r{)ΣO\W*L+X٘pOY2LlQ[eS)!…u{Vʬqx-jVRa& LR882OJӵׇs"VI,@ n;f R@wq+386-bE!Fې '8'8>{P$RO7ANsb'&hkZڬlI"ʀ L1ΣIȄ>͖LwbX c6 ktLAqٺt$QMjRM/b_t@ zί e$J#UBy#+$8ĹvqP_,~r$vЫ U"V1t{K:j~R;*NHԯ1@2=JRyRѷ|obSu <@D6w Ox RIrȈje޼ȩ*xf(ЮI)sЌS Sy<6+ɥx渖:Ii&o8; i .vۚҹaf>8_ +)NN=KJW1FIK+rx߼nCi ^XM -+_$?BywI6b76̖81q#R[؄B}b'[Z1lG;ChcB<rr9]jerw|0͎ʂf_d|/յy ({ڣ$1r G }p?:ʻ9HQf_3@]+uܓyn4w)#wG5ߤq\5QVhupr&Zϔ\[k$nD y H{i ((T)?aYCҪ@ʹc8H$Ҭۣ50n&9đzG5Qrm"%Qo40Y=QE *pg8%{K&є\̐D Vev$Gy9˃_ #]u9~w+\eT=?2NJeҥ5 F6(yIZ7JpNsZi,*H`dJ>^rBl)V!~ Pib#t 1pr~UffleY 6yv7?9#}$@1"[q,qn ] --cU`Ĝ@~Ӭ{(,&$B9`G_>y p݌$A<M2g0 sT0>\Uݑ7i $l)*nDu(ݴu뚹MW(;*.A/'`Iho]V'H9uR K;%[fx,q?c*i&sk,:I涳{ef NPsۥeG{1I#aKqqZW+i`Y?ۯUBsJϳd!R#ʨm7m|Glo%Q[{ﰟs@09$F=1:AHٺ0,Xm8x=գe4h[Fь7 c'>H"WCH>`>Jjg=1$6l6嗒sSZO>i%FBِ`Ip2=G &Gb}0:UDr|!_ǩ{v)G@r@~0'HϨ#osssKbB,LH8l_e\00*K2 S!EU1L=S4׼օnb]nu+"R}eS&GQ9tR5.$",=hA5(_B(W~ѭec<>!@#uB (/$c?5[<d u:g9y$.[jC:KC3Œd:q8VhDRz޵}gn\yM}sYK"ef՗r&*0}ІU. uٺL8_#~5z$ˍmsjm3[BlE}raCCYΤbFvsZqbM9!He!<}EV{M2?Nƒv }{uպ%yctN9#ڥOy92)/"v#HIP脆Qr3?c*\þ1Vtȅ&',O+"u%gxa2),_ofqV֋+2ƥm1Ӟ¨ۥ`eĞ'w>է@tME4rfSζhӢKn*ʿBO1fѮ,:!,@ӭt:/yj G#?t,+zϨ5vv:9u8F;Y YH'2ynk>coV.. i02:k4FeKEvTs+xPg)_r; hnOϒ'!S06< ؎)④VxvN^]^)|8;a' ӅJGNovzƝmHc[X ;Ui9L 9 ןAW/d2C|ߕ=+ɫS;\QW)\Cf/qVnD$DLch%'EbAYa#i.ĻIܟýVYmѭHXnQdΤmn, :zig-D2#MqơH!sGHL~5<"iުGq+NJj ˻ES}dہ-x͝?M O6EdqghfsEܷK;#h7ʘO^(c3ݘ>Ljf2q۳Qoښ(B*u[}NqN*M4lN}%ÚUb1>^]ԶrNl3 Utln>UdjrL'"kwe,1.IӑVo_%7;`Qj#dJϿxOlz}iQ2}MeF6wZ擃p9$s9X?PT1^?&E[j0 ;N^]"GI|bw1O5rv4 )mZE2aFx#zg;lVȮV2prz@Ջ1i!܃9zbOnVYP#>G݌ T5_P~{ew=Vbf0mB855C;o/y'!i.Cdrb3^n+R쥆I^LӼ$m$˝Wi{}{oeLNR$0<"h-S E. RwI9j\c%n)֌nb+5ibIE# #)U%"PQ6:U U~R=jT\z9б ]d4,G\}} } i 8 I4*(KR$)*E5[ _bD9IVy:}Mv aaӯ"[F@w!QǦ"g֯r|:zry]3yZ*0J}TYp7<׭VsRC_ې&rUqOy#:3xXó#')u{6kim$B'~jiH3 %/m}1^:g3r҅x#!G>{9|j$3jH3bHrIjtKezAO|˨Fkr[V+M"nGFA&+Yg g$y^3>}jW G#9+)H.Ru4E_S̙AѸ i?ҹ5';|4J~^39c;k@̮p%lvUNTBH gКƵ9|_#zJ%^OMv e$smIK*ĬC_b@nLsVҍߚ^IbI<5jKXSm`m b]hQv߅dEQ۟N}5pVUAS9䃞&生 W7[R$]w+Ӥq$]RN.DrIr{uP l#):*ErJ"5$(#?tdWm',??d[*LK3G }EkE13# ɪU_D͸]"D l89=sk$j_x_QQ< Wyd 'gh.Z3гl@fOimtw7΄]nN]XCжLg9g$*b2;秧E4mU*Rs֨g!& D+BCǃp:qWel4r18RHSiMGSϹa*ʤYTQ ۅmZCŬup H3ISZkٴ]K3t>oEiO%V2b sc':9%9+uf$>٩,,ĦTis:~u)u4DF88qf,) +( 3ҭ8ӳ9f>A8j77q#Q1O^1ڳng/4<̑#ygi"y]#n 9I$RrN#zAQ2g9u*d/\uֳmWG`VDb$\qVdR[l$;N8 >HhJѣjV]I70N'^G$R 8 aFAltk{]LK`C֥yI9qǾMi IYC{~!hD`$`JAiniEb7]60rS[:ߓpv݀N\ijX\"gl @<Vneiӳ7t(a|;rx5JF1sq@-H F!FG^?O¹hNg9.JKA8;<֫,Vh ޳ȔܸG^[r<>p;bI'>l" FH`Pڮ\ښeԝ*%9aGQ1B0:OBsO?ʒETKmlsWXʪ$xarzdUsǨ'gSCO֦t%-Yjvj%6 9^M UGm8 p@ $r$xzMp\<ޟrtn>"Fd*9=<|rH0Ŋ'?Lg0=r4ʄ-#$~Hֲt+ !lr(n2x#q*\۫z/ʬGfh NTi*1؂ި[J{# a 1V."F;X28SbNO$OSJ<&L: }kN)74 Ik)+8R+VpPła888 };W7jr҇lb@B.st[K(3 " 5cv-M"Fr{rp}bK$ (J' Z:PpXc fi CB.K5fK:.׹YK /æ~&6|}rˊN㵍"UVd񩢂4yT23?@+^Fs6=tPCņoұu٭nFTv3mےml\BOZ2kKE\=UNP!+r ic 8}IT < %. v'}#pnI&lsqbr^$ЋQ+Wp +t+=⦰Dq} $-$wd?N~T0||\@H€:oƋWX4^"ea8\}*wF;J-ʻnŻ[֨ v# >g7@o'EђYvq x{m$)6:eAn*e˹$S B )3G2U&ؤmq0[iR6r}:5sk1ugam|h~HT-JʞNI'-XN< mGaԊ.{` _%!k"X3 B|J:lf죣Կq*G,(4cןaS$,e`EP2}IXhiEP=c!G*g޻[*p}\N[bszζVaGDZ`<~B{!-J`@^=*o?1*JaT)''$cjiMk@sWJw=?rܤw=X8飒Hc9_j6;^;SGlsMVwFN[a.7/p UQǽgR\XPTATm(4lƔu4 [sP'r} TmA[ttYUnXq3[Vm-$NB(Rq!{cACN[|f2A9`#T|"'9ɏ7DwlZ2REH.Hǿa/~"$8msNH D#$½YBDҴPDW n2kVhdbYB9ֶ+KhmqL05pGsVfLas#.qFsWv‘Jr#T)'ƣEU? 09=+;J>lW86 ʮPA U$6kG43*) #1۴Gz=2GJ&fIwV#a ; ю.lJw(!NFTvbQx99뎄U{/x99 pF7m* 9[Sd”Uy34\1Ǣu<2ni}:b>)"4KnH܅%{?Zk i"yv($U+z`Ts&&$;'YHs"iEq 7t(Wv-\(|{@ p?¯Y!F` 3|w:X" 08b cװ~nUlafjMs)0/hU w#}ME k1h8 E(Pq$-s]M! d!#%=Z!OXf亝d2Aq"\ČёU,IV)rrI1_zaTM+0I!;yN"yCa 69?5M_M)bUzykIry=I8hGo10}N?Wa-S$m)c7+r+6I;#Q$S ov\p .ofdgXLf,dqbi HSP) Qi9ڣvdy&#I>MEfUYew' ) գ!I 6vTHn!ʖ <:uRċ9`_5r#)+3[FTCۇ5 i4 fv߱* 3l6d /'N~ |ۣ9!ڤѢ3&tPEnx UU}mHԌ<"st%'*T&8MQC7 Ӏk?y_9ՋWrͧ"M@[cŨ֚#TfeSf"ܳ;TO8Ff6 # pc"۱RT[||lbIN e8k&S-;}5aP%l7l~e/*rD-4fMha]@,=xܴWN}Ǯ;Sj#62 c"Lĉ Uwn0sbR*sM*lb,ٍ`H Vo s[5ڸDه@ j9U5Ld1$WD6F-7e좕I ݰqҵa* ;[i#$|!U0y=lG|r@nf;4ٔ-` 0Fc#}+VTVIʆ/Vs "H }Zٖհ+n cISM7I-v;ONfKPTBrkH$@*'̊/S;#U1>k4.H* 9N}ojµ&k̔nz -LvekGX~"R1q${2 k{_DYub6Zu&,vlѿTg'`CɥB\iz蹶h0/! ?C޷4+h,/@jBW Z?5ѕ'? hWzus l{ 5ʥOKel s֪[wݽX=NQgh;ٷ\VB+.pK/+ͣ 9;kVVV35)|œTDu<]G?h;Gp>Qsj k|E9xGu@ G=;M(ْZ2fE1;e_s#Pk UqRCls~W\6F Wkz-ʖC('6jexs=-2B,PaHRh)]Js8,'U"\V0O%ܓЊ>ime41s=1iѤbW"K-28ؠ8' bq4b#F d{0{hOya$8*]>ŒM]m@9ҧ^E_O<ۢ|1 {d3o){ə) T $6[,q0V?0O$W6TaTar *0xLc+Rm*y @{{V.݉+1TXG\,[N ʬۖ#y^#aV+W<4NX!L~Ua#9P9]==Uk's;U*>ֺp1;Gq,S"yeH$ O* ImN ͏'[OLzUnW߰W\yP^!9kVnb,z0UV`Q!9?l־BFEkQmqrVf=͍S' ;}6v}؊9}v,mR3G'ճygSFU\[cTԭ|2'fBO=M8Uiw{t~9r 5e@܀X{k+_[E$ mvm>ܭJT\#G#( U$Ia)TFMޯgQ r0 sHFO+n1rSu7PnBȲ#s9'Ao$m1`0p'~^U{LL͸N)&u/.BO"b2zU=[Ϳ q }2VRHiFUsZ61Oy !AI$cqp$sְ44TOE iR@U`$w=2y+Vծ/n*4`e'=`~"ær2ܑK$-ak3,~Qg$'c7Z2ظUV$МM\ܢE'jŲ= zy! vCN89Y8~A8=*m%Ʃ r1`Ina}O18 /r \ve)&4]ȸR4s0 ?v6؝ZXnۜy=bp*m*v8^001ǥi\ծ:ShX"HPIMWF V,䔒0ܭ}Trof*v>T||NQ'bv;vhY3 3R+@Ub̈~ԀY 4ح#vsrx f^DRP=ZPT~AAVI''bO?)d9Cpc|g(n98==g"31F8aQǨU9EӳѯewԴJzZo 4I@1 RP|3]Ms+.ÛNȻ7p[ߌq׽Bbg2cȟ68=7Ld%H9sҖ7D㌯@ysk 5FfTo nܒᕘu 6vvۡkxQqqåWӭ.! 7sc=1ޞ.:zŕ89uƮv:4^ILKƀDC`y=]$zώᡍXe@ rJ.^4 3çta);TW2UXIq/ʪ}}U+. >SهU`ev;X x?f%[+439+ Tt^5OsfH \| N iF#s$-B6by9Y%|Hx. SKȑ|HV h2xjt+IL2n9`rxcbGN;Dh8kBQ]pd29`c5)E>[֬NwTahutC&H =j͍v!#`؊ NU^tdQ#$'GQqX:Ujg,C~,GQygV^FNp\ә^8Cyg1\n0yp*ίmy{)T2G h˜9zuamBTF8PpTcʦGh՗@<~=f]_@m7gsg5UM7ir%Oʦbe~}o\tH:4+kwlWR k]D6MN$o ai\qDLI668RԹN\?HGVI# z9#p# Aɬqr5'#z`[oyTCᗝ@kt6K ƞP$E] ,rEfuUز4n|ͫ=2GLp~EBP*ͼ(  J{{%cSdZg;po#pOPO1wFRZtg7Nvd 2*1)ۂa:SIMv0Xp?d* {*[_DF̈Au?wYI cl{u "H"FFeA8&T,_8\*0*4bdUZc;y^)^T65`_&HI0*H;\Ӄ]~P˂N=@~OjIAu9!G0{Ԅ*;C݆һ&өtygS%䒠Rzw#8!,h=NVM69E neF eOjs Mz8rbB2k2|٥&dim)-.@] $)?t䞽Ўz3 CM[ #!y=rzKK%k@m\S'8$'-*3׷{VYK'}HUX0J$Wd[Io9iɥ)Bga\}#,BD#I\3##9s=F(`Dˏ;x(G9Oi"MN7g ~e*[Jwܫ5rq{vxNܜP0:tTz>5MK,#$!x >[%T2P$ p$<3u. X"UI\* zAwrݝp䃸ܩTRr漄e,n&3+*Ub?08I~%͔#.H{%r1 ecVQ"dBOo"Fyy_l1睹A?z:i97Dt$HQm<õ\\;λ@hRF2=@>⥷MryA|3cgq < -#jʍhmg$D!F#˄>v5 HrNO$QIGr5Yᘣ.I `֩jeᕑЧ[pTt!kICt)MDER0A7l|9I]ᱏpFSe *` wr~n3mbX2E@Ȅdu]Jֵr&o'2BZFSl[㞸Ƕ+If-TS$w2Bb0CJsߴLŘf`;,B8T(݌<q1ʪyYge(6@;nE4ڽ˧%Z%eAhݶ=_CE] eTE&%$`YZŚȒܩ)r{(ŬgF9<tܛTmb~2:+cQ`y&Ů@+a/Gf2$Qr0z 8 loUH阙 w1 #У.]w7n"jB&aF8>R1MMeM|bI@DZ4yj`_#vvێ?zԈy6XI HF>Qn斗vbA(:Gn@*JbYe7:8ck~bFbd %F G|O qfeNۂp[&IGUd xVHLE@ʧ$꣌cb`Xfy8NkE$!i'_@]y@A>p2;c$Ɠe]6[Jp;[̤Q wo96:?@#9-L7ҬKpķ~8=Z?%g`PnׯZ8.bT fY ?ƓKqKTK n Ù0U=9=jJT_6F ಿ_'9%_av2DQ ĩ#E-QLMP  @SU%%i-\[bh:h~r{ӥ: s=km# S<鎵"߄+.bt  {TV<-{csXrkW5:  [fKn%Xa1zQ3g $cGfOOJܭiGH>T]"du d#ʹi~c>kE5jFP9W1KkF~x?ZGi\V>J̞X̶L$~iJNig1.ɲB/͂2X3SfM|Ȩ$2dVAr3Ro?ٝÅa$>oݽ%rI[m[xDA'6<"`yZ3FX d xP0CWV.e?:H¨37B)A53)\x''޴o!ea  ~/?'֣/v v쫽*=jZÛ̼זt&5Pl޼u:Qv]<.ǸD3G\wW}Mry)68;jgTYU௷MeuH*It2\erX/(^ɠ ,{Vr YHR<19|QVoeͶ5" }?Z -XdsakF[;+p 5(^|ϡ bX;|$7AcWclp%ܶzؚ<]BDm!^8^A{3tRH5e#zjU#0gaW]$Y#SfnVBũ A(K|ß͘wE(6k3xru j&v``5ѴPD$%s~$ 7}5M]*`:ڰ{Kt5u3s?^+idH7(5續F}Bq뺴GxA!bX}?ZUVK#*[&8B"##u<ǵQy$@NF~\2)J~BnZDo2$qoD^:U@RD4ymˍmw[ cJ5Mnn^N 5˰Q(QM66(&Wq뢌gR+s*ɥy2x̬ dq:CIidY-I HjM`ȳº\rI<I -Mir7kC )3 W0A5%VW)m-#jKY-&`:+3YKo4`+ ?"kk)˓d^p&eExotJt(.+2O]ܜ~5:Hdo)e;v1j6̑F!dq9sSmx /a%#uHFV<*pcZ$2G<$u"Eݷ*p<56lDvFssYɹgFXwvʇ͞r89 ?\1B2NO8H-%e 9 #W+AS4y;œOՇc$߿=,]M:6U˸ 9>ʴǘzU3%M8՝t֟t-l%F!+dTfki!cnUx8_$K #v=4dhvq}+2$ZJ" FJJlZc>Zr%Yzp3ά˻;NHS ZRJ*]4ˣqBUÑ)p{r)uDkB-)vӕe[X)!9z{SngR7n9>gIҖj3手wcnŰRmS?SEݲI2\d` \Ib"A#?6= gt>_nן5-eRi'_qD">#ӷ#Uyt9%Rl f ˕7}<]RH\Üg$E^RTfim&KϪ*鶨>%,ygh@j]srϏ*{(Z1d~4 22?Wm nM_KykrNheY,; Ig'\68WˁUh9ͱFA:HMJiN$JE|R(HPF[}+%-nn2P@ 9`Qny-U b\nܐ8)rrF="DBlRH\kSTWW+"KTLgް>+sOaK& Fmc>=2#f sӌu+1h=:M{y!Qb#eKp\3]gV ܺ'm3"{c2@>cp1`TuT H8yb "*Ɯt&nRz%FdBX=i1xn~)<<1֟,ѦFN+Ν6] nnמOooLeB?c隫io)idtcی{O\-!K LUBrOaVo\0Y񎟇O^=j+I-oZ5F1 y{1.r;tb1#ҵtud278lslcWW4IS\2!Ԗ]1i1߮1:Tыk$m\gtx]%3:rFx =֬(H`\A86E7}{8Y$`0I=VE9äjP‚HLqW}aKd=*KPtJ%J񜓳 'td?Њ#,Ђ|1j$)o O:;_AĂ=%h<{ugx8;~=sUI(F]0Xwn}gZ}:-ȮM !I['OnAnLFv95Vyč|QEY 6ېAq5u6eFkhޡkȵc-3f%ew7A-ryV'ʎFwrq9$c>йu?"1>Q&AXh/iG4d c=}n#*jۃ'R'5v_b"Tg>ZۤVP7!{rERqEH!\K`MAwmLY$dOnqY\ޥEYƋQjֹ,쉷ӁysW'qK˹\EC1~.Ճ\!ebʤ`Kb ]U_%/G'ls4J%1 ­IBJVԻDQnbk[eLD8!r=?*ՎLOېO=AP]0A  pAIko 7nFiLlcR*N6qh >0±Ǯ?Ư+ $,~9H%fDV I)c ӏsSt6YTRVHt<؛+ nI?VFp1GXh@܌q?i(W8 ?DJ|Ʌg#`'=N]E"BNqC7BinR)@yc׬%=I.H&>g>ҳ9Wwnҥ7a$@ 9}88I)A?0U,qzQT!kxY ͺD#`N{s}5‰UP3crcGcXѼ:M ,(rQI OpBmX{y乺F,w$yqs+p q9$qHmlɿ};;8lg,3qQN[w8W ,v"tܲXs5,þEuk+pߨa\܏񫶒; T=}٪U*IaGnC8AyMed-I9$>I$eodG"0$Gm*G=:UQ֝EzѧڊBppix!G$Ƈ{f'8cqi$oAXԪe4.2ϵ1TnC 7vx`3*Un7O_Bg•GO,Ό HOܣ#'<҈ E%v9#?ՄgyN*] {D0aM.%Ax$w{f6PaջgfvJQH#p'r5HY uUo)˜1| xǽW|$S&߻qOsLu8 ݽ$<>P<srA*@UIyZ's64;N;Tdf xU;Ԗo)R>QH3#zc53عc#󐹉 NFxգk8*zĒ3:+6O=AnGKxlUn8yy%a=@@[ȱ U+u'ZIP G֝Hsk$**xGko&/ leǶTzin8J[dvHQrx{V}N%ҮjDsϚW2y #C.Y^299}>=]#d_+5ۻ11!( W<?{W?(!,Nb9e64I1+M>e9;錓\4j#RE#;*r.QmoDG? DJ)b sHp#a;Fk^o6tog#K)$l{w >9֣s* ^}uW 3rrvW_yXjI䐠f$栽!aM﷠O1"r8!I$``<_W&/,[k&r ?^՜sXS+Iq#LI($'NNO_' ueMEoc(Z5 %Pryi?k]\Ϥ)Wp1u棉b,\& @nⲋQ;)܊F2I|dġ@TNAUI; .exтe1Y8LpVla  #o~ʷ Hp01p{Q+T$DP|aPIUF9*Ab}1ZLPSO&8;[#})&'I2~(P;\femss"fve9G+Nkqk.\ 1 Kcz}T啤(Ns'ҖŠ:/^6+u?@ŭ֙"n0Tᶒ3EDa$s9^kI?3@cR|ǭkYퟴīw8LV,s>, '!c]"2fge;X9S$*[\r)9Fnaq@:Wdb%qoFOcW5c&>V\ {˴{V;yӱN⦣(ƌ"p(8:fBD\TjCLۈ\8GUI-Hxpw2[dtYʪSVا%7vsq"@ـFBTg-۹nV(cqi$s}RxUQ+K yQʰJfgC!|J73cmQ(ZƩj5;P 䬼A"wpX*0ymIgo0kl{|Y(\0A*E0Cx߷g7Vf?;rr:c$~5_RI+zr*ԚhZyeڣ Vy32 wq=Wҵ9N+XaJn ]8#hf:Q4$N3.y"4O`F` eO[_oCo"K}2  pyVK\rL%dc)h9uPr1c-DWXĕRU~Pw^mV5T[Љƙ ryueo9g3(r KݺW*+QuZhXY 1J^N+$X=202R/82n]ܶ̌=M-0Gup%XvF%@fϾ-K֖Mk,O+,׎VB,W: ?-ď3S'dz!F290xj>Bo3C-đăySOcG\fhK$RYh& *q[ƦbFY.K㞾YneS->Z`NON1ۥ]&j#o/W-,* 0wtB(. dܜg?@=+[M$b(,I9~AU#'ljn3OB!ic:u&7v+9bN8c֬{rv`2<* +9+PIEd/|FU+}H, $%Q@x,:ՍTZFa5-I*ے+S߮M &lB^G'xӗ=Թg ..LM xԘm#bV^qimw5!V*I ! cQZ0-66IƬa9?+{k.}= ^iZE #>9{ܛ.KQL[ mtso!.`IZo5 uc 3+>BNTIYay*yg,[ Ԡ\S5T 7̛zN7,ib5АzVw*idmQwl(89Ztc72W**c>*j;nE,-6s\fMi+J8c#2ۤ'ˏ3SiW7/Vj+sÚzZr2(+ut+su+h&X1\jMFvI^RfvVgc&q𽁆XcgsA"6U`W7tI7f5o8$qZ=ıMֳs@3?![\۸PVwg_iY%hf Gkq"BCXaۤy۞@aWP0K##mYq?Jf$j'q" L;ڼs`*=r&ۢgW-ʙET6ѵh䝀2Tg^rg^CaI=e}r;Ŗԛ33Jrc\`z8""MjvH#*>9eA$lm6r[ zp?:E4 ˖+6zzjӹ*Z؎ \|Ú&;1#8`iJIl.Y|T[=RFGdFXQ$BFTc'~e)ef~pS@=?R& t8.o kv8` P@ ϵt D0ݴG5c7lȥ!;]UMFk+ӕT).`?4x '+ 68֮e_Ȭ,s ag֜oTd$Jv~܎?6y#9ھcuwL~Dlzg֦Pm2XЎzױ0漀G A`]ϥIE }rT޼׽R!'?8g7rcbbF8kH(5`q~F3^]Hw: Ӯ9m,rBC$PE)xTGI]\'BJ 2NNh14/,&El;sRiQr@OB1[|vq 1zR<6,{x9+I泧]I8vz 6-qDL4K N,-bI%?xWO<ƻ lĜ)}ڰu[a,B9$Tҩlq"ʗ,q/JnOj;Э9*t۔`-GHJl+DKUwXn;XT`#N;qw,pfܩs ~À3^hyy|rÍ=AZ KMpATzXEnm c OjMNRi%/{WOБ˕o܂p?<5Pxo8,:aҗIKy B#995Q-7(u/rNI)RWt H]B쵕9]oxc[ǁw[kq(%P0ͺ Zk z9uQtLD)Q6OR3;*cwB" ͏}J,™.&rx`ty.%qkӇ-5eNAi(yD(^{ԡ#OOZ d`񸌂s]t#fVV@ VUkKg鷾`$r#jj﶑Ю$]vb?1YăLe}jMC]s \ב;?uTԢ"R}6 th9{Kt-ddߖ8fX¬}nR2;B:P㿷'VUW`Q1Sl89xV.A6?1ONM,<[)' i(;2y}C%XbV,0 gxG$$yr $1L~Ub7nx$G:%XH?!}++TKut-<LK ddjҴHm秷OZejHB{ΖyLۭC*psfW"Sٜ%(9?b9PK<~m~b 9 O5ߌ3s{~?tP+%|B`#6ќjdW$ Hϡ硫JB#(CҨPg2v6w986Hq;(ZHRYetBN?yƉ7d|yPu+(dy8;ҢQOIQD aӴ높K*ƫ xlnXma8֭Gl"򭏒O ~Ncѡ )8Ns[Zη6Iw]KګkVET,tGB8qU505@Cn. @!F}mlX %T$zXI\pi> $ lVf*~N1TFXP"/psǥT[WB8sޭ[L1*tZ٥%&ѫJ/:S'cc9erYۻAjѺ˅8n ij\m˵<0G\=Ms]22Sf-Ӻ蒰$.19JqFy 7˻޴6d#Ȁ$8 Oksc}Ϛ]PI!l*|tNhK53-),ąs@=9ǽ[kC,L3(VaIUmD[˴#[ V}fܐF3QiBL"*0 sišum ZMs=͈.$6-w,3;n%.mX$/09OYݙ#pYT`nztk;ddW`(7Ǩ]K "MSwb Ė")v%=޺] $9n4cfz8Q˦Z[3)$^m3ʴkՌeۈ foc f '/ ۥv}KG'{ ܡQO)(s^+S&-@z7AzSCQAКƴXp8rqi ?68 d8ujZ]AAF14eFHK1%YW 8< \́~/$6/?ҒFB:) r+jIВXJ#qϧC9XկiQR hE8yC1e9>kז,r>fjJ9}a/oV @ߑ+/;r%i&jIF'9$c?I LqFI{ԭ- UP䍇'`TplȻ|=xueZ6zS%TQLX;u`r`(=IDr(Xn2yjVԠ{mTyKp: ~JT!ijѼgvjH02O#8pG34j@E!+NTm2u-R:/8{msJ^b)f3)!IŽ@ M78A`g$g cUH[^氂gtd۹[vqĻv'fxgOq}@ DrGrխn5fYr>P#TC\#kHԅd[3oar@pִ4Kxe%A`ҫXl޿}%k}iMv2YƄȑHRq}57=ūt I M#9%imZS8 IjXtTtwf\~&lLe$6x9zr=k%kf&m?njx$dw޶YF"d#iy#]{]=q4hcsں݄&3/ SNiT9릣qڴ~d-2 g9sILq/"B0~yV-i%+ NtG$Α0;Q>`qמofmКm ND;40d #4HY,Y7gUƹv* eYR"#"rF攦勸ڱ%\ "۾pFNzZqk=aTϜoXf"( rp g?0ɧ 8x8H-Ԏr8 ڻv7N隆"b8' 2r~qHI^HϘ>lG\Z{Ky+ A-_}+X_[f)Yr0[99$V~S{DNɓLFwAҰi&)bTTHT{nEO"\3R0 R}4mrQ O·yiX4 #EsG 3ӂ(퓫L&YH'85~[6@|9ot+' lО:g?Z6dFO{bgU6Q&A gyϹ5NEK{+)J&B ׫y\Ǹ6P~=sRYݬH6* $jwF2Bb7vX}ɇRYr=zm#X@g-wC.}B QZ2:گ4΁e+w8߉$k>csosCw%F䑎X}J\WwNHZǖaȤ= scB θ8e@ 1(,_\PC{gq>ļ;x.e8'ܮGS|&Ft-.Ye3Lq8haH>^N}T1_ 7`}[Vr5YdۆE`s$`JT6wẐGH=9 4/hՋqL5bO\dӐ %O嶂hVyFb[wwmGɆ ?)#iG_|沵'-!#|J Til4Fww+;v0o~k @F:~.I`{Ξ?ݩAc1xH.<}xNp,#jC[ wJ"U+2G^=i T?x)S+l` @2FyQTr8/nIrG8ǸMd;7$5 hTh0>eF^tU^ 9T0H!N=x?\%#] N.6[+iI!3*ve_QﶸO]K!1yeA\qprqvZƱos"F'g qk834G0L#?Cڷf*߸:y*gQB:#M+[n#'tG&0G<'=gc%Lur1e#ִn XdA#㻅9cnMGmt]O TҜ\vi6 =I-Ѿ~rS{`*snpOوy1vB9WʆkTGֳ.;w;8T#'_\ٮe՗v VtI ̑$FhBCqw#ݴ O%)q/1mUڦ!Iey` y1>MEԃU[X"9`#poke 9VO8877~Qך q h ;st~JHn$sw3#&11B!֬^61#JdІ*֞r+:w@Pø<j{-"HBKX<2yFRb?~=<׵kNym!vB@`لhc$ c}  R+Ikx`dp@j^]nfD~',r:t͟G%RmŦCk-)]I88Wdܬ.ݱҧI,w3!ˇ, XpF(So,*d($m/B3V&oUb~3,k0z%k3w/nݸ);ܑZrI.fH vc+ 2aYbc)̠UJX8sۥC}S0&߂H=3=T/"QeEe#sZVMd 2s@m'WMsw+]ܨt.m%YE( 6s֝p S#H2p>ajĐGwil6[zt7Cj0ō~8O)%{w%;8c{}j8e'8Ǘ.cZ+å˽*GV+F99S];0ftU܌ 9FOҭJnQ^Vkm"X@rOC$r4wqJcL gPzqG,r[ᗞ}pkbm=í7.T;Fcҷ'kHϚjD6?j+?JprN Z!lS$ˆǔaSl#Ymd23mzVո|E4RL& z}շv1ő%rp2@E`qXzͩC) Ok)P>lW[gY,+!ӄ, E RWؕrڠ}2)qnd"=1ClK(wl? P!MwyrzzT4gKl﹕Ar9<`gJ)yT@cuƒ3~v4nvH?Tz:]7s5~-k57O)W=xc 4@>2Mp"3Ҭr4vwKtjYYJP3Rj2cgeR23N}(q 61=ixmY$/\{Vc\`˓SO!9fi? m,FB;vƒD,p&sh^,So=Tpl:Gw)w 0RO~}{f[9ҳ~={S6qe-e-.&\a; wC Ɏߌ/#nsJ8GlZ0]F9 :s^}*wѽl$HB$l'Z ;v=+mF>`@Q!10on9]UeBFܾL2rYq+&{,0&_aZw0y0K -wg%{\†Kk7$;–srkNj6#L,8Vb~Evɘ.@ڿSkTjmn{2]0@/ٸNpI>?֯bG+d@q] mDJX`gVՅ n*)zKRW.#3|xJF,ot'}km1ö'Fq<rJIһ[|9'[IT#fK$c\crOsjnJpђ$b5Y%i^Nq8mt-V+susE٤Q .ߩ>j|bA <PFJO`[1w:0 nHⳔ;I#jJܻ/#4n뒪`TL՜Co21l?zх?U頖-̠@9ӟγQ1i$6oyǴm}@i % pmc{g!5b7gaR9^wGWUeCnZ%!Yԃ֪݃pQVnŶYt4$Ky|Pa=zq³eF^'-g, < 6N,R#`劒wӎyB*vO$pI$ kFFY=Äy!qԌ5vF &ronrc ǎOZK*c$9=+]Nzй$r,R\KqBcΛ+G7̅;+s˜|\dx$ZlX|wsQI|ZtdRO^׏ 7`@EI';v59nMH>`ʑYIġeps!<G}*f43F?ג1}j1GWiU-*s(BUWTs\Eu_˽FX\&{0xzԂ/2UʉX[ ccsN6-5SKhVTH2p+H=)Jd@=7cZѣXxYۣskXCm{tIҭYo{chʝ$˳6ϪNc>#C/ p)W܀?lw_՟i`5-H;pxRX |v^fk~b3vC#ʿe.l)5 mob0yc֟o [¦3r]rg>tIcy] < #(w~08j@N1U$TﯢǞNI Uu}`KRN#=AXS$^l$G^jKIpUto(MfuIf hU6ñկZv;ac5e٦TS 7g 6i|}_q$kSHm廂F,8@0G`ԑD*Nat;ԡ2;RtRۧصacK\C=G˜=? ׅJ3~-#-#1ry,{'1)IG Iܟ& A޽ydWW9DK]@%s8FD8yUUi#pap0ߕLd^lQּI+IfٿI~U z{Y RGCǭSyy:B=g+]GK881;Ywb[l,x1xU->o 1b0I8'lu-!YIŗ=r{cQҲבϔҴRd)lOAbTF` VX,c;p3p9/#+0q'OIEчKo&eW?)ߞk2)bX&0`xp?? ԂY./6<͂rimnF?vC18]OԞH,2r ~Z:s H#. ަV:JnXi.YIK>g 2E )%_s=Oҵt[!WCA3=Pœ_'q}5i3@KT XCӮp+nY̊#2EEr j]FpIH<c8li3hC6cC}ojS_2 [I7<2[xĐLf9r98*Fa0{=>K$49^50Z{ bȿ.XMhK$8^#]ۜzUbMd@.e(wԁH#L$#r$O?Zk{j,ۙ>YU ](Ihɡ6UhŘ8qO«Y˧ߘIbp$z`TqK2^$6miǨG*ivuWW gi8{8%nrDH2+sB q]Wօ8|7lޣc$n$Y>bzq$i?SH{=NBM/5\Mly G#IQW`/uƐ:Bl'aZ~rZX2$N2)mZkESi*翰5)Y<}Jо[sF2>RW0fs)e#ή'3t_I;15;!mΏ϶1޵ˠL ֱdPXq;? Rav3;5I(b@Xו ۑא܎Yumx9>*5%>R4FE*FO8v7f#n0Oqr. q'i짹ǽuAOr[Tp^Vd!C,.ܞ0z=~idnCc3) ̃x#aYق|A=Uir(4gP/dz6k@Q:<~pxӎ޿Jy'AN2]I!R31Y)hVB0O`r9z\m%, 0y⺱I7+\T$ඝF1['sӭey$hFnBqʟƺ aU܆;HO3V >W2Smiye.:d:s;Si'\Rڳ$|`Cė'>jWfq~EG5AܴRjT,VVi}#hCtzPAea- m\㚂I.$PR@RXw֭eV +)})g'Ұӕ%ǔDC$jxlI{aOagiYsHeBGg%ij`&XV|Ǡ8=*i`ILrr@ ?*iW]Q]or),޽qM#sc[< tӠH,3C^X# rr 695 pA 2ZzD)3 0.8#$uMmR<`Z—qc0ё׎2xX%GTtg(s+u'گKLp~OΫIm D*]SdddgޡEX3SIpU?hErCy^ 'EI\ĺp 8ە7<\[,[vU Tf$6GچORFr2[j$u]/yXBzXPbaq {N&J9p<[>l2' qޮ2COv0®nBmLӧi[}XW,]z? K kK-[a(̅2RG_pq>_lo6>̯s1L#b2s@+2EH ~ RF;|шmٹĤnP@$f$=M1XOVLc\n=,fTyrd9P~^WEY v .=3 MM0I@D`fH+T9]6[#iURvu=EԆXVAo񃃁du7[Q ҰI`:;\Vt84]9iSp_z~Xء`y: d][\Il"Rݴn 4Qu9$3L-dTV8;,7 +y]R\~, #]XUBibJ?F|uV,E/p_'v3:VhֆHnȽ%ĞJ 69't^AZT1Wa81/LcA)rAO_ҺK8#̊Ap4 #Yjn#[I{c8$zp'Cc.ti1iq,H*=5(|fn-sC%o'Y [$qc>U{GWE7-$Uʰtb5}ibR-h. bO͜m1h#ycr$ ۀ㌞ٮv)- U2d7`'x53Ji%>ɫ@f 1@ U"jiE)s]= #OFf>`t UՀr >ΑUR%G MTmoOGEI.Y k);AY۠gic0qwTI!vNC,\XpY-FH37c=U$!ẵy"dT3{c4XAd`~$'>Ҵ ~0~jiX$ߴ!P܊n,l[#$GC厙㧨e[Dyc*mr צ1kIl|m(H 1{oaM 5I9 8;[N,Fpj8h 9Ej`6\x;I#đ7٣e-䴛Ao ZKy s;Σ;€6t}=ɚIG6<ê}JR\S)h$Ip/{%mH"HKAA'hjk<$䫕wl׭R}!WcT>_ Ve).ittnQ;OջN9LA6풨By۸Xw6zΙhy0c3LϹ4M\r~9W?*4Qpss$ꊌC?ʈ%M1F*0Uce{UBQw~Ѻł*GZnhyx] wsAU*嵴2r_kiU]QUkfl4 ǁdFn#щec7nq`W}5ڥX¢0ޭ2WC([V/2]I!R'#${n%o-- yMQDJ/+8fCu9hʣnqVwo}J4oE1U[=j˵iZס.!BԘxBӐ~I_R3;+C_S6Y[0AR*8ji,a@Y's*H]$ N?pV rZ]ҶnȔIu {0mNO৖0ʪǩǺHµQD6yG:23؍+8Me  NYRՔVD` &8ڲeuy$V8R9SY,Dl3 d;u72ZhӴ[ "rmYlcggZhfXc;OmmlYHrܻt6WkNG'NImddkFPIhhۧḑt+83Lc_Ƒ36}j f Ul$uZKi╚'Mn;J-vk"yT1D|Uc- ߐ zIgt 8I[*Lyݐ=sJ:4recI3*@nvf  xVC* \ݸ>ľgvϽ)52 Yn(68?\U[`O,$KP)lGD\E fHrc O#3Z.[#ЛY'g9ݎt+0| ۊcկ@Ina\OƳ.dhܒ6YS.{3Gʋ606Pr޾joE%.e=q3]쉄e#Z8"9'7( n?JT[7}QP^ >qLD'ZI2 8;c'ڶ7VݜZ0m n:Fy{BH~ɲ4W`7Y2A 97ԍ,5ݰ`>Q\UiTu/} %k33WV3[2TOI檧?/#0J @xu>#t; 1vP[ZG}g)b\˃zYŒ0ݗOLɨRdkSBv= 1Vx SWyVӏu-F8UZΪ}bjeR?*G$; *,F[Hcr}?ǚOQ" %x׮?:fTEр!xij7#aDfP gv~_OĒY$ .cW:J47RnssK+k[u؇r+rOqtо#.@5I#u'lp@=Z%[vU_!~4軠Q6J >V]bNG|4nPcA pzUfUʃ~/Od\ӗ vy~NwIZ4 :I,f3'>{:}*UQ~I#8?"F#sjas.Ib>=PmUA(O >b95RS^PNԖ+bnM7R\ UXqNONIO҇h fTavN ;WP)[UHS{qjNSZflZQ2!gVHx!sМ Sỹo4Gn vc5vdi2mG۸\XIdm8!eSB1n+ُo=$csi k>X%e 37{ƖKKIBpHRG sLW(s{ S] 7 ϼ$'z*UKҺ&лGq{Vd|*p 8\7| Vظ+;?Ue)7c7X@d$b6FFޢY!}9? E,SX>@5!nx{;>X7]q$a^~i 'Ox" iy(zIjHf]Wrf!cX64iT1)1Y{W9'%Еf* 2Ų>a}$18ATV 60ޟ=*g5I (Q1WF=UŹn<#7YTi=ZGK$%܇= n3f *nJp}?ke}L9X.1n6Asqƚ]VSW>$a3pcsX|.$kim l5}XZI5.U#N:jSkyPaZR g}xh#*il 2{gk֎Fn[R)+1LR3NWl62JJ)09N "ݒ`NJZ17 kGt' Zfwccy8y+ ÷SeI1!x@9#=}3Qp} R+SIwAGm982腂MWrI'ZIBXߚn2~-#/*@9?Z8<0sqe y?(1v|?p.*/W+V,"E*\߯Q#|spSȍ|qONQΊ=\k)ԜmBh,ZEN cq߭:ikUee'88[-PιfH:eG8^;oW2-P6dt:- ,xG4$Us$}CRXWpr;qĉ5lыm q}5ƔBW<0rz+ora)+lsbē$opC@ e[5V}q.,q<7"(Ӹ1=2AI`.mC$>f'ER:7;1ySZ t5f/ϷtיVB%FIt =tl~Y0㓓VaDȦWC *Oɵg[7l Tw2ymH-a<(h9Vu) 7(A$Sܯ]$eRO{%}h[hj3O131x3qQ^H诱fk8Zk֘ `guhY.*4%Y_%Z5'g$!MԺuD_ f^1EgM96IrN.LUlS eI>d(r@+}ATe|nipǏvSٴkolwG cLc'=9NܮMW,LcE,A` }2*1nY<1#a3u$Hq @̳¢ݮDS]OF92+X6q8QCH%z?*)dߏ^N:Uޝ2G̹Bkd([W$`c]f_YYemeR[kO(0\`ۯf]+sEy)~;wP󘡑yQ(;#Q֭.'HH'c9IàI?I6Y%̑ W@pTֵa61iw\N8X\6ȡv`?7~i/q}8 .E&DmNNH=xOj}QBS6fRzGn!!dt13+OC9ݼ6a Չ 6z}*שЉ.-<"D$08~nըض#Fz|<\ͭNr+RTx^e屒r1O\qDby44k߄A y#95d 2F5NI|bGI'`u tA&ky ƻ@ =xraWwXq,% T$*+ *YLbaIB =>C=G ¸J,ߛI^ܜbNE%nYۢ31B'2r}$2'ڐ4e`>HG7Q4K yq0nN٬DVm Dps])"nLjqBzNg 8rXaCҘ1\M g2i%[H˰u^\k1C`l |ztȚM˨ikr 9?_Jē4 ,2d;H< RAp*F T6- "<gIK)A Ur;gJ\oR$H4'622)FT:kW{o&%6wL8P+Ư:d0ÒZ@iZKBڃy, qj6Z,iw%Eu`[v$i,Y,!6 gNu%Ibd,m%Y";G%A8Vݽy!%l᷺=:ZUm(NTZG$v"1pJĉ+ r}NߍQVUHUW;wn0@iet|F M+. Cڗt]D Hg8?uv670snwt{V51bFUX6p~)6& 碟\ i~Ѥ_ċPA& 0vl ܞܩ;NpzS綆c ċ4~YbѰ)!@njՓ^G%31~<{v%ۑyb>@n,ǭX$:| I((PEܩ/Ě**ݡ3=H pzV-D2)LUhH>uiѨoaMX푌Bca>HڦwB /~cy%Qy`e*ݞ|imkvr)?teaXTJ*9uI#Mf N (9k>&VN 8*r#i6\p3}Z-AYl?H4+#pn֘VdY_wҲm}Byh<ߵn[i, *aN siEͩs{Jq~N[f4j2}@[DQ*JFX.#8'’Ie  V2rW$nuZXѠgaЍ>R$Im4%CeQXc>?*ͳt #7΃<6"&xOC)F6,Jaь*=^Jp4wXS(1P413֬w25˿;qHE %ym')P1֫ډ\ٝrtΧ1Fs#ȯNqҥUVL7r:n/}[ Y.ϖI=>^9d;G 9^W{$ loңGr<3Ίc4ԝ+ooi,S p腘9?Xv,M$m|R]Cu"$ʓTT7QQ$1Ujoi#EnQ0ۏ˥i+-s<$4?XNZӌeDjD#>hS,RFޟ4V^tT1e$)M.L&i"yp sqRh [%9N'$cڎXtWtHR$#5%ܲ@wG$lrsch7B btn)J fiEucVRJ T@ndas>XDdlq=;T"FC7RD9XukHeX$cwҤtWBK3\M-@ 뙚G0Ÿcv2ώ[ҧ '{;]Bm0 7cxG^ mEd}k%"{/ l5Jb;XcW'Žj`kZKQg\Yʣ3ުjQK2޴|Vbgu]jBxYfHNvssS5+W5 G4q$B1ETpP#6W=SA-ձIg'(:v H>UsvnoGh z5`Rk?D@Hb3kY鸫`Q=HJ͑u=ܞ7L`M%ɕ"BCwe6 { *+*<, 3'M#k*0n-%)BەsryS"tubYّsяW˽]F°mIg<Q!Lw(SZ=wx?SoUn"UDh$#OƢXO&n$g̀ Au"$aI:>1{ApN v89*MV8Y$ <̟Z@K 1 wǵRʲ ($Jx=M(Ihhh͂&rx;u۳L2 X:#wYr[Amܧ{`74B#͌m@瓸s7Qq[*(.$IJ;wv.Y/-+.sC*9]k4,eRs8:t*4̜JV{K#-YY,ː|5Ghdb@ c ǥZXi" dsߓV$arC7l*`rǾy4"ښR^=KM4ʏyD 98"ѯnm}Ni<۰ϥX썝$g={~UN+iEI$;39x5a$FUXrFǀ#\ЛW*mrŚ;ekc V֯q  pI9*xjԐ qH=>;ŻsH`6sϿ^3Ǩ5[w'_$ӄn)賁qXsM 5$r3 @ۂTdFrź {t0 MJ*6tMi)Jc&a~ͅ0 O:Ȳm/"- gmR BVKgQ9'ҶK_SfzÏ/?ZAzc-'\eAFLkuU(%ܮN4[UnaT7Ч3rTqW$rAdLa<;՘є3q׍:RU7tҿߟt\-3KX{`y})ǭ^&v9# "U'V8ES2r3mp U?Ӛ&a 88w&%9\cZ'Mn^b0wR@93Ow/bVwSXJtor@#i>Z\dO<°JPi?3j3dW1A6GH_`YPc~E8Hr:UonYR%;QќۚcnZG2zNkwйE K <{ST n=9?NߝY^dxI(1gZV !hĚFrW#jD'%2H =sQYkQCp݃F{㚒{sef B{e%$jҺKN/|ԁo~k@E1 XgxAyx cq5afgutCao !۳!K9QYa$6Yc( )O *e bv!L| 6G< ;-t E:jin#g|:DE mm@۫1!H`IsR|OSDF岬ZX媨'@*}.YI1yIP==*EIU61toqb3WEXR{((>DO N#Ʈ` N_v4;!PTސ+.0xQMҋPQE@s`њ\ɫ6⛂2F)A;&y9z(۟HvÌ&xQ]}3dg)rW +X;rH8 cxW5HԻ:lSh=艘 Gֹܣ;[R;zy9==1䐱P.y')FӐI\֘Dp[-Gnӫ)EJ;_ ۀmZYgcʜHF@'늯"d';gc56=2-'T6x~}EsV,a#?L/w,KxP0:>eitI(h*x=TdʏCKk]+،6rz֖4 ocv9b{Te4&*[ݜf,3$Gܢ7UubZB3ȋ$מyjVdcItbn3QBs|rq7ؕM{c^*hdPرlI#ZQfow$ǸNG$g:[0N\\F>HG81䞹܊mw7lIвbP3)@힧T̑VEfUXs{+A 31FJ/n+!mkyUQM3E \yZt++WTO\d9gk *P.dw Ghp>֞7@GPs#܏2[,sRN={UYn_C@|˖01PBnLf@YsqL{՝ ^_V2 HRA8ȪSOSOcU~fpa*0]'gL(eUG 8^d7nFQUWdsԓQ `%V4ޯ!Hn:iٚ1S 1K*uS ֥Ċ rqڪE5l`wZ6s5yAkq%c.H9%:r:~&Kw21ff#\~AbI$*Ee܉Z5*]lRw:N*6w#JFOCsIҷvXvێy*`:2+"\m%z}h0~$j1-8pzӔI'՘ͧubLj.ʹFs_zpuF32aH n*SYDX UZ#+9@?)( ?CU9c~wpnUJUpǀ~h#.v>Op>3R![ʂ\*LR\0mbW# z^!%mrI85ŭ] o`76AAni+車z{[$e0epO8,דɧ,W* J @il!٢IlV,;N:kI39{鼿/o6$kc'8>沒,Ӻ4MYdC[E*IV,I>wx}mU[1 . g𤿸2M E33(ax8s~Ѯm^bWCB3ffR-}cE+w̩QϱjE/vLc,<F=HpW/pHH*^*o#c*8cNe2cu7$PO)t $$I\|H=*gxрaDlXwcUe{^}+2pC}}}gm:OKdHJ=p:.vXOEq4w㏧fp<1"$}?N/%ܴ8#@['?V6bv e'W9:Po5)b;!HݑTcÒR;oQZ6UX6UJۙUJ=¿|u4ۋɒKw[\(6:ZotԹKK\ט =r+ǀ3@1|fj^ĠEJ$90OҪj6n0pUIu=tZ"0`p{XRZE{UhQ[9A~\vlciV dUZAuѵly() *HoדY:mdKRS** :gҝ5nY5t`jAF1HJȑ0c=qX /.O#rx==+u.ZiN~p 2ÌLild+M0S:z< {]/<1c$bb uT(n @@ 2sTm. X.0Xy' *Y I$ $h!qˬoutƫ`m{{,&HLŐ~fR|Ƭwrñb(lXt$4xѢFF\ȸ9 vbWbl!X&:CUrgdbTJFO~[˹'8Ȝm\G#Z6ӻSNJ#&xprҢ}Y 8]*OAl9n"[ U)ٜw9 ak+ ɗ$TaHTkZB/Mp\*2IӭYIGKtp8~s}Ɗ{҆˞ø[ޝ@*F9Ϯkh8fhEy<4jv*) Ϟ)\yHm'Ǧa-5y(YIܪ*ޛ|7&ȊH$#g`;wK}q-aw(ُ?w*ՅyWɎ4F|Uo$ ż"\ rʹ+ORN<~oǧUn(G߷zc\'$˸jZM>:KG8dnY:qgtqȅgQ-(RaՏl"c(wwUt;5h,T@yyUp'I!;7ңwm]İ@W]~e93ӟҡfe9J<{'uL<٘"O,fw#Ptst]E˩KdFdox?5qfy1T{S3M̄p:t>qtb3|qU'ȇA,Wa,>ׁIZW Hv )qڅ9??(1f-6l+ՏQw/w%5JJsg D^1il;4 dvG_nk 7)qn2m/ߥBoDtwF=j>#td 1dI w*7͸ONjyedvâzVKr5fUYt9e  rb/2TFSz_s,?(ⵌ_*)`zqK7H9eʌT`2|Oxn&iOC<[fo)-;.cm):nj>Af r+o yQUf^,Gt$ڳ4bY ?z M|(M#2i<ɓ̑'TRkȌ(%zJhc=@=ϵ5cUS'=vRw&͊$ow݀HqEGL?I}sKdYwaӦm=;m%`z Ԍ"ش(sX>$Et,Q7%.p9> ҜtNE؊e R;ձ^BbipǹYHoTP}} Z ?m!xhN${ַDkњ4 #u_+emqqOU-Uc%"]wg,x۠>]x[ FIn|UF<~K;=ڥ:Ubшo>b[2t(6RZ4rlc*y^_O (eA<ks:_P1L#Y]h RGuYUz]8:wP?LLe Ǹ޵*b&uE>һqְ^2ԉEGF^O'w?~43fʤ8Q#T4wۄ}=.D*oʧsܚQ6; 6l/˗fށdva9=1VI]& s==*X(eRqIC.K^OsKKI]-y*#;IPAScpLWr}yvq~RpzTRH'lJ ˃3\V3+qx;է%HVe\;gk7R{kǵ~YAS!#D)hʸxd$ApO\07}~fQL!xو`)OAiP ]G'SN<1Dp]:/7ۜ\sVxSZ$l! ^~"xHcYd\푞j彄PS#HTS}MbT7*[v vǭ[,9Bےoa%vnCA>^=4lbl8,rH޳WIP-;:29ZLyG&wIԫHYj[IE7ym<%A=Ǯ8iuF;vv@cҺMh?ك|ezp+)&ҼUEP3)K%QIÙ#fG022:t5 L>*rzqLWgK#%Ľ[ li&!C[>rZ[!8,LWب3׏jƒNy';rqubhǕ%7e/Чq~mk{r͒-F z!a1Q5sw*ߪcaʙiu3’ܢ͝I;98N.RHꑦ\߯@)eJBH99u,Gru$ 5̼*_TlS}$R21$ngБҬ*7#KaO}iѼjFU^N ]Dr8;3e yX( sðDg`ۘ(/fܠ# WURIF*{S~LxV= a1*7p;/Km89>u!iK:j98[rHad2zts #LĄT0,s٥q"m,C4r:EcG#aEq*S,p(X,0IsW2x9#T,}GfMt~e?}ho.>b{0r;b_:J36JambH$ֶ,HZ8܅lW­I9r6)ǒ:\FyY'ڭʁӐ29qNrsj~8'ty$ܮfd"JP@Zo3L~~NT >7in7X|p2N0Zu)IulҪA<HN}zfoZ; ~;o\x; FC<8yRSZjtRx^L]}bȭR)rsLѡ ,!,`(+NqY-5^Lljl;j+ %(kL {JUDz6F^PdXXA9^MVpyYV޹s t\\߭V"H}ݑ@-ǥCi )),aSirp{HJ)Qo6v~@mpG?k^b 'pepGAgh#ӿn$f'x&Rl2~* #vQ 8gUs~FL}̢XX#1X<`'\&Hc՝6-eU۽t һi8ϡJ8Q֤uM5ʡLOF)+ɿOO9[ʖ tەQEP'j q 'e*OV;1搑Q:“9}3)bЮQA6ʜu$rWi+w\.%}OP1ҫ r3fv f. JS"$:\U+ӓz2b;d|ɔ?09)w>_z){+(Bc+;[ql;]ifxo5:R:'I$EF#J&.?%V1+gCk̨; ǜD c#8kj8lRF̖zwgB QB}1fFmUyIR&6b^C{b!&IZ6Dr8#IXxV T6ށpA u㇔ֈqw{2i$H$ʪza^8ɮĖ:0C/ ,LJ'ߌuV.N ZB|e,Ij Izeu:'}_#9^\ʓݚNL>]Ie}sg4Fi$C7mEs}}q 2n1rO .􉣽Rnw6vF@a%՚*$|4I9JKW65{e0( 7m+g*KMdƇbO>-J,H*`~س5Vdq3FN鹘G\t҃)G[ ѮfF$ mȂUH 12berzUb]̙]B8BpsqD(,t#n. 1=7w%4ce`k;{o7#;3>]Im}Ip8XapYO8$W޷cks*ĕǰ=i[ȆL sֲw3C6X"#Fgi![p.=jYv$fm1%_rЁԚ{ӑ٤wdܨ(E8rjXa)/NpGAY|VRw*G0`0I\z㞽9XE1WI|2:ރI{ <14Q,2*8%oL1k 3HWpp@[2Ht]EhI/גH#DpsӐU|)bCoi1.G ہ1)EsX啦s!`8$nQZԬ zt\Pv#kTX# g训b4yvDYI,{`s錚~Ҋ3]y n)3l'SN+gFnVw$"@vɌ+?V"fY`@G0y RhLIpEӱB cS?h:"SYh`$A8'JEeh6W 983})8E%[rj[h tÚ^mdԟƠIP \Jk3ZJGFWqGJtn\B>8(3' c~XY P}?\aw5LaQ'fO S'Ͼ{vr3P5BǯJ̒8)"y|琤_ڲv9݋+m3e`p1Q%Mq;nv299オ1'ҙY ǭ;LvڌJp=T}sYԕwN,nE %YrJQ>^Y,G|20fݥJ""M2]Of_*2A}9GkM9.㳹߂QSVtfiSR5ذ "E`w^G˟j >Sm)tsI1`| O5:erva-wy (+9}1'$g$W+%L <`%~"~nьs#؏R2:''[A=Z}F]HB/;YN:U'U]AQ!giXpqշd {v. j2Fǩi1 .~U\o K}qJnJjkҺ\vmr@UH;`95 ުaI3G#>\ꓪ$q,trц9qjH{WHQSp!@;qW({̘]E,Eeq=I#)Pd' ‹I⾵R#7L`}h~S*탞Oֳ58Y~~r#8JTeRB#Q8coyn0T`wF90FsIJJԊ3z^e,00 rz7x5,"' Cg#iǡ Pד[Z=c #$)XԒSd)%tUաE^ #UY|TI'#֪m"̮0y ( q1&oʁkBO$p=+Μrk6Z `r`='VR+B'{`I<-k$E c:uEibbnByA%;+If؇9h0ϱ hɖ԰`9!Cƨ\ 3,nYf+nS~aNZټ-eDl\aꆍIIRw|m!^knWݭJR E0[el0ys9kEgs,#6vkӜ;lqxr f%0=kR;'HِǯG"^HʭI[!H; 2Fp8zTA)@]~luKkdbO/J`OsP.DR88==IChɵvX9RN$ƸSIРH72N>{R[4eAf+XVh#kdx-+F+ *rs\=֏痶IV(.q_l([K1E-J2N~5~xb-'t/2vԊ]ItLVR@*?֮ob"WT\<0?t\Vmi,"/qe+[ҘUr.>l' ީYyq,v< V"V|lǩ?\VnRE[2GjHao HO+[\BܔR88f5 x+*rKzv6}OCi:8psqNpm\[b=}XEmp8Ҥ,NuK *G)P8j}6X!٭yl@}O3)3Dep@v<8[k~nS/i%2nb*ri56q4"c灑=*19g۴2xק55XqĐwc|NkuTZ 'KwNet{Dg9P~$KY~9c֝w%xwdHQ /'-b+5 9wVgkiImMEiF fEBeuBcts3[$NƑI#v dt}I/cDxd.B՛rF?7ev6 UV,~H2 +^IMo\+Xɔ=+ ndRs2ۀ2*[gx em~'8~&2\ei.$`Θj8ru?Nz|1*Hۏq߄ n*6atM.%07d)D̓Y$E =Zsianc -،UbIG0d*r##~ : 1FyxVIDPʬ !x-,sČRG<`?;{Yg% Ƭbu9=%-$3t<{Vegi?e c'p}@GpAIi"svKH<ִCf) -e`cf_ʷvw0}mjY Cw 0 X}XZǻNiJ*09xǧNhSRܘu؀֫,>}I $Grp?6{}j9mCopÌ<~uvV :$k4ajR¦Enѿ;:RO: t`XWr#q! @%pPzO+.7rpAP 'ox*hSHR{0uvպxO+oLhc|ԑrAܤgXW̺ Zƿ}ptO)Eqq޼R ~1=Ukδ;tfܜ~Rr܊jEYSVr& $'?/ `wd]OGN Gqd2ØA]xšm{4I"qn!Vn!E-gi%hBœ޺iKH̹zd0.πpIwA4f; .d(9Aj"@LYg*3VJWZf! "z*!m;>I뿨)&}l$ _u&hH7]ÀGi}q_{wTx )`g^\ysdFTyQ-ڻ#D!D#wL}ǽPԾk2ƭ4T/8>wtJ FO"ekMCHȊ7@*]k:Ps-23LO ϭCpe'dh$,9|އ5N1]4pC$6iU9^T.-*C nf;c'6.vYO̩q5BGPm4T#9DDjT $)>chΎP#ZkV&S"@Q0dxŇ=ҭ~ь^Q@#R9V WRAfXB(!v= w|˕X¢ܞO2BbG9}>5Ţy"#-feSB9}rҢI":nA#ǽg+#yEEjg]o["f5vDr8\Jf}ypj!ؑ H\V<5W~P=m,rVx^1ZKbi&[Ry/q+k-VV]A} t|/y.yOvI-J̍"HADA+uϧZ#ڼ:t;[[xH$ځ!Nz& GP*m=ऻ2$~VZa,Z9uPe'j¾69jNJp1JM8I)(}dBM*B#qԡ0QQi OqI(;C>_j^DZ"[د,:ZVspަUNFRJNNX٦y r~hitVV7U>OziƝgI{,+᥇xߑ@/b1 ڑ;xIPnuaQk\i 2K >s#bgrT`1>rirs$̴5,mmxg%{dqNa.{`~@y(Cf rLrW [g @<}+Uɿw[r.fA@cQF!, g3 /p,21}zO`ZϑNNxjkv|ZorjOO)ygq[Ͻ630?Fث"mܣd^:tVKGoQӱj}Xu#ңq]瓂y5uۋ#Kǖ6q[0w=zi$To=Jz~ ѷޓ9SHGJe' CTem]I8EzsJB(p}Yܐ|4dpԲgv''$~]*<#V79+Iɧeo b7t+ؖr?.(U9}]}le' ?J K JzVF>U1N%IFm> Nn.ϩR%DrR=rHՔ0q;U=:uT!N_AD.:uyUC]rC1Ӱ][r;-;!,a`0HLUd: ,Su$`)V<ޥw-]@EUqz JRjaE[*qXLZ;gU$*@.2\sPëڢiE,!$`a65"K%. =IЀ=\_3 4Yqveve0$8t,nבcY+4Gu47eU$ON=k}9c-&Ir TV>{`cȲa(@ =9IIN1GTHcH8n$.q#\2ZC1r?9I^sOAZp 62d${ϥW4hazϨiPQRƟ%L'ϛjNw͏L/SIijhRIlF88jE}"s¼duПJ 6w&B2Hy'fzkۋԵ٘m23Z0{"UN@*PA5N]Zm!f3JzH3޽D)▐coˁNiZ$ ZLsZ`#'}蜢!M oxNFk)ũCb{;)C$ےiV6VPgӘ`rTZnJϯZ|pQI+"#n99)H隍31`p6w$^ nC`?ϵYRKf DI ʎn  U d5N+oٽF<R+[w@Gg:urڍŻ7%,<:sǾ]V]9r#s+ NAg89 %VIbGeVyZ\#$Rksi 2s iga{ppU#ػySX\-$0W}x {dR 8OcED`-}Qo&UEo,0#bYyA=45'v^U?)فO:2̭.TG sz=fnaH5K,P" g۟kJ|N6KROdM.~fTU؍'ڣc,..Ձ(H 2$p3[= xYHelּVĂ98y*HPy%INzdiE Qڭ#HrALE;TR2eV? QV|TZ!yuQ*AN1s ]j;x!, Xr+7f4G_'lbXܾ7rd8ǿ͑\IOxZG #WA,@+V+vY]qH' DWVM,xX] ϩSi RJ6F0J+(K88.dLPLw!kXcnW~lr93U {RF |aI$9#1SJJN,ʊV p'<1N\y;xr2cx%c{v숖4KJT3 =?}}w,"-)o/%rzzM+\W,mcJ(cFhbh$3G'cJct{`6HLdp8Eլ4mr3:5FKYcAl I(-w*3Zb+t/I(Y1F7 qbZ/},;[W&hquHT&*K\vK6@^ dg*geg][nBJpq>}xBȯm`\+m B9Ҭb+YU3)_?)?5cn =9JSԮn-oS2m )=scH#xCElmxc̕wScXn3́~hB(ܟʜԪMikoDaɪzD$vj;H3TSN/m+XrCtw+,)<;co[RcWanRjڥEM]y9(Cy%`$llQ GvRZG#VOnjpv 0OJJ ,e #.pW:7e[c>,0>^ js"lڰA3ZBFpHi3$r}WUX͐ۆ8LtY$&%UWnVUG# 5q[8$:O<*>`e=I#! q*Rwǽ8UV8%u遜ӌİb:T'*p??QF`H?JҔSn5ct5Ya0SU,A=a;KT|e.i-,zpUȡ{hu \̓ޤky1hg`48n Bq<]EHX񞽫2Im q:`v'LT|As3^=:HVaTHx @9xtdI6>XQ%Œi$1mU 7~B?Y)oRz֔9ʴu䑔*cUw²qǵ[AqTuk!ujSl_/BzLZJv .W%s +vpeh'l[.V(O2d*9:}}6B(UՋҍq۸@K6Udq`dW"<䝟Bo ~TS.czS"R AҩKn!.$HE~Goz;Ñ+DQRBJ3涇M6uܻ} \KIDW(ϡiv &5 c.X\lc[Y,XG_Ԋm*2x$)_ss'8)5d$w-Ar<8$}k&`LRJ#m@l1בWu+:ȯU#n|ZN"*d(IsPH]8.kt-^Ť)&ó֑=8Z{~p"< CU,4Ң F2SCkRde>s>F9^}:2ڕ'eu%s(Elya[ ܟj*r [8ZKY[Vpj"DHǜ?.u7fYKf^R )2+ӷu=1F:0.yUF#1 㚥UT 6ېGqϡ= \K~e(8Y՛ xק@~+זFDc\.?#u~ 2H}+yzt4kG^sӧoJ[xۖdkɭMe ޜ- $=q)߹mhKT.HqR 1-紕g6r ׃MYXn.&hP6}7!=M67qe $ˎr;U-d3D06PTa= s޴e B{#eGlPB2}TZY*(yู?,72U@Ijޥ,3,OU/n:JMťa%݋Mf,6I '>4 "#SjN9')L{^DE,ZGf=3zU&b2E3FY9$躕n7uq<9$$˕i;a?Zxh&iH0 \{իy)a|~ߒyEI!rJ38Ӝ7(-U^[f6b IC,ڄE>Cbf:WHk2Avd=ݿgyT,͒}:y.4QhYobGNk_jD&FV#C/N=5_Z[pO6>\chy[hIާ׹]\7(V 2㓎i_C3 $cLtbFPNB θe 6}}):j͹d}[Mfk!GfRB1Zֳ^$!@,3*l4@Ifdq,qĒN8?<5KI)+;Q&gdnarU# n~}2{V F;TR`mMhi\?iE2n}Ž&+ &DA2@GPdI ]NSIck/UNī)A/==+{C%q?CAN7s/rZ/.E0A:BE#l8?z*(\D$x-=+;{%*e~Q{W7%67\` 5-ci&aT)>+bH^+|y}<] c|^ 9je{nsvŢ$# ƼUd'cTn. ڲi\A r1"Ȕ2S-q F'Z1p:J4Unf}ɷJ?vH?7sZ}7l޹r:?8= eqp.Iw 1_1 ype j=Ol¼i#e]#sDžϷ {#;qC/`GAjڂ'xg$ǰv&G.Jۉ@\isk*w*_4P9GpK _l1EsUu2$w1f-6<8]gnw۟%g$p Gn1Ӿ} ZmR)Ē/2W4e[(G!w 5 rI%xܯdY svf睠 |R:ʩ`OZH Wl@-\7OjBGt%| fʑg9P} UOweO/yݤ3sY*[sB|6pq7g*6XJc qӯl栓M+HrI 0NJ!yhssŶѾ[YD;'^'XHxF F}k=Nnj;sګqDDQ\(U> czuim38i[1` NF88EjŰ2rz)H"l<ȨZyF)\Yx=GZU_ՌZw&-_-#ߑ"o;JHbXwζ% 2Tal4GlgLupsg۹Trȉlc28 HlGiSsYIdFMClT=VtkSaӥTIht̵3KV$147hy<H|q89 "Iu|NUlj͊D1ET(UFw>3ih&u"ĒXT0+.UY[wM(ٌ>1;dUQQbO.Ed mKqd i\$wL߼њq[&v Ʊ|= mG̣$gX^M4dB00z^1LCA2Oطmn' y/F2 S!9r0y=zMf%=_nÂq#W0ѲqQ:u 7~J^A##mua*3ʓi<-b[S֮ui-ױ&d6#?s;Bp02928TzdJ .܌{ b>nH32Ip? kD5efۖq#O`?j*M) ˑ{k0!9Xd1E-xb{Y҇R:IMD& +3ǯH2C$xZEϝ$Q?N@-`PxUϿOηS3Z+ܝ,^ ?}fn4h0 te?ެ2~Mys(Ds?k9Bzh3DƏآaFxL;.RXB(AcD1PQ '?ZHm!";slvJ\ Īͅ ʡyfBJؘ\~8YXHE6 ڢ3U¼z]]q&>y2H{7g(ӣjTE6y!3(W=~I,,a2D42\22 ~oq|jRdѸFU@l|=#OUJIݒyyz#vvNQ?Ju1HЀp,Oc=p\ZN.OFsTgo:|E$WKtycX9hMma7Jn| Ƿz[x 偌?WܱANZ{g$,r7*OQMHnu켄xbed@(NylV9!IlRE?Q;KԧױvGGRߍ8iE^3硧im [eA05ՙJ]1pւ|h2-Vpl@L!hݷ Zۘ#J0(yT:2+L1q|X=qx_2NQi</N g8Ҳ|CbiYE0 ~"fR}+Dt R3ڮoixVgͅIx=b=k*4)st3gS\ofu"m! 6y-Z7A!%Y6ۗIz!"HgT*`6" ]q8TIYhJj\&"l9h>`'_"4ɘ6(|qH%T`GRGmӷt $hD+DGp| g{t'9b9p9qnӓ@ZG$wq<F2Q;plبIn@Q3AdWC֛o7?XJW_ۆ =ϭ8p`:s4ɶ]h0 1#{[:+pNJ#yҳtȧ[&[bJqg0\ *T9gz)Q 䞘`{z~Uj$ͩJM|Q1cR 6nN /$4(VRXa4k㎽9jY5km Mne2O~OZN3>9q@@"7Qcwe ]&o2I$ᢒGNHW/vLC}\#\2uc2Yřw;cl sޔ+BIMmfI%K:eJ=<Xڌv%܍-<1!8U\I"ΪdLvԑIwfbdyA-LOϪtұpѲmE0.RUXHVhQ\˒СP  xȰ*RK9_w#=kBL* Q*eLTX%Y#8Pxb2_‡ʯ'1~VsO;)'8ղ~O#a8W4YxY@ #=>X6,#4ӏ qck=KrGgk 0,:ujc`HA$sYW-¤FsxI*2̬p=}+=.^tV۰z`c5;4]lK+ݡgD3!pNHq{<h;A'FsЏƪ?M-*m"ЁeVoSql[ۥfkkh i$yd8loӚWUF\2ps= A{sC3,| q7b5W9IQ@ M$g ;f%wVO0;0 p8K\OyvdMd/Ҳck˫L߳E&ceKaF> UƓwulՑfTHVK$[D$9aW|f1, ć,Q {cҰnl#d`*ҷ|5Է3@Vm`cⶌ\"M4ΙaqBrr0?\ՠ$]# u-|ܒGopsa~dTc%xȭ-t'"=cp~#+! (s玽 VVg^G8"&Q UO'G6N2FI,O9Q<I3tLԮLWv4_̎`|e%I!ʝqsVHŸ+8e!?'- r{}CMqӑ*ӄ߳ΘiInڦHIy9q@OL:Xmgt#-ad@K3,kcFAϘ>U( }wǵ=8苵W2w7g${՚u%c)AXd2D=fҁr=*kx:~7hxZsgƶP#P X0#jN{fm"!rH$ zriYxaVn"a*n>A+8#6¨A<یdn0̻ᴶoLp*夭rB!"Afݲ21OI%y-OJ"! F8^j+i-Y+ۏֳG((zRgwiJq±u$[]AW)VUgò˂ xyz5 kr6y"lz>3=8vHUV'5g{'$}Cʊbȥ$9܂99KU|Ѯ-sTuvKS:7?C[&|hN1F~}v35MY=KȅH# PI>$gҬ0\O֟#mF;~ʽ;gR6ٻr;2;\vq0?.}gK/j#<ުD:͸=0qׯoֹ9Tfъz $SNrGzkR}4:1"T9cU|]lk5=QY6@rK;ʏJ V|2+"( m+3岾2[{@Wdh8i-wu$/?)OVŌ-410u~]D1I2˜7Q}2 Uw$*e @Qy;Rid((n6hT06Jʓ{>H.cKd~>[l{B 2dG8Qh ژ*Hr=Hϩ&٣Ih#F9df%oIfb1|=>"婊/%U Pyݟ&_KkȄ*A(UN>7uIY""6A'ir*8tcs$V]݌q{yM&q0P$`14ՒU a fI\Tdh5~yC#3֬38K$+ 1+ǖ3gti4%8>TXLUF{Q\G,Ge{x*8)Nmכش.p4+јBHH$>\bfIVY'x8 oW]61G$2 z}:ŵAB6vU$|VВi%+vH\/Cl#PeY=p\?\AdRmTl{daId-_P}x)Bm_bԴ5t幒a j2g9{ZWHw5Va\=k! L%@ /Cߖks ǵw_C|;Ois>T(QMr1! 'ƬQ_4^{#GQ\ 2{YѻM\"Oʛ~ )R32HYT>sjͤJhbS"1XI1½VamL[[,QL!C*ltzt7!/#!P2>{6\`d\sNPq↬y'PHJ\1H#e"L H:vm崳LfS(`g OqYڍ]URĞ8Nm] qQz̰[[ IdXdW :7njkr:fYaAan>B"ܑqtx<ǧbm5y|2`NBkrQf"`+Ƿ;P -iryC#.ʤvGU|Cv~i"'kȻ a>F?jKI5 "Y ` ^`d&HrO͵2x/H959'd**0?UD-/xF] g>k5E΢@E[koHGy'⥳۷>≼]HʬywOMn-ćh'AV>y!LKˮp3Н3Moġf p cڜ?$`4,qnVӜH#*J~b6q9RijWǂ̬U jiՌ&c+˸nM$G"[JmWr\28V[7E*>2bp=XsԎ*a3ireRd ÆS)芊^cs=̼)`5P -1 yy{K>5RXQn2%vXq[GklYE9U%NN1"vB]:Ī0#gM՞P%A$ G=9n$IH@&RPԱ@)KԄeXφC/#rFik*hmB+sFe\L}S8I{l%Ԅdu9#Qβf0[%|N vc 1\]^;i@#nT]UOAfM YKqYy4n&W iT%c)##hJ4.8YfR]!I:RBpca!QkDev̜CHWďje !Lo0I܃7O-әR{4!|-sjIc$R+uI8vtHZEsmYn rNp~E'{=9X,Wl~n>jܨtIZ$%`Kn*_"(A$6&_Fr^8P<Ri!7R.݀Fcֱ1>l-hDe=3=>U ku_3ny=N?!W#B6 |13IʦFgzVmVgqᒶobQZm@Rʇ#ۮkRkȠ7FGY*,tZaUP55U ^X᷑ rASW'q# Ĝۻ?Šqn&R-1Ī C=~g 9IRVq-rFy=&<7&ݐq^kyXW%U_5>e;r3YWҧM6ªʼ)p#GLڎv d$k+0=+ͫRH톑4o5]Hŝո=y8 _46OxF(ʣ~3\3:d_*4SRY%FX/lmlbBIJ #gau~f 8Z 3 K[0\ȄooD$]0ٴWEW&acډ-Vq WH[ŧ%%˺C]pMUk8MǭMe0^l{Z&/NW\ ˳dDrvA<* #yl7g* 9\fiFT)`~5$ɱ[k+) ? T9y~j3[^"I010:ם$DJXcf+',y#ֳG UA`FH>Ux[*Q)Iݚ]]̍Ǹ-O*0$dzRNi>GFQwް[!kfN2}j+t%X(*(˚ٖ6W{lc#vSm4_/=)N4lr&~0[sYi$9$8㌟zKo #+v9 zJ+-e:_k|1o*M5 t2ƍ&I'pXUS:A\q4ޡHc*HzU-$HJҲ,aH/QֳD. 3dA=9Cp 0q'p$8#{G3jM%<4]>M됈yF۷皌MnsHUYKT7l"D,qD?2wn2o(˔t 3zd+Ź]RJIY?^D *)Ԍv-&O7Fw=?¥yca@rJ01Ƥ@@OLw':jN旲өZ73D:'?UyPaF=@M,rmUf?qII3H/N2_Y1ii$["  "691yi߉AxT丘 @Nͫ(68wHrS Up %lLT>qi9C&j,_ eTCĐl cK'؝6xX|?\`TcTҞm2.Gnk0$LG\=;2y*dGҺ Z6a8n:WGψׁufo/ tl]\0:ǽhN/ x\%!VH[MjA8#ǭImIveC+Wz>ҳDŠmR{{Z@PNA~WN-X g9VIhL[)FsINplyBy#N_ZF~;0CeY )KO^4%VrzOiJS~I%$m‚TVN#Rq\c42?Jawk5ܑU,# ^~ ׃ GzGq ʰ #?uI;y$f!w<o\ߨ{v@=jaTeVRIu8?1j5{o,kbr}357 Ė<`TVy4Pj̷!|W #~hrRv grA<zO7 ,y$pH< Gk%+rJTэ_*ɸbqʑIfS(Z3F)vdFT$(^ nx*1ݭǘ ldpĶ=GjH[ֆx!iw) c};ko6ܧ B2y:VRk"Y#̘)b!F 9dT:I4| ``CO79JpEӠ틑^E}4ʪpf+g01ڭj-Ȗe Tm'tV{@Xs[^H3Tn]PI$nRpy銘äIA_M)L qstp' `sȥRSs,B%VWwe<d-ZSȩbusǎ[o"+}UC9^q0Aj qԱyim%LѪʹo`gʹYGܒy#ۊ{=  S`A1ӐzUYlne4lؤLx@ ǽJSX+u/hS;EB[ T5Xn/_,-}?N8ej;$vc}$c9~}e寘0ĊG l@6r3UP䞄Nx-eH\-s Y^Ī(_8rq|UQYA*:fm0>S.UB][$vn6XzդS'u㰥 sp=+ TJnB@'`ϭLik]~aJ8$d)3} `ZFa3Q9UJ,2Gk'Σ$ܜTlf1qT 8X`s\*;H$B<1OƦf p[b/㷊g;8Q62F{~+c̏~2??Jc({? q~cb'hT [FC6<0:S]N3$ g'4.wPѧ82 Ju%5{ A]$Ue$I?s!czN,ŚF؃R w<]qe$SC,ѩŘ\GZZ5/_]/`[+QOsTwPў󕼚RH +VnIs6bg2r \#V2k[@7+98'^+Lirbʯ99zpA4j+Xu=J.&xW8p@^+F·q;^i%h FpHO˦Mwcٲϴ;rZ]%ܱeRǓyT6V2|hSt{ "H<ul\alg󬛝*?:_rn] Wj֡FylDʹ0n=1Ǯ Iy|㲂".p1@skyڙZ\ӬI6,.hll+bym"+;(`^bb6\+$={[_ѢBP-NW wIS/o2mi4кn.ն]t<+B_M*0|ŔǸ6)f_$t8k=EfuVʆ$I^}Of[#Ԍ\K5wRBnBs>oC.#M0HLuI?2"`_y''atVY2 ";`lԩK;6|w,RmKڱH,x3N{>\(hb}sTm٧*ǪNAֲrw0b!U-Ag 2G\)k}*@#͎N EjjZl8s ǧoΪZs Ygx1< IjR59e̯+o5dc Cn9kyP,P&UFH}AO557&x.[-H-ko2ə |G'GP%/xIn_ (*AEjF"h cO=zV\^3<W-Sn}*Ɨ[+?,3+dರ֔ IcfFxy$)0Fţro* |F0 ^Vy3qHq9ƒ"# K1+ax?MWQ ȍAǯ42rQv. kgxnLe$Sz:d~&hU =s;ӞdH$pQbGoǾ1Nq H؏5IP>\2Hu7s3=2yCg+;䁎sK-șyfxԿF ' W.IJf' ׯU[ԎYdˌFxӐ=8\۱k+9!C yx>NC]gv de ?CTVcރ@!vs~븮ʷ_gf1݅v]rHj.b!R@ێ0pr3niXlKy\,jK`05'!r `w&Y -N;0!'r={|<-ΨTvp8=rGRZ& ё1B@&_$d<~Uai*k*\}VNha3$6oҳ5aRWgR0L[\C4@|s̠2kBM>n|w@CA=k9.T)F(Y[‘HF#h:pihQ?i'9 *m$rƺ|H )yu}+N# >R0އg8=q]2_} +MgEθEF@1:Vbp>GJ@;X#sLKX;w46揕@X=JmMn^n +#=r USVXuLۍ[8*x'[qypCfc"T / c<z)4Vdwiee\=+RHF I8mߍA<,F*\7pcδ#r%R݌pǏP=i굋61ux$D!_p3W+fhæV2r^]p ui$#y*I<)sCx&˺ziJ 1݌$3R& C?\R\pImĠg϶;{v"v7vspOL\Qr%vIYT%1;R*wL! &? Ɏ/ı"ĺH$Ngޤl.! xp;gVQR6tfjZ2v u^xHrWp=r:ӎIn X]BP=dAVBx#$O~]Jz%V#byuWXfaАxUJm!(n!'}*+{VŬ!@b\)VREE,YX.yg(J~VgG$)Gvf OSP6m(MFKD5ZX1pV%NqWe֟0{k:KQ \a4Y1d~{<4B򁝛\/Q?If 43.&Uߪo#x>aE&tnE!X!=F_(mλMij ~kz s'ܶٞ;YcۅPvnm"3A, H 1=1YQ&ۙ`PZ`N'#ˎzdsykou4I 0*łOGJJiW3GO-1? <delB1jmyYWRG;2H==qҼi9LZ.)^ۜ I2H#Rpx#]rgA nE$,AnˀG)Cq1,A7TނQtqX^k%9_2}P=s'LJH3wrrq޲|Ay+iYXse-9!F 7=?Q:\ ]j4-$yrpp;~S851E4)vҘDyIB#kq~Sެ$FHc wZmna48d*CӚtQqHcYwF.gac,'vnjf;{kwYF+ڬ&{6-OVyִ=?$XE&@$ok{:p]>cWkB780Dr&FqUZI8? Lڴ6$P$nC,.z:Gzl' W0Yy#B}%fK!ǭk$d+:tD{ ,r6F3<86S\Z' ze};cn?wsZL S1L ~qثVA)3W=R%5X >r.:^0 96@>ljq<꽌1,rX+ϵmw%ߨ)x$r.]pOV/CTJ {S-4(6UxKcA 4.VdA1=zc~"wpҳKy'I6 rA=S.GS#)`Fko!G*\t+K_]y--&UU3NKUy!9ʇ {gYNp۷?UFsq 篽t7%%!]IcVV,76Jkp@x4وV[ d'H=ǭtiAy EnNiZDۼ[{", AA q)Y#i}`O|czo'<Q[Hہ$KK~l x4sݪk WZ B9ZG%T'r߬MRۈW׊ Siv32?`&9Z(d^]1t2R4m#Dg1J9ϷnxOT* `I;N( `V4C+ ctP-5$DGݪ[ϼ1[FN "-FI|׌UUVfPpZu*E[NN !d=87+SF+PO~hY;I]ÃZ+AX\6x6i%\@89F xrۄ.pG=8n}GJT{,jX(I=T뚵䶡bB) ~:w^/M4RRoȍ!,Re##j2ǖ$u*ń_fm9>~2;"PIfaz aۚzu0Z鯸~>A9d*4cvXU%߂X/px'Rr9知e);-_؅x@H#9s[!epؠ({5xM?o۷1#M,QEi"M[X[is!IԀȕ?} tFO '#?:K?ED}Cw_DIBn!s+.D{DBrvGSGV[a &$a,I,r ʞjRXGl)D6}s}($h _PXdzߡ|[b'Xѱ`rG|=+~'$xW=nic$8'F9 Ir09CZoaFH\JP4.$d6ݽ<;P,ӵ2~~nudF%nq$n@#?֣.Es*8ӮfI+h8c\<+YDnudwzA@\_d{*Й"A;+-F8ǭ3[@\hu('`Ih6V6BB9.MF%/ V=K,= 1R Z6 S柔D1Q;1Uz).z4[$?NjZ˜2oc[7$qXQVt:ƤJ _Fb1ak8'ǡ풌dՑQJQЧ/:I C1{wc#=\`wR7HE9׊̒o1aUN2A:I\DIth289_׾it2rs׏&]Sh>kx^  S"9f*lly,/:H%HPN2:@?Ʈ3GUo ٖ"08ƥPnkoIq}=n-!,Qv@V=9+֬"fKimUTPk!%Yd0#c8f$ ?*+"Y.dNdPBxf- f;0F2tS,ϦG͌ [4f?)|zSՎH;oޝ(;:k[JcD[P'< {Sfؼ cikwSCmaH@49rH 5_{*BIpk)թ~uؔ6O#9Y֣Ny8c)[ =sLĉ#$gLΥHNYJ5,Jo'tkBG 0RO֢+C팒iyM?`;nEcB[.h_71/#c1czbԵ6}jM\8rN p8'ڨkqݛuwI>Nns'II/ZH ßWQngRRf<b7p>o1KFZ6ݦEw]D2ݻ?/Ey2Y*( )ӿA;zm[-! bFSG`])i :Gテkl檪MVmuvQ \Wm.b!nJ?>y:?ݹbcsۃ\1g*md[)eNv}TP9hΌr}+BK,&#Cg'c3(tQe_y_ȺpʘTb%n e%f2,NJd0($ǰISRInd݈<iǎUGc)5d%V1mʬ]Yyϧ !X>]RP eW}'r2~:k{XOa^[BO-$mp@M:E(5S] WK2+R9r>R MMI a <32aXܧ'LpG^V$;܂O?( g9XFM= -e[I2~PC$Rlʐ ;Ԉ-E%g p˷<3Ki $,Z+`ydV^}-'"dd<{v.$Jѳpw5K'Ep`;02}شbp7?)R֬-*23c}VH\9#\ǨipEĒ Fܚރ̖+b|+~}lN1%dDrG 8٢y}>1}e MLg ϡǷ@nm_u OT+ d}c?~?1SPؗNc, dcn+RFY A< ɧ n* y8®aA^r*=zZՄLP"!2ՈmPA=o%e]px0O~9*IJkxϐd-Y'O~.'6Ncv@$``Hi4mj+kxII%ǣu~k;Y7R\C呖Sn.ڪ?sO~_m,DgAFۂOJcdw4eʻr3>}irO4]8FGq=;ed$`.= $B X!g`BIB;:uH]ƭ0G"l ~n{X{oqС@Uot#>Քmt!ٻe߇5iV:(6y7w o$1+ ''ҵm[]2r,^C)\ =+> x-kif{_Uc2kzlopw"J'A>U}6= Sqʹm2RI]p󟔟ƵI${*w~ ǸLMCy0|1eq}kA vL [J {rvmZd0,W6 K|*8'NN}zVzG8;z5DlvJD`?C*ݘ!{'x<ȃhv^ך\ӒkiZ?4΂E 7`< n}x[o#pw2$MqIqc) ſw6ϐO>Y|4.AlCHoLi7c)Y3S;ig|6\mScZITdcN*W, jHbV;w OS-ُ;$EÕnxr^e\\m+$SFO܈ap3r 8=)Vq E$&6y>e\s㯩UJyqyj܌rQ{Z|4VВGi[2:'v#9Ǹ7sMW"ebv8a5#M fB]n\V7h'^Dm[觯85&n,dReUmPSc\Mly*K } wҮhYo*v]х*䍧}W&I4ȭ_.Vdwf$0YI䌪 |~u_Kl,mč'sQ{(g ʑH[N= nՄ[*r$@7'"jZf/cwqb0#q'<kuLb+OCFE5q,ճtZ6|?π?Jg>ۡVZ6BݳA]Z(7$aHwe8۷ZhmgLmBp8c6/ N헤?o+F_4ark P !6\6TwtjWvN$KJ0> ~9jP6Cyl ǩ-=+>/Yr qlH`=QX"qy&TqW\ nY4E2 cJhѥM̹*ۃp1ֺR\ȺksowKA, %G]=bDrHѸ̠0aȭ!H 8v:zǒ8%$ VޡBOcdZ|F+r[%ơɢ uF,I>Ƣ{!h H\8:P[]K%ǘȾ[`:m;&|D $;Wtn%nk4{DKi%8݁_LҴQT\mGsYP(.Mgxuo3DRET)N9Hƥ*If^đ^4drǾ1./6'AWMwvWHq*y;I?j±Ktk=1[\qcOFPVOTE喉& :Z& )9Je^ٕ2e(m^I{#Em 2nP?}N)+NW^UͽFńrs 69oҥ;t(7~U-I!R<[jx;+>&43ETJ==#P[!5ۣBv` Ac[$>Rk2IܐHfS2p>[Ѳ 6ܫݤ+Q8GLF]yY֔[ I© oAWLv$jxk{[1)ONe5FZjEg K3-cx<=soq*$00'1iObɻq U9qڅ6/|19=A;XqMTw[JnGom: ܻcDaHPZZME6\}҈*$$2+z$ڪj'hM3ЌA+E9)OzX..0`R1۸V5mwt˽9 `9 Kb8YQOUdM,,n6F95tܧI+XbdbC)\Hb*]&UH#z P3$Ja8ydOX}6AV9sjdNMg'%YɁZ^M]ot&Efн;-  z dTͧA>0WB2`H %}LL|Jᔍޞ9D&.:Lgh 9>E4rO+pU^wPw[5} 3yw*c1g*VaKgPR6!;{aXkG1h\9}+8e>Y&HuhxmR&gDdtBs\ż0p;dOlWA\Yz{SL?XcvMbq+9\[A<0?E5j 2Zlhdw!L*A)*s:Co嘴H8=T,&̒0,LM jKYIde|zql`Wd6HЮ.!wQWo, "6oH68!zp6ufHm8^*vd4Y8'R[{k$3Db.Ҽ{2O rS#$:kHfR$4ĀAa/-]B[{]g8NW&-GS 4W yJR_wcw϶O,qda{V}I,sZӒ&֟bCYʱ2nݞOWVȰtyX@ӡꥍc .;g"LYh<vlMXѵ3ܕ9c =}}/0yeSp%E唝?_ZuGĥ2=z&T"o\ƾmJ$)?{,yʨP@'#sR)Isrdw*dP |^r3NPk T1. @܂y Dk_0lAҵgɧ:m9[1l]`!FJI,=_fiٔ &T tGxZ226 t*7H,O:sk/DUT~^_^kd!O<3$)rZA**aPo$ YNzW|beM^㧹Fp',j5;Y/Zo'\JkhTP(󏘈1YOxc ,kXsdMs!I- 7h5r\JȊ y@ngTUL6A2R߈R)#ڒ2 kBl.l4)Y ׂ{qP6D;mkw*0Y9`k2I$ 9ןVt7M$G,95O;D|ʁyvի9@U.K/ s2,n`8> ^m<ׅ+G=T`4/F|2O r׌(l`1ۜ]pR"2| ~rVg`g iF 'k[E%eGpey#p瓏n_}qmn/nstS4g*vdw)i,s*i%y'cdVf&#*J W1ww#ۉ0%'j| t7-$rPҧE+fOu,A#Қ>ʛ8N>{И渂7 @z 6Y|HKE$xc,:U.Xas5$A?LTlb_6SYW$(*YR#*#C#?C XTgOo~;ը ÞUS;+8*N_rxD}9R1wǍp3\KCHE'9o>vrW =xʸ初7ʕͥkb虭Te!T2Bв y8v.RAub<lfh<\:;{Q B6v+zKl#nz11±`d.q4 cLrP+IRE$. xi;I/:\_ zƥ]ŕv{IAw UR#ƒGU6YfU$dȬ+y߷IR=ԗ| cNNIc8R[roC׏^M2,n] <3jjstD" 8 m$Lx< ?8=V%d n'u+Y:Oz>y-Hi6F_5<TmuL11sKyXV4--bx)kQA?ŝ2P0I A9[Ri}9 FYB+:(Rᮕ*@  *_+8C:*As)XcZ8|1A'\ʔ9Mj!B#bG 61N$5FuGH#9s"ۀOzNYC)&0hVXy 6{Qh{n#!YP`}RoؼDp~7.9bp*6ϐrʣvq9}+ӗ67;*m3`j9=d[,wHJ0#9W@XG8c zbu hj3(i8$7Ot:40foB,؆ @AZ'jBuNn,8( 8|(%'w\6k;0pykمyo21p1yũ$ѥ\4J C#;{tM YaK ;8S^VGJ#o;IJt4FY2Ntg}Eo* IM;o] VE173^+%PdmȡtlA>ڥ$nުC9y8^j[Z[<6qebp~CkSv)4g_Cir0EOCva%d%V*ioahWIo$mHU>P=i]/-gVE(WBV[KuhD\[9b6qQrJ2Vu$qT o: αtYOo$援_܉$iAUUbI9P6O zVNHbYeܖaDFWaOL1TV-[u« 9=G*.T7Wоdֆ!HcܓI󨌐R9 !/.Tp&P0qSU,CyXܳ9bFNvRfF׀EM+CkdfiC{O:hd^B2TqMbS$}k?Q[{d?0D~VLqb 0DYtw|WMF>cc+_@E?8];$둎U'/5bFD\3 }؇v`,L I#ɻvWuQӯɊe|C:YĄ_.~n0=tu>mڄ4iGP_<ǃh6w?gb< 9nxt1DۼR ~;w2qt纕)^X"dFΌҡ`Ā8NF(R9ve\,_fS4/ N}llȊmױ괨2S$ FP(^vQT9cd# Ò{W1|v{VPalʒT=8㊟JUkkm=Uc򓌃1ZA:pm+inDqIP6^0‘p">QH?**;fC̊,3q1Vy@?0?AYr3伭ɕP+_I@ v7g2[#xT ]׾Ux#-mm ,H\=}k:٤]7BN޺iپm Y +,fd`F ڹ 39]5 DY|2N9#"oAm 9aB2zm[$Ӝ1aeGX`CmW ?7?SR6ܓR5eu ɮ{W]GqolYv_Awh]`"PPθTnH5ėNѱ!Cl01`BPУ4qbӛ,_u$|8{SIzRa1 YQH#2*=ңBV,_>Bgxl;H y99[VI+A˸:YUUsw|gFjմISsw=>wY`;&v$As銯ƘwuEvޡESr ޿sxw@ P'b YA$SC}jƱ5o4(b77#q#ޣh0+GdQrwC "V{%;ő1I$O_h7[K,¨Qϔq#oIo伞nYAaLYq`M-o ,ձU[ XII,ZCKy$*0@=֡5v yĚ:iI4b:A泭أ0#`;5j*6{uYNdB9GE|gR %̰ñ7In ~'۷b֪5[Ɗ!q!} _~%VY A[i<q]m$U΃bhe`W#O#5V:L!+ )\.oC2Z(B`c`f鍘i.Vk{/8w6(`35J9= >wi5$pң"7%8 A֏\6W.+}z/ǽQ6SW\r #.WDSDLdZr\4,˸Sʨ;T6SۋH.P#2qSZM\c \29>f)gB63n8_)R_F/+B|tXYH`3NsCYV1\d`d)($r;Ͽ[|bddeRFV Es!FEFOҳx VPcyʌcriu$.%b;dsP^I-\a;AW89g޲) d W2nF\RBr:YYӓtgbNX띫r3ҫۋ3'REreܬHe8fgc^4E P;XWS43c=ۗ|]y`qK$8:pŇ'%}Hq[p.enWgP@|Rn&$#Z:[/_ / l@W4U-]J)M$ b$eB`z*gJFT+ޝOZI|ґ)?'U`.r\mv J.+]LԥrK-gާobT K {[تB `F*DWrg9<~9%W ?:J۠N.} 4*A/,?@*RG wnP;;$~i q"!ȄgxlZsJKIq 2N><5W2 $nFC;ڴ"ħ)=B;IsΫivhYaYz=M>ZIr7ԝD*.ۑtNk!HCg5,wh[3Q]H"&cGsҔ9g%&r<;| 8"݄eL!CQ"Bd2 Vrz5cd9Ù'"hc8CeqX}7qOT.nk*G~assd4AdS^A=$ =Jo.ⲙ+|}{x&$G;ojYrOf`a1Tm/|Wls jBbOm%\FYU; )yZ6@)m"09.ɕrqOu! {YNMzv{ʌy 6\m}qTa(e9HlfvĻHP*  ?#`J!jg7YR#ң8RZ7 @JuBqs3Ҥn@ c+jwQvfMwa!ϷhemF0* ;1Mv6nRǠR}+8seXM|aƠVwqRC.}j*(1bH#AY5v-.fUHV6 p3Sk2>VcWcHf ei*gHyJ&!7p1x 2'$T^JOK]NK#n21)J: dWk?}:drð>2`\g!\t5J,K/#m$˓^G+*KQ S+y ڄmMg )3ѿ¦>`,(o_ZJ\|ę _LSbKE/o3BBcuJ;מ8PʻTH N SJROI eϨ#>՝^ X_+i>NWkgȂhZ(o!* zxz:}D;G! $sQ\Iew U`@A Gj4!#rܜ`wpMg ? J Z_{4e2K۱CIzTf_3km(IQJG70A2*N@w; {^jgȣj)4c+1oH %F3"T@ݐI ;d}xq2#>\哴[o}A̟+GǿzeqM6)̫Ո'U'(PcE@U͓r? ~B[A76  qz]S "e 6ch.^yCv'y0k9I7Gm/y!y'㏨.SRy.ŀ3ҥ2`#`KXA,aA}Z'WzE+rsKTj;ybV2Cʝ=0ws(5f.kXuc -ϼ>q?rr[wH’ F޺A;OXw2,#]O y=bzkV;}^-_re7AӿZ~o4!v9T--wD㪜d뢺1a_nHN #޳(:dܳo cٹe~shL);ITX$ 98SAzމ"`^?ʰIw%!=!txt 2n&6F\bkG>5ݺ崅Yg3T$~]jΥ ԺTi9EeZմUF㸨Q -:d? .%9O;x#HGiT玤q;}+*smCaܑ HVHbxlgd:ʒ" ciIrXR8@W`ÕA=Hq[WS@ULy\cc9ǧ$sr}+ur% R"`;w%NQ\fDMI J s##iӵgI:5C=Awa_Ywz܏1LQ<{#T.FIP1AS Ka kÜl(eu\}#Z&’Z4^TÃmw&9LeR&]c<1|W)Q@!3 `}6l˒$^s 5B/rS#re8y^gFӒ`ӯɚR;f$sV!gX"E‚ 7au[ʻ5ϕۧ [;Yy^95%ݵ֒` i8p=1Oe%I6D@qQP3GRՍ+Z+Nl+0F{uqjR#`a!N mE+E{g\$-TB5CG2[AptY6=\m4M]tlt镊\<v$tTs8c#o fD>0WfY0HaZEql3<*cfNΒk~¹rH<]-Ĭ"-'I,@L&uA yEp@n>=?Uz-J .]X۷8QQI#k4$Ȃ#͆<9Dv#p EO9b;b͈>:㷮+оnc>QeÐ'>B-VGXᅖdI[lF* z4;r#6)gp]3Pu9Vksr"è2^=6ģ`{J,rx?ԀNEtRog޷!g)Ĝm }͒W qڤڪXzSx NCquWo6iM\U֚mDcA`7 BRoGa8{ "qo*!?/=+gNK-|f\c$CVEA*F* #`=>^}km&r~n UlXNZ&oK"X\4ɱ1qzzc4C,j- }8ޢSj g9N'ISSq>>Ϧ i8TBmL^yIcr*`n2:) I#a>NрrN^[=,sֲ5kS;AFi-(YT00<f^Ӣ˩Œ[$2啋9#` RY3ӅkݶqMPhIYIET-;$ʏ֭hddaPˑ$ZtrI Kgʼ(>EYht-==cͭn]>[4hZ*[0O+m.dPp#(Cܚgu;f!gj].&`~(ZWoKVYS5ư+,yܫ;tEkR 'ѕfo gE95֭qk,D|W\RHfa$I @G䓎ҧkIwoMIYUl\0!ɬ93,[, ,HjOglMPŁ @=9ubo&`[E壤i'{|>ӄ.Ql ,/ ;H{hWHA'9lfI}Uy8S7*mKYkf+,1D2@R5;Yld\"wC+m zkiZv.Lm,Y8_u$XYwbSx AMX$u0ROA# ZI$nBFG$3[>7\2rn*KḰo\'񪶳1K"J@#%Zֈ9`x)1"lHwĞVaxB{8YD𺋅h. 8 W1&s:w+ll-+{dDTl^Cn*yqkpƐE%UVIª{lAԏ1B̠a}94 lc`2xNcW$26"ON@|\ g&$'sNEwo1cMvoҖ͜i|/ #Q(zrE:U.ڑ.-Jk攻D2X)1ֽ72 s+}F?:6\7wZkiY3$h Ps9+ZqE[vtP.,!XP2@>r77:~u:i9=ֻ.d_2P i#g0*?k0MQnqRkBeOXŏڐ-:d|ҳ_xAbR?Onsj 25*e\\b]O18oU+){k۰ScPgyBp?1U6=BS4Q(k>;ۛXXFX3 9+9R?UPMD {s#:5%ܓ.=5lpev ?t1J)T? 0Bv.rt2ˆ#'$ W]=U%mQHsOzd'uFi-?1uF'1Vtyh% d?!nTzRV"deexcB:j lc|bAᜊGJ`?/@ZvwP0e#pQe=-cY" IȌ px+h#͏ksu9F #G\۬N^DtAJ~nz{r)wfb"$ϕ{3?|yM$B$e+dF/̧j'U۶]>&4S 3IڦU+ʮC`ʶp2%? ~5J{@TNX6u婞Xg$.I0M:.%2 =+UOa.$K NKyCaOf[ ʣTw=3Iom.`ϯUXī;Mh]PyCGYWRA*yxϵ[=|TcdiqdzRr&%t;Xt"OO%95n+$>bqj:XKHS\J!LH +]Iݫ%{R+L$홛 PknZXhQvcH%>| D`~bOFDH`qV,㱲wZ%pX?Շvơ~~[+[WI^<Ѡ}*iLc篾O=TK I<٦iǂb/·֫Dc˻VC$ɗ k^kE!rܕ`EX$`((kf1=#o$9qT$c s ʵ4 bm@ݽ?ϊ(.nz&3lsz\媴ڌfIgQ>·nYTEj<ߊkPӨM_}+%.Wvr`fQֲQG`#&& %)aNrGn*Кl"ƣ I=]4i3.[2a?*ybLaj_&WYYǵ>)AإYI ,ʅzE{3th #܃Ko 1S֣ 46 U 1>biO"6UkR}fS422%$ ͺ]`^{Tr3GzjA;Frqȥ AaIYXDc J6‘8.dpT#pUe^ญOҵQv{PH'p/8gqLWy{ X"IFKBUֽ=f)e:T7[|+A4  yOvwHaðꢳi[&]R]]68< wV"Kiܧi ~Ia,~5M"KxYA1<{@v.% 3NwM IXGSǏZci5q Ͱ:Qճ#R]w3UbJv289}ΥE+r'XS V$ۻ#o`Gb,jďefg]{LxJyG2ӻ=3ϱ=kxc{Ce 2E!dpB8Î5zjjַسB ;3=zPXiB]vJIrf9Ϸ9{Ȃ!<6xb~O1y '?؛“@AZm܈+@}ң*}OZ|$2;F?!i拇7U?SN[jJc̈ |cOH`Z*dzzTWSܴ$T|N9#`sұuGbCGqrx޲$14lye#֊Pz:J U`E =89nHE6 ᔪtH<sUQ{׻i( FGmb@Hajܳ[a q1|'$1}X^ionBw'htp>84BKRtGqlJ3ĖQ?L^=pZ-TF P\Co>9P!1^B9>cO]ϓ CeHQMdG `rdu=Cyu"X O s杨Epʑ,3y; px @)40&DQ! f)]ٹi}MVp V%#8۟#_I$B X0e > E1"tazrs<ԶQ%#T=6y^Iv9%ʓqq>4W \Adg?Jڷ]j`b1vRiLRD71o r<ݴTHU'M%mD riw:Im$F}^sޭZi#̀]aIu+liݕyd\G37*ĎLH\x~v)&QnUx w5uݼ1'!2q3WѮQ7 Ngr~B2܂FIRX9&0=8H5]걠b6W?AI\ݼRE.G#jψtnO*y? 9zqym5+YGژd,,`3N;uD9`lYzY)G=IuV9]+$ $E̋bG OU0/]nhɓ'x<]Ih'M*ribgo8q9 ϽkIΪy”,$]a qE#;L2o&6ﺁNG|3Wq|!5"dBT9qomDQKpx2[!£I W~D*K+ 8=Gcsqr.@%Lfy++#7qFCzs{ո h <Ԏ29FbmeffTI86T:'COc}5¤J d#<.IZX}w-vM;FbOmy"+2G22ƻB0MAu5w;M-y8nG?w;ONIi$I#9BLE9(A,*sQ%$㩣^[i$+.Ts>u-IQ('ŴƏOv<$|*rr:gӚonD||*2?cj˂v*쓟5m@[@Aێ[ >Z]˓oT6D[L/f呡CʂO+.R9l2c[Y9"c xv:?q$趓i(QqYwQ]Xw rVa9+sH#k숔3)jE-'&giN.z}PYDgWmٷ;N=6v7%/,NU~yjͽ!匴l傃 0N 5ZEPh Ӛ=;qZ/%%ŇC.zrG[rYRE#5XDobc]%ֲdiX\n%u"n*vHp;B}ܑ؜uAquAy Q$p!UEϱMmY3#npAZno"HQdITBQU1~P9';/yȷK6""W*v`zhu啊D-Fp8I[=stR3;`9ɩ忴[{w WY'xu=mu0DF+9 [-)9EM$ڣ<װ;FB`1SkZz^{vΡdhc0>ՙu bq8iCd 8#ڲQZ-M H8g 8*@l EC,,[dU,\`g %[yX6I3ǶTbno/ZW 882:nKRmMv֟e9B1.쏕B:}j!-*m"h([v8;R/dƁUXF!A8_j鴖Pʘqvfњuռs5#o-Rg=ҳymfo8?JԸiZ9ጤW p9%=+w˞`f4:`AL!AE6%E>%n ϫ{qJ&2[ʊU>\.=+>i&?"ey,Cۀr0xװ{/Q% VQi\nݩjlxwCcrnRrgLՙwR 6/=E]k:=/ۜ? ʒGY kORm,-s0nOl `w&=5UTOe9j!j42)8Q3qsҗJ$̉r=5Þ;zVq,jcL %G91T) bpN ԓ6&wy79Zkgh>pioc 8+nTbtpc 6tVüqYG--S" A-=}+1m(b 9HCYY\:2іРU{yr$fXKʬ##voU{ir-Vbv@L`uc%:mB+G ]Y] J|K11_#n^{OZ[2nVf& RL2ltz}Α< Y"r+S)[lno, %YN9ϱRDXH,dp;¼nm#utӑԶ-[NNNNGj.P]<* a#*yLEH:YcXsqMRR #ӎ?]QI4_a4JβM468q?0zcךw; =մ9pyg3 ؖ`.~OMB.F@]㎣XJRm.k *,v6 ,O؜^)ˍ$> 4!x d ?bk}BB|@!ؤC7vN2v_JZݖGP 27|Ãۊ# 43M^tY´RrdOSzVj*qw5lH @,ps3mZ..mYb"U}y9#DN|RLU_qs-1fQwʃ Ew8X [Xܒ$xYݜme)c޶[ӘcPT]Lc'>ՐM:Gp#늈 ֠ P!BVA鱉dCrQU;~ish4[KsO-_IU-2S}cKФo}kĮei%dy^%`[ UBd{dckqYެg*=«A< KHU~<Ƕk(OGrHdv3Cۂ684˹ 3 e_2}Kj12$QQ:TsY2*39=uNբ3]$HBb{8Tr%J cc\)IZ&Iu˕aFΰuf/8Y$cU y?;-43m[df,9Au -O{"HY9[?׊#'gMȬr#iRw&"M0e(_gKJ2X Ո#Ē-c@2s~ULCjxd%BAa횔ˌf8hۊK$E_0{Ι{ri̾횂F |xqƕ'hLlᰪ\)%[Qm\ȅ0*9?U*IHc,⭙6.Ј uGM:a7Y7 x)_'@#֘Yb wFJrb7{%h¤MOq\9ߡ\1uU]TF zUIbVB[9>YgV'# Х%yЯ^Mv^rܴ@E2i^^qߨa/A! c#p5Y Ct Kn xS [E>Zy>ՋZmI;/&"!0ܯY.gӭVB 0L&-&b`:Ip 6/#?QVm*ȽYڥc!e I*G | @JsOSOKF_Cޙ@ B%SrZ8y6mHA{cFQ -éF ^9k<¢i"_/i('} slhq޵,`VT(yTgcdtRjH6F$AWO^uؕfYDHrO]_CBk}`HW=-ڶ%}Cc +upGBkIƝ⃔wӭ/I.W4<<Z<'b4l?{ׇ,;g BW&صJd 9qڼJN\Uӂ}IVWI$YNC}k3Yl1MyMw̍$_hLԌmqw-1p]1/=u/B8׊Ľ}CkoRVg'?V.\Faw,=mlg+C3ʸvVLXw9x'iUwAvmSWOԭ)s 8J ҉F/avcfG$bueWlY(d 8ϵ;Va`15'heq!̔FG V-I$OE,cpJ7`ElVTlT氖o^8?.I?ƧT`Dr,{zx YқČ6֕ibIb,S!9?xhᕸ뷑. ?=nd\1H$(N@i$JqEKkkzKD]#o _^M,) dcj8QeIp>>LU[/V9?%q$0xVJϨ*_[k-*<9xfK|Y2@C}agްmd.Q]mڬ"b%yRG]X!(ZIyqm+1 wrE.XG[ōE-)$ڍ2,cL&=DgUQ+>nG^#^k(AyU䕬rͧ,iw,omc($-I0kWGK HqQkbhM[Q!G9 s}*VUƼ*NXIƥYCWGF};2]GrKPx:57_k_rgceO8RX4k,JF31yg81t?$n$rmČc52Ε 9lIp]2ƻuw5&2nуП2+mi_(BT1Ϡ4Q,1`vӁOB7s_z*d4V3H`9 L`u8/Gc8mgܧ]!7|`Ƿdl<ĸ[~hܿ1PQv\7L4>:ln p|GPvJ˺u+B7@j\/pDrp#'ۥ]G\BcJBˑdqTl#wkd7QI6wQܤ;IU@+UoO:) FIw~wnz-#U8-^*5ӤQ%V>=GҲa\jWV#YlbI$x8>$ZOԈ Xt9x})5$UT@(?sLљmL?p2 9e꺖%դlWX"{ zw tD64i }85mmQ^Mi1guq%̫*`2Fñ 897iht0[AEH p0g%_թdh fG<`nYV66n@A,sy>:KV\,g΍v~qqE9$ٜssJFmHT'dbθ$zQ49*!>["ԇ"Kc :r:djm#m[yl @끊SG 9'7s{Vu+xjF2U;4 =W} #;ڍ %Ρf֨jw&E@;u$R8pOӚ/]aOA$/gn =k{' ;$ȶ; a?:" 7UAB9iS5/u4j=ə$.Tlgd\ Y$RD`}с'z\n0J|8uIF8Y2isyo4ELjRF8|朧+ō.7e/[=A6يm9/03袳QFK>`3 cU4v3l c8]f2r8㟥t1CX!4)6ZSsΣ.PnL~ScvOp?K XJ-}\6s&KcbftcuupRM;|dvh[lTXw @)Լ9i#V~N91)U9,3m,2Q_wQks=={TƛFOIɥSt>Լ{+6#0Kh?5Km'ҭ)>繠8jnOC@RĀ~./ov$QrG{To5LjAsWjR^A;*H={ q]11f;pWn1כ]8EisHHHP=O@&I!2 `g<⢎hz* Px'QǮ -Pmc{Bk.V6PJi$qGMĆ9'y*FAb猀G=v^k$u1Mi-khTqHvdgָy%ᔫg nqpݎ62A},. mkecs1G#"D\N2GV(]~G;w3&,`cUMė0\C:v]#`c'rlg[o֠uIdh\P ##cRMGϩp].xmUA(|`p1j@ icauS#}zuxB0(.TF qC qV?BW̻;I#K7Zr!v\A<1bCtȶ&`;˃Tŝխʲ.勖ڼeH8'n972UГZ{T#YCi6dLЯ\t/.npA1T-'5دp^p"$ ;ĂEdD{JZjֱA9mt+fbXǽC|Ѫ&E3HT@ݖa\VvpB(>fI*׌}3M֕gi-#})`ݼW ׂ r0=j!R++ƬV8ٲ3TL m0G  G9j F^/i n /AlfIX>X8 lR75So(Du|K]h-N~8x&WL6Y 6 y_gkd[YR %BCOaYmX>HT9P㸂NѓrCWilokyJpYB^Oi%՛0Enp3}<94IpTl$g+BBkd9#'?*GͽUFWKfAA,q+NKnՠxK#$A^=:5b~ty`팻 AHkeSN;9qsԒG4ibۢu2 \"!8`'ުXKuJ^#+@dj-GJYAҩC2%0x><B;"k EȠ #Ge/R1W?.d,B-:}3*pF \oL`$8 s;Z{X{G#=+$;˓sm$SpuGyudvW Prkz6Y6rz򣚺IEƒ0DʱD`sckyq^@1 pg>PZm|#,c'8\%,VV6ݒzqSu A<#> tי?DiۖN19 K[icW32tGS +hrO_gܹ"._d[BN3:Biɧc?°jikJX]sEt7jXV2rr5&a3*@ ^h XQm)s@+XS80T&bVg6Aޒ]id };qT㹊lo.2+=^SR/km!i"$f0c$0捣h&h|#p9== =iI~%Oe涴xT~vʟKmNӽ 0ru*ƍb·i>epA3Y*V{Jܼ؊d%i4܉KvqQfd,T(K5rXQ[dr& 9 g߃uԺqoC0ٻ8tQgfgūF&8{6H`KtkiBN^ HVt%+* bpAx?ŎG9K|)R䪕P9s$g'TkH#,1}PpVpm\HrAP'Ad^mڢ3YTyT`g$sV wprOʴt[Wdޫ椛 )|Þrqt pgvwgk.:б8R\En[= I8'-橵s[ne U'Hu<'ٟδ/E Tu'4O %Z5q<8GXwxީi76:3YB]XYE3Qsz !I+I늣i]o(!d*pp2p?kGO.T5%k3U4-fa12\ҳMZ۠LMpR-[c"j< Ѳ9mʿ*L,gv,mK[M:H@V-1jg+4q4Gk,$[$AEXa^ %UVX ` RɼF 9@V~Lm,-וKr@w}ARWnB=^,\1Gm7ҥI0wgҙjs<ʶ^LoQSϥ%BL%pN@*#\UKKᜥٙY# .

Vlikrƕszgn%$ldۨe9nmdAOuSܬyNkSSwηw`~RpXU+Kvi3ծ~E% .K5\4r#vw)9zQ..ܨu°BnEai1wy 3T9pǮ* ʕ-lPGqa8#Tnli s5M,$6B ĚYlW1zr2}pq]X%fm۲8@ F#7K4KeRw ȥ%d2Jקsc'ni\rw?* Yz̞ cu3Zi,G$I,罺sGrʥxs~UgO' D74#nᔞw┶Zp["li$aB~ln /sB0-TzjVshĀ, tEKy]HISM_l:\▗ܤ̱q{3$CyTHS+8?A"+xO$ x+qz W#?d,ǀwUcopq#ׁl{uG$Hj*a, nHߓԗrEJ#ƋK2⻁CDa*ޭncHH>WfaҨ2f|٘b?>{ZqV_"sW Yuɴs9<`ջ'QY~)Q&9RI';U8x j.6 1K[k^wG+9~5rRPI#NTK]HM4  @8Tf0fkxEľLvzt?jC4r\Ė$x܃ʘ6\fUb!UIHYlgDȶXHW%f<yMGjwLn<8·c[yW0Dxuö¨WeGTP?OQVqnƊrB|@9]NgISΐԒ3.tXmWYack903)&qAg^2 QbQnf!pcۤk#n|ơI{dhiY-Ij-2)$c#]݄N T&ifr)n=i"O9EHB0sX,F]z 9hg)[aDˋt[>ثQ[T8Gj1)YQԟf[O N1~lg5spwuxaI6U~]E@c#T\ar.:K&0zIʸU1e#4:ى2ƶL2iҨH]̈ ZJ)lcw?M[yWԆ:MI4YN '`f[d9rkxg +-`0QKm0$^9Ko5Hx,Wt"R{"q7@GJVp9 1~5}K&M hvi=" 1Ҷ+WkS9F˙c"4EvN3&’ZcUBMr{4DaFK9:?*[|l lV!biZvWqNUՋuŝDH(rҴFy26q\MDF'W8+~FqS¼:z$.DJP cڬ*?(`rhYI|P-]Y">8xrg8`vkRq,QU@T0R9OG{r[vpQ(A A:⊓CǛ 8U$rco6 *#̊psyc#JPLSSVc7x-'41ĻN3E_$3>֖ HHX7ˎAb}vvՏN/10d,p+{.`S*(yPp"0}QG  AfA#ڼIS3Tz y,[v!3܃Zv*֯lĽ×* zz``y9WD?tsWo$h!ܡERbMqp#9U,61"grBNOz[{IO2>1_|v-ɈҒ`BǾb#\' xnI^:o&:eHydڨ.W0BFH VoݥyQ|dUbn}2YУr\2`dzM{?w.iI)V ެ~K rNGlOQYs=Y|Rn,X`w9__*̈́-.<A 0s݀P9'aߊʽ 'VE$Tt ~8SN9[摰Yw8簭b5%SQѲ}Z _2+褖Xb%Rs^? Tyم$<34GGʀ=ZӘyP`'# ~X %I!x1U|m͗N7F!v:^h*Wۨ}=iWEżb+0s۞A,ѕ&H`q 滉M.rN~OVN龥4&`o\Nϡ;WmO.BH"p3A56BiG23dgj%VYK"z9cQ :Ti2'_RGMeV,B6D,С;=1皭FZY33yyy7|˅RʽSRf$h2(—bAI#SZٔy`d->c<=.- [1.Bu0'OsM]D\(#vg U+NJ6'yobʘAٵg7=˻ 0PXe83UtU̳3293֜ze`[+ː~R>]4b/N-EGd2Iw~fȊS@Ö%p)t Zt@ pYIb%nfVX*H;GvUV7A#m޽ҷEI^*Cي'j?T{!ӊ@fk*g$|y 89=BA$!mA 8< Ƙ^ʗPn60v,8$cex<$c3{Xޛ sr!-C@3'1#2)M KO?*q3I6Emc$urL{pɨX$ v+:-=GsJOGSfi$pUF;}U s(I1FF0\j"g)PJ0ϡJ԰ዑ:dL)`tiv$ ymhxUȵ5eny $O9K,$ Q%Xp2jZ(.ȡoWr~+IFg) D6*9NT0y5FeheM:Hwf*ɀ/pO$޴5u4hP2p[pgk&x#a3+lj7IA;vq[A*A,Bn3QO,d]"e$*8>#K2lY !D/e-`I$vp)T2cҫOpJv8B*njgBR~F/7bL|G?^:Ӓ٧ʔ#tY=kK6ڂ^A:1D<F =B:++FP^MQ6ܟAw޳I;ЧOq-[ȿ.t>ײA b! $;#F:UOY`̻;\z;WO{flءY>z ֢H(-gSxIZ$rb;֮rcYr quq.|" S8+gK{";:'@1٢.[F]̡v10\'*ԧf6I*m,ĐKw 98MxK$w>挒HR&T_B29fVf\7'GSSj[8[&FkP{o # }p5pg FiWLP@>{m-<-8Ȯe U2  |2GׇM0Aqq4e \rr9>w|`}!pTz(8ɤV<@QYw7i)Y2eWrJ P=4#SWAp>yrqXkD߃Ma;&n1@sԐ vP֮Ȕ9(ܐ0?75z)$UA p9yL@dQcj(F܌+h0V$}I Yzz隡k' ;E#q;r{5¶aPy^r; Yc&c׊V)$7$8Bס]HKm svaMmsu3Kn@ R2 t殭Bxemp=x?j7V%,ՋDdĮ6z銻zmق+!@psn+3R-wb)wŴt9 skV28xȕwʩ˙64ܕv=9"=8ߜ =cr1޹}ܓܡ"98ܓ a1WuymC(KFgZH;%|ryk [qQ̅rFu# jBRXH8 [YjUw mvXvmY<I~x?bZg (cUl_1qBBUt2aY`i%fCz7W0[;*OU W8(*#C_{h]@][²yKɎ5ؖcKڎNK)Gy(x #i~x m̮v<ר>I~huҺpFp@VඝxL a<\dK[BH͋KtN `Nq*d4m#l!G\ KAz|ȦxU]Ýs8&'/\4WCDI- [.I&P`L ۜc1*i \f TuM.Y3:\BnnXsm["i9)>Qw»Z`'vgzVcv ]2o(8?:h&+Hy#5~yȌc&(e8ROq~bYK*r2GarZ5uNIl%qlIs$!09%? G!W{GU/"D˺Ef%F;M9RJ6/jZD@@&F$g|W?yČ8ڸ> gA!UP)G=1q:`]Oʵ^H?S6Lu PI$hH oʠr; y7hYC4)*1tPw+|ttz4v&>A, U$H4qGF $0`yJy^Hu#%JXOp̗Rm!VUZZ'Gª.F'_JP\@f >tcϸf'NHbz+xs]Nvՙ5:YKg`HǕ; ֤Ӄ^C 6e1bz=MAxW$0TAG*9=#8ʁ#ƦLn\csg+I14$Hd&|"҈`}l?d;䌸/#W:8ʒ 8^NjCkr\(YL$8kqܬk]Q|F C/=yӵceT1"pV]%yf!Zb3N*1<^@@8+X^/D~E-S+)\W'D+>mj[nmdu8ȣkRGnӌAzINh3 xlФ9C>{dXdo @1}%q>VX &SSMos:X(6;cim]&g9MPHG,aXu٫bت\:;p@>fIC"NBb0O땄VsFn YWr :{ "̒&qt8>!GWUQjĩYL*÷+e.Eu {IR} %̊F}#\H_^@ 5uXӋ8; R u:*1;')6JDSgdH`ִܿfD`"VgG@xo'5^fhKBb6YU%i`SߜRmKM{!YbiVlF܁iЩb]rXdqFl¡pnQ W(&3މK6J(9T"'%3Ӓwv014ʼn68?BEfnHPw=gU^^dVp\[1$9g<w(b$ WlVl|6Y$?Vm- -Ezp!G#~c~fuvzV&V_+nuhJ78YDaVeyJjU05=I-oGU)³|}oy#] 튭]JXHGC\p,<ҪI{I&R x`ٍ pʽO҉eQ;WW+QwK,v>9ڼﺵ'[.pȰp:{zsO7bD`w+\̳˜2䁏J!o6ka\̆BgllTC+7$簭{#F1zsT渍" (ZSrWe9(uo6s~>6Yd9j[$H>XQy=iR&[vIA<ԫ)MN">JS_XyɈ GԲKKC!䑀1U1C3 +:uu;Ÿ{M5đ>T!ry7CМ4ю1q)9H#KZKkM"60I=O5^!r"$҂w*!$``Ќ;sЏ:Ri8D1x$qT/LgrN;UT <="D )<(8E:s{i! +PB=xϧ5+R6nN^D%Ŏ-'$PW^$֏%I#`qs}OZةl?:G̫9}Z# 6@pDcv<븀PTcqGE:L"vz`7P8+.TALQ)yLSBkcf6wz)]6IڝsycM9x 19m-sjvoZNZY>g6e a9jٗTX;`NZB.Cqn8%bS w Q\79k8@&G9'8t=G dOR!ȎY0m{ w]l|p!Sp= XS ħep}EBG?S$gWB̻'P0YdvGqy $p^ O'S*T[J07Ss ݞq+gqn,;=}+9ӼyRFtդw,<=>ǯP_HbɈF<`m,<C WXSd YMQZ\}ݾR@qc5.KI洉5Tw&rw#AR1^z Ř<#DZSw}Onj֣8HF?n6!,S{NI j-f&g )?SJ-sS!rV8Z8ɔGCP(?SxQkhLRXIy<{pXUngO#ȏM$\eՉ( $*Niݏ9˜`K$"!\,0ug$P[I|h rz֯WV3:1P Q9ȭ`&N>Zpw+iټ/P#ַ j$O'E'r`dm(~w;@ӊSt̷9uV*W(ex~0;߽fWdbXq֎m+Dx)zzRo *B88qۚE+5-n,@!cgn0yЊlUP*) 8wu>>&A8Ͽ<ӄTo{R=#2m@퉇$I}EP$ 5H@r2۔}A?Zᬾ0UpI>{[ܵp4e1k7S][}p\!#pp$^h%p%-׷Q,ֱ$3Av\,w9PUWS9 MO1 H?!=>opI^ѡ#$&Xvo or{{vCum>dOZֶ*HbqY6r7CP8F@$"Txm| U'9`?+i;yI+ە=>a]Fw Hll9nϭeZikK)V* 'QZ*4ˍr 1%f֮ax~p99>K ,㎃֍:%Vhco.8!*y \v5eYlHY0`0AqΧ]Sjܩ$)gT?AP]L[˝9*e85Ȓ(K9ą#n/fZ!"|=21ޔF1_žbmiۀ8۸Bx'$''mh &dnnŸ/'x][! HHQ' zV ` 3 YwgyRI.7s2S4zS28ӅneE*r: Jkwi%@`$?* =I ƛ%^lr7$8lYҰ̗CsU8}7e/z T.CJidh\ӨfqDҸB,>Y\o$PݥdHj>U9]˃ ˳{&x$}ҺFyce9%q&iOk}(}(ؒHuukĺ|.0Ab,ҳ+[ۛ6"S5 ci>Gn󽜐A$kB9Jkuk$rrI85fcnH,+Y)$O^[C:$FV$ $sߥ_6|2'u8Mi+E r`K" Q$ed:AZZZ&M\_]6-ň+y ?{ƕO6ee1XgӒ84jv~n_< 2 XfBZ~}v2q(9$\-UE qRlQmΡz›bfYIw>j2#x;WA9W&x%vl2KO; Wm5H_}GU Ż4mԢ- $P̻'"y,N.@O8z9E*S7?civ֌TNvH]GA\=y T]3NV=.X.lo2/!c *Kd Ijh* $b0C{W IXM;(B,>O5vvil(>e˽q$ۨ:MLWq= >PG92uK{wՍpn?9H N{XSqsqM% Vuǯt' L?BRr:qAk-c1ԁZ,RHI K9NwSo$rOn喬N 9DL1[cbOR{V,y,m9'j'=j/|kh&V;ÐGL{m"$br}L6N-F-[D$GuMA?tI'#+GF֭U6Dqni@7v,F}+͍Be2epZ¼Qy"v6\r}̖)6tފ<F;S*3$mB< Agݖt⺽2=KLL_4Ƽ2mn32GbOS!;? cŠwܽCn$Y0ˀFx تWgdg  dWaJ[>p e>Sevܲ2;[q&ɕoy@L@噻; *IUخnUʍ=?̖QYU*69'?Z|լ>jݤ2@;ܓƮO4VD(ySfG|dڲkx NFN,͒w $qD&З%tS6sJ蕛bRM .iK!vc\/o=ē]an1B44e8p0ztUneqݩb9*bq_Z(z&4pB۳SiXU*Á_LSmmVbDlʉdevt3Q[^C}*]K&؀g98mŰY}1?>`8+9Uinf~gs ,R,)u8 }Tɼfr4 WOW[u do'ii} }= 鷛%]v?Ι‘\jCi&csV "RW#in 9*֛oq+(wj*rbyP8QVWngN6g`S p~pF1{չu9Q~:rGDrRsc޳5h.˖:ݞ}G٭cj3?cJXܨLe+uRԦϖ=%<#𭋛43Imp,ϡ?5Xn6Gle986VU*F 1NM ?{PidMpOE.rTޛowY-M]!!(֭Y cԈ..=O~IZ8d01"2p7 N0kY;>CvbU4@% V{9Mu9c!¯EM7ؚE&FFx 3|Z͂6dE:71o_U䓿Vkkji4%v7Z6BX[}iݼN DABA˱= ~;}R``~nWp\> ;ȦC.@\$5Սũ%L[Ly܇Tu5|gȖygb0I=?:K-(r }ӷH *&a'BMi - ) T$}GRq3˺ d[p0G߅c|l2,22V"2C4<.G^Q鰤w:]}*yff'+ })u_R\#!A$V|q\[derG= JvngVI&dcoua25Ŵ3[(I,J||V=mi1\#"˓$vǾ*ך,C5)=HjRxn#[IR57GNI^VeK !{E!y2Aj_:kh^D\p}E$RK J1(8)WBo.@_9$GtZz'(nmJI$$O9Z@Rq\~UdVRQH)EA#[0DaW,FB+kdVQYb/* |C{$e`5A%z{ 侹Y-A!n,;xbI#2ؐ:XT %(j;\n007nz1X'vHO{#iQJ&o'=ǵ[/;RW5ːÂT} ֠(ad |eBZDhEhj`\=E0UYK?V5ْR eaAHgDb[ьM$91Υ%p}FdR W1FzU:mmBt\ 6G?oZs5ȒdnzNnL1R(U{fIrTwHA=KT\`D$d<O5o2V&%i۴&w1ݯ;4g ȢU1щV]hWur ar21YfgFFǰ>_0el5+"e\JI~:'Q=QW,ŶmdECzXl&4LY@i<(wdIQ% $H-Nc?W;sqq>T%-l`'8?c 'wNKc;Yb;? f07uh"̱r*Gmm\]?(okb1$qq~U.|&WL{wWy]Bֺ4T)o#3f$m=3 =ͱ0I:*"\n`sj_{j"O.)=4/TKZ3C$Ok/NC+e uOڹ}kSlk<wӯJB[ĸP# 8ƝHb&ѭLylT5Vr,)Pr 2IUM=D#q[ ~un 9IIB.c'b֖]>9E(=HB Mۂ30U@&|kW1̀Ȟ^0S=G3*4(}i{tMQS{nW4K,t!2F}r'ٝK$H3ӐxY ī"(pTڅ*-;$$~jVkd{ؕV r9l? )\vI-޴-4nI&pd A_N*0s媺}9D*Or Lؤ8Mp{6sӥpw_ncdC)(wI@.]dtw m-Ƒ$bA,.>9ⵤ})=L [PxKd5zDuRV@ x|wmNElQp@叧JUPȌsMIE()IZu[JH$@ :?ʹ֞bG}! y>OPwE)s+28^BY d ji'~L4VkSB:kI^$Y_ɱ}kkXīuyV]GBt?TZی+DvÚqKR\qT3‘`>ճDcn%Yŧ+[VNZ msBMA3RMҴ ^i!Tg IcZ$%&Op:~uCQ[4 (B9OVդ$n";v5S=*sVDiQkjI`:qRj7s$ 2c{T yha;dEFԟ.:.xcHG$u#IPHH&pJ$TK"ll'zsQQY~mr$h]l8Vuk yg[}`x%ŜD`94Mr=M=lR]ߩ䏩X+y =_˚fx,ngsp` @R9ϽtQS} oJ8 Zie~rÞ*;{_>g]B䏘)jBKsQY8inGy"*ٔ|OFz,Ɓ} Zs!v=pdU,Ur0QNNHY"0 MQO Z<#N3 6{2Hw U7 pTo rw.H2qR[s, 0E)! `J#4g|a2O?5NN ꊲ#́+),{qGQS<ؚUc!ֲ59-0Jvƪ|mg"+o:c@elp1~QWHli2Hp䞡x ӓjkOM5spg[}7|*ɚFȒ/@+&bkU[[7>Pl$1$;=XV#%j;y#MAbC#QKB$*ny;A$t{f ;XB[ȿʑӅWQrt4JOeN<4yg"@P}cb ߵvyxn#*a vD o.B܊0ۏ|zy74rm^YKbCŀ'8 [jbv @z֚@bǟ8G\iii.Mv&0|;W'iSc{Ԏ vfoע2AGƲo@Hmp=OT ՉJfTK`.sӌߧzYZ9,.e''pp0x@Jέh]ѦU0_+lZ#~[vӺAVsU?"r`6G$HY~RORjZiK[ੑmr7nǿP9hv8nly o~7{dt1yf U^$r"s;w4RQDJ&TS(eT*# ?Sa[MV.vm1<`mXsER8㚩_qMfr$_Cq8'-wVf2b39$ڛs4ޙQnð%zYM .q?vbYO˵x-ZMIJFj<)(c@隕_)QJ#dKir9:~J`#fRKmnOS\ʃݛ{t2S[; Hӱ4]!"V 8$}ԑmeXc%d,ǂAIzAU%uu%#=3ZMXj<fȗ ǒp=EK<;H(I]so nCw 9#=CY\y,P34c,A;Xu6o©KIe.kt;5vX;< As֙Y SJ0gUK#PK 1 Ƞ#8]nr3\|gx:M&~ɔ.c#T=~oJ!{eksKud*cPB}H#:#[#{ VP@ #II} Jė*}JO]|6}Ko+, U8b=幔yegj5&F@ZӮ0OC4o<1\{bp#'vrT#.mxEx mzX-uxo3[%M4\LQ790B]B2\9?>{# ?OC*HC#76ga}[_CZJ+w3M;I7;y2'0 /ǘm h%G-ЫJ4E` ~Da,fw;}'}zQSrzI򵩑t-nnݶ)v.[<GT#D@I co^Aoچ$I!5b8;k KuP6r89n='=.\[<¢I?9#'AYww`9W ,mi:2",/BNzIz#I.'iB'rn8>-isnM@mH@^4GAlB,l5I[3y'a%_2=&Y\Ԅ[^o;`J؊׳ <^CƎb]Ϯz4պ]Y*LaAspq)rhIj?xAN:8 s@?S\no{vUF`0N~ ҍ9nB鳧7NY(.%\6sk4)~+1u%T?$ER}r9#jF "_/zG33q#ҝ*lKjCmcFyng Cg>Ʋ00ibpzeq#x1 ?S\ghI1W!ܩ=+'+$e6mL#v1'R ǥ_:AzPGH8@n n먤 ec887wR_]Z.$)# ' ٛZ .2"9a7#ld]E*X˝$UX7w >g>tcIݸ !>뱣ZZԳNyDtշ'yM_\*Bp[ OQ8~Fe-b,R+o$:Zr\s: ێkN058ɻ&f~VG+A+lVI+ޜִh[xX*  aʷ4h !#>jRmli̞YEXg! >yQVaLd!;xm^+;Q0%*L$C.GcMM`eNuo7n1 t 3t(魈!KdT4d b7 BBe`.1xQYܳCnw wX6'$tCX_3UNYq[!*|$2fC 3P׶Z吘xE'NsL䍤aK EqOW2YKFZӗ2\[k(onnP0 @]z75{Pᶚ0K|8@ˌ27Z]&9Ss&UJXme@$ǡ=H8[U yEc吣kѸk^HddId X9 8'Hk:8o"VWRA/ZowI y~֗73M&7<_ȏƭj_,E2Lُ#/c8zlD&!^BeRQr[vg*7KER13Rfsh9| g}2˩Uaprx56Q_Ha:1W}H8gEN^ e[Nyie++Wm G_ޛu-ũϘЩ¨=zζk%($I29=e4-;i-+0RαBl>*kw}3f&pFTnP޲Gn1J+1!v=A]ۆYc\ݎַn6ͨ'4s>椖OH.Z8ebNzD׆S;08U~u>,A Վٹ2@'=pXbiGVUڱf tY[oMI,oo?0pGRG^ȗP[0yΠwA]ۇZ|!~؈ªɝ`$~U.X%Y-N'I`;H9TvFH {!G@BIki`r[Zo:#Ƭœ'GԊ\ufbX,H>>5I,r#mFr@.oc`w?8YSI VŢt)Ӈ-G}M+H-`,O8*I=- n`Ȥ̇dPI4viK{"M*7dYWduFۙ B}w}N&ǘ4ʥAbӐ?Z-Vс[pxSדXit,ܡl/VMo]2rs # 6%Ishm5Otȳڸf+^MLVFl/^9݉͵Loub1IV}2L3rL= 8J\2}#P{ Y̮e ~ۉRDHA8B2zYP\r.N;m 0ǩ[to*0xw4{.Ķ׏γ9`Vܡ6m'Ee,k.e`rXzQ09h)INF(@ʫ.NƴՂX:KŲ.%5oՏXTInvҭc-X0Îj?f4-͹Y-|3~03Ƿ5G/ \OZEh$yPUۑub/%ksj%yOJƍ;)lTVG]$q屔L*@H0<E)zٸcrqel8ӞqUt4Z:/SQq"d~}3Kyov0bR +.Yؕd*;rF*=;?4i~6$Z3v:|;leD->rffV|N V?2@輟*X9ϱlWM4҅ TMմs#Pv޿Lǵz;]/R#Cay|%!fl@+w]łA׮ ɺ:axB0D%^ЏOzBx♥A{VζʞSBTW%z2*8,\zҋv~ '%=k2إѹYCvWL~KM&RwqRZFf&1 KwfSFvء^X{TL5tE#YXAFw;U_,GҖ-;J:aЎu.b&IE~ gZu٥|[r>bOOZ[;5͎չSꬓpme ͎=5U6ͺFN]dnSk%!v#=aެMuwƉ1#ޣ{lqI7 p“'`֚q̣F襉'3Q40W`"ۄ'k=zI- \!?- 7dww*>T1$ޮN>KWUYO'&,f4kø g+6PH^(t;b@͐x81Cp@` ~J}qAl%'ɫe&q00cig*Q dxL(Fn6NC*d;FOo`V[!x‚2?ږTFYA$S$DcILXU<ՂKrѸ|yȪV۫A'5j|p]'9EH$귵I.h򼌃;z榳6@c98LO'ĕ?KO[}cnϡ"kNwm\$ Wc[&` ~T0XƬWw>gH K0; (^sɭYFpG%m-*T\^DTnV$Xij (9?^q +e2HA88Qx᷂39 $Cc\Ug6 WJ4շ <=)P-eYJpAS" %@XRiy VVa%8qb`P߬M-H x9#5_5H?u0GwAC1өaU3OT8R%svgjֶZs;a$){qp-n8Cs犚{dycQ5{T'0D䌸/'4ye̒Oc:6^bԃNq>n1BR)HıNWo^zX ϚDf&FQdα"T.Őu}8nl>=FY]=I;r!-EU^E㔜VY,ce;Pg6TyLZ"H#5lYLIqsY$n"+ M9BmkZv:t/Iq v-z~+ZpId-Q/00{qpE͔*0`Cm>l'KF/\+ NZzsUzD0g' @9c=8"om۱Fu*F15k S3Hb(|>7w@c wJ;⶧*.s.k&,тdeWppp ="&ցTrBǿNT굝]5!WʃcZv ؃J2dےL xC:*:j2D<ѬSD NxprA:Fs1ev~U˥7:|@IpvYU݊Mcٯmr9ݑa5-N9eu&%` TSSUL2@;q{S2cXW'iy6Ήosn(]Vs!6 $Hsng='vfP̱#ؓ+7Moqt5cm[â#vYs,ʫxs+GGO:' )yJݷ*KGͱQCk\KĄ(x'>H:1Ve&C11=kf6y!I$/=7ج<-$C)JO^ݤo:,UX ryt_#%f@z}㟥d]O9^!cP1-젻+pC"1=B:x51 CaD`,vH䞜ǭIr$w@тۏQ \$Җ pХy"m SIV5p*dz]\+ZR3zFY$7xtKQ=~ta>=bX3JAX~A]ÃҰs4Q䀖s2*滆U5;7.7Ks=yn Ibh]+ .sϸ#be_kv%1"+ T2@[=)䉣Õ0ح% 0{B{.n.僖*ysֵ/mre-aapI$q6$H.)x\%}YCm 0r9*Finf{,$ \Stz[]U89_54M̍$aebb|- `#vc9^) ye}z>.inu`OSިm$upR i"oWI#s*9=i\P2n\ďBi` 8 2vXձʾxH|8 sud5o~چhXp"@亅Iu;,<;PHQOVd'ՌSD H2 evOӂXly&Hnhݕ?t)=7GJ]4fݤ؁A{w)${FrԎ * {Z͑G5lSl&z.U?}q2ێ 9uu+O9 n $HGsF~dTrpcQ 9]'fh%Ih!vuڭim E@H 'qΎ+F2|`w{UB㠚O4ő¬GzՄX(GT[fPeC sT2{sޞ~iYS#vxq}1[:oQ>Dy LٝFO9U9 =>6_hI 3l9U-ۜh+#Qc4Z5F`"rAG>U'л5&HK20NpX;$VN3@-{H.RN[$dZvZ֊v,;7n=F>PN9?ʔyt']#^zjR912[" $HN>84])t+3nSbO=F k^0*cv 1 tIi`gd`; F>o)rRr[c37P ]9AqO<8Sc5I7yJɽ'+nܟƺ;V/%Y8xn#)!>rC)#/Ob( t`Lc]8w O9"2>⌣a}j9JܐpUri8.ʷ9ezWOE",!dS ¥8'۰ٝ>t&)*-rOs i< Yޒ= ]G^i#?>;:Ps,,Ae1G'k]fl["w, 浴x[iL4taVriil 瓸GA\Mmc$3ʀ&bI$M,,<#ufBU$n Ҳ ,~PP͌ϴ̳6Yu푶z 9EFeXV2$4N=fb4C݃'=J&Xi06ݰmY"9g8VnR"c Ag$G8>%zV9-cVk[yf~,\ӳKbʼaWu < dyfecG PzS_Vrۖa BR7gTnk,)"?3NF8]9;Xzח-c݄~e'n{Ia)YwNW| {RIv\R W 9JƂv3{tӻg)YG5fr!;]] "1sIDENW83)xlm@7nI0E[[С2Î2\~uwh"5/ur8Q?&2ќpAyTogg?jo*U#%|_`yo$>VLHے0{qT*ZN7S *\JWT!QD@cX,s2sy#+ fqG9}kkwk 2p+tՂ殞Y`iXP|q*)иTG?/~-#"'HR$IzIÈ_wFBduhE0 l,BOO}9Eڻ64>[6qG t "@rzi #e8RX9<IKYl+=dAXh}K'1 ūFoA2WqXy1o$ Gҡ-6&cLmv'kJsM]jfTv7DH#`]T{?hEc2i~ncXXZa+E }q@iqR% dTgTbWI =ٕaا|}i&hQ! Ml'GnCTwi M$ATeo2ao!TeV#'?ӊprE54nab8f.{\ G#|7vǮcNeaA(;}:VA5,w#e M'r($0+f=gJ)%;xAfgZq 0#+o@ӊHf)"Hp6ͨښ%bQ<bKj'snvSH|ʣP*V heqsSќY=HŸjɖPLsNj+)x`O8W*d48+ͫܞGgdi0 īWyuᇭ(bӜGa=/qkI,DRR8"RHg9DR| F3Póor҆WDM 񿨢Yc׽D6}1@yAqnKqn r\si1 NJvJd&vVmL^ig 9+Ŕ$դ5?2ߑ8"4<|t=DO3ѣ)@`#׭S vq, ~u+GAH_=!3~9Npjܺ-)Iq9G0+:)q^'ܹ*NqU 0"^ƛY ,O'ڵn*ʢa )Ҷh\`Ahn3Fv zS4Y{m'  *o)yɀC=}$XN [0Qrm'е}f0BDOuv9,BO'@=3ڜ.bDnT*n0yfI7S9ڌt5 1P8XX^-"P< *rO8 :k!s U[ʣ֣Y<@Y !!yMiz69_hJ(gvӁ1|ÏEIiИ?g<-‚}A'e.n58 ZnsHp8 \ ί;Jsӷ\du"qJVvjE;Xmn`Wؤa!JijH$A{OR]nd!IPp]J%|@@u``BËu ja; z5K}KSMYڢuٱ 0`B0~ϯ5/W֓Z ]n$in4ƆRi 1:y)II@g bySs%w"֣p#6)OYׄ;@# hq*Ƿb-* ()Eg%E$1]|y\^)Z#-c*㧧<Nz~0R;׿ 4iQZXr@%h0[1TvHDrI8$S+KIHv2gfiGɍ$('9=u\ <7@?ZURY.1r;Ƃ\+,aAn_|"{q?ang;b.Ϧ:wE.4IavccۧK`T$Tw/\s=Ed>KL$C"Hmr}TmGM:mZ֡mzm[ڈd >0j팩fb)T=č&ܲTc@ɤy҇J3"b.p9CQCm9aPy9Slh$s8Q&d !Up9RN CzV}o!hB?w!"PH4.Pd(#'k6N'9<*ŞL)Qp0s=1UImQз Y x"L G W9?Gk\#sVB76N#:\8 (N3qӭA qw4P?v۞18$=NmF\G[&Wwa-'V1HǽR]F;i!ʊFB 5b[k#bYX!`sdܖ׀G%ȕDvHpW(G 1SU];Q67f !B8StCs̓E#CpVCdʹ}j%Q"*HǓVVK0C )^jk/y2A[>tܻX9+ϰn7XYv9lÛU'9NJVVۺ5J͎9¢i MOC[\ʀyq {V%}#ʎBK*϶/Glu`z[GENON=N[{8iZ5R~S*u_)}vQdHX?OAZW,"OGCO#jεʶrF;FC)8|>MG W,pp@wxⴋoO"dVKSfobSWe|X` 5徟,2B5QY ~!G*G*CiT}ѩmIBPK}0+'R*N0%i {老|۬;g @~߯zgk+*>#f1# ҹ6DwAm(1 m'$?Hruh xbPܒhU#ERt66Ȯ]&y#J%0׵Ei] wqі Zn-9-]3@"nYֻ>ޑQM2;wVb㟼23ZSFbxmɖo9vzRg$1[ K|=O`?.+6))+t=BWUvnRFA?{R[e6rUv ۩2Eu<ۉ;m*8RG`UErꭂsN;Qrm;P3&#bnx8]Aar笄|XoĩSn@>'o:n}UOÐ?_J\_dS s$8+ͣE&QO1ps,L0IA'}2+{a xf0 Zktcb>+O= ˛gca}?]7(D̔t='R[|~=Z/k-򥬁<鞁0s4pު[ XKc2{wZ8`s){]J[kk`d'@b8ݞIP8' Z6Z9,($H෷qSCi0DV&GFtUs.dJ+OQ܋U- A2ЦdzՄӭk! @ٳl1[Mkg8, ;zf#,|@AG8?jRwfѕݢ@u5 hU݇x1-8DUl6 ?0w= c+0c?SVteyk$q)(<+/ Y]ފSy䎹%NrIϴc͠UO0BUt!MhIKMg gz9ϽXkkH" vHǵLe+r.VsR[xY2Aڳ.82m͂ 668qU;mdHQFK0^}0Jn)#ݹŒakOHWLIitKFVL2 oZc Fi_Hr\#BtSQ!Hf=wF $2XTrA>ƛ~l*mKpdL(Y ; i%FBdcz?Q֘;id $`GzٷGHg9"VRvHΣzYn4o֣7WHva+Ө'>anI<ؕ\G Ȭq _vdg9jw|Zu-i+\-W 08N?ҩ60Ob"vP0;ӎFYTUԤHۻ98NMBZ*"4/A?Zjo1BHG[‘7PnwEd1XM at W&KeK(F2Pmf\`C^VԖ;kę,ѕef21wַty6 J'ryG\4HA!Oʪ1 mM0|GiFmuÝ&q2|M}%o.9\?+SOgK.mq` OE͆T;u-A80jK=8j,S8lU8fJ u}689^-L,^?MT۽"4ȦӃMpvc$`+ _2X[m`͵C ܹ-lLN1.]$dkܱ y1?7 T,֭J rW=Co< ©ǔSFN?ʷvrج$k0FG_V&Y]\詩%Ă@(VrG@mKAByldGN+K]!E6rNr:cRFud,9#eUr;£N FH.|R6RQMDlXo{S?uO_~G#z!px#h%P.G }:`A3:\/R1ǯֳeuq+_8Xdfɀsۿ㚅/3%6N#Z*$\!s@:sMJs/DΊMJsRn0%bUϯ-R$6^+yBn~PI|DГ5JȿuI?0> ֳ`k+EVRO~S%vtCNhgS4' !L8 e w#+ sca#o} X(m\`5Vrc9Sm2iofC Kmeogu3f(2$LʙpsUBaZ))QI-E-d+jJmaie"8#={yaYTVږB8 v҉+«،{4dqg * 55~%tt:^kBdu+[4R1# \&[&YVLX:5}VBS“=7"Kyܹqm9#a:sZڵa "0 1 x<+ Okݩ: HrUuWM8Ǖ6؁\Hت<=Bij {?xz86D(b;:ʑn7QƩ?U+A ,J%YH }[wKmb}ĨA7yV D$K9SrxX< ӯ!do6K$G.@pZp澆N\Q\7p}d)gP@zB0YYV1''TlS,M|>[Ht}K}o*fQaFX] p1epL8?Bzc77tT1TLnk&;ء{IA a;_BA\gt>InKeܛ܎v2:ӎ)V排)$v:|Vʑ9 arFHÈ8>flzrB'ۏzTXX`dUbߙ<.&hel$sCrre9.ǰʐ.Ub2G\=*ط\p9q98^!m ȨT2WNp9⤚-,?0|N6vf,WMP dL@vg +(PGֱi$ JnNfᔉy]$ ⶝(u!+l#(%j1өcfA"@Y䟓' f%4h2' vΙ&3,^BVQŧJ$ıc߆2kj)9ݤ[NV;Upx ۡk'͑b3U՝ x\G'cOQWD .-KcF9%Vq^DrY1@#ho+F*3#t(#''ߌqKk4@Qt@欖yZ+G m=+7Ms)hk)hP*-NXz}֙fT'rIϨǥZK<UNVm3r諗P9 3{O[蹕ы4Q)XFDuP9^y{ Z#y dߒ8<4kU+S!fGYe'8jRJ_qcyefZEhEW rGj$H P#fn;2Zx[2)̒~[lr(8~-X!؂E/\uT&/ 3%˖V 0NG> &3\!1oRN|>r;h.Zo!RBک7V[0B\~s?:vqU6#F ?0aG2%I$NOOnmԔ}8ELb24iB¿gX `3PZ(7nXpPvc׵>ݬGаgrx<⹥NKޱW|"F SIpJ4E8C$;H$cVnw/&-a<ݞEI戞{Lw*#5SNȊ<Q!NTdqSK7ڌ rp9՝-&XeYX ʝ{9#$aJ27I7)X5c6;?9 0޵G(I"HĝHbBaUyqJGh}*6HUꝉԵ6c"^I9l&/~}_PB%g2'l=k^kH㍗ w u\ޫo- xeXs)RW9SzAzxٕ$m8[|23UAoz猍&lf[wڡnv|9iZPҎIZĒalyOך5"V 2~n8+[%omG3+ V짎K H 3HiOBGnjuWv4ۼ%+$n08sV4wo*vtیˋH&P*I'9jޥpnԘ08QcuFPd=Ͻi {lO2B̠p6{zcYF鶮Iך%K,[uhƈA8+ lvG`ƙxo"3(}:sYKް'r EYKJQ! vQx6!.F4H[)wuwF #UgcY\ IZMrʷMv$ThI䅤3ȩ <5Ρa$BV/XG& ac9+m+2HdRYz|%8c!H1[71C) b8J'Ru&֒mfVx̋Pz{ָQ +4eŢ'sF䜍|yEh4a3:/:$11yUcT-D1LH`H:=+(nwJTEǚO޸K<`=1zh %|`TwBIlN sw$o1X ޭ]Į:nv=? 5/%.HKPEtptJZ$ۛQE8/|QK=*ԷKP>RG}jq86`2`rjf)ML=N4u\6wc,([ 2JV!iϰ5\:l-Q<9{*O,1 n$WӞ޴Il)wmEmdeJ 8'Ҧhီ[|4%0@Lf\[&mn7$-PJɣgۄnPL>\&ч\bb-t*)Fjtj7ԣs̉#8ȤY-nԜ{y #}pR:FO9)_دov iqҴ.Ͱ?s@Dg=iTS0\'ɼ+]2άXp}>œ·,vRQ qO^ wi|y{{Ʊ[{w2[A<;PH ls[pEg„U98=WeT|řY3c/IGm 9$ܠq#$z<}*}vHRIa Lv;򨢔mYeZE}rXjkSݩH>^x$:\U H4! B]@$dI#z޿?fS#V2GlFy9IR4K azp>U%[gAwLqCtKs m?1^3AEqњ˹qYzv{6@6Ft?CbjWGvohF$qS\Xwmw^813N8ֵ5ӢH]K܂2.+V9 8%pk餓3Cur!svR>f[ElTIy 7=CvGIwQfvǖ6zln2r}E+\-]NxuR!e8fgnMZT"wm zuF=3D5;T]9P9[hFvO؜Aj J?#=q#b&90p;&͍['C$Ē>no6*qc$q2S$$׮Ew1!=V :tY.A$`~UvUF;TƜTm^ 4IYKv NrFgg+eX/5.Սs䞇]l%IU }{s\t tfi^-q@Y}1FDv1U$vxA0d+F8PJzkK^+so0c`D v=F [9-qbFCewY7uf:rVkD˩Gq FfpY2<ϫc3R@V bHDH>a!Lc= }O%䚄$&2lr8${]rX"rC? 2ZQYZndEXW{th1sOv6VR¯=L5[mbX09{dxl"(vncp:V9ȸgkf`ApOǽeg}W* , ?Nx[Ћ/߰ȫ b5l.F*+షv±Ì!tiS,Zl쌨*Ro2=G8e,}9LID$~pن*2I^IsyW:A:#Vfn툷ŦR0_=B*i.HT¦)RNpOc ƍhȔ67G_SRЋa3F1vAp)JN(5vmyGJ$8VzKg+L }soAfA sӚu/d`$v6m@SO#Id۴dTXR21ۨ"ܬ/m= b9ܓ}Ofk <`”,ANx=jLdvcs'wn56FI 6z{՝GfOSeuULv#2:R1 ,B͟\%B4U!BB (烸UsÇu;3d+OǦkh8GK(u H玸:|m5Jǜ"u'=:sNEaB12UFrX:A"$lGlYvdOcX4Uȩ;auEI@+yo^[[th\,vҺIGꫀQ}s6"0G}01<ƶ8Q篒"m+[LcIvênaAsZwJY&؄Sm^8CʪrJ3wJع+투on<@j$4䜴cH#Eys^}}k\AeCgIR:+r$6;{i%didF`C`N@sX͵$P폖X-U;DUA~w@QhYZEacs5_)x8o1؊͒ ,(IÄd!n70PS+D+IB@Ie4FKbr6nGeEp̏.x@¸~Տno?^D$d m7'Ԝ'ԓ6.)fJ/0=b { $RvaӓjIoyhG2IP=qVtbI&. $0g"^oM25}2\O)T*RF.gkMy2|_FǟcLyaӠEo*\Ԋ&8wG`7zxGF>}U1Z\pTV)#yL.FxUmQ$s>b6B^7'>\51d18(cչOtKBֺ/!Y 88[I-]E9| N#WӪ[^F:#3 J>=Z{A2pOdGch*qtL>k"ORXSNyp8QY'#qgKcK>s; 9=y=U['St`U&;f(dH Z$5{4LaV >"S*$[5w>fd<FAN"u)|#4a#>*_U'dQ 9@'kGPec!~ 91tm$1Ynp0T_ hwȳ=Kpe50 Ilr1WJ۸4pwaM.i;%~%gysm[@v2gk\γїx+:9:VklKh$! d1sӯnx\>ck?5VEi;GEշٞ8 G O栽"14K6哨1Eg1M4WO!wC6^\D}L.ѿ#PZ"G3:u\\zh8Xڴ¢yryɱUR 0r9@WcdΤJqЌ0ڧ8scrog]DV@p:dֵHK)o& >R)ҴeRi63~Knڿ1ze2pOg:ƛ@ J|[( Ukb:H`CaO˷+ZODO{pV]>ܞEj0yH""`aU׏jKh}af"5‚sʒzYI$^r'1ׂ?*涮[MŽIM2@FsӞ=' " >^3ҷg[;*: =+&7.eD[u1Lx1o\Н7wa׀A2n% ?wѐ=$6ˈbzuQOLy9\j3 ۶&Y[@'ֵu!ܨ"/'y0%i&'XP$ʧq#pp PO*Y.U\a榷 I4!#l9~&3'o&M6$,eAs.` #2|UMHP6|rw<,Sc&u^qQBl$)pѳ[*ȇ ^qyOi 2oMV$s~!ێBp=w\Eyl?{s/o&IIT|̽f!4ROmo%SvLKOe*M2n =R,:bגjeH務$z=Iv\"Um)U Y 2LRATǹ6,f;Lѱ-0y'U2ږVO7*:2wn70V[!˘P3H sH╄as-@Lφ<'c#Cm4n#RL9  ~t-lJ^YJʥB a3k*g͒(mwnJVȲm`aAӿJ+mŌQW{H(P=]88{NWtsGDRQ #4GAkgjQ#y$ MS$2Kx5UMl#5=l /L8U:ӷ;i7w8F7sqzַf6)q?NMf!X|@3:8k00 Ƣז+Rtp HNbgeGN*k-!"fQU+[;UEMHFF#?1+FJՋxn7۴ `q֫I4dRXnutU+,Fas:͉mͱA$s{ zc菺&Y}y_\!8ުIVe]ơxCbO.)F1#¬Xr-AoqR)#׍{{q[ +I5ն.%Q211,rEYuOlVgd\ $;1UmiRDDVlsMAE)KAXM2RVW;Uv`{5<4\ @0r6e)^h G|j b3Fђ1k*Ou<~I(Zgb@?QRL!`ʜg?9oE%sIHD'rҕGhBPe(# A$\U+V_ [4!%Y9zʍY*Ly}RLm9aʚrz6 hiOqe'~=k}˛,'?Ҥ{IJd  ޤxXZD$4}U"Ч+-RIu4U$S$q\#OU&mOq @w OV!;+؃q0#:jϟpdXv$r1ަ 6|3Ym.'VfF6 ؓ ѹhn"t`nB&BI:r"ͷ;zVcY>Ynd<`דҍE(6Z(λĤ#+jeK GClCଣ ȶ{i`5s>￿JɎ, CsJc'n"̆k@-ぁկ%H@:2_>uڋgd#یR$]JB<ߜճ\пbZ0UUU;wҫDaV;.XߦJؔFA#!f?CNI# $lU~F2nBijTI <#'?ɵV6! SZ.""P@`FX|jQJ-ͼASǭK:kN4y۹vdcyL+/?1]Ӏw.r1RhDYL2aO_jU~;c}T}κ #rj#%Ƨh˽GUep˞!Y]\[qK.WFI;I2=֎t%x" ȱs?+7;w6‡Vz}+>,Иn]L884{qpnĥDwStb`&ܤg47 "I9HV 0)-2$=yIE:@%:#FP#q'faL67{rUՉ+ $vQɼ?tΉ ,\+DJK6|]=45S;p~lNPk,6Mg2'Fv\^];IC[4 v2>߭(GC8odO~Ζ^HLr6Xc?95ejqd srؔ-BHIʴ{UHlNXGlU]4jzbj;gf9|"`á2v-VLvjI( RGH "hKޤI";F:ѩZnȧkq !"P09S]GCe n,s)SY)㏥Kb Vִ%qN=>\ŲRs׫t Y9"iB@blI pf*"9L{VNz.徉^NyO7Tq׭sO_|*jΡ0eP՜\cU8rǹ|%DO1wtD*ym3dC 1Wwx Q>]27Ldw67JI PS%k*V-|֫i"В&Tͫ.KnՆ E+DEQӮ颔QOS*Ρ7 {+K3rϊ[{E$xx[Mo:E.ww 7yQ)_+$;(vS+}NXN/U]bQ4I2/̅z!Sf I Hw3?҅~){ 7Lhyy%'wD~LK8AުM8$A39݅=2,yYH|JJ!3l ,Dd#TY8>ZܷYUa5rm Ct!Xo\C"2smHpcXPFJrMa37G2ŬfԂq51hvT3H[+F;0OO5ڼ^l˸U+ҐlYa(΍Wm8 1d-UsZmp$F~nԑ- QԆ'MJya 5/hQбq,0N ."}Vep94UK:W;7chqڙžb,v6C}3YİA F*sqqn4|O bG*VM D@UG[{r@VmsYO5h#ᕰXc9?Z;;e[H^4|KO3QtЅhfqpbBK8m?ۃz9'=K Y6,mIqѻR:A[GU*晥 <\w`68 uVuVّ+' JQ/Ʉ% W\<γB%kqvڌ2gf`/7~Wd;3ʶbn >'U[G&^,0C@b=9?[3bnI*z`}rܿ?]nX#39WZXl!MŹd}*ΑeEhPoc힙kMaOLuPTјEFCuiRv wNKelɵ@rsV@=sNa~f8]Y[={*r RRt=kt34<Ԍ8"qsK|qHsJO͊n8e^L!nx;s8XU MxdSA9Qӡ<+hF9<q?:̿ F9ݟ>$cvёsLn$N6pd;~L4R\6m5@KHa~6$nq\N5Ƶ3SxVY]{>ϨNa]-rO$ʑMVeqq$3{;ZOn4{!q 1 ѳmmWShdtw9D*Tic8E0)' nB2)dX݌pK8!'=A#*&E?.'%R5d1߂I\ @9c'sǽVdYh)0 ʎv[lEACPq5|ɸ|z8ivndUq8Ki9,c Vc}G5xvBҴoP+':W'Ty8'F& 990;+7Sݶ\ Mi<{"gA9OzK+.-fYApZ&zYO0!C& ,Wsǡ.KX埅8#8& *ۺ&.x[TlUfGz͖F6̌:q:OV|ҴM 'ۡj4ABvg=RYIO록:FG jI26\ ǰkZ3m0LL{`׏ְ~o_-_Ƴ&B&dž'k0zǸud֦sZhm?āp'?_ڮ3mV7R\2JchPea֤Y`[nWUQFB⑬un׋q{`[9?H))LK(ߐIP1אkE]."YϚEUB3=N0r)-n-> Uě3H?/\j'䕑YR+i 9?ZѴY&Y5q)TV73YA&#wrGLg Yi̜I*$ YbTRJ}MV0<λ\":Z|~ '٘J7,xc{S\F{4]෶ON*6M$jҰ#6Ұ\F]jqyrv*,ҿʀ0GPpqV(ȎHr5É 9PgILHRw( eGz|#E(ş*Ik|2+C<`PONsҰ|뱢bg][Mmm<ҧ{X$?y~Ө,1C Y^&1 p9qOZ-p.P>΄awݪt4ݥO(w.q䒧_.z6U)t--.&X _b4UF}F :]ik%@v[j$p9Tyg']C8HGa8Tn]ڴ cy{cYFPƴ6XQ|\#=?Z1&H4rT4溛Ljp=3ӊҦ#FS1>kD{@f p*EXfS$J3|Olgُ"8$ܧh =iӯ[:vwl ۨ)-@U$>mjiL3L\n-9z*Z5f,8=@#V5Fص}@e\'#~"fӱit.XHn_NMf- ޏイ{d F𿙭 m !hpv,I@ ?V̹^\νyέ$c1Pr:ASVlΑ ( h[-s3|u'Z -dV+>sZI/OMS<KVa qcڳBG<2EIbS?( [. ]Tm$T1luga2m%PH Y5-<s*ydw.G?Mkj;6@r@yێ++#pN3d{Vr1NHD@|N Js9F[bT,HGˎvO^HqR|utLh6}`z5J w3?js''j.&+UUNK1MnyR YCu5e8H`IQdQE V!B@#Uʹ~ů2AOk3ė jCn$ H!'8xJwN扫[cfCG*唪1?*Ń W1bsjN8|}ЫӌWx d`T C~Ϩ5\ьMZC8lyUi`ʿ(O=m<t[K0r ydB4c1[fa?mZ$rڟݼX|O=Q9b|| >鄣##.k4%'%* \ɔ^tdUwuoݝ •^3Z؅\x"iBFw[8K8د-sx棢 Z~}ڲ6an@.\r:$ڶ~[E $1ˋvRQpNn: Tyi>B忔d#H)F٭#FB(ڄ:_Rq" b&tulrGkF}lIp)Um :ҒԹU>.-X !;p}5zbp$J)(FlO\T6Zy8w\ t]:N8k:6Zu&uF421ԍfFT(@Z8?zXJє\bpIj! 梐@=:S98|1QetV70@{l2${%H#C&CqJǒ_C=J۳LKG@ 's#p-\|;q09[W&I .Eq0yk5}m-0V'#9;vnZtVc6^X>_ۼn\#߽k [(lV*w<z⹻-BZ%̃n>v}G̷,ya$R}U>=(kߡwfM9ېL`dn;b.'%krc6$u\Es)V-8_GomE7|K{zUE_`T7c3#:ZY a0z5b Xcoznx|5֭($AH vxz"mk $'< ]O I2JPo#tךٽ䱂Ax6~bYc֣{H⑞ C%q3ҴlBLз2<ʶB@b0UGZ̍ck)刲z?jLeMo_==4βcv˙ nYβQN!)!7Dvp1ϵXHkI'*MC?P]Y]-Foqקɏ]@ d!\cukIY]heB7 ;dB@TZv-=ʮSݎxx-TjE!Au,ĖO?:]Zk[24,̡NޜZ஬R{+?sʯK, S|s ?*±ʑa" c3r3̈$M4f6uAⲯ(;ʋy[HD$ߌr| X"ލAXu'ՍJnl P?ץ&+l%R%цȈpl߻˫-I"G[2Od['H%Ko% 30 /\qPZ,= ح;@lma<❨B )-- $ĠI(Mɤ*2g[ʖv c=Y1PDZA Tq| cZso gN* ] ex!>}+ OB:1sHۂ坁O g/Tyf[{s?vCqV} ncR"EQFpѝV 81wYiX۽ВոSp{ h' &قUsTݹVW;H8tW;"b 7Wꨄ-''QΣqePRw'=J}̖l1=0;K2mcx-b@=W-Cu #!G,C77RVѸ{d1b(CL8=1{WPYqhpH2?UKInLӻ~F^tAm3DYR#`u EBMO+bV=Jl*B$V*MB+h:X|KYeY,%$~H$sn &ъ0VvSpaye#*b?Vumd:ܮvZBZ_8ǶGV[W%#؁diBR?#Y [2Zv l4 ̐I}_1wX rG5f9odl{ԱB#FT TyWbyAogkh 66d<ǧGuwx`~Ȑj]F)X)"9R0'ۧUY#:zP\̪C :tnM\TZ3Ji `L)Ac܂?*yqZ8H&;~=5Te8ϷR^(/q8+Sл)7ڠIlT nFP3>&X,M6ma9PaP$88hծcE?6Ł#]ً+Ǿ9`.1 x"5Q$I# }$Y܋8nY[%٨KNK![X??.{{+''ΊE|(|sgU7JY"cbK|dKGjfVEn{[~%r2m8N*-OK\CvDb3#E1Y" *(8!H>A4&(O!JVk,9L<΄,r?®#[(Coh |()LIa2(1scּv@Ao$IqLXHPUō#,\:?KA'fEw:(YNCʁ70`w1ֵEh[쳼 ϊNIr;{2(|rdnS^GVʆ;5CP<ƬFcw'lq!_#@~b_Rz*]N{f8ߵz޵JY%7 ŸInkVX3 }EOi^t#(s7vE5}D˅Lw_} nwyp{ڠ/pZfsqq[Zl2Aq>SXxIkJctD!>kei, "H/ז#_E+?AdbYw`(մs,HzNםkQ}*dⰧ%-Ye3F8q?D%E*ˍ͜NT1EW|YJld"JFZtf|4HICVLAwIH/$ma~_wyT)G?ʮi+64|.[\udi}xm[~*{Ƃ8/plְHm]J/A8G_rd[hIV*z2jiTb"a XU i q1aԊꬬm ʅ^ՃwcxSC ޭp[QFw{fj1l'ǥfj7̛'0й #9!Ԯr;Ż$Hgw<4pGLo%yrT^ ֺcFRz..T>duET0ndO[;a_!ۏ^iͧEE%ɸ@ZP*;Mo.(Ls#h]ҼDGn| :g)g)(ȘڍhHWg8+{X[64VЫZ?8s`wr]>P]c.¡A<oĜ"tHciX ~ONrЉRBx LxIvѴh]n tϖڙ qp+ˏQ8GS^\ k '֓Q^*jyQFyҴ)GjF?1,Aq^J+MJHM6G*xVŃ(uy=nQC[ -oev+y)nV?dF99xbxFwy|8=8I{KG6ţ%-2#.ק~y$ sm ؠe,8F{fxr)rfyQ'sYHf|ݬv xeZY+nT[Oy%$nyةQh@">\:th Hd!x~UMYتȘH< r144dlKE̍Pz#po54hJ :t'gh']X4@}^O2DbF! !x9ԁ[Ԋ7T9DY jI@`x+.]O-'yaƥD~dIO'>K%Tb*s9S뚗J7U%=bsq *|zu} Ym>#^E8,9_dl7>iWsB'"S,ĩD, p2U%d4syOYX"Y0Ro @6X? >tK48E{-mɺ(F՗;R FmJwCTn-&&'Hw#Ɗ `uN*۹v~a/'=0M_u-/r][U{ !dO.Vt*4]J?w'F09b+2G<ƈUl HM .ҳ09cغoE/!F *UVؤlO*ttjM5жJN %Yؕ8ZM&VTfTUfxAҭI WAB@p9݌w8n;_me[k 8S b\_O :[O^>/QglKhs:cݨ QJ#>DA}^GҋIYjTm/.gE(ݾlcG4f{O),=Aj](7J6.zF}Vm;t΂8`o2s*UFpzb"wAom;evn8QE北%đ]Ueݰ^9TГ$%K*_ Eyn'tt*ŢKJʁHGmQ3I|[Ӯ)Y R!v)iwqzԚdmESn0W֪궲enu]*4bż3ltF(*>CqX$g%fUa,kczOe4mZeIWd7kY_(_d1ǸjҕIpȤ|vCt"PB~ag`'Zi'l nLׯjQÅ5k"k=rRr6ppTzu~+–$g rEU3Op[<'<ҭIbKTJx#מ8Io-Kѩ6dށāe!@<j<&nmɍnq׊k:Kmy1O<.̰bSa)0J5URާA. y 9WDn9 -zfoO̾I$`c$?F&jH(T*RN5KHd#B.9#ZfJWn(<|x92cCLwiI 8I7gF@zڍ?LRQ/*U(Ȭ$UA4|T6<3ܞypj̖Eh>jqЎ+'ndsr{ cs[z k$ecO\sgnOpt3#LGuie 0NqLgPZ܋x 0}21~A&vm&xj0|3U9")$.C'y#%NGI8-DVѤLwhZzBK-ĢFU2fn$t8n{g6A [F ogk4r"z@ .ZOT3vǽ7^s(p1,F t\V+䷅ @`t?ǧ X2ԾAia9'puZ_@3#@jIP0rr01]*SRo#)z⦖8y@sڳ٣t’[2ܑg+H88 W*<䖄 dϠRDiqRx)3$-ɿ6ʒj'N`&;v{oᔶN9;*u-Ģy\mGIچUWݏs0TðW=SYH~$IJ:~בջ3 ح^) rp] ۋ9.c|?\nnE"ϻ 1,<6n+`G ʓ|Yo**W|w֋#yc[yO*6 sj^CA11u={jtyMOWeœzfSvh-(~dw*9>Xf::U{V[9~?09g9>bRj;>0rA[5yDfFYX?+9%qVk44 p ܬ Ǡ>Hl]DQQ gp>V椖rB%hEPX`|د;Zt,'WzNoOޱvLoh,]`sYW,jUʆBdSN;c׊lZ#hUX$`#;{Qmg6YEtN}:Έ7L.Щ%0i,f9^{zJ^<ѩ`:.u2E) =;gv%y<[=_r]dQ&epRrchg;Ա˨=8YSpj.`:T:&wC)\ns{{W6؞ u,e# -<2eYD,)]H?C͎Kje[ @ ߖ4``X~9*Yqq|duL ^p'.i~naIf`˟52==<[4$ݹz䏡~R2+I- Ty8a[xM#H.䓐vG^=>gݕm wQ {T* v5i4aCŒ0J]FOE9O![׺pHˤ`LeO N>iCMI K>deh )nn6bpyҪbx.ZYDo$du8ZCj;)Br=iJqSWGKl6r8VJ!Bfs{z7Ǽ48c;^.od](O^sӊ!~di0@$(= )g T.*X 9~N̜GvFZɵ]Dmn~TF'#E om Ssmww?Zi[ F?KyI9#ED@$J3Io#xp;X X)'},Zo"wٶadlퟯ?c]۩2D s5`P)oͅ }>$,ź(VV9BqX5(F-j[Ep[eU-pqvzjC@'qF=ΞTgxp0Ǯ?:׎C"nH {|u_:jVY;c[hT| B@<*y$T@ĞPBʜI ZOڭgD>bq~T7>knLԭioڂ0kf|ylzc*dox8TZɗNV@wew}*)1;[ȼ>Ug9}iREch]Đ:G?sJ.0i+.#w ,P\GK|b]^vVVctU+9!2|y*OqWc$ HLJd n9'$Y\"w y;#Q1zԖnM ˼#osx14qaAzgEyi3ER\UYs$NQQF> ̶fY2<7FIM&@Nj[#D,s [5%v'+2=bD ÚЊ$*D 2鶺;$$3x*vʫܩ@q緽8_@٥YIf(Uڹq}H2խrBU~U$#v($,%O=֤e cbVA䁂3ԊqU. "s^:T6!`CA<5nbKW*WsG$Mn, %{o s©Jwa5v eP"޴NdXe krNIqڵtq4@G 6OԊә$M1ZeU  zUZo1HBGaU32NL,Y-}2*7C mā|eR-25&{xb3RiQДHQwgJU–3]ZA՛arsw"٫L򋀸 RGqBM#Le~?tJxhn*y1m>!y\Mj(`)?LBչln$1+8U0,apwn$Y%{k)GR]&پ LXmVzvZKA|"x)0d~aF:ӭ#|>!31/0'2YnUav)t݋+aP>WaGAJF@# YiI$(_0O֩ȣ_`8ێqҪT3'KfIsesn}:iIOUO_R B H 9'{}i[Gj~U2<$(I+-)"P(32G&@9=h/a`!co'<ӷEca:Zʢ[}IJ&PNT wtfJns(h۔oz^aP75G.2o缺2Fg ÊIx7N=xVe?tMmÐ9Tö L\;K6)mhG&A#AR+?ҥ2Dzg٦c]a(\S +ɲ](_,ѳ2cin# x I "(~Nq1cխo*itڼZEn,6DnWjH}TgZf\F#JҴaln5]mjqG qWsњFV☢ˉ 嗦[纚~cyO r=v^$ƛ̆@A0+]ps$n{ؿbgUׇᄂ)}:(^}d,yw5eo@Nr}N*6)$elp1V˴OA]ehevBƶ RtjyYjP(3;2 OAUuXf>vdr}:VmR%ju6We(V0SVN[{ڼVh zxJ2P QۨS =vJ絋o+bzmiͺ\g {cI 7+"Db֡PVyW=wS;s^\F b#" FR%GYwHN1֡in%>jd w<6xw[XP*O?9)lo4~F. !A[qysƑyj,7v8`A88Wmom*iM*`zv9=ziLsD#%Oޒ2pXvS׃J4|ƌ@R3 1Vpѫv9'LXM̢!B36IiVnq9Ltr*˖% 20=(V ]8cu#4dӮ$"(ɖh- J)6]L&adFcIsn'WYi[-Hے=QYCW*2s9ROfH%xT6u'q*R=[ۢ1tc{k1.#yUZgvRQG(>:{9c3otn݂1f>)IEqkVeZawk,p] #!1`l? R{hFʖx"fv14kvsϥM.Rbh" w*ԠDWЃnҲ8׬[ijӈ9*?tfNU]Z"gd]OK/sQk)l2p݊2O>Uv+êApTۀG<)P"{Gp9>*̓<%ӕcX8{*$n&m]K~ChTa4{rxm=T2C۬q nFusW 9%FACAOkRr߉O\[^P"I6::ջzKT\1ϱ݂\w$]xG{OL(dmUSiYwI܃W-1 9pOP{ssW,o/BHPIÞcAs|dRRBg 'GA(^k,)E*F^:eD# H  zigwyv(eY8x~qIs2Kh`?gw, ^$s8 w֕"5vE_Uzc e`T#G k޷Z4B\av޺hL^K(?/\v}3YQBm3s~$ݤU'tIqie2(Ź]H`H?\zǹMu!N2G|Hj̦g@Jr@a=#I)Hn#䢫~:Z[FrIhne1+9VwSS61}S{;Q[OVn,CzAox"Im7Eapt5Zud%a;<6d]Cn#j=Cبmog|J q@8zX\/xV,z+^Q /Bs 4qQ[0NGQ6Gi<6̻`pAP?w8.EoC xcޭXG&N 'pON4u;?A-lvrU10s;>ԑ^3H򑘥"[np}1SK|8'qk͍4-U@do' n3>I7k{$4%DWyϿ8՛\J|G]۲)-d{W+.!6*#`# Zwq'~nƪyVE۷y+kEg: tz`nu`6g]aP0yqsM lJ-ED='3G%v%U7mF]`n:Uy%ռgS Jsn%[RyT$ogެu$HM8;c'W?~7NF!wu3 ,G>ҖYeWO1ñU }<&»4Ls6g$~4iQHd=!Gbw`zIjȯdQ&I'jHn%xեV9@@T빈F*q3Y"(dArsNJhVkhap6!mdDFQ^I3\Bֶi!duUJ5ʄ#?e[Kf;v8mnkGfV䞇Ӄt9zTftb0CtVs9یv{U5( PM{g8o|c+=n`39+fݵ{J͎Ӡ"y21A;c<(c , ; J DO._-1ڡ z)ۋgXݢfc\=߉[R';+$wn>Ͻt- gͼpKgYz)65UD#*=q6cB2vD9%`EoD2 z"lç`mpܳy'w>\#.gʱIf?D9^4Y|B򣜒H?kǠQhc&;kybITcnqۗ٦@K/̀1uI5܌PpO=82%;^=AF}n#I6ñ=6ZN-=Kmi B"Ta<4ReX\ s< [<Ā1ۅϦw~ R۴v"gd^Ջ+iۋO.ɖ#1sy-x#oZY<5c`sp@w8'O(NXc銂[+b)'lH2caAe=s[S;K[ giF4[+iՕczT3[%T2T)Rzo_jK;=7W}،),H2?tzHyD19P{mR'$Ri'rОy$'51O$jB0N9'u vbYa!zg5^b#Khуf@9'*1Žm<1H$ wmOO󙡜:ٳ :T9[X"F\Hc#AN$IdqkCU^piE]5eom#ɺ9:䞄uU#X˘#l  Qj]Z WDm!9j1I Qk$q͸{[/}m+"-J72r#Dw3F QVen-@T0lÊ"KK-ZK 6seF-W| Yd`] {TB3pn7osi!!X";"1v@iV7%IͿx+ ch3Z]y)j!~e;\wٛ&23meazVԪ^^ZȖJP(ĩ8Zzh%/l2|ۆ{z 6WZh֤nZ >tY82TUx^tʊj.mΝ2m[` =fg ^Ć?YQ4\Oq.ɘ6Ү[c<nwƎni-B,˳1j17g%Fj -w~Uj5; Dl|ʠs,%4Ty#!l{QNsmBVw+-g}dB)œ rA=ici$(,{sy ]E /C+BxKRd ua[# :S3VԴl]"Fb>_ sZPtolyeFw9┫Gvo5HcSO ųL  =z2[^M-U9e!p; /TmKݴC͝7[KpO[TYvŎ_;֩%Ž,)}TrIJ7kB-j u2ƭ1Ee,:v˰/ d)!<n@WߑfY]IEY@8P 7ܑj|Lpz]MRs-Q*98r] 8m#[`ݓsU7>]/B˞W1OC$$N"<bYםǯZhmr"w#8SVYVF WI-XPFua}*`6ځf@ǁ?i7duco^ iu3b[G Ďg֯/vX3CMZXL;^ ŭQK 2-MX[*;l%֖"FvaB;cfT `~)f8̏u+Ř2}Ԃf"8YC?ZHHw׎Q{&]]B~Tt՛:ڛBY n`'DKU(.j8'wG֥E̓$+.܊NVfjWv4-/(_)>H-UmA3£`[ GhJ=W jN--IC#ņp0}xk_2!6 霮{mn`eJ@pG>K enF2zW/d7B_5.rSq`{jv2$~=*o7Rp\{mDi1mYvsYM4iżk#vϵ_8&S*n1I`j#\oOQڏkrh_ZjovJw"vX{TB,~`Y1jV[Mg^y.HO9F!tV)15+]MCzmZH% gs#۰,"@:7Zvcp:nz4btf|zeq~)RRh's/a.&m!y[j$ؼa䈍ۏL r: ͎R%!$2mNv涴i&iDJTnݘEDvP>LRN@?:?#9!C޶Tl"R ?\VeĮeXb$u;:WwWW9'׭\A! z4'&F7"*Y^3 c!̋6OOU2zZqX3(܀)w&.͋i%LM+Idl~'[RK1C{Jyfr1eU†=U<PQ^%ʮ7P"I$;=Vv:ppTYfM =>s217/!#).=ZsPevc>m2Y\!G?B9cþហ~/aB\2 F@nHiܪf {~R+!?şOZOmASF&b|p ҉bKo-urçSܓF\[ys D5'Jyt03ކS'Mɻh zgğiK9@W'_4{tF$-<)8zgڟ+9Y2F}68")n\F?+ zNkN5_,MZ4MI.a!nkYqsϯһ2wR+ĉ\]DWn&WQK 1ȍ $ew1OWNY@^\bn@"nx "٩˗\2x>gYG%Jr}}*Et1vA{jS^}4)k$7Bxd%} g҈Qv Ŕ&AP9lU9Vsrۘd)'[J(2cR/FF*Ty7}MIO2EmZ|󪀣ޕ'RLsVк>2422Yچm*%MrWST@9,j46=n%j冕\uz1]95_5p$Gl s4bʠsޣ8ֺ)AS7-G?.G9DbG(W9Wt<ۂ4* Z'f Uuq0B\JUr׹=jK@JRx9zu(.U$C(z3#@ 84%kCI=jR%PKnP\T@*\$9⠵_>u9=ʹTd㱧hR/ڣ0#w$ }+2) 0 㸧st&˖p+eK!I8Fc, 缈hۀ,2?aZ&7-(`u<==MyHY!GlsܑGQ^mquP8=Ҽqjw:lfqA6\|YpC=sxZq(bMhEPC2IWn<<`tQ5j(P"Lq#S-%eVpSR>·ʠӁz!H GL05IMF[؜ OI3n9$SK ^ ef<#pqQ ppG>32+/'̵Ei04qrQDBݴdQT-LF\OH󧈩6ٝ&+Ə)'i c*,< yj=jɹbDAgA=qI̋d+ m>sIի'9œbmJM%ԍG#*67sߥ^qe w3rC9ږ!$](99 Pp8P@,F;9l-e7H-4Lpӡ_J$-G|72,ea@NGUۚK=Z D`d}?)է_yHԓ Ϯy=Fkk _\Jc/#5&' ?xS끟5C[Ԛ{-sd`T{~5P+^tIIIծԬ.VY>l%xlQߞ/58n?% R_vItCeķI m\4P J ߎ6$M9}|bV+][˄Y duo$5.YDbD%Op~@<p\BNpCrƶtϨ[sR&%ձ\0ru9mh] ֳo7p߫}y?Q[D,4npgsҧc aS:HU=Am{- v ܜ#kjejIݾ^U\:g>Ջo$חo4D ێw/k>h 9CUvU}K9P~վч$AW;RC bXG=}꽅w?ID429hh|BCyr {v@ $܄<O't erAW89:φK$Ol¸F_P6GLrve_1ǜlv3DL%u-v+jeJ FIpy0(qJ;2# qfOp:I[@S Rq隕.gdXW&q C;S]e=/: K3H#p[9㎿{Ҧ(][mss\r%0T6_*$ aFfTڛr9!@LǞEH]3H gg*pờNJT,,Vǎ$UxRF*.c`pBGOʮy[g˸.Alg9ϡ~G/!RM&+s>+ {]^D+_ϿGg|y01{;DO3C7;ǭk*q^n;JPqgBs׿]it`fo1 zf5KyoeنFͷ_b86w +F` UqwJ2rmٙo㸚) njsȵ% y$"сӑoglΨIA߀G98cDm o;2@=`{cޯ!.i%]{G=Ul˚-ʦˈdI݁z{73LB6d{]oF1Vf>~5zĻewBz։e)Bt7fIjbX0Eb$&)6720Qs;w( Zj wv)" \o}j)O;7v6͝y.]K}Bc>\sWBp˴>=Gsҭ%E%#*VnpV A}e۞c&d Np}LiA2R|֖ºeu :|\[O ܖ nүp1W$jQjdg".3~#('I22-#'s#;qԀ dʲx BMp#z[T2J %ҳ${W"p2W8z1w:ad iHfwD30wc?q*Cx#Vpk<gvݺ(C)`Rp{SYدHҴB2m|hsX[\\̆{3R?Kܲ$ y[2eI$EW0LֱJ3g;<GI +.|u;OL{ʴe\cmCTN@Xr猃+Х7r,LOUwk6鼸Ys zޝk0dlmذQ#;,'Dk3+EBy1Gq=39ayCq2ʡCdV'wr I-.$sZ|=e*Y+`9 ~~TrlKbS$~_k :Q*֔0I+5 '7=XcG=)(<֐ȦVEoq:ڪxMHHprsu,p"=Flق ڥobΠv6+/z4LC ̨q,Y㷷o=~EZtb0<=kiTeCYI>ԯbL򵵻t:ˌd:PiijVF0uScZYV+frnQv?yXRq]l%"K8d8y~d W nȄM munOKw:bLڔںz#dTi-.$*c8bmXAI̧8r)lHU,I䞃Xv5gdb@{J>7[7#[ߋ<:.CZ Dh1#0<`h7r5Ԉa9a>,j?49ʆZ7H򲱛nx'(Qo!JѾn--屎dSyڠ`pI98oA*C8q֦M](ԸڲEIsp| nR02z=nvل3cp$=T7rE3²yd`F-ƖVô9X R:4М)47b Ѷ팠YIVk6bo9ue9Sn2G1lW%8Z3D$Gˆ–$$%U܈LR(^1q!}:={ HOߨBbLꪄ\zgh $lV#c+GWJ1PzZ- F!t#U_SxV c))So*` sNԾTnGx; 7x썻 cV` 9'lK v\ mܓV\JvSS9 LGI>`GfR\SU"Uea ޣWͤBw9*e#,ZvI#M ܐxJTb wm`ws)p.m+7[35Iŝgi\)h9v\fdfBˎ088?kiQF,̥+*R mJK^+f"MUbNkBK峌.; lcF!nY76G|WsӿZb\E< b|}YXV#+0U!jya\ @QӒ+]=NŒ{hLf %>^]܁[崹(AFuU~cSTd( ʤ ?t?QJ{yRǴ)Fvzcy#1?@%IA;&LemD0TsFws?Iʚhȳ1m)\a hIʝS\?ck$b99G=zj7C[ۢYr~+$ȨV;noZiwTB[`x[VZ$ o<_CN\b7ؚݠ#Ĝd~{WeQqU6B\(:Vy^xfSylGsU庱QiKSnUC3w SyܬHPj{^M|u(@vG#' d}k%M6{\Wmʺ|}O5m{fp6Pm}E/yHeDs-ʒ9%wHk|Q" );~9K׿٘ 8ݻeU)k}%y2w=27~EMn=:~55;p1$ "3+^{]mMwesgƟ9YAFcWQݸ+SQI!HHlgdgޠL HUO8݌t=qoث.pPB̿-IU#v?*w4IцI֦ӁyU]Ua $ *v7k ۀGq?׫Q'in,ʾGPE%//9#K3 &A!<)4"\vO_ޤ3Is S.rOOjPts ~0~- 9„ ~^Ρxƥ DT j7[#cp_Z-"f;"T$0Rī+['ykĬ0^AƘ2c*eF' v4(}RB3e) .WoIO&6heރ ^LVO+7X0)*<p3*xڌ/i0wֳ-@D rrIKcF:N[,(FG:TKRV L3بnS~B.52d,kӞ[!ldyj3+.S2R$PCs5;&vbI1Nb`[ձs$ mnn.jSt6r]̶ ṚdYZ&L +Yn8RFl<]4+kg@HToC]W0 IXI3ۊeȋ~P:uVy);'%L#7,5oYsQ^]@"xn9'}Nbzǒ2%˗mNVYG;Gbu ۈg4̋>` uO:ʓT,I cSV5eItwۙ1ȧq9aD,O>TWo".nجd';X.@#fVf2ۓP0+ ITwwe,YՏajK1\' ߴYr> ֲ}h { _l^=KińYRO'ުP,y¦v2{Ӝ.Lo $x'үG{k$JF On-Q`)'%BKV5b[xBR?_d:RQ+WteS{_R9?wioƞdUP#8ϵzTN[ԆPVDG !2sZ"Tg͎nOZgIc!U3r=?^mzZb.ĻUB2i͡t*G#U{?3] {u{WN0\hjhd |pUWʌBKrnېy>ҩs de$yh@U^zwFʝsϥ`rZgr8$Ֆ6[yI_1 x8OaK b:gVi.28Hgs od\D6y#񏛌\r_(fh_ߴQ9 €=΋åش-j4m.qO$V"OciK1$cb nO_2OIqOqx .XgBkNwf˞!+&Bx7u\uIJqoS돗9pd5D{x?$Kv8Tבi<.buϱ#wE"68^a%J{r{_1RZ`RA"ĞY<8*xz{kQ[bosW4Vy jsǠ'jN ?Q]'>\q1iCO8#ֻ+l$($N^MϪPZsuyKrM2JH1k~TIE55A♧L i#GH,q+[Qё˜IFyǯ)m)-hChQנ9bKn=?m^Ehaf ~V/%Fs]jҌq8RMEOj`XVFC;FCǧԜU/'I\IsuZҟg%7og<Е<V$kMv-wctu8SINZ$gY+/<7 o$\~_]Gy X<1Qqd& jO&sy`!G=;*%J2v1Nmw 6g8e=@=uqP@>?J:jN4Jˋz֜O,D\>\zG|NR+\- H;[q۞*rGs' b̭Qv`}Cdz`FL_sU|0 j8FdQSwi2Qo PJ4N3I'zl P(7p8#ֺ5c".Glw^h-drP c>•H+ϟݑRTVXO5Qyk#&Pxq?ZƏJB~1]"L$ *d8';Vݻ=Mi18 \CVDΞP (JJc3]Io*“n#"RD' 7+M@37l3Yl ~U!-x6A/ 2бԑڲKŇUKxdeW*Q2c)9ϥoV t)P1V!OG{86,q43FܪW^\"_"x0ptzbdw'Y9ǩndOrhڻx'cUEhy)ftd"Y nW@^*3ZO[yYDϷ ASQH% C'Zth2Â1p|vu)QK{alK&y8zdsޟQ['>bd $)=N~1Y!QJ++|9{X%S^_j nϠ?MNvlL Eh\ (T< H#5VI.4QCmԂ` Z~,nmFJ!>:S|S͘R'#AߥjrFc"u|N8P2Z t> >&;>t#4󼱲dvi,Wq]=qJQbkʭsO[Xlt*ʒ@zWV)Pw8kCQ|lAdR~Iힵi$6u-`KGn{"%!b"Eƒ{KCvKN2wk^ky%Y q*pHd}Y~][wK`' @ poj{kb7E=2o:M1ysI%% HNMt'P6!ʇ)# >`ԦL{U>s]*mU0qOb_iྈ.c_sĒJ,X!HݵFO5y N& +zWc]$K Ct$SJMs;ut)@>F;~cߡ]Gm>7⡻ aa :t$U""d!$p}IqԫZӹ5wq$r;bbp=F ۲8̰ں( l=;je$4`em8Isk*}dI<}>cwWU9 nl:U `x`lֲ<=fؒIZ Z OnC#khYbE?†L9ퟡZq6/ͳp85If C [JrhDsJ)bIQVwЦvc5ssdC,noP7ߚmn">\mO!}JTTkQu{{U!G=9#8Ok*}x-Cc?nƶBhس3l%ׅ'GT{3 DvPz MFi͛D-wk݃d2nv8\Ι|AUʃK~2ZxTEP!I I?zj+fcK,\br2Dd?W58ҢIU%9 nYlr&5I<71&!UƞY{v.ٟlN?ukF ,ȋ%\@ 7RQ%wvc:njѠ@ʗ0bP_f'4TwoB%lhؤb}׬/ qf]Ek3AQix֬ۢ"'-ĝɚ\.`Fe0{'+[#{A]fYvKAVƑ O+foڧ۟XvܢAZB,'h$d 40<<MC"дfʭ$#{qީKl-7,+&aͻ]̲%˒V ~Q_Xy')l!UNI@985ۙ;b]Mp--9GpOzެiȖ]Fz [q9SSoJv AV^XM f*r6޿xDI[C'*]<֖q"+԰L E<ɓnRx̽ t,aa??]A,aľr>~cXJ*RNFoan2 ~WPY?GcG0?[.%HVIai+:mm[㈬i+*#'pN jt=JS]BvFA c++GORg9Z˻i})rS ԗ+l21#%O2v饺He:*L`|@N:uy)0M'#}<.Xě7"#nދ.ôG$@j% gqq f1>ccKzcjXay^EFMhq=u74ptE<('γQfms`.m]oCYFZJX@'TذƎ\{dv'eдݬ6HpA ~\iRu00$+)E'RƨrI<翵lYYEi" rOkH28&7O/񊵧LJ6֍JˁQ\B Ki{' 9r8ǵbܥSXm$ͬBg}9m ~CZp6бk{Ɂ[9yxʷ\ڒFT[YnP0:ckOlZ؈2X/pa^y1@NVO{ڵ' RDR;~5i"ymS)ZC ҟ=(LxVWcOՊK;(MGjl8Η>t9, ~Pr-TD}cxg?RHbMbG40b nW{3.E{̦&g<.2Tˇ1*p2p (mÌZҴ{rG<0W=zJDn3꿆o -C#f;~l+XANm#9r d3XQf g kv$U>UkQta<{r4XD wT( ̾8?e^иM:`}IY99튲K+ᱸAc+|>тUTᇮ?!\EIYnP51L@*}-Ci?0GU(YL$@b9>zii,+c{~5!9;dL[BWX^6}A}VlkR9;H sֳη##Nj{[utc`)**֪Jmڲݻ4Y*n $=%YT# OJtʖ xX#}tX`p_JQQ~^#|xb1Z7Q#lQY7QQ lL5}.ngh]U%`5ud#ꋱ^Hw,8`ϷvՀ:1J=Z)U}B+;Qh@1bv\˘mH B VKdPWv~R?Ʈټs#Qj[VYH"6֞K-)BdQ;[p :L,eFp6AoüSiT|t8TxΒbst4ܹ5 m%YAܮ-|T'q\wtA+ =NM6RWL(zfՠ$۔("x9w** ^g^ZTa|4@gH1<1y01_jҝk2 mҒx 9c.8;ކ'F Q$P$o չ8U͵*՜ɽ6mm#ic!yF0}CQj;|s dFepqұT$ɃFop s)h|3ޕyѤ3T}*k]>TEUfE]_wAoq% 0S!pW,Ɔ8+ssڲu'#FǽaR'+_CI)F#;#e;eȾa*wgʴwCcC{oklG)9P:x~Tc>[qU`W'<\IuׂI+\qXR%*ֶuC# v種U-$Kw)몕sS C-@D2ᓺ#j&HȶIR:>V̛K>]R$}Jٕ NzK1dg=kv1 9I[{d t=V4\9k{!;\J ]$?)FI%rxqG?dH ygVܗ M}ja"6e%X# GH^Kf < ,eP*gp$ \QиuLr }9{UUT3QT,{q /iv4e.9J)XyUؓ '*4<A YXc%T]*ح _R'Q0l)WY QJdZNWa&>nzZTn! (`v$U$;ه;UwoJ) &q0i]4r=~*w' | bkP Nt 8c=zE1q*.{{ygG;Eh(0Uʣď+1s,VP0ʞqژ差?SڣJ#ٹnd:bd±+?ּs_5,u9b*!d]HUWj<ԢQIa9%8I%SzQJJFTG2#4;>;ӷ9..w!XϵI#RDf~pU/&%"rq}+䖬蜹cmʰ18P@Ky'#trnHϵAXa}yW^ S98-І7J$#aa O$~UJII.ZEWhvg\9#" E<@B2\ q }*Y`KFr2#r瑎{=*ιkjr;@(8kHNњ+EYH{MS \T:}+aɽfVTǜX:\"aH ,8˖۷؞AnYw].88VR SO!r8$ݮ!-SYy;Gr2WZGX5##! ckƛ-)r6!N>z-K}sqLy:sh* EѻmH܃zW D{%َV GAڪ;^о שsl9}'Xŭ67j|N=FMUj6dEs;HDedFuXm8®0V^>uJ.gf\0pAc`ӥ3Kh k;1rUrdtVWjʑuUy *k,ab,9k+p(셷Jqϩ"IEɏ(JPx=i.{x<̆VAe~~5bjJ1i2ϊp,28D{."I`y8]# X;0M;]fӤŪ\H>TB$񃟭i ۨFMoU=9#26\#]}KM1 s=9;Ei$X&WX!I(ry51C~\D$ydqzUjrIG]-%|wRW$v6H1x$b͎P,*6q»^jPZswf/ݕ3>=fcOT?,_.>uFEs/bR\1{˶*QD,N9~)Ni-55n B)S9<`w /O2$Yv(8Rxk(W{gF T;c<ҹGy=:²kR֝p/RqH*>w8 w5K43n\cá헮w3 ZV' 1?t/TL"FgI\ܰ13,_rTⶬ%l. ~:5ms$5i.##u`8~l1-',%PIsNCk1iHXh wCrWcw$6v1Eګs1*ŭSK%3ZYz2GN,' rR_.݀y{KY2bf] f .ݫ'')B2RIѲIIp;G\LM%'9lA889닩6MEޏ#'$b𛰣mܗ\8Avt3)+pO)v>EP`><NJiOKxHʓ t}lxZ浍6Rb͵} H7[9NG%l)3\HV@,ێ޾ڥeH! 61-b1QRܙmBJIV޸+?ΨjI,.Ė`F}\Ep!y7,lwE-vWZtcy!8F}HКZs|4hHϱ| nr[DSƠ'Ԁ2Ib;̾b} f,XFNݠi:njW)7knDnpN;`—Qhԅy,t'ӁS\wm$%T(\sJɊ#nH0|х5er 8c7]դWkZ$Ewn )q+<0oUa6y8݃5ma yj+a8a-p.5ձ8%$w_+΁d6@VMta`\53{q4r ` u155ЎD:ߴVG 68sϽ]I.uG̻~nr`p88+KquBr@NHlaWdIi /BpaF:\[ qJd hlQ«[5XHn`{RexD)x ʫrH}iօR¶#89܏|U*awVWMkA"Hrd IަyR `ƠDEr@<-ϑ+sNO_LҙbʒIs#?g(%+Tyu-w9{'H%Ef6Q rx=e"Icܠ1@H=y#󫷺]0`QOۧZ0ۼpBQ *4㦍\'Y%.mx3͜STg9ԾOll"`w-Ϧ:3shNC ]vN׸LkIBjĔwC#`)&O*ݕdJ ˃?򪱻XR>L}}OL w/WKA+y77ӡ^=>4I/0ǠczVVlT7Y!gT]pz=PE4@#2Xbrvk T v_$.겴 p;;ڽ՜8dMeISXZ1"GslSEThy *vg8f~+x6CrPG$}+O9B2ġw q/pL{V2˺1.r3X-r y+z`ZKZ3V2ʯs,z=pLܤ#i.Y"/-ӃǨ=,#g+;l:&R_<4g*1QWe+ݶYm8O;ItG)9ټ$ 7Pr3 A a~7 qn>oγK)2<O]|>Ā=rk qj6W k[ƁD㤑?sUuim#%;H)fIXnŸE϶}l<z[?{Fc閺ŭFbcĻ9j%f @faU \Y]Ep7C$y6TJۊD"ff T0pkJIi]$Oڳ4#{P3wPr,3['sϰMs?k4u#tx9Cʵ(0@?^Ҳ7f}%@ҩ&g\TrWtDPhz kVp`9?\9yy " }jc 7hԤt!o$:9=bK|αE ̎yfEmXjv:p ?3yIo֓QJܧv#VҥiO?k>e?51EɬHѻY6yK{zՋ+] c&& uU.md,ybM3뢓S,Qj+u8iNd@rG͌q]n@ꭖA c8;Ird2$m/RAWsk8<)Fd I+ŒS5 $0zyj%.,瑌 rTyj#@qV2kbWK#Xqos$67Q}qҥy&ݫt5S7I;p1;X E$?0|=Oj"1 aMsByhEq1V&$U m=U\[jQ_ hڅ&ƀg]qpOYYWZ G672;rU~ÊXmh%Huٟzd#OW ;ui(ni Q666@7|FV%T ~:q{MchIAO} a:^7AV#w^;mI,HJam#w1m{‡ zc=֏Dw]⿼w)mRż0cGP}kQW#(_徕us5FmF>snE@`4ZMZ67qypђI?#֔Sfn\Wl0#W< Rc1.( 9Jnm6) wu=עt-E #8qT$pDG\ N>)oq*֡9EO'R2qמGU#[yMGۖT'FZkp?r@=Vp%bv(ܾYby<9Nh)Y۫AdIT,rxϥ.m2&'=It vd`*;.ݼjfMxnfȐ+?):RЪBjfZy)`8S׏𥵺RCԷV1ۘZFC #iGVxtMņ 1۳3rkJ)r"7*O1e q9b}?ƦeL\]8m9BAZ)nbfaS@!% qQ;Y87$mY`&rpzk-dAd>Yаܮʅp{]f hrrr6lKC2$qM^wێȠ|N{p@).Ŵ4{`w9an~c Φ[28 }qںe/t[I.GC`jȷ3T#D՝&{KMGImb[Uԭ[)"B|̥=ơy/̂Y<+H1- yw4v]D* Þ; ]O4pYE&UD A<~B^^8XGL\"| x_@e+=x4%k낎gh_^ܞqQO FEڈC?@:>*ђM|4?H$9Eeuoq|CH$TVv* tjX-THPڬ:=7zt#E'}$vm=gap~2F?2Ӹ6K\*9* }'L ? wF̆.A=f*Z sY,xW;1횖-i\MpA"RyE?jޑ43ܔVܕÜ6Mڷ <0ABټ ᑑЙ.Zzj~dP##BKzsgq 0@<q#=sis5wr=:4m>!dWw7bo/ r;C{W Lo1ڋٱ0#=7c~;G*4]nnEQ,qH35cOnг*<CN}=kV6L3noV~f:{NNKbfR"-P F}*Fg6Eub8??omg,N*}y֡o6;͒ZJdO6e$KfHzGV/p++}i56W2+4FH?=*+[%#&uylW |KrH'HlFUsQ4 GVIJM\)e^A5hsSF̩ 1RQ\V=Q"T jst$S 9Y8Ԍ:8>tIc<IevgZPn2d|¦k9m I95a0x숊V8gN.yM]rT8u(5y'vQһHm Z`]QnP#%P|uOukB;Y Lȸ$dkהEAjp+У[1P.}&yoM{Sfrzq FoYyxF8Ld03Ē{0;x)q]VLFwm#P>SVE=Է@Y=:isr.SjӖ:Ȍe )ܸ?q('W}Ƚ}Ρ}t!r7o.[y7p u+&buы$mZmnz{RdF!&@e)d W#ȫ%=#s;יqaqWQK*A,ޮ4T%)%cR{ ƨ P|T:ZXڎe`2y'.e0W+1QVoxgy,#SWJTV<6[hdh 9^ҫ&Fn5wTvaݷ8T@m۟b&" Bf$ ;099wzxԱ4rў0~< VwgLiq9%RYȭڲ22VR B1שL/:G G~ ޭM MGsUܒ&B~t)6d{k7:Nzoݲ5;A>,Әb;PI=i8~8u˩nGxSUd Qs֋$8UTR DK20w Pqf6c,(}јOPxA&BNESy$eV*$b]ɒdΥH4s ۯUPȇ L C G8IG雷%]H'ZDʤ QW=kqoq3AG'U{[K T?6 #JV \O} @z`{*j9+QRjf4qdRwwϵb]kf\ "5vi$ (E$q,. `6Ҹ|Apioq/~9>'j%w4N.idH۳N m<<[Ay]y")rqEPֺ2C~a̦HF;rOj66n%A"(GR0IkZSCY𥈞3 HU˻D$ZZf?Qҹ>[G>n>=(_BBӠ#R4ťQEW$cTKihͻnUYP\H8Zź[w%Ekr)u;l㵐7+!88ȭ6;pXg0j xs(i#NcqENcnjߓ\۶Dwha Sy5pHi 8hx$7F aaMShi@z;VRTֿE~jCFROB~(dK>s *N8bx F:G`-" Rx)%c-QK7֎xr(3hE#i:;<~l̡ϕ.WC;F(s޲4N (?iK{Ga $&qZVD=0m-{d/WQ;w gi)58BnA"饍#/:$n)VsMj)̙J̓qRI%ОVXfF|p}3T틒,.K@?qYvWqk,V9]]l\~)t4kɝ_`EnCo$61^\ǑY5Qrh)a^[@+Eʷ,͵^rPO>PI[$ %p>qV$z?:I)L3ƿgr0 A&*8`N3K:z/yطs e3NӃcjV4C$F=c!M8qZ?lY̍R<vMg da;-axbpv220}?{J[:'Un,fK;aVFU%Aq?_VrJs%IAc(hy\yiO(Xץ`p AS }5.NiX`;g { 8[j٫^X֥b-جrycϮ>1nbڸ^z~e>W!`Yӱ-ثq (V.w9|SՋ$?l[y$, r:Fv!C;m`j35$! 1vFmEẉnuۦ3X1e-E@c䁟s o.r~nvW;v}"}(1 {S|\R&Y$#Xϛm5l23xjۓw3!OIaciDPŏ%TJP)bO\z TjHRMjoY\wbH„ʍw8=bI (lgOa oI&Bs]q``]S$3k[0;J :Xn3ȐU\,Fַ5X F9U(~QxMJl|nNBq[:M֕rsfJ<٩llxFrAT+y5/wu7xӆQ( ̥r^dVu'>n^ Zt բnhs *hoo}+iuu䬱F@GRCdH=:K+{DI#:cuMeri"26L6`Ԟp|qPNNy7Mqc1h@_s uo\*2uU_y%dh{wc9eSt[M*+ iErH10eMǟ `'GКҸ3\`#%@c=z):nPpn>}a&湩՝;4%l_ Xy"7 6?J (0:zw?e, 6[$*Xu իoee|0Tu] mېxBLl8*Ee)B}n;9# |ʆbG篰5Yî(x+ L؂8"tp sFimKR7 $ 1q]d[e|;xɮQwO:gyV uXԂ;ӓSn~M)7[FdӒ!@'ƺKi$fU$P3ۓθ G=ډe/"!9<]&ayL3aϭmU(F[rGA`.U 2ۚLꮂ,YUY<0 'bʶ4#hRhʞG5Qz3IYO&& vD򭬐H&_x8댜{V}whRIdMmرznNw_$Xd)ǞD{D,PŮ5 W~R`[ '޵u;[ BK !g ~~c[oH/.4Ӷ}o&]Կiƛ#N2bH#v̶";U]JC< 9UU-%"c$ٝv[h@%z4p\D]Ʒ`{KhQˆ0X`q??IY]8Acd)ZWziTĬӢB5G@߅l h0O^1XWio:ۼEjifɌU@zgVIFlvCWq{2f FWJYDV&9J[/2saZ=i_6#gKZg!H9=xJ1W\#F1&V~=? ٠2yH) i'9aCfÌ{W4۪>g?fd#nx?eɋh杏!b}ʁ?l("!~Q³#F*n|g$%.4̛6{d ,jP 7uDWұK[i2Lʂ란 k_0T&F0MU:~`j wo*69^}Zn{<]#yr+n]=eJiL,'1bo Dž8#-M#0%RPlj%H]^9PEUW|E4khK98b}{AJm\+G@/. y ?:ЖKilL՜n9GUy[E/!5 GtPg ^[>cׯ4raH~_Sҵoadax66#i`<ɨLpwU^٧l -){Y' vhzY7 'TqM"F>i:q06w +n6`GkN6"?+q\rNKS$A?ڭn `+ps?6,J ;ڮ1f!#RV ,!R#=EUwg*WWqP@"ѢCoJaqvuc'K~#@!;Td#Ҝܜww)Ƒ4TdaN$rBc,r2p *I 1퐇2`돭$V@@I1gBzYΚQATh2O<@NcU#v> xr=4dßJ"b;w&rT??xMo:qd;EL33r( AZ T\1z=i B)-ڥ~b98ʥI92ouw(;nJέ6béiFM" Sh!c4V=)J/Ҥ,+v㞫Q0DeRe7:0>jbHP:`j2Y.A$r\`)J#+nzzԶΒETU^UJa8SAqilme&qkOs c_,ɩ)(LfņFJرON7 8>ҮI{=\m*܂&\ՔϾk*/Ͷ>IAUoDL@aCQvW.aȓmXk'4i5ui7y[`?#?GxUVlFzjha11IWle dyUItӋw\c>[BZgF^ 1T%cspx<5 U{"Oݿ(A X/f,.  }#?V5CeKOحDGA'InDI֍Dž6&Dܳ~ʳ巴K$KU33ɩ5)&ޅ9 i8 ${|ˁ%XxI08⯛[h9FpTt9JC1U`2?8xWE:wqdM,KnjG;%֮<37I~ۢCrwxq.}j)H_kFKǵkieUr Bt琴"!8 L*Ū4"XC<Vp5erKEo `S-pu_Sz{Fk$G,:Z Amvr~\t0mcXHrP1U{Lmpzn̗d1[nCg{tUJ'hِ:ֺi kWFihԏԷ3??X1/ Wat&bpeg+ߊevR_D]o{*O8p9ǠIpHyfLg 8osYZd/Sբ͠<$1EyK\I4< ѷ!HdwnV5eg,2rzjE 1B<bcyh"S|T>h ډ#A?3Kb1{ҟM+ȧJ3"#>|v#$ޘZiEz{qݹo|VmŤĦ2b[IIĬH[Y&i#;trNЫJO65{ )w%@wᑑ^ϿXk0g{#.9vje>_BKwԳW|qYml,kYL}Gҩ2l.sy̩YjF$E#"߽Ojc*YHAZ}Nd=TV^U9PpyhYwI${V],FVqxej}Z8|q mqDg vjʨ ,q׊ԸVvp}*&YPP*Iٙ7~ļ=DĬzBF$rœy[vqis^V@zo, `nL۸eInjg.[%N[5u]КU pn J3Ǣ"uT^ң]9hRRW*$y!"=t6X?V4v Eq>pۚnqIY`m@Hs֊ F@ ֊i>~ʓ,o;Jʀ썀:luI&mee؝qӃ׎*L7w*<ᘧ@9=ڣ⵾iRw!HO`yR3޼hru-n+\y&T1u\ٳfXPD@ Nl\{)G]Oұ.,^)XI>`<#8RRnIYͩyLaPBmrx'm?:*r\V8\F댓U[V!h,70?0׽jJ R5̭zIj77Js; wλN4FwVYR6+1pHN}l5k`łe ZtT+,PdHҭEYT2V2V ^j*VE,rʚ5e,ɴF[s9^>g9ǵu!X6d5[6^;kq0-JuUʊɰ{PM<}9+i";)ώEhڤRH.ckXD1~ΑI *K? ,Mq͔1 !,He}<ǯ./`Cnc+"!Q5auaw4wO7+Fe9Rbmg{D  Aʎ:SfaFN sn[P^Œ7BF1$ u;FɝFzA$&KX67 ;YԄy" H"Kedb0hԎU ?oqW4IcY2OR>yhҐ'gbY&Df,D0cm0Ji g$4Bbw{4F&P ďǷn{RR* UOިǷSےpI#T[ߨlnVR\=wjܻE H(*"v)j٬iW_@{jOoq$G'0XqqZ.{5ڭm(q^-#TXf׿QD Ǖ?sE]W:Z[.I;pxVe%hBvr$7~%0G%Is+~%";cwb̓c9WRQDWEVH̑]3OAsӭ2indXR)-E% * Afe0"W!I x#hNXr):ctT$nIQD##1O𤾜`˸#07B*O,|YVՙT$p1oV#OE(.yVɂق1OA *$#fy;7;[L?*h aG 9>E6k42+JTn8<0zSl]m<\-Ԫ1cLZn`qw! b~uuH灁tVI*8V<1@g%BMkfVnexұ5ePF[ɽQzX8{U[^[1 ;_sZ:P~*JvpXHg.0v8K8V5]&g,G6Gm,%*[9b ]]=LdSDIUuJpЋ\H<13g-*al񊣢w X i  fZM$F+qYUH2rL֟ګdAe$bpTNsΩO>H%OY_%B׶9ɩcV襊@ u?F=2k[4, O^M[v8J O5 3eE㺕lS#X-yum~cBc$P69ǿm)vC `vn9r1ڑȖbrV1riSВ;d$9Jwp7#'Vu&m]KO v+QI ǡ?:pFKiyIe$ݪJS{My ƺSdb̌cp}?ƹm.$өbO <JF$!+ c#׸Ҳo#uzЎ[O\noПư쮒fx#HahmۀbG+jUؘr18?L&RTeyAI2FI)Ӆf>[lc:.V0El8 vJθILU;1;XҒV7ڈB쑖*d`gutU'Vݚ0p|`=^U8.U'ki&$~)dI5Č64+v=QpRk7)Ԟ z]{IɭDR$f]9|p{xZiO1X"| [Kiol$I# =¨A&~erF1uISbE̟ 98$MA<}@UJxݟOQJl|$7kBr@$q4(d]HsUHe@Q?y@ZrUcgƢJnfS[$(NN8օoYPȐ9 v .O]︥rėF- 8.^W3<3'i7@m?WRtHS<w#?x#%y9[N=DRM8ivɥE$L[Y=ǽ[tY%uӬ|#.Iꞕz+S"+U*zP%,;8ی?sI=w nei%wǨgYLn^j#:\IԗLhP~FI.mCݼnsI5˱B9K0 񓞽u`P #<ٞQKPǮFAc&[ZIJ}sQ'MjU[hqukvN9^[!kvUvF>QWQfQ9Ǧ{m(*D=2TuC>]VL\e=N19sڋem UdTH;VI2+y%|ftP+.r}'*iʵ U6Nh놷+(O4e3YМTw)(T*"{u,oly*cn@~x8cw*Prӯz|WQwܰڌ H?zw,pDχGv9${lg|InRш+ecY˱$?͐83QZ*mYOSgLD{FDgeeb`#dBb{UKLjC\T?j<_9Z/w#[UۆT)U3:"ҙq0{jֺm{aP*.IsϵAXݱF °@;bs-Zد "|*H5;IqMu$i$ʆRn czҹMM"q*z-84^pF&Zciε9Lɧ)-c )*2oʺ#\ *2+90z~5D8QuM$q Qh\6OCUi"8 A= U ` ⣎%PBm9U(Rr}t&I M ;ƜY*'StsCo(U3y*8J+_E 6>Z/`tM>By~#=:W|\;Iw3 #ۦSlDs0]MQ/*RO 0x@[0#(G:~o͡KaU%|>B(ba w|'\/sϽNj y[ZSF=V1EKe-<ªF7 D8+CZ:rHaYByR8 F={FXN6?n#ƧaIF5I/Y㶀jʵma<|qk::\3*Ns=^-L.$bc 5)7h%t9ofbV H1CN>}y7^D gi*JQj"ٓ0<1ɿKcn$(n'Wi;$^l` 8 9!{1Ue."Ѻ??3Vđ!dgP8>^&*$YI*jX|UuQ!Кs&'!xX(G^P,Wye8 }W:Q;ܛCNx.5V{r퓴)BZt Z ?2̼a)~T ڒ 3ҙ,E%i$Erv<YF*"fޏٺ#W؎[ ,=iKuq喈$ۡf[]5tjCl9ֳ;v<ڎǹ$Oy~M]G:MQĒ+$Fꟈ1O8oyNFj֦qk4 =GЯlUZZx.Pܭn0"$٦28$>ኊ[]9, )K?'|멮D1n1 pCn)BQrGJ^B"YYiL =:r[Z^D3m%s%ٖ)#*>VsyR>j#:hʨ,0)sE.].LX1 + myNgxF}10:IAV=4˃-HJ!89)'kȨIB@7 R|E9 >C32~"FWڠRoWh@F\ I˓tiv ܆9R:6q1$vGʠ&C8,'!/·}).VI."͒%oSJ*|DKm5g bs3[F;Ԋ%\DvmO0|=ȭ%,s!{#ޮr]E&#l<CzRZo$yp-H}?*ᡐfp)?xJu_cn#R3oqzss_R[0E,ɶ=sԆ$I^]%UҮi ّKͺCT'RF#bGޮ񌹇M)#Av,dQC fbd<zViM ʣW'5g" YOO?qBk4eȌ܎xmɒC)UCѸ;Qݬ߾WP-vTmVvF1nwN6)lY赪s Nެ}UӄW/3)_zaVͬ1ڋbX  Aq$-d 8 }e.T8×VRg2VBO=*=: I'}A{`jF܂}G?ja ÉJ`KT2k*im7!#M|lg5Zиٮ]t:+xr[VG+"$'n<{}iX@!zҰYs+}JnUwYafg*AUQʷ~1,E4Y\I dM.݅/O,aֺKƠZ=ɻ($19/.&$ (>UXq5GW<.x#9V#F8H5!u$m7wbOi iҢ㔔L9 X\=ϧU1 ,#?s8#CU"YkITH͸s{ZDKc[L$ ,̪J"ե[ rwSJj>d=]O FV1O.r^G%l7 x,!ݴdMo'h)#ko-bf;8vZdS8dvW+(s=\EfYXc8đ9ap~o:KCY>K&R{ -5i%apwfʓ}F)-- D1|:b %=-=~e,my~Z95CN(ulc)]B̛\qODϵLds]r,9-42+n>3v%b0ެ_ٗ<$`ԚE4FC}ܐ}iM4zVdt8YMz[Łqx+9Hv%z$Ec/?JௌRh('7TLBFN+Ͼq,pę~ZtPRk@`YܝPp 1YQQɑ nRGʠszV燴9ddmjXbF>]?#UHg+u+rܸ?N[~+ŵ n~B ;D"yʻ1Z٢,6e}AWFpI#=NF1%3L5+ךۗUOJ gXbⒹiX| Fn>|¸`k>r|b>驠iX|² r{*p4D0J ѱ GVGf&iI+F9FfuIZu kl#(P~%nVՆ?2 M< #GI K W pMBwYAJ#g{^=Q1 Pq*g\ԏ$1eX {UX6ϱ\cy9AIh5c3('4[LqJ`$k,$6En31VWZK`LHF&N6$` ,2FF}3U{\n56|dޱV)eVX#dR9ڭ¼8 UVG RKRlpj,) "m˞E+ۨry3l 4˰*$@$-pzhxV]PŠ(⽑VCV2<{nGRdsc4OKhgp@z* * Q&z5QP ȞJ-I;_ QY `p?c˼H>cc+>5q̪)lP>KgZjQ̶y#`X*MKc%ΆTN `?Y3%s YcTRV%y8O)[$S2)ʔVVAo*#7sc گBJ)ů- WwC8ci^dx"l4yAf 뿖&=+;F:HcO$bT@p]vl֔YɻNZAϐPD8'%ԪfQ+sIʟ@3Nk'Xo&,W8!Ap1kWژF3Q6ս4i)i]a 哈[pA'?ts׭_Ep-rpN\6܌:29-l\"dj^x⤏1-Ey} bX2=#u"Mq+c3岀$bɰPHi,H9y ڠd9iVEKܰle8#2+cw\AǀۖcʊFNrNj7PǚmK!!u޲uev>J 1?S]LݞLª( 6^N}'ʕvJ[ vaT`R:{;EşFVU`u=Msv1P ibo9BMk]=сrNx$:P7dʅ$1'g\g)-"rռ8Pc` 'c"i,(0f$IqݩΓ.;p1׊gn95uޣ,Tq @=y*1߭e-ʼbzfCwadO]U=nզH?9'Xc0mKNC=aU1]p25=6p܈@&`sg;W_$:r[G4M:)>ik}/pUUO8ǿZњҊ>$%֗mwI#C}@iXR2K(a STt[1a!HdWV^Is5<4A&Ib{oL{ge:r]Z.>fTߙJzdbwqͲ; ߞ}ݴr%}~VHz})4ckycߑAF Q4]F? 1ǁTe JYCwZrʭi30anѥkh ItG zReɚ9&/g7?s֨jg9"/mhT줼 qՊ:{rH 2o$JEbarz?z2kv9'%EgrFb3ϰK{n͢FgT@~zTrq\EG0Td(` Al}TUR/Z5Hkyk2\~i->nbs8la2}=X*nZO8ϰif$%(- :.=+(܏yE"Y32q_A/o9̊ŗҰ^gB6IJ~d㻚[F !pA_ҲbTކͤHUUGU ^4E%Ee##'?2UEVG`&X+r@i܅w.̧B"[Wإ}t.ʖ7)券O{9+\HG Pre:X$ti l1YzmⱩQ9DRiͲfb.NB~S^ld&QX)Q>m vy])=A9f6[j<X 2s?i;]nW>tz Z1h9|.gxY9RMn%؉4ySOsҪkiMF ?nuJAN̛MKǀ!H` n5~ᬌ.e 'ϡ=zMJm gsӞS\d6\jad-ZYwln/- Ua08*,41G*b8ylr <~NyRQs.@])-TSJ4k:< 'ٌnUCuߐ*s\$;Y7Pe<{;N`|TROhs!e~[*=y+"*lbnt)'1֌,Is,, ªH |*#f$(FA#>@-?A'*G{NrN)\oUf7R0Ť Sr1ۜ~\Mn (q]|[x%6]vg_J,N%hR~nl7%fHAu4iqA.YAxcok]5[I(es ؃Ҳ,ͭh#o<˜- V֩rzv qxU;iQwNRkxaF~ ]N+t̊p'#&8-~,n0 UX:qZls!  ?YOX4EF-ڶeٌyzTF+>1C8UN0ժOG Zw!7qL2LFb9*W® 08RL0)噸'&UBh:fβ3eʂngpDDGI*G^r<&)lIQ9$ǩ/Eh0T'>viټùM,>=2[=3C R+( * =98ϰԕfhܷocO֔`sbˋ+"gejwn#,ĉp~vz@N!_$m t ֢ܤ5I2;ח#n[$<#,Ѹ2A=k?y!k9Q\Q CyWrIl6 xQMI4?~oC}&r'qW wM1R }cוU7`f\hnYd1p8b>oSQ$EE}ѩr0 8;}+bGXhcB 5ZܩYo_@O±ĝarǙ|Ϊ tW,Vce# F0sU25 Ok6eF=V- F)9RYMY&ͽӷZic*θ5%#+(wnBKt85䛠V;M㐝T'rT 3ַ/snRz^h5xy9;=B%[\* u5٩He Q @>+S\i dqV jӎS$)mmn1Sv fc:m, p1is"yCG5DIcqӏ\QWX4HŐA=qV$e:ݻ!2G*u%i#i"@({?BcKp$S`])< "&G[o|Wt2Xd21@?9Yd1@\.Y IU|+^vO:"2 }ڒ[s ᷬnfy$OCոZ4s@ > 1Z|8mJk{4fv'r^ES氎_"P;\|1Wm)!"~O5 8WN#X~G4mvR|-Gìc횵.O3SQ˻rC* aS [@ITduzY7aENcJ&$hʟ1vD$HC q~$tD'@39>%R%K vc(䞿H$r0AG֛6هu.|Tv50Ga)VDH6!P,&I^ׯa籺\4w dzR8#1pI)'{ڴ.Tg[b1`s5-Ϣ*7[[9$aNUO6Y& gݐ{})ѣUF &Ҥ+<Pw?̠sZŭQ,r,(VW/ tUB6~<rJĪ隅GXURL!tV% C[ZA'2qY=%9Fµ$ џ/.JR:Izt@Af`З^ia.wg8,JbXJ(;_,kjP[_Lcy<|!6@&l- v=[F%D<`\%yJF*e; EG5?C.3҄QƏ*L'Vq\ඉX*pTy#:bQ%wr470IqLK8w8"͞q1X73E$d9s7 ѴѶkIBKKY^'y#uy9x5.BH}smCq f yo'.# Uu2I?CIAq3ʙ\zƲ'Hf̶n x8J"1 0vz[Frr&Uz$p@ I21Za 1d}r'bB{#Ea $0^9kI>V%px;ԼۺqW6ZGfvT#)NjWb${`r15 R10rd'4k QץxRI,በYy>5%G!JoVo^L+kBw98$X3\J9S ;goA}k T&tczRsq]F&Mxc?ʹ-Rf%#B0szvm$)FÜL4/unلs vXDeTn7ʃ`y݀9Yy{u-hieFqLS>tZ c41)ՖYbckgFoRȕ#BL{>tT1Vq">@?2ڠrJT^E)HH3$dNm 3БjbgY&,dxǥ_ip9B:bnz= FCdfq ˦JN cǓUsW@󷟺i]s0P/*=*9]% S_ȈU0̿6Ad Xyi[k#hCu۳]U8zWAL# "'5wld`#*}i +Jض!$7*t&HF|f܈Ĺ&APOBѕ$Jd.@ HYr?˘F~llɅT'zյdD gR$VNC/P{//3<5LSlLS&RLO4YHیc茰FC pO҈l1Gɧ)r&]ny*HЎT)gݷܛ{xGE ֧4 K7MuPpUl|V.Nn-5N YD;ط=RiCO̒pqk{m+3"4rl.$zT/.+v$ D>\r23V= n)V{=IM' !VW5[W|th_;A`׌¬c:;N?ZvIa(9&d_nY*hprSsGjyyLjb*AnEr [x#8;{ǩ4>+\}b0]<{U~rz¶4(XʸNz~kHl'tH1@2[bPI#w=-JiO$JEe>agMSI$3OIV#/q0윌 c ]쏘8fll+9 co=n%_9n0#sV侷 jOsӻhzkid u@Xͻ,HUzS<, e(:LUƸK&7k0Yr8 13yrn}oC!9jNo,,t a0)?ҫcaoY \ +;J F>U9?5bGd $Aʷ7Ǿ9A'N:ՔH#P{@>Ȋ A9$ r@瞀+NEkxȕZ@Y*~khovpp&!Jx cϭixM_IK9I ;Xg"Qal`#a8r=4yuͺCN8\_<o~USD nP.\px Ԛ3ǴcGTX6XRlFZ4$H\\O=j] fuq|ȬH[rNOxhɸlg%4o f ^u.0*Z+$7#ҖiT$3"NT% rsاƧߝ9Bbc%I;*fHsnǧ#9ۖHBAzKY-ÅsqSNﹷ;կ7Vj 6 <9}.(V]SOVWz?Ƥ6 ӯxG:>S9Ջmc;hwJ$nNH>}0i O.P=kTUvQ aF1zvPj}؄7<"IB;^L[$ŴI,D^︒K{ng'gh܈g~=Tⶒ;(K rP$`wN}`Aq&,mhY009}?OZ5&:̥30f q!ݰ |?nIGzfd$233 ̠}@M0eNIsַisVi$h>'}qZ*EEjddb}0Ψ=9mY IYR Gyʲ9;VM-)9$O>8k nP=x#ZLN<;<͆Um{R)-,@b0HK=ٮ]*watA<$*XGs~dNگKZLF8Mj󞼮id(]|RA0lwҙC:E%ďZC ]Uv^xkyE"dejRN?26P\b*6ַc<\d#cqnhR(`;*\3ēo%M:qTcRLe'#ɺ&I?T,2 =iH:!,Gg<Ժ od0bsjX+yEeg~FI%Jn6Iwdi-t0Ew|^h2t})֟gۀf Sz=j.?f9du'o * :rE mVp‹?;T]ZDI:17Fgx6DZE" gި^Moszg?gF 6gx5/62jPA ޢqi] D0ILΪNA?Αu-*]I]pHϸ/B6Xd[p~²nuP''OZ[5Tf;͉|( 8oP75Fd,Z̾Y c֨b쏨}Qu! mh:̖ L) )8%^h9ȝlh-295Z;kI' O_Q1ڙF-9` M F}+]ۇZn|)ow[]ƾ[0N}9-Մ7QEцzʝ:l?Z)#1 'k8MrF.Vj^uek" m9lI^PL##P!acNxiȵxJJfo#d,)̯<)nHp%}t988=, #(Xc# ޣMu39Y8#JִjB+T6m%I)(Aqۻ4~_(%H9Z Wv!]0c<}F*+I{)IhU<Ǐ`8'Z7b-0$ֱS\A!uepXQ93j\Zd(28|8>_u8JY2i=p2*AB &P'x :8?w'uuY*A?+,D~DR58(I>uF۬b@Pzׇk٣IB# rqҕv9 B9PpXFxַQc5J+d$HPt_ʫtΦ13֬=9TeF1;c O\r'u֝˱q,!nJk?Rx#rxg=N$ pN *c,H Dd{z} sE6c#gX22??ʤ1Gg ˂#pǸ"kmhX4(RGj^\4ÐGL8UM$re}NNx%d ""F.31ҙm\STr@J9WYȻ?3-S":mF ҒԤY㳰m{+:Jܸ ge;sZ|q|Aޝs-Ig&,[e m*Jt]*E#keGT3ZDqY_h `0U9rr8խm|`;`WDyb/ 6h٭hsdx*iu^p{7܂)w w!UܧE29ɖ9p aNPJVKVF&dI<,n5] ܍ɑ3cܚZK lHds)Hn'E 僂{~$TOiPHgpahClcɑX|E`W8 0)/یA`O5R}'-GNMm]sE|^eUhe[uqZ^#{w/(P(aǷ.$V|6ƳVM7˅ 7!ۥrԒN^&"`Hlp o,G;n3ԂP{xnڝ甌d?Zt딹+OނQYIvޠb:5'kC!E%VZ$O$*Kv"m8n=NIKe3qV giA*@c9 ~Uvb|%~P`!:|b1Glc˂610GT q-I\)d]$Vf?"\y Bu?JScYxfc| ~w"BvI$Rxt*kd"˼28F=fBP7 fȷG "OQ~uwu<);6=Hٓ 8JEb}#u`ʶ# ToZصBm62a/jޣ\A427Q뎬ck-r6i]) .P2:v5PŇ[?ֳ%ڨs"dzU/`J? "?\g>I4@֤f8.fCoS=$] R|~0y8'ʴ"oP;d?uՓ[B1mn07cVeZ΅IEN)C5Iu*8֎a !zR8$z k:).n %`j`Ի#qtI-:L*~Z VQ=pI8x4[O&(JH皩O2L1P}>/i:5c9;Re>I"X8 8c52ܤwJҔ ]5-ṊS*0ӽ^ Yd"E1PB~*[QB%y B|;<1_i)N_ Zm>"JmP A#Ҭ&=-ӿ?E;Ea\(*C{$,c#;7=sPI#ܮ=? %1|,‚[UBmұ~$Xgd.$QqVr1#-Iu#7&L9AQrgֲrvD*Ө'Cņ#oȚuĿe1FX0@r٨IE1\?{w=n&P1)PNȵLk$+.X`9n@ ber}+iMqQeIZ)zV(Q#rOZ9!%+h[J[\:v[Qh pTXGZ嵭ٸ# G냑%rwϽeg wVزǶ2.N"DV@{H+VӚ⍬kPFqr b }_l[(XdݸjF'leԶ$,ZpL1Z({W-AsĬIGc}?=ב`0é#UgxKM?2cH˹#|dH;1rr˔-ĻlG 8 N:U6o0cU^1SaWWPZĥI%1M^lg19WjλY@&6O^neC MDSUg}HY7gΫMjG-c[k=ʌJ<[?1~jtX\nd\0rzdݺ0ӟoZt*'7r{\Ұ"0@Ud{1]($qm3' mB|~OWqp>L1)ɩ167KrXl~J;g%c wtfaՐ/)20=:YD~Q*2c_jR^OޢKhR\4$O)@sI-FMڎTMwU\.v7`cWY6хwwռ%.Qk$sߟ3֣iݺ'޺ z1D P}IXZᘐ8Bت7п)aBnzϓnip3I-UAhHaޟrw0,@VێsZm&#9>deUP#sRhZb$UU%SHY}~jv.Tn!_›K54e^I ?em`"9ڟǺgy_1lffHyXbq9eޅ5bиufs#aGZ^hUP2yX.*I50VdvR23؞}D64&i<(ܻO W[֪Ί'1/5 Fvl-a7Me ǞωQU74 -fO~7 G+rEkxPTY>eImγnfengw(HEpV0M[~>a?*&oUֳHPZRch\ucV8cKHwܼz'p6zזCw9-83ݕ^\:5mfi 'tvzkbq!Q=׋.ŵ ۋڒ_DPW<=궺}h[k*4WRnN?5tAq?ƹA';[# ?kIoPsvkUFmQ٫cInD!_A3 v #BABj1!R[czg+GbDجQ$Vu[1/9*̒0LR-X4GX1shw3dXuSY,fͨ nQȗ9,KON? Ke%",Uk<^v0V/=j,!s%,,OgUhi{ 0A XLt7ݮq +!,\.,季1ȳ9qXs܃ҙbPY~c(>ɭV)mfOS)'|U&`" ɘNю5W ȉn8UOɨ,lk]&sBծnjk{VEYe讌>euc}EXۉYC1e'RUt[i`ӥK×Hch5&j4f_7kL`v+x8$tvjw[lp:sܒ1U!&&?ʤ,׏roYdF=zUr]h8kuPB8?-]2 PwFq1=.&Vt` ##hYB 1H-X0!I%>#*I23Jf_0 qW*$-JvW17#<!O8&K\Df4݀T?f&pUou GHϽ>"t=mF(@ g@ 9{&d4wNioek{Ԇ8E*79 wKvpIP,wpQX}E\"XBE "]*G|ǯj[Qr#gh>pJzw$VB2=O$i8>UOPzBYJR=DEffxހE]XVW_޸9$<#YmFp.ERvOA*Gn12/(IY.qrUs$K,(I=:z`lG}gS Q֨T 7Rz{WJeb.DO4&NH?:ke"c@"$5͵ebs=0I[GKy1.ۑq?gюRQjܶ@>vڸgS,ݞ)SNi'#9<F9K{m{~fH|T{?Meaei +IIVv)}iT;Wg'9c<חJg#N)&VQI 1돠?^M3D3†VWeC*? A94:B<*E)(CmQ(!AeTgfY n3ϥ6iUGG8޻h^23 ` Qo^zw azr#Y-㼆RF_PTrߖN=YpeYUCINp ѭjWWnkP[Wkq-ŦȤ6HLq~i?gET&@Oߧj s c?(g?%K݂ӹrfӄU @%zi@epFJHV W33GA"@̪,x>*iI+#ynEyw>`avi8l_\TFiYDqm@]z#%ԧ!>TTfsX}F';'(!B\-BAPr9|ջkhe«da`2vל[)2mg;B9=E;N`[h,2Ac Zn1F^+8$֢TvF$c֬^"[Glx-}LzkDk$aTsE[#"Mp8sʤ0|Dz(g82sclq"4QA=Ͳ߃0˝7r=ǨIq+"3gۯ#4d̸E*.Ȑ#<{ A+=-,EWP {{c8nD,FD8;Ǡ՛KuUYc@YdpW&6ߑ6q՚7TbΒSYiGmBKLqӾ+9$ -$~P} EDUU\Zuv1ʶ! ɻsBnf$WHY9?lgG f*T.#xX(}Ԏc< -yp: M0ט{ʶbe.3 gqU v0lڛG[y|Uc'NG/m`Y& è]ہck7QcT͆EI%*'<1<{⮻Er VA:w5G/ q.@ZCªf]ŐFGϥ8P qRFNg0$f-F:Ʀhhhd%¢$[HI~wwX?.{ I; ``ONXrRj=w2=NҥI8ի;cij㥉+;0&:UPeXH~TyI\rZK~&u!d1Ts$c5BЉ,y9G^3F 8گGo42*N34̊#R$Q:ɕ%˩/6V&}÷^5xɽGkv (|}=FjlқҊf},V]/r@ڧg~sFj=SPi3C$ mO:Enfϕ9iv=?=]8"}pv7iYJFvKy2tEyƱȷ̓ )S=8"\E7g%AXۂ1ʸ}v+ĬHۓ>XŠDs>UO\i~`UtHGhPezߥ&̑yb+ۿ[W.PpێsZbJ̪ZWC4RcF`~=a^ {zUi7Oc|goj 4`'+}jgPH(L ɺ9>G| m%ap*x,yG/$qjʧɣNT/㎟-m Vl}>¡qY dh"3j.%I(U#$[S3b3B'y1dlT#9+5.Tc Ɇ)fkw92V5BKqjtrC%)~U[ Oqۉ%0ƺ >4k>d[yHcĀ' /*cW᳅S/4shQJ=j# ߚ@/^宝n1Ɂn&ylEn`NU[LeB0s5-C6۹(ګef܂4jUT_(g<$r2Euo7i S3^+YJp~NJt&tY,v-nN:qQiayC)qVb^|cɀ \ aۚIV%0dԁOZQPʥwBq ;eaAVn]'¨N :ttZ_u+nw[CTHeC$`y;BdO0yK7VT ź4hfpY[=8+7q5.M i"0s{MJ7$F=:{v9vRr4-ђC*L9nYԄbᶛew,@^VWE%їv&ĸyTF<@h\,#Q,ݐ2ެ[fKU6*ԏҘ,GJŗx׵R $  2y\̹#v Ẋ9"#dZYbCgyFlaV\Ȑ!#*Qurܤ~wyqnY8?Jc+r2Hn# kuX;WM.txn2xV`kAKy刺 R }+Spg}.bWrBTWDa8?JԴŌM bp[<~H۵ YSNG+XJ:YJOKГz6 ̊*Fbjm2g!{j5ȖU,@9i6y k7h$+GO,G ~5K'tVwnzԒL( *FJV>mVj&ص}E)q`aItb%]IRoz(T427ps֋xG3]ՠ`r+)c_>Iʶk>逜yq|:g5a#2G c)k"FfCd}khDҒ $V)z&mqT͑2m<7ִtu KƷ 9iQJ妑>w%[**ʹ6p뚯# iTr0qSZyX62za7fFMC9%$s=\s,JfV.EOe*ȥndF9t0Qz 6_zz]BWL)yf֬@nDJ1{jkZܕDNX`z&f <GֲdSd3ү[\ڐTPQv!S9-@R\2gօ{a ǯZZO6hY1NKefp, {ҋRw`ӎ=wΧ"RiD#T˖L4e"?>JֲHQEu)MS,O@ޕxkyU 9Fyaqԑ?▖ .Fn11^mi=h A[:Yuڮ6bBCpSX}!rom «HʗhoJK(GÜDW0#ei23w*jm RZ<FfMVFJm-Ccֹ pA[Bc)|7=W}EAF#cw ęIUxx%{ 3 p6c854!\sP 0;M8ݘۏ5N؉!p}*8P(gH"6si΀f=}E/!Q#!G#=sT\j "')3hnRvD]\1)F1pleHG 8NhwpPv J<ĄoQJLj9)R 8ǥJM;/'8UDIw+D;aT̊%W9㨧M]#<c׎T7&DRS'`8$ֵ,![k|Xw}0v'5*Yo\ Xlx\[iJ1Tn'9<55e+Vb!''&ȑb9n9@ єlY7Zhȭ831C{c4V,3 aPdw$sjپmI)gBrI6$xH?(W o}HQI٤VR"0;pǖd=sVOҍi tTb-+030A$_A=:UtI&貴aC<#'¢(5~ &8f0#PX%h'@6v V3$ n#1a-mRT``Z:E轶yUIJ+gzֳp#/owOh񴲤orF߆)@Z"*e, c0 2Gq< g>1OIо]Xg!Nt)ޅiǐ DX[n':L S{UV)oSSh<7o PrI#2\I#I#<ja'gsn]X@% ~p=2YYuhC(\.GrՕi;as^Iʲ +0g8tjw4ľ]a bV 違MeZ!BD ruS(/4[umшVBGԌ~Uވ,qF#ISe;m!~ 6Ttۉ$9#QIdbVHn⹉јU{ b[E`* C,E/*}s΢rNڄ9&m\By۰AIZ7 ' K6c`W:Ӫ2eEUwÕSZ߰Y,#VB 36X_qUMFNRK/%F$#Vxl48r QGgM*B zXv|aXsnO4jt՛1btHىCnSܧ*Il!@E208.G'V\KXdPI>cƺy=8淧",VūDѢϷZ`nq Qs}0A/-QwdǭU nS 6=y^wJIfȒ%ǚcZxЬ,ǯPYivm#*w,\+qn@O܅޹ ⱩC/fUm%3E6Gʹ$3ǡkXkgk Iwֵ-.+Smy*krOC}zVu‹e!١Wa:@=kW6koSl](Qn݂/9S?C}%ƥpH+uߤW3?1PHN84NR'g#ҔZHdm7!?;F? 8$ebw q=Gs7vp m#rΌ ^_ HYΈ#~' SjN۾~5*s[CFߘ1#?^fLsCy\g "f[tҏbbdrL7 OEBnTѓ_fd9B@@s-q!^5p } z-o $exrN?x!'.K:sgO5~բf^i$ `6  w,]*u?]֩s$\͂[u-A$ew3p{sۯ5\Sp&eϯQ? ⹸Et]˅B||jזVLU#vYNO צF8!ݧ "xA$Ud-MB<2ȑr{eSVCqH~ |, ƴ'{wG " M2>ezc1'ɳNsV6lpvÙ3؊d6Uh`:d~{`TM!ap8aP0XwShi+HUJ7ONszĻM`Hv+8lWKsZ`F2G-R1m~q.dmT8ZPm: j*k$[[p]Cg;O9[;;p' k4vT U> 3m R*a)NZ- 朣cFXA1 8<`yj[m8אifw݀UBU gLmuY#wv\[O88E38&=Ρ4Q? q7R}EIB7WS >\ ' )ⳢYne's.@$ry* bm2(ڻ1ECv1ZYF27f krteـc5|tַm~L.9 ጩF 'vA߯PC pogtUD'9sc.k R;;z"7hr*W$``} jd4A mj=JXnFb庞vէVIĐ$ JVggx3QGP(bd2'?ή"@Rhr+rj{NJ]X%;$v-f'Be",L,~qjF.[ݐ{rjV>M]Ap +G })^\N7n%ryV ZKKjVeC*߻48 |ycXnܘ`w~ jUYbSrWoQTZB;igEFddaQy_.-!>nK?=@ L #UQpȧxI/Nߝ%ݴR;[1$= ϥeG&>m\ uF0<) e8#$U죚ll퓕9n=:D r݁))ٍ;'POKk"2yѹRxwː!J*xCZ7 VUm:q\qGgʷv\s9_ֱeu[j1kr>A-QQ&7x"}R\JPWnN'_қ!MX՝f >\yרgK jB$dg\*OJqoHni=t'#PqVɷYbVɒBTKF2O8犻\2Ke-p,YbQ3 N >wv<~$|3l m(/1/ʄs>J4GȎ^ 2yx HO6bC`808cj!xBc*'?)=4z*Rid?wnF~y,7ɍ >fir|ea"s:vzJ1'\br TMYOnxU qѳU? OǙjϠ sƨg 5xV1.o>dH'ӧ[H>+{CO%rwr>VŒB\KEE{0mid=Mp1ۊΝwKS3rv8fe;FxII3#7rGG=̅^&`ӝ;#nIf$AT0q s~ m3DcUu*6++\V5kxu4fIaPٮ9a,=뢏THH;Tg>5%n#=T*ڛq~[r5SsH[r?5pn g.$xA7W̜G Ž O55"༐Ff Af^NlTnZ7*qѢ6:Z wح9=JͿr&d, *s{pW8t˝r P,;H7Usg{nD)b@ēzczɑIYt`\VؤSy  GV5fRV26I?6@VѥSIb,HfS/?^hm2pεu+{+&* 9 -W2\FXF67'F)j3NɵȺ7Kwq# +9o~;V4kBtuY^3n^~5{G)ZaX, 1>-ڷ3ل`OW+-ݿ,i^p-M8s9|CcjhZIn$Wrd= |Zvp<ddER=dn%; bRS\B]z\]qI䫰t0\"xW΀8(ӵ!MG܌Jl zԻ<Цd,ф^Y0Mqƈ2:>985bH,+0#ڟ+Jf̉0c"=:VZFWv89)KqM5d6:?'x 3SLLB+1HJ@&.[kuj`MlYgbՎ9n}j!r) 730GlVz5d~cǷ:i/̖]1#ʹ 9i=;fyiF L-W1*^LfX^/8#N8gU(C-Dя>~5IvLVo1WnGʜ;VvNNw_Y{vxOC!dC NFFy?ʣ&PDJ-Ē@SV U7+ceژg]ѩ8pI/SD#.f9 Ros)iIB'T9NJ lwj}3~ [މ<ǓڼέfA 4{Yk2:|£`e!zZ5S a;PM[LW8Hd{iY5*z47$U+' 7BrۑԚ zZkFT?Φo.$lY7wGJ2N=5,X% F|eps"4>Z%$u?*f3N#/ uFYU1y!n9@}T+kC<+1f5xJųm{oܧs1<U!5et)b1gWdL8]tc̩"#ָ2&\*r YV $zyEM/ꋸ)\smsK"eUWֶQ;c2T}+'{VQhC#ZsʹG$4`Bv9 ׭_]:k(Yd oLsjr[4EQ[ɰ2pzֺq{g>D[ptGYNwk :Z$^QRCA5<вp$zASNN~^7U eϔùqV8* N~~m&1cӄ9j┩Mh_J\2- 0kL!L{5!!z ͗ZHK4a]v+zTV.eˈ wyWpI1.w u=+y\bA*F\v[Y]wnG7 SfuNU8v-dx&fs#_YGp;fe29hBI-d2аPX`oz4.+Ո10h o}=kk |f'5鍼RIEMFch;hijKQܒwWx%VFd;g?nK{ Ȥ/[<ɴ[8I%ӂrW<ۊmǗ':x0bh~[oVԤ&"R5uՌC:0;zAs$bx wޣVt>jݽ[*WޟIhMl[ht9`GO:kƪ37r1Q]Dm3;CzQ[(˄=D9WyZ$]Sǵ^KŲ4lr[Ho嬅n,R\rQs1Ӡb^t@E~q@nDK !dҥC'O\s9lJ׮LX+}k:yU $r@"A gެ[iKA}㚙!(Ս GOoz e,1~uL Qȋ/ tt?Zu&VF~ohtd=\KH"ILҺucoh"6͟q>"5.Tm }ykZ>~4esFE_nUhvy"o6*HaA5ANU8קN9vTHxP*>:꾝pT5zYXOo2=fZy(%uVaqũis #Wc>݉fi6!Ucuj# NsZzBJPWAf`GvEZkNn vt.*}mc8P&^u?Z%˴:dtŷ-Rz}H"2ZݼWQBsOp%.hHS,Kg"d )19T %hAeޥ^Mi:$WbG5j{  =}V"Ep}iI.eL\r)HeT9MUX.kZ ŸS'?6]pWӔmWs,KOg3)!O\jDvwFȊ6"I j 6ÏcQ-BA"f^K,9fC*կ++]2iY7{r<꘹jtG#)αE!8nsJLilΤuLmnPdޜu\Ip$xgi 2P$o`:`f0%Z>$% vşOPE3 @brr9&]ވJaf~֫0hOaƭ铴סGCI%mDX@֖9a"0 H%$d_\1o,sUNZfaZyH 2=ږ8#jYrTF:6 ]Y"8=zSr9෗T@YY2XWxnHO_/UR QQrq3%A[_*2}MpZ.w)52_kLpW;H @ تO7(@*qdq>/f_<1a,]S#;Ԟ9]4,S[B#qR0Оx6k*Z۵Χ;]j`2mTN?73$ ѻƻ‚NGFV8m)-c>y5鴚e r$1`>H-`Ui *6 $5Θct UUXIǦjF5S$01h'C=rܐY>,`.79NO5WpV }:I$ bpr9Yf9M+IDV[c"ȡp)Ggs,$zfx>RqڲP{BX=O ctsQ019OCW._8F'-xj՛.${i$%6nW}uzΝ$=HvN2r1vU?I[{ߴ藕cST%ysC4lXʞRI#ҺTE>S[ou{$H }o"_/Y0~nYhXc}])L^2GұT&̺`hs YW]=-fRQ9\ǹGh%"TIVr=lWP Kp&U@sQUNz_AS7W#fFɮ[Gn;;D+.w]=ՍYʞ+*Ky)xp*2ǽZѭѨ$2~n¯+(x+c4WImH}{s/)d-+0G`>0#`ozᲰi07ݳm_ZyzEQ ۟Zʵ{7sS,Ύ݁H<%ؔNHNOܔ}ϼ)_S^(y Riݱ]fIap vJzm,ޮ]KvW=FWfdwc-򩞋}t:EK!e9f)$zETN?a{t_33֢ 6{⛂#f*u-d)9gf1ȎvRs)ʷ湅գZckХo:f>9@7NWEū]}WqWc~"PGyifwo =-d6² qp2ۈK¤*c5'K~2d%O+!9\T2A'O+‚GTc4a7}R6j-(v' ƒ).y=h!h&1Xx;F>\\C V4ːCgVEi.t~G%ԡrq[W剆=:R&$Cf(mY: {zVgڠެp_ 1ךe]k g7!'*2W'0s9w,ƛX ';?{U5%MHr@$#.JILӯsk$ 4nrOI1Hgn㞇csgGOBގ&n PS\0ёGSAV^:[ZM?6W{m&ao8lxV҆ӚJ;,m`,o5)6cgǴ3aujvKdJrǷLfsmsmԛ[Pio)75T'`+r$/q jɷtzU H#F_I'U0ĒʛK zJQ1N:- iC /oέ-Hʈ1$_AoIu _*DP̫%W8PX.-W#CإB @lV('Y1ةlUWVw?FsW|"dD8huJk{HelH,,Zԅ6wfj@{[\˫OH:8ےˑ}G>AInmˁX9GSU{fe`v}5$DYs%0A_~ ]Dpj7zsraXݰ,CSiϙq=9{H߱i"GW}͍' ˎJy lӰE-s}bưA)S,@vSOŵt3nŽ,!$̸` so)XUngh# ,$TCqd6/~=s\7h6C:9k:&8d̸0zbuA&3K&ӄ:rxZJ ۏL~%[vI=a͍u9nU#cpiZrٓo)BATP05̻Rv~ᴎPnmj%4}:qY?˵noKѮ k9PN ~ Zl2k2Ke2N?U~LrD^G(] 9V吆({U="Hlлe՗ y8S{ҜZJjpT؏"lSc2=2 Zb)?)]nUv=A{%ۺ,JvsMKE2nIIWwzV(bT*JnÑ j9##TDd+G*ϯZ&ֽ;FVdpڽ믖m-d Ʃ c>u,iHmzuN})ţ?E$q׏jt5SgFzp1ֺc*PwvTՄI xž*Wq)d@軣0YH;U U[Sܰ\r?U<&FZjYA][ױKvxY \X}Eͺ"*m!?5ZIm~bFqKjЦklmr}4X3ᴃibV؃ i,`VFP6H6?.S>W} N.GQÒ ׵VI.Y w938;U9;W׊j2Wk*"aIg#wsRM`YUHOQL6tӴY̐f9?aW_L)GCߙwAu,{]L *y"mhe`zZ͹[6B>V v#.UڟN?]H%4*n٧vz h׳} 6z5"EOGFKqҺ{,J,Q\ް*E#2KqjԵ|c x.wI5G \խIbo6hX`柦 qA,Sӿjiŧ_Yw{|j۔6:fgtO?l2}ke-*J$. ~[\XYJjgV8f'>H=Ƿ"Ozѩ2H7>z5 U.w|Tܲ*c2H$r{Ա܏"Li[k}⽛#Ovw5f#%BO 2A/ُOcҮqo1A(q qIcJqKGrk?0̖,,AQ=cSdS"3JƲyryC'T%gC)-(>QIr?R՘z-n+\}j +GݝgqG1nG8k)E!.sJ'ʓ_3.KL%p:nݸnF۔#vx;kC4R\axdiNI,:Ǖ+5fcm< PU ֽEMTsA 2c^w vϒ7 ݳS,+ Zfr2Gؼϵڒզ1< N^¦mͧH]hn.)ӱFwz}j;,Gn!MINA 8V?K 1 TRZ3r]^cvU8$\|]r_I-m>QJti <6 Oj5{$ZͫFuxʦr)11yXV4링Ƞ# `1y9X\r[IeWNd` 0r\y[YTl:=k,j["HZnh9]S*%IVQc,(dSЌvg "bς>ݞU $!@ܚ-{kR`ٮM^[8cv8,zZHXeE7c,=k*sOb;>LOX!]nX(eP1޵?baE;sKڄQ=;Õ;Yrv{VT ʡ*A4N i۪۠e䷿ֱ&x%uedQ攓j$QgAxQٛmBGޭ;PlCO4`cZdHX#"5,sF# ,zbaz>r'V =k "Nxe#8wJ̱W@SҪjhK$XQJAݒ^LĶ[#kgW*TmzdA'#vҳfޞLuc rַtܕ2vHV[r&=}`¬叕ҩGm"K ' Am")xWzGsHxI>[FۃAcҫ}hpFwñi8㾖-m:qNխ$YmDRjM:t.Ye~ q$ i{  #̤rPdzOyϹ֞Ee2?QM.K2;]DC)?(RY2GVxG@v^\`v󭦤֤J4*fUq :+YF03c\ S&t+)- r=qK{uΎFUgMy3 WaIw\$7в >7ξWy< 5X6p,ѨmΠ>޵bZ$Ǧ?!Kg8X`C^a |ͳ9,0h #ԗp,hZyi۽h F܎2MUmA  3Y.rNF܆BĸnvKz&.7T6,d s]JJ+w6tc=0z= HqrNxB 1YVs`k)֦!59&+0 ~Rz&ny]d%8LBd޲QB; )na629̚E'SB䟭Gޙxa9b7Sd?1qsZh&%NNz=IA県vF9MXpJ*)8'qּV%EJ @CBSG5"hu~b"GW&,Ӂh[G ǒ=h͵Q0mi@]F]8 j.%0*0W;j4zDɆ a$aAA8jRnDq nһ@McQs&*uMB{o?0`FO$փw,nr d0˶I`[Z4@QFw`߆޴H-K6P*9 ہϽU*1w"V(j6%_co)-0U,'n@qoEp֮;py٭6.ҪY:sjo ܶF&mFpO㪵3%~x#E x 4gy,]œzU qm. 9 =.#c"[^Ͽ6J0: 1? Z\ dvXIHp{K`kr >sN̻alzgiҵ[Ɏ3rlm88K-RDF=kbyL$R0pF odTxfUbJP|R;ҧ,'BŹ]E424eD2!<ߚX"S͔ ^N0sXnpl=s]mE0޳9S^b*(XAo pO=>E`7y$9$`А}]4eBy8mŽH]7ɿn6"NhN-_4{a郂:j Ad7q# \{h۱yL*)UJKMo!ՃQw] Z'!\:`r 7RIу&I$җPwpp3747s$n ӯ>BH ”vͣ8ڨjw!}c@3Qk7a{s <[Fqu5[yԚ&$i#g/5{%aasG'9Ԗ{B3&qpNGy!1|\.͸`x=2fj[MFMe8-ǽrĺR^Dgwn'{UiFGң[Y0rT`W7+gV&HpH8rHU_KlF6 WQ`& )f$DrXI(+8;\s8ӭo;IAc}sVJPrN#Ji( K>y'eBc`S]m>ڷ[ʆC(dcuӟΫm̒5ʃӟΦNDNa]=$y Lz 7 ^BL0 BӚ睷"]J 0XWvҺە4yݣVBg=9^~u{+Q'ִiAA09,2XWUFruSԊN4cZl0'gN?+ k+x򍡹ٌ?Q5(E #ArkjI3{PHLaJVܩݲ~zY\ZZ(͆x;z[jK2V*sk 5ff@9>ƢIeB l9=}TVRd-$Oںˍ|*Ȓ]>n$3x Y0N+V9%e!%0ۗI栚H/ V(BDm$uYÚ2T$ޅOyl1[9{jͩO"< n)\?*N/ff)Lg$ FIeQу?`OS[ʌ6[ubM?S[0U F8+Vo_4@'=N*quLۑu=V)$~Z9Ǡjt}qYsĬyɑQsWKBH)" רηl`h:;q^eIm0%WwS~$g*2=q;=,?2$ڪ&8#9RԞI[E+0ߍhJ֕+g;R+ZW4(N4l ȡ^zc"ow\0adϯbŊٍ2ajymV(4LUrpzjT&SԮA HYۍ1=xHek輩-MG9ڶLWHvg"eVۙU#p`xiqv)Wk)r>Z(^)dRn9}GViV6VA7Td[Y'KrGAױڗ/3E%wx-<n{C uBư|g$N:wЗ$ޅ mpbFbHp)CKI-4'!H8kqkgC)*Kcԑ\֗uc2gmH|LqJ*}VHo\~u*IY<-;DOp`߾>鐺!~N?aXGj4L+K1uɮA5>J,ce(R>R\7FU0烌Q ^{ 6Fh\x{S! _aЁԱv9<ӦJv1 l#20qj𽬐Ht=ϐGɬ_I FN2:gֳ[MiM\v +99<?lI"[q 8,rQ{swq'i+>'zĵDI~#,NT{~bQ+'r 0iBv8[[ $l: c\[E" =OҤքrBsB$xϯҵ\I=$Sԋa>? sqgІ;IQ}*mrub2zU:[ M<xSV%+)1oO|V2#S`+Z+tn-9HMOA"G+2HIOkhœv'g[XH9 ،{RByrrs? cBeU0O9Gi†?P1OYr'F[ζT+tMhMMr E;,P [̪s+ힵr$}: h20[bbYH"y6r\deGoíC8@*"KD <7S|L1’Xc։$&:ݗm\HXބ`-*ſi*%-8N3ijDؖϧST_Pgo5K<9Lg!Lҫv/*3# ׌knXC4d۔ARN VrN@W5N1G';T'-#3#GGO]5iÒ);45e=0IX+!_-m9ӏ5H*˴v"UTG``vI'?- ifU7NpB:En֒UǡSnMb*~18[rvl#WN-E{}c<;iS3)Vue6N }:s4\yR^\ծ*_5;[;6er 1k+R+Tv\Zr<~Uv ~`~BgִFR%=t'ibyeI9*Z&hnf@Z4>P R=kc4;%բ!ӒA;SGnvƍu0?8YJ[CI ok-<3!+i Xo8L:+<8P@W\v;,wd#OƦs,W1VdqORiq\M qquW#s1jZ9.Bn̉u% 8S}k"I1ƺ 22jWh %Sj)[{%c ͽauH* t-X켉ywq<-fd,[r1;N.7mX<m3Іlq*ʅzcIڤ2q `yUiFVހsOZV"sjJPWim!W#-?xqCo,$7$a]t'QeljɁ[c:{R45&Yb$ >b7)_j ܠsv:ܤyRco9U5Ể8F)^)HDs1n`gDij\Ay%NHڵ^Jfܟ.Qމ )+aqjcw|ұGJy.'nE\u⺺$dd:0w yAzFʿ"Jܶ)lDή\3HpvӞUo0ȾP?AM7H0G";gZSLDXdVn0"9,1'ޫ]xѢQndG]JSuepy}*a1U~D$)Čǯ֧a c~AiUWWz2%-^Igx'j灊ZySrP'=-Ԫ̌ ƬX4#!ы2iqf_3M-#"nF$MFd2FHZvPn%W?\,N@_FX,~DV#ʰ}sOH޻}UqtL.e1²ּy  Yk{ƣVg6`qЩ ԕ-on].DAH\[~ "$m}#>\ʳ3yj+t ]*qSn br=s],R`9"Kk1W>ںHnq+m:MKq' ,2khdwy۞9kǝE20=j͌0mFHa;.c2m-eؼ U5f%w c~Q)[D*'4sPPiBj 'A1ᔕRܣ[J̒Gr"18*ji6)PR2xE<\ t7jUiCdI{x ?ʳVI(s&T/*xR3@yp=\dbgvdXy#U )mܢ8s隭 uu#qx^Twk]0ژ#uz VY]L=A̪ 6Lu9-OȯvC,{Urxj v-Dt$y"hUEx>^I†Q*EJzEpO1KP.69NR*!階1<srn!smG>Tch=Q?A\_*>K)`'z3mg.WbI=}gqNm{ҿt XznrphJu}jZ3+C5s}F%bBYJЙ&:[Gpr#z{zˎT+m['%}Lԯ$)r5.۔G+Rj&`kshG. vĎ0x\U(ai#\0(P8EOy+K,R. HxpְWWPs7T%|@Hs{q<߳iTÎ{~`o y2Z7f @j%EnerA2x'YvҍX]["X€:o)^>IY14\>ᝉʣN?:1&U;lbsW%Y$UYm!ǘp?3ٸZݑ cJTǀ~oy%\89FuRF~oN:{V}vKܙEmyeTu$>˸AC;?-)"Hc}r3\.Z6鋈mXC @x梷[,D2W  8Q9.ޟ3ǭZQ"Fu]!i)'Y@\)-QiHω(R 9$?jAcdq pnAA:;yXU%C: ?*ImVO53@chB;+j-U^ɠi!޻o9;?* ѳx. oT`g=1[+uA?qXՏ*\FH.F/3sǰE~օd{N3x 48fڱpv}:K2ۈX)WvSBN;NlA,4P;0#ڬ'o:Dvn_gABpya-cR5+rOL5m^bf@s$ ڦѲεK0Sw  4P$rY1ұg-e IQ ҷnwZo 9 `jnC]Rz+k;\6A'3ڵ)&-‚BżkgwLe?"?ҲQ+=[< zUҌZ h: ^8ݼ+I:յsGMtxH-:Ƭ:3oF`0`THpjF?1K+Ӌh;3*p]+< ;&Bi\u;uZ(wE'BGrGjЖWgz;-|$b6>uώ6\ p)mӲKZgA)h&W9)rJCnٕǧA5P*C*I8'ҴB#*F0k' ;Tziԩ&a%0N=/Z5,m1FTc#r]x!NҔTd]'xhDډ-ۅg9ϯ bԒB~Pst^v%#dpsOJҢ,Ki~$mg ck7IyZPXַC5Y1!G^ƨhv/k1q~Rت[=OU`-2Zzx,@zSy?^)Up( 8PsLjGSWM:P Hzʸ^3[՞h3=+Ut˸\~g[퍯,nA ڇ9eb lGݖ_Kq. ~)Qo$U0)ހ{GK鮭V7xOR{X:Z2Ici=jH"07+ `*R.kDu5Hr( u{;1])}ȮGWC=qjnsҹ\e] [euy 瑚 O+xX$8J%XDӲm`r1ʲ!DHTʖweײ6WI:6}*WU (LsZXSfwNoJ$R[ۗ d*H `gT+.i'ubEo21*݁Z9#xc#,t1[ZO?:ɱTQq#%Ǚz 3?;JpuɆhȂ;dc41[J'R1=IOneCOA~udbkJɧXK W`'F :KrMe#](D,L>X̀*;V%*[`_n1%ԚH՚Fu$zSw"`6w>IVOKqyL3;U+eY F_fWZ򪉶%عXVyW͏ f7 da?]L̹9 U_2>nmBr8 SqMsG芖/8qQ }0@@ g6sh}Z@4bJF?)tsI&Waf Z]˷׽h-,񂻁9y>U+?Y&dF^ߐ FLS:]b}r[΅8z[1vm9UB%9cQJƓ0畍mn{µwܹ̌ugEM$F Sֲ+" ːw [h$3~`)mDoA/mJ.mʹLW>=ƾ%H LbF麙\@"#W%T 玔㧄yUq,O9(j iqFg#2|vi&ԫ$k~5|Y`IBQ&ȂȪьw5uvY3\p TOoVt2Jo޿UxfWN%PHIcte`XՔ& FƯK$PG[nBErc3WgLUTtUmjr4b<'1c5\*[:[)Emt+5J;&Xm@z}*dH #1f__V"a!"8cgtv{I d40ێy-.V]) g5mo_)yORW(z22,9_?:{H呄LHRZ,g*q ?ŀ9_cj6I%څᓀAiu5[V_j48^N"max>Nj)]rUWt-nSBH 3t멈ʰi0zr:})"16h瑎).bpa؎,j,V u)ꑱ9w# w.F0q*kY.?Q?HܥUc6|Q-x {u a9*֜0 GsYΤ->- ّƋs8B1RX<-#=*R)9C۹HѹbI뚬qR^.I֪,O*{+ ! e*Ԑ2i,DX̸W 3! %FH~l)\8MD[9S Jq㨛16'CZ"H$723's֦&%r)n:X ۰UWҜwqOϗQR>9ZrXNf֊x@̭抮o"ŊIB<Q c}UkXԖI zyMN=9_]6Xs`ܶEyJ)Ǚ o2n8/]>a#OIsU4kRZeP_[vzM} R͒˷ &ўGhn,Xѫ!'D+4HKXMoFIYˈ`c\[ԋ-͇g {tZQAt/e' /& 3c*mIoLo< Dt͍yiQ]XӦAI#˜4wBi5)P>bIssJ1km$1y`a$gB~fnr}:~u=ɽ%CPx\Py;8/Y!H۞ST#{ +O$vbo;cNr{g:9fUY.2_%+lWΎ{`gs?6yXxgE̠`8}cѯY.Zg.D``ۓT"0p3b}8%i &6d/'r9yUiCqG~RO\;Eď(F`~e64zʳ mKVhYىַYnHW VDx$mX(~o EE f$`q=]ZGuy@Xn7uI $ze#;C= 9&NM9d",UP'15ļn՝o4w+l`n4oX6EʮW&Pl*S&F7|gǥ:+[t.gGEgxeߘ)3)=v6 I*x?Sjn-B LDd$aڱa][H ّՀ9ԭX$QkM*=1Z(({q;׏'/̈N3O ߀@Pi(`HdR [cۻNm'8m$7dW!62Zf)wSBx|}i؏q~MD' ܒI=1W徸MYؽ1@KyfMYd|QhҷB#{>4ۜ-2psҤ$A\o\H<ҵpI鏭J֗I̅ʪ2*UDL/I2Hm',s!a\!8޲|{Ŕ@g?iӳC%1PJ)ػO2j^şi|HnU'^C/pDm;!aaɮވf(vcY%[g8\c$wjݩ JR[-J1(˸8 LS|?vYĄsѫSXE[791YѦ>h9ynsz!7eW>}j]rZhʓ?#~*Kyn|(!}"6B0F:GKZ˱wkl֢)] ^GӞ=I4,L4bMW1P.s297/m{,bvwro;=Ш>%0H/;G^}k/QeEyR+=[<^L_*Y@$'ilfX#+HA=CZ{IEneNu;Id norqwdR@SvT㨮):R($Ab3Ӏֻ:_:ٛ`GCR[cc֦gu|#,2GROԓC&̓9GsV sb Nw8\-41K#'w } bKsNSIyIќUo%MbD Z#Vd8a+y<&oG ^,ݢ5Zl N1jϱu2bbbHqRgѣ|~aqֹJɢ̑,] A'$z,37k\I8qe!6,Os|>Ru %®ÿⒼ;!Xx$,%Jc~cVYws#Os353GqF8htu&?uu>ԪV&InMnl7(skR k>j tyrI3PFW F- KHnVYB+o c\$sX4 eD(F@'Ah,W^C, =9cow!Ywa ہAֆKeq?Φ\biBѧ d:lrJI5'\Do$ m?wfp?Tu-9(^- 'kѹi_21 ?68V}";H~p}1YsDʘ63ZP*#i'y#CBr穥߼^ C*le)(w9#V..>P@$<.ugTFT`zA^y|Xcom#ίS pr;N SoVj\/s(YIa e{Iߊz=1]a:NWpO]zp.RmFHTm=^[,$9zO0ͰҀBWX"M]E$j7ddp9:4Mk$͠"Gh$ zqSn/dX/ Is+.)( NlgdubrtNkA2\a>vVBTrx[iu!#d+ RcU8 gG!B%+qZT[MkftUUڻ/My/$d:1UUf؁.AR"U)4MeWT&K]]M C*ɹT@'SyE!wlUCj&wrh,OxѬjq;0ηiB%Jka4.`]}Z[ȑ$YBB< t4Cp2#c#ʁ>N+io_*,ֲ"r*$D۴3`1$l ji,L[z%oZS-2Za#8!}~Ʒ.0 QYEiA>Wv5aIT# pßZ !x FH1UYP&!$÷N-'RM  gww>ýr4 e|0@yWOc)¦p1~fۥ>`7>zW:+&}oR?Gjet$'mW5z?SI2@Vp*ނ=1Enǿ&֖]xlMt,1jͼDvU~nO*ĭ,*#ǵcOp<~IFopCڰw0i9&-Ė$*=OkW &8qSA"Q#C{Tr0E]h9>~_E#JzHB7W6O_~uwP1nH8 ޭFT,3F>^2kQvEv6!*TiTI;2]uHc>&g:BGAT{$eb ֨[yJr6tLvk,v${c' #&JFIoiw6|lϯJiɹ!}´uT))Wk{}ږ% u9< uNs<ܐY[ntܥİ#Dι c3 ̷I< gָw՗rЂ/a2lGfY7joOD3AbpR6a1*7c|9iwԉk$?J<[7>l.g[ c yُ*woJms< =ӵf5mNgHE Ð+uMM>wx1ֱtF eM2kZPh)#}688})Ud]ww~VG ~!0Z4EIR0=0{eȅ^}GP[܎0*R>]hF{{qms$ݷSȱΑ69z0$ p "&Fv52}Õ;28^ƞ7 p*ٰA=:|$~ZmIJ֦fϗ)h瞸fDAIRѐyZU#,k-r̮1NNLjhjT:FGl# Nio/`91Ny ҨKx*`Qz{R-SzjhP\Eq|* n aUu /dI4J2G7aEk<F {Uޣ%a*ޔЂwވ5̳͹YI5n8GJ7Yq3yBa,K)9PԲ0"CZޔӕљI,kx] WqZ{ G?*jòvRxZ|Q2Dskm#mkOwZiY݃/sk^5˪̜|H[3## 6L1zWm!Hٕ9=?Οm8s#gs_  w˹F8'EIp Գ%ݲrN;jk'ӕ,r2}Tn1q(p*1TCihjKކb7j)2s|x%0CcOb@+52䌀CB4N *F )F.V@ ֺ;EM@) XtFN9A؏Sںe{DDâ/SuZԕd IkPY0UA9 bˌGy{WF(ӄH0M,$Xq[1ޠs xVfc*@YG(SfIJM!1NYIg~z,,m( s}[e, F9˿9^(CN7itʻU Sҭ^}"v,dLs}Du_9lorE @rXQLf=ϻ<_*@JwSI0r0\!=; 'ء,AX_6Sæ(k[뽶94 F vS8,NC0Y{&ՄOpU1OX6N V0#Urnfdtxci]Ow15ͣ,e%hnKdp³|+V_T[=1N$r1OZԗP{.me|l<~t#.A\99!U33]#=Aj䟐Jcd ;A#{;ScWk%pTHy'QTQH|+PGjINz rކr> ,?ZTD;?CDZI$W3[fE_0"4rsU"-E𾭆;7!z`&dSkujVEcNFӧCKq4쑔ާc)P )A }'@\ifVp|Xr8 {WLWcZ~ + g6Iuptf8<ҶF"(Kds5y\yǖ'Eqo[~cִuS,[7f"bVsepQG3GhWP{ .ojY{i@"(OOZΝENJMi'bϰ#XCɝdb9 qg~Bʑ09'nO?o,[ .Ta k:QLaws;TAy>Bѱ[tgS,>rFN>9וV+Dŭ_u76IE%ܦYjPk$ YN=QzֵjLJcXb9觱(c4871Xkib9˕"DN})Ÿ)4 K}%n޲Ll ;wݸqNq[+c_4cu#ٗ,v'OO¨Y_BB]@A^J׊Eb"Ť-rr?Š*m-9+ZMgs٭1'G sJÂYD]VFXҷ ވV1G=9W\p4x-~t-Zs@H` X mQpǠ˵ӄ7AKBCduc@EO^yRFQ~N2殔n T%էkdVEKrO*֬,밡NGQ޹Қ$M.9}O]ELdWd3$i;[r "va#PjW1y"^K -?]OKX<6G^Te2¸>Qc뻥Qmpvv6 K9!F6Ja5ʱYҹ ۈ+H,.-*xӚ߼.m*>aFIqS JtrVg @-%9WA* UL{{U<64bG+F9_IСN^ki7$YԚ~HYɱvӢSIm=3iTh$jt0\$ΫT>%ތvgnqV-执0ogIN)ɫEt0v,F%]r@=jޚU02P_#'j |Nz;[bg!A blYRrR$cԥg͓XnrF0w@`!c*dnَ\wq()v%[:bSR!~[D%UKu0zL[YgxTY_RV6ܲ??:f>n≧P0N6g5+'%I?RۣȪ6rjȄF`0*k);̭%2フިGq5P6J<Å =mM{݉DWAdkq% 1#g*"AEsO=6#a rҵkk7e*+HLMc'gK屮`"Ddݜt5]Ms|tVdr0_~(d]Ysgh׉.̉o;H v#Q*Mq-$ n1neV-2f KpOڬL&# 2a>[LKDtZ0r!ϕFi vl<ŽHa5NܲW4 /"kON}c*m-Uگrݞ,.Gr#' |~M3I+o~fsdlJ6TMF:Yz_C kY&HW=Ƕ;Զ7vFO04[%BFQ[+a02 TfJCѢn*WPU;O9miȐ@9ZQdTۅqq\{sZR,p9`==\j]L''b1or鎙.Z݌3%:=x& @C~d޸\F:M% 6&*FJV[*ïj ,cV;UὄJ2N7NxEW(ɷK-0E4RXpǩ\# UQ^=kԝmﭞ.[}=v:ĀydYtTl1>'i 0s#+2gȥX95(% d(dv#tmC|2rx#@9=\EXeLJ37s @6ޫY(9>a Zabu֓q{S(ʼnw^yT5Iw|ap^e{Uky#jZ-)ɜg&3!QU௽`cKV\+FPfzkCq-R[U9ҡR#*uctT9 zV7 B`gn}Q`._!}VXHDE@A°,~U%:E*@00Ì~Udf;&g ?2:[Y ڱȚ&Sy9^hbLn b(U!LI{X`=ap#,; ~o+FSUbN1{VlI<3s޸Ruzl6;;Y SIb,#Ri/wk1`̃gҶal=4R\"|ΖB3p9w! I[B]G9J˵`0Y Y0zzgZLºvN$LX4Z/myŶ˕”RXU$%Es8莯7ب'=ː>JWC'105 yἵBHڧ=H=rVlv W#ޫM cQB+]EE @󫺁;o60? ֚28 ճu<>ax$ rYtMxK!mQcoY *SFWn\Ii $n2OTkcW#&FQNmIlhYB2X|4Ѩ8%yZV#Y#QVnHhXv8'^F?:BDÕ'r[:Iy<qEId$]):u,s:&웈$RPqg`!32`p۞m,WiV9RР•j4n)Hֳ4An0qj\qWV4WW֌z(887Mqj:与Dr8ǹ&a\3`K p3C'kk_JN{ɔNX>vJ˹|W+-V9W$-x̅ F<6zޡ͊beNsTWjŘep-|ϕ2 FTi\;wK zymA-վP*}Gҳ%"bQ03*ɷ/fH8>pkǯҟ SIEd:GJ2{W:f0we;TF| 9涛YpⰗI$fvxaF{bjmͻ@8ȓ8yw3[o)\3ZSՓVю8hZUQIcwpb=8`S]mt湍bE̜cHXk6]'M%1}aVo@m*?zHk\!U\tp/ڼݢʫ,Ay= $Y/:F[pGaVũJȷ :`R'dSUK0G[{T0KQudVq ȸ9!YwqyM5""wҬβnF  ZR+_dm!C*`9a[ɅGpźz @R(1~cB-u.QgLW*x~eCʤhW՟3f|@T!IY1Ν&fNos:UT ƗG;.dAR{/I x$N %JWRf^gRӣDL_J&7Wb%l zVqcމl#)$f5^HҢF"H9`pMYัhI# }viBN^I葱i:[&({-n0 DASU$$G,昷$&[̛F?΢I\lwvV3 iEhQK5eTҪHgtʿ8# 3Vی::;FG h*%tc]謓J2̇sׯFG2v8Um?A[u3S;~2ml1ȮrV!V?E].YCc{`}+.C+[8, G{zWIIoqôOՅ{Mg4?JjqntB9wHI^v`SgO%ΆI`r@w smz-ջ|MHROLTteiϧN~.Uk}6R!B,d#AnK մb}c~jbnL<*$9UW{;[1v[Ё9-GʓХKsi7+។܀ٮHP>p^QrIA1ReI3!pY}]8sjnS6(h^]wRKXث'|'U?$ ,fI?$LVwp@>i%u{[̮nb~L"y+.~a׏zX- KG,0>o 5#dSǮO@:o%UngR&zimeȁߔ׷Z+m}\z{M$^SK kRfD" 1=ֳre;K.%UEeES5Aq.gY |߬crEc<\ZČ`TuR,I'Uma՗).&'pq?jެRs#iǯ?qZ͞ !I#ԯ.^p͔8F3U*nGV/u=1Lob#nL#T+㑓ʝ2[&tǛ&-5w#'O€Tf/yCN6$o.Ⱦij䟼@$TAzĨ[oS([fqJMgy wQwnpw$Qs 7pw;]* +7um`05Ew[yGh㜀z=+sl:U?߭9J\ƒRbjYkGPrp{:tj!#i'ڄv$p jA4w*OH GMi4V4-~:P 06jXK= +>rFSQıYw'qۻGH dE*|v$N&vf? 35F#ڦH!3t<LYYTI~mX.fq?w'QZz,]7 @M\齙YIVq-234۵[[pF܃q&En^)ܰH J `~u3sk%vw3ђP!m69ۚX\c T:+z9YpJޣ"(RB?0>V#PL"$iJH;Ia,pHH F֟PAFLO\ȐBc<qSr>k14XmŃ`_^3w] 6c.t5֚XeXkIrC20mҝ:^ŵes)%2FBԊsdɇ@1/^~5I@`&$depl<}s[BQF9SnTI 2h0M8ۊ|ym8bx[ZW%]6VE<6I70r3f\sp2r ̹?:ֲa<':fUH:Mjׂ8IHd=-mlp%sui )`1zpOYOo%ү A NVlK i̓=tk 1xYFI8'e:$a@ )%f T&TcUʤ0;sV t:~GUyK'hX`7v9;re̜E #99L!W*@RUE%Wn2Q$qym. =MB/Wо[;28ER,l;SoZK6ayR˜={GB>mqЏ4YnFXmcKOS=9-PFNAo5pG<}0A'Xw9JW!G0Be#3ßcPB:FrOޖNN}喷8gw> ܬv(gޤ?#Oj&aD(fusY<@N_0GSV2\p+!+d]I3j5=LFX[`2N=_XE1urz< AN67S])FuIITxZT2;m4WWxt{ sYX9gp9< ң`'bI\gEdyl7lB)-*3e9ܽsVy49 v#VE4f>[ao$>a.՞9Ӎ|F$v>zKFIYաlE̗-hrF+AD"rp֮)BJ(_ʒm(_,ss鎼TX\dB!*mBUZ  pC֣Kjj`yIQrr9ڴNUfTBT*Tc(cU|pN1ٛV7)đ+*%#{Si&yjx5 7hψ\v5oO"ؘc]sxS%nY0s9Wo/G0-ǥo跩ujUcm綽0lsaoNrݎNP$ >NO5dV,HF9]Bī0WpDK/!ge$r4׺=. HpX:K] FX6853 *h6[e^[6Gw<)h_N=*ŶBǧ5jb}}Yy'I*Uzni&u&0y[^56?>H9Ea.u,40>A}e\GtBqcWW(s6tj/CPhwÌְ.F{6Msi{<2)=;)ɚFl5t0툕BZam' TBc3 L8>C%ԨUR~auBZ`bL$l(wciۍ=N:RZ1@2IMr{BvzJwqi= zlVIX>bFA KU1I._.b.{Z24m]PsʊՙR+aܬ'UtIdOfzUC$lH+ jLOt#`{{֐WZ8sj*AΝHiMt*>bEg:˘n+B@

$p$.\ _19kg\[ė338֊~:L; qdw5,S+K4Dmn͎)8֜M%)r+:5U" r"Vz-kȱHq2bi]-Ԋmm1rU[v%zi-WA+h+՘ƛ`1Ui.kd:jiDW*ǘ9O I@Mu*:nOn"T{ѕ(:G0V|XL.2s{U hap݌|+ڲp +޳PR1z6mroj‰4TdF?7oSXiKĺ@TNQIwc,vz]))5tTk);ɆSiY+`m1#/'B7v0֡"<*V56hZiRcqpq0‘oPyn0ϴꑨ 9=Oj}8tݻo5R#>1yP@B|laGzͽRR$Lc}j˵,1ڮ,!l9nJ4pK.rAZE'1NVǞ@f@#?Ɯ^'SL* %ܺ*աx 䑎r#v'c \$!K#ddaO%ge_(7VAFJm];…!TҌ׌v##-V"ʸKt99 ~Rs=eiItJIetђd9jf$$D &_-wm$uvD2#?>c6v\N$Ho5+`7"1OAnm=? ul"f| PoVlWזm`63 154P|VC!'ڰ!nD(X+ںJZNY于Dއ5{rXӵPdT82K" `5MQŸg֥n3| UX@2I=g[gMÞd{~"n^>`vŬLc=?:EUzdos%qi9ZI!-5dṁ#ҳP* n~$ș[#Qo0gL 'ʴS\yh"11ޖW$}9>Ru 8aQ6 2xB"bIn' 1n &BU;O& {ze*4^رV@I4Q5dsr= ǜ{RM2ev˵ O%zƢrX$<ƤȍȩSo=>9e*89Z"I-@d?(=9MF$΍2賐*Iw.!VR8= VSFrs%)%mK 4U{ycیv\f08~u_HU4x#v&l*ʺU覎(9aq!~^?qՔc5ۓB'X5b۩ULķldL,ZΌ6W!IKX29>]EvO!p(ƱnD_hEHZ! l/#9?a s$lN H&c2X6Ԍ*hϖh'J"Kn!}jKi&Y$)_ 0#iρV<$Dl t@5tc IXIh9L98+FFxWb7s؞"z:0/Ҥcf /z EԖ+2a!UOb{ IA;:Tܓj-^* 3:UmSCab djF?3i̬!V8>Rj m>rnC9<`5MsZ%xF@Je8c 3A$ꖣPJd269'(+)?:I j٣]F,UrÐ??«!s,ɑ*=e) =ɐ;`zI7 -B5|OCҳJRv.eV{#R{QݻڌBĬ9Ztfէ2@)@a*ݱi"+$v[i܈.2 rn!iDs#oPcʼR0I8CI[:R[Z*&@9RR{wنV ])jڹ_x.V\]Ԑ|P1vO%R,<9ۻnA?e5̫o&8$1QV~;KAS麅[0]Ӑ2EMqnb3 FTczsUD,$&YE榕)8>MW_ e I: `0SZH,a'q bΙlKFE)C*p0;UY*Os[<nU%F(-:l,Fu(va8aha' 7tڂj^@p\]i~jN/gĤ=c@)QӞJsb9?zZsV8 C7%Mߩ&>Ð^GH%c5-օi6;0/žO~"|*¨jo0I _ҩVCWأiqݼr +Dcf0z`f*H_*i&a"`}ZΧ5K#*敢M,mEBՒ YW;D%3+%pC{VmΡbE;Շ|֕I>[zFZbۆp;`Y){B%9Ժ|qCu'Ut&|r> TLԜ+]u0%A.R]NmhうPZKh[%\˞ \:&J)>`,FB$!3=ˡyOtzt"i +2e v8L>hFdO-Y"W݀:&v=}8. p&JƇ_zuѸ V cGm?j'drfkhd?Cމ#ܩRbN x.Ҿp(O֡D俑LQf9bG>'b1Z~mC- #$ciP-?m,ZVh>b]RI-43'}Qs%- ^Zi#2G^_›u/G4$$f@|9EfD !l[WHHNNp0OҪIl rpO=~$,v ˷{[ zڇUNOKw0FY]Nyp??։'WiE&Q<*νed )#;y<³y.%ƃ lu'$'Jy,~th>TzrO?Lc7׶Ds-G$HR q¥UQj$~0AҳQH^\ƷF߆):],e,0Η7hpG8=*hyU8!2d Ԅ׳{!6 kdelyq|#իh‡+]VJe ]6 tk:WrV9d)@xM=~ԕ#"2>9F5oC^ƋݨPb?xD~Xb?7lfC n983Xjd@dfr$0qҸ_42ѫī ۟-X?P@E~WOֲKHntќs?C)nr>1\?m2HXY6}}cx>VYnآ"C(ctZV˙ $Sdcp;#{G4ٿ3PE;I^bɉFrE%QNJif'W;2A 3ɗI$`sUssR{-̜-%{+z+H)VJü[$RC*cuNche\X]D'`Qpk6|n{6w2 l8с"3.@=ϠTvhȡ=kHOˏ-7+B8Z I(_{zԎ_OE9b:b`rk4eM*N[kkJ@֪ έ" ^HXG&v Fw1 1={7ܯEF792=8WKRm|ҴI]eEl $?Iᤚ`BPN9nu2_Q)u j `tXdHQXu =mS(f:~Ul?w#c)K/RjJ:[&kFfV+!Gy IJ#3kw]vL4Wa0A99ZHngZtʃ+:iLrM$\W]zS)O)mk!Cy4VBW8qpWz~uJݭKD>idܦFq!R24ڔS7!Z pymeO:;`dMұ9yB-=PGҷ۩gqлL{ Mu4hW`9ϥkSfp{5I$ -DL/@8ڷinVw099(Hb;_ְU.PF=Q5#bn _I`V !AU3[h0B߼VqG#%}=o%G2*D{vҙZd99Υ.^M\J;LF?6ŸJH,ȐF <eś9H2,|nDqT9VJtXƇ9PjƓ#&0L8#p!7:^qRy /ϗ:d0eN4-$Wl n0y<A$q$W VwlFp'RdZ]ۻAՍNh hS"H2 '} fE,k( Mh\XK)%g8>$RBJ =_y!ɽ&`gEiLv(Nm'Gw0T.2V;jae$(q :N=kX$$}FI7Vs$kT]Fp+{@%Nre{ 8q";]!M*ySZ7rd$Յ6Hfa1h@&V;y³yWa6e6 s޷6P+~PZZ$H$j &Iڼ)+K޲F]3SCï<aF\Im N7RY&2 `x>BTy.]TҳvFH͸@\u{+rPD|#@_g"E^c8&L#bm'\Ņ,#<+)J =*CtN#}؟JI+V]5NP|ۑ)J; %KM*:SnU@>u1}b\tUBc&z[1p!K-X:jv1J3ՍIܮmEvI2H;<3]B=09$S h/c؟JI [P&W4-ĕ(_ ČwNXY,IJly7J]y9lC/qU:>D0Rq yRV5M?o*U!Fu|A.&qִU==ޛNWtʲ?7K]dĢ8c_n1;rkv:uRF7FL$+o[x11X,A ý59d1Tw9i[kt9c%OjI+Xd!/`sHgQR[.tʃ9}FYhFTWs1Fsc^.3t4rt9* =ɶXQλQcq#rx&p]QZ\J4N #+66i#*q8D<yY >QǵG6^cXY?3Q^/aCH=HqG^28sJK ?ΰmjnŒCz|"O<ԗ.7 `qKeRR؄VIM Z:!]2)#X7OFD\+glp:U+H(KpyTL*BNy*8-I)ҜSoTfݙ]J9NʬP#+)%%%d'4ik Rd_<SH>b9nW/zc(F>\]!Y$LWsJt8 :)7ぴJCjֹ@,:zTP""ϖdf A9ZfǗ2;RoFKB8 $V1I]dvzt8O'I`!cҢ6 xB?>neUXDێ=%wp̥Zo@}*#o\F)#ՃvYS?'  ڳa|# #O9(WQLț̅u ).)$(c-vMl088kIQ0|D>qeBlsM<@ezVXH"eUݎ^uJY`thdo(giVf|J-<;wg0@8RLVɨ:W^Ts-{DdZKq}{\f#t|Gr=\#A8Vw)YѨRFxB'Oa()&x' t8ɫ= N&-Da#|Kv99ϽHs΂8Tp' ?˥Vs&I<3^VU}&\OnxQJZ۵_wd$>6H4UxP3 rC`{ȮyBqT'R˅d? MGbҳeiqc=zqQ,p\Mq=B.^x#}*j"dI|ONSnX89*#%;X|wn\0 GOZ˂<.E8pU!$B\hIFC{c"j܉ Be~;Ofjѐ ̅sw#Tܖw: H GzRMmvie]6ƙYY`.=fTeffA*Ç%d:γn5$_fr +x2_j[Py=r=UJ\ӳZA6u nIn"/Qn~β6TgVy3V_n߱ .$9?Jx8-Xګ'r5! dOֶcmZ,aG{{U8-7gb.\zzS㸕-U^0} ΕHN:ԼIaŽp!{rqV]輆91E#:ֺn'ZZdʠk(JQSo{qIeOX%&/VB?;,#zſi-F1n'y9.O}&κ{؋o^fmqބn:n`Ye'!;5υ h98gKu <%DdpڳWwy,8޳6,ImӇޤ?lH$q?+Iu]$ȣM`HLQUP9lXtI#b[rsTQΑȿ!س?nV #\1N I.umi#KdpNq3b:LbxvskYun_l}sԑݤ R/K[]J(,jxqU$h.ov v*u3&'k qӎjmI-av%8i+ݭz\ dipȠ23O$[Z,NFŎFJ}E"_knNp>ߍElGuo*ܨG9et+]oZZ"iE['?JnU1n<Б~&Jj VpSzX 1zey伄TūdljnFv /r:4-N\_AnJ"zjKk;Y;r(&$Cȭֹe[ݞBdQnZwaͳ2oP$ʶ Mm4P6Ӗs]I0]=d[BG=)|F@RQ)SrM@';)-22x5Nf|z}*c,ҟЄ 85Nk[*+M7~ $pX`1WYAP8AYw3›'?ijW1eSV?oiNЃ9n>ۋkHcG#jlE<!. c_V!$|ҵQPj䤞r;Pk%.jg1$)#onAI:oȖk'l`oӭLX Uusqc( L1#r EQ.Τo\ԼT0P #<j᥉niTt;?JFlS\.Movc'rYX1w=(t %Yd# 6=}5VTHNI)kfQ Y_]zKMx\}SlMH$sjM3Ta󊳦j$]CCsI 8 Vm#|=%V*lbA{5 x*] cks܏q\֟ I'*)bUHՙn.ň❣(\#> ޝs 8 fEit/ؙϘkJjL W-yuEةrn>ķYwg ta>K$t5Z>'X|+)^M(GEv?X!0ms{[7\JZ9\4E[P9E`w&낭ua^wtmxђxlf p@q'UIJ7BH'ܽNmg[x$ቓaU#.$Łyn 4tѷR8>I&WE[-ƊŗCrGF@LnTbe}\;fI]&Wiw^sX2Mްd1OI0pcq8[{5 4#ʇjH0Q$(t!aMcrֳO\lL吜 Ӭua co%2O9~*nC*8:X*'N,Ǘ,*0XpsQ E6[E(ԬWΗV0DZm<*$Vk\90= d+E91)\AnyRݣ3t>U4cy;)i?3v*#qa^Af|+A E[YdVCMvBki$ <ڜm3 V8榰LQUc|ޠ7ZKf#YwH˜tJnfqB)HU<\ݪI< ssִd;FTI8ָb쫆8ɢOj(FƝ E+(Xu[!3sK,J8-̢T@H\эbݟst"LG#IXc~,'b35NviXGFm¬9\0=E7QAs׭ (W pøU3ixނlWBg4\J&ܣs=Eq185O0":XӢmjTEh t98<l/LP+0*0uYLɭ;[-S+].dU6-A ]BA'n*94,B1pYd a1#-܏zXn2IJ"70N桺lhLPM@ 7ȿ2wQ',:Ua$H'mV\9s;D21QsL}V$xpv%b* .#g_,:mֻo]C&nIO֪)tAF..ؑ\׊'Y, LSmC:#[qs '˓o1ߦk+8]W̝XWR#/ݕXco< ֫l6s]tŦ`+-Z26>{m<S;=fqI8tЗ^9\x$񚸖,XPzSg2ѩ9d9gd$!M2nZDu6#"A0/$b8,N:!e 2V{2$y3!11,ǀ:N4~P:Ҵ+gqG5hnbfWڒXŻ3`7B51V'r?ZAn+r2.5|2>tE(LX_z1 gd'+R=Klpï@Vej[IL pEb)#gLhi;\Ȭc,hơsb:U,uUYȎI԰IrvyJ0irŎ6[+[!4SQej~1ј!\I}x'eo0s 4cv{V$2ҸUٜlٓ1۠e#*:Qo@΢DQrsZ$h2@bT^kGB.*O֣ldɌ۷91PB5ݽј)& KZ lGek* FJۦI8Ph-RBWẜt,%%C J˕#nq5KBͣ[(! ZtҤ2r>AUn ͭΞ0|>lMX!+ەdrUUT5#/ GΧy,ESnJlsGL-˸'>?,^Ù7[ԑbơJa[y dDY4R${0H9榎?os0TY ˑ^ԏwȪp=:Z bo 2)#+yCuY|nIHsY֦*-N7,ϳ,ps~5P$sA Ozբfmm[1aNb^SG:jo6)ml@6֊\-ѬB]ycB$kqgKy"]JdkIAR}M S/f)~ur;fb{O~C& v~>.tdbe8jQN.-F̅ԮL2VvO:l7l\1>sOcfp@H橦DA!5ӟ@Y./orIWHԩyoۣ",(1vP r皵ڛF)~ohVu^6xhR8%qU4R^s֪,v53w*̘t  |w0Yr{M }GLԬЃhtسr}*VLVu &b>HpkF r`7` ~im+E ! :ǚh5Ia<)n8QnSwG?Zy0:$Q3)lG5oL2KlD*H7rI1~5kn8}9')E&R)JY[%;r?*kha @8Rwm#HԊߋ?j} ⷅnvWlq$lNUH>+cQh%*I @(01ګ9&\/[DM5У}08d8 ڥ# ,D_8퓊K"jj+;5]'vWӌvohe+pr$FR>VFH~h+F4Ikf?dS\¸CZj|ܱ 7 mXAݕQrmߤk3.Ͻv4@U|ѷbjEFofCl#)Q4P C~̷уgzjMQ l IZrBQF x*`q?gj@Ӈ'rul%%F #K(]3oT no^`E~Vts[:Y n!\~ \.󅓁l舫*>QOFWW )*i9PaBsHS0 xې t_-O?u{Z9tVTj:!C-bTSI;~&"$aX1f<xb@ac197gm?˞)U~)jW$tq]:M(Ϩ%[PQ;rg^ERW֑vi!,rJaIFii +?6O/ l|u[HV%*b@AV c#V}$((` E?(}"*YF{󑔿adTVeey`uJc#iOB)?qȆO.Xl:Py,0`?Jv5u8`zXɒ3`+Lhd=2;uvŬ"a81BQSM^)U7cD7d}㽎7b@]v֖U]Jhw ; :U#)У$sJո82=&d:˜&(܀vs[:/A2a9WW9{MΕD1€V}(Yw*G*Y ~E1[ɏ.U:k)Or\n }31oiR2"' c?k"# SEdYr\SNrRSWw .#= W'iN4J$-=0?,D[0%yfrGޮN4]jIk#,R:T}ޠ3!hfRCۀ{MKNɋX5C鬊y85i-u3;p4f5sn@khrrLV jn~Zva,LEnx'tz~Y) qקҦ>en6 sTO(ݸU,L^krJsTj J̿=9O@FIڱ J#b9>ꑦhۼN y^ǚkk;\IWe9ϹU­uyr$xنGҳ'ƻJ? ַmxd۴|+_sYE&5q#n SniV@o!xS L0}Kp[:rOr׬Ш 񧃃қ ֝"ƹ#MSGqI!8jc- qʫu-r7|p}9S54Zܑs91j[~'VNFZQ,  %\.#,Kxkk 7HPq۽Zc;.qITY\jo 60G ʱ=sNw7=}d~M9VZhoC;3)@zb}.K1H%ǃFqPp5rd[}%NMU)>fRFr_æ+;V.IF6߻׎jF6ҕ>nRh핀*^M\麒Ր]#e]U+'4eBYgsd\S`I#a(&aR6$/nMhOia_ֹ]atܿḨY;\|T=kC eGbO)Ia-Hv`MtGv5XǩZr;_>J)w<:+_fyZ[%U 2y= >\$6w}+ "r0~br%$&ʰE,4n;v5zԺj; #CxPn%rqדZzTQʀ05ť.ӹڲO Z vuq0QʫO`,iԫ(GlgX ]C ')-qhkAĀ~+gyozh3LmUF:ϺKy vҮ99C }Oe9곒ꛎx:Nvkr̭SQ}Y$ER2Rk/Uf7Ia ydtoL]*i%:kI osYL7O~фm$sRijTJёUP3ִX4j掇bX7]n @[iD"kP#k hc;իU:'ԭyk>71Wk}:Gʊf_,HL1푇5kBI";YO_V y1ḓצOؼ]ѕnoFӻ}+I$bΤZӒpDI_SJk뫈| ?Z1-\+R݀tY?oZ̷_+J:g4p~NFGO_E݋mRp5QRI,\,NPcvG)iTtU {n085u><*; ;F NYs3+yNz֕%TQxoXp"-v-ѸUS w#i⡻f: 3 . Z]8LyO++& e#$@>ջGQ2Ve+r8UeY_yK_ҳUw2d 9楿P*VnMİG$qG(,znt\($ҭ N2v&ױ^ $ʓ&F/AVgPŴ֫oZoN~muoF:/FFhY-뚰GAK` +.uut3¼C2BepA9_5s^b9'%OՓK)2'Y٫4h; /L>bG9RE[lWr:$j)ݶ[`"9pxjeCbKZ^T` ޥ7bVR=#Q `9b<B94_ƚ湁'`E*6׊G1,s5|KQ4\Vf&;asXwOSrq*qk ضĜWe_-yǟ>sKIEvnlLEkr3ԘVc;4lښlOl"G9;9pk#P8Frp0k[FݚSʹT0")fΪi+$=ĀI#OQV;(6ǥz0eN0jGK4&)Fx\}T$DR@⣴yG,8Գ8?<+n5+jr `Gj hGk @8i6I5Ѱe|AefPlnG!qm% - #9QUXO[’.犭|bE&[qN["JRh}6 %*jkdY B6z梆4YUsT&*RAd8j{ז"NLcIE>q**힕";z1嬑ilaB2\UZ n3ہ^CG\8"6;7|qɮwU|2_ ƞbѻ\n8ec} 8Vf ÷,#ngJ'6S]&:5Գ@|tDE܉&ڬ.q׎ipۓ:wve2)ԋ*J/'֒Ꮩ׵5?{"دvSKN@C,]߽*:ջ8"R88?tb?ޱ!yg2YU?{rnW(% sTK`N=j6WP+3B$֞BD}LԢVPb s8sډE┑%oz JcGI2/ѝ#7ԾTQ"Vnants-2.2.0/forhtml/push-website000077500000000000000000000004011311104306400164640ustar00rootroot00000000000000 git checkout master git branch -D gh-pages git push origin --delete gh-pages git checkout --orphan gh-pages git add index.html .htaccess git add forhtml/ git commit -m "pages commit" git push origin gh-pages git checkout master ants-2.2.0/index.html000066400000000000000000000302111311104306400144430ustar00rootroot00000000000000 Advanced Normalization Tools

ANTs extracts information from complex datasets that include imaging (Word Cloud). Paired with ANTsR (answer), ANTs is useful for managing, interpreting and visualizing multidimensional data. ANTs is popularly considered a state-of-the-art medical image registration and segmentation toolkit. ANTsR is an emerging tool supporting standardized multimodality image analysis. ANTs depends on the Insight ToolKit (ITK), a widely used medical image processing library to which ANTs developers contribute.

Authors

Brian B. Avants - UPENN

Role: Creator, Algorithm Design, Implementation, more

Nicholas J. Tustison - UVA

Role: Compeller, Algorithm Design, Implementation Guru

Team: Gang Song (Originator), Jeffrey T. Duda (DTI), Hans J. Johnson (Large-Scale Application, Testing)

Methods References

Image Registration

Diffeomorphisms: SyN, Independent Evaluation: Klein, Murphy, Template Construction (2004) (2010), Similarity Metrics, Multivariate registration, Multiple modality analysis and statistical bias

Image Segmentation

Atropos Multivar-EM Segmentation (link), Multi-atlas methods (link), Bias Correction (link), DiReCT cortical thickness (link), DiReCT in chimpanzees

Multivariate Analysis Eigenanatomy (1) (2)

Prior-Based Eigenanatomy (in prep), Sparse CCA (1), (2), Sparse Regression (link)

ImageMath Useful!

morphology, GetLargestComponent, CCA, FillHoles ... much more!

Application Domains

Frontotemporal degeneration PENN FTD center

Neuroimaging Multimodality

  • Structural MRI
  • Functional MRI
  • Network Analysis

Lung imaging Translational

  • Structure
  • Perfusion MRI
  • Branching
Background & Theory
Acclaim for Robustness & Other News

ANTs has won several competitions Unbiased & international

Learning about ANTs
  • Presentations: e.g. a Prezi about ANTs (WIP)
  • Reproducible science as a teaching tool: e.g. compilable ANTs tutorial (WIP)
  • Other examples slideshow
  • Landmark-based mapping for e.g. hippocampus discussed here
  • Brief ANTs segmentation video
  • References
  • Google Scholar
  • Pubmed
  • ANTs was supported by: R01-EB006266-01