diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.help/files.cmake b/BlueBerry/Bundles/org.blueberry.ui.qt.help/files.cmake
index eb0cb277fe..3481e5662d 100644
--- a/BlueBerry/Bundles/org.blueberry.ui.qt.help/files.cmake
+++ b/BlueBerry/Bundles/org.blueberry.ui.qt.help/files.cmake
@@ -1,58 +1,59 @@
set(SRC_CPP_FILES
)
set(INTERNAL_CPP_FILES
berryHelpContentView.cpp
berryHelpEditor.cpp
berryHelpEditorFindWidget.cpp
berryHelpEditorInput.cpp
berryHelpIndexView.cpp
berryHelpPerspective.cpp
berryHelpPluginActivator.cpp
berryHelpSearchView.cpp
berryHelpTopicChooser.cpp
berryHelpWebView.cpp
berryQHelpEngineConfiguration.cpp
berryQHelpEngineWrapper.cpp
)
set(CPP_FILES )
set(MOC_H_FILES
src/internal/berryHelpContentView.h
src/internal/berryHelpEditor.h
src/internal/berryHelpEditorFindWidget.h
src/internal/berryHelpIndexView.h
src/internal/berryHelpPerspective.h
src/internal/berryHelpPluginActivator.h
src/internal/berryHelpSearchView.h
src/internal/berryHelpTopicChooser.h
src/internal/berryHelpWebView.h
src/internal/berryQHelpEngineConfiguration.h
src/internal/berryQHelpEngineWrapper.h
)
set(CACHED_RESOURCE_FILES
plugin.xml
-
resources/help.png
+ resources/helpIndex.png
+ resources/helpSearch.png
)
set(QRC_FILES
resources/org_blueberry_ui_qt_help.qrc
)
set(UI_FILES
src/internal/berryHelpTopicChooser.ui
)
foreach(file ${SRC_CPP_FILES})
set(CPP_FILES ${CPP_FILES} src/${file})
endforeach(file ${SRC_CPP_FILES})
foreach(file ${INTERNAL_CPP_FILES})
set(CPP_FILES ${CPP_FILES} src/internal/${file})
endforeach(file ${INTERNAL_CPP_FILES})
diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.help/plugin.xml b/BlueBerry/Bundles/org.blueberry.ui.qt.help/plugin.xml
index 54cf7c71a3..7d7ec224db 100644
--- a/BlueBerry/Bundles/org.blueberry.ui.qt.help/plugin.xml
+++ b/BlueBerry/Bundles/org.blueberry.ui.qt.help/plugin.xml
@@ -1,43 +1,43 @@
+ icon="resources/helpIndex.png" />
+ icon="resources/helpSearch.png" />
diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpIndex.png b/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpIndex.png
new file mode 100644
index 0000000000..88f80ca0ab
Binary files /dev/null and b/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpIndex.png differ
diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpSearch.png b/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpSearch.png
new file mode 100644
index 0000000000..63334a10c8
Binary files /dev/null and b/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpSearch.png differ
diff --git a/CMake/MITKDashboardSetup.cmake b/CMake/MITKDashboardSetup.cmake
index fca7a6ddd5..dcf4a81171 100644
--- a/CMake/MITKDashboardSetup.cmake
+++ b/CMake/MITKDashboardSetup.cmake
@@ -1,160 +1,159 @@
# This file is intended to be included at the end of a custom MITKDashboardScript.TEMPLATE.cmake file
list(APPEND CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}")
#
# Automatically determined properties
#
set(MY_OPERATING_SYSTEM "${CMAKE_HOST_SYSTEM}") # Windows 7, Linux-2.6.32, Darwin...
site_name(CTEST_SITE)
if(QT_BINARY_DIR)
set(QT_QMAKE_EXECUTABLE "${QT_BINARY_DIR}/qmake")
else()
set(QT_QMAKE_EXECUTABLE "qmake")
endif()
execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} --version
OUTPUT_VARIABLE MY_QT_VERSION
RESULT_VARIABLE qmake_error)
if(qmake_error)
message(FATAL_ERROR "Error when executing ${QT_QMAKE_EXECUTABLE} --version\n${qmake_error}")
endif()
string(REGEX REPLACE ".*Qt version ([0-9.]+) .*" "\\1" MY_QT_VERSION ${MY_QT_VERSION})
#
# Project specific properties
#
if(NOT CTEST_BUILD_NAME)
set(CTEST_BUILD_NAME "${MY_OPERATING_SYSTEM}-${MY_COMPILER}-Qt-${MY_QT_VERSION}-${CTEST_BUILD_CONFIGURATION}")
endif()
set(PROJECT_BUILD_DIR "MITK-build")
set(CTEST_PATH "$ENV{PATH}")
if(WIN32)
set(ANN_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/ANN-build/${CTEST_BUILD_CONFIGURATION}")
set(CPPUNIT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/CppUnit-build/${CTEST_BUILD_CONFIGURATION}")
set(GLUT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/GLUT-build/${CTEST_BUILD_CONFIGURATION}")
set(GLEW_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/GLEW-build/${CTEST_BUILD_CONFIGURATION}")
set(TINYXML_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/tinyxml-build/${CTEST_BUILD_CONFIGURATION}")
set(QWT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Qwt-build/${CTEST_BUILD_CONFIGURATION}")
set(QXT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Qxt-build/${CTEST_BUILD_CONFIGURATION}")
set(VTK_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/VTK-build/bin/${CTEST_BUILD_CONFIGURATION}")
set(ACVD_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/ACVD-build/bin/${CTEST_BUILD_CONFIGURATION}")
set(ITK_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/ITK-build/bin/${CTEST_BUILD_CONFIGURATION}")
set(BOOST_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Boost-install/lib")
set(GDCM_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/GDCM-build/bin/${CTEST_BUILD_CONFIGURATION}")
set(OPENCV_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/OpenCV-build/bin/${CTEST_BUILD_CONFIGURATION}")
set(POCO_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Poco-install/lib")
set(SOFA_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/SOFA-build/bin/${CTEST_BUILD_CONFIGURATION}")
set(BLUEBERRY_OSGI_DIR "${CTEST_BINARY_DIRECTORY}/MITK-build/bin/BlueBerry/org.blueberry.osgi/bin/${CTEST_BUILD_CONFIGURATION}")
set(CTEST_PATH "${CTEST_PATH};${CPPUNIT_BINARY_DIR};${QT_BINARY_DIR};${VTK_BINARY_DIR};${ANN_BINARY_DIR};${GLUT_BINARY_DIR};${GLEW_BINARY_DIR};${TINYXML_BINARY_DIR};${QWT_BINARY_DIR};${QXT_BINARY_DIR};${ACVD_BINARY_DIR};${ITK_BINARY_DIR};${BOOST_BINARY_DIR};${GDCM_BINARY_DIR};${OPENCV_BINARY_DIR};${POCO_BINARY_DIR};${SOFA_BINARY_DIR};${BLUEBERRY_OSGI_DIR}")
endif()
set(ENV{PATH} "${CTEST_PATH}")
set(SUPERBUILD_TARGETS "")
# If the dashscript doesn't define a GIT_REPOSITORY variable, let's define it here.
if(NOT DEFINED GIT_REPOSITORY OR GIT_REPOSITORY STREQUAL "")
set(GIT_REPOSITORY "http://git.mitk.org/MITK.git")
endif()
#
# Display build info
#
message("Site name: ${CTEST_SITE}")
message("Build name: ${CTEST_BUILD_NAME}")
message("Script Mode: ${SCRIPT_MODE}")
message("Coverage: ${WITH_COVERAGE}, MemCheck: ${WITH_MEMCHECK}")
#
# Set initial cache options
#
-if(CMAKE_GENERATOR MATCHES "[Mm]ake")
+if(${CMAKE_VERSION} VERSION_GREATER "2.8.9")
set(CTEST_USE_LAUNCHERS 1)
-else()
- set(CTEST_USE_LAUNCHERS 0)
+ set(ENV{CTEST_USE_LAUNCHERS_DEFAULT} 1)
endif()
# Remove this if block after all dartclients work
if(DEFINED ADDITIONNAL_CMAKECACHE_OPTION)
message(WARNING "Rename ADDITIONNAL to ADDITIONAL in your dartlclient script: ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}")
set(ADDITIONAL_CMAKECACHE_OPTION ${ADDITIONNAL_CMAKECACHE_OPTION})
endif()
if(NOT DEFINED MITK_USE_ACVD)
set(MITK_USE_ACVD 1)
endif()
if(NOT DEFINED MITK_USE_Boost)
set(MITK_USE_Boost 1)
endif()
if(NOT DEFINED MITK_USE_OpenCV)
set(MITK_USE_OpenCV 1)
endif()
if(NOT DEFINED MITK_USE_Poco)
set(MITK_USE_Poco 1)
endif()
if(NOT DEFINED MITK_USE_SOFA)
set(MITK_USE_SOFA 1)
endif()
if(NOT DEFINED MITK_BUILD_ALL_APPS)
set(MITK_BUILD_ALL_APPS TRUE)
endif()
if(NOT DEFINED BLUEBERRY_BUILD_ALL_PLUGINS)
set(BLUEBERRY_BUILD_ALL_PLUGINS TRUE)
endif()
if(NOT DEFINED MITK_BUILD_ALL_PLUGINS)
set(MITK_BUILD_ALL_PLUGINS TRUE)
endif()
if(NOT DEFINED MITK_BUILD_EXAMPLES)
set(MITK_BUILD_EXAMPLES TRUE)
endif()
if(NOT BUILD_DiffusionMiniApps)
set(BUILD_DiffusionMiniApps TRUE)
endif()
set(INITIAL_CMAKECACHE_OPTIONS "
BLUEBERRY_BUILD_ALL_PLUGINS:BOOL=${MITK_BUILD_ALL_PLUGINS}
MITK_BUILD_ALL_PLUGINS:BOOL=${MITK_BUILD_ALL_PLUGINS}
MITK_BUILD_ALL_APPS:BOOL=${MITK_BUILD_ALL_APPS}
MITK_BUILD_EXAMPLES:BOOL=${MITK_BUILD_EXAMPLES}
SUPERBUILD_EXCLUDE_MITKBUILD_TARGET:BOOL=TRUE
MITK_USE_ACVD:BOOL=${MITK_USE_ACVD}
MITK_USE_Boost:BOOL=${MITK_USE_Boost}
MITK_USE_OpenCV:BOOL=${MITK_USE_OpenCV}
MITK_USE_Poco:BOOL=${MITK_USE_Poco}
MITK_USE_SOFA:BOOL=${MITK_USE_SOFA}
${ADDITIONAL_CMAKECACHE_OPTION}
")
# Write a cache file for populating the MITK initial cache (not the superbuild cache).
# This can be used to provide variables which are not passed through the
# superbuild process to the MITK configure step)
if(MITK_INITIAL_CACHE)
set(mitk_cache_file "${CTEST_SCRIPT_DIRECTORY}/mitk_initial_cache.txt")
file(WRITE "${mitk_cache_file}" "${MITK_INITIAL_CACHE}")
set(INITIAL_CMAKECACHE_OPTIONS "${INITIAL_CMAKECACHE_OPTIONS}
MITK_INITIAL_CACHE_FILE:INTERNAL=${mitk_cache_file}
")
endif()
#
# Download and include dashboard driver script
#
set(url "http://mitk.org/git/?p=MITK.git;a=blob_plain;f=CMake/MITKDashboardDriverScript.cmake;hb=${hb}")
set(dest ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}.driver)
downloadFile("${url}" "${dest}")
include(${dest})
diff --git a/CMake/mitkMacroCreateModule.cmake b/CMake/mitkMacroCreateModule.cmake
index ca54646499..749a056748 100644
--- a/CMake/mitkMacroCreateModule.cmake
+++ b/CMake/mitkMacroCreateModule.cmake
@@ -1,428 +1,429 @@
##################################################################
#
# MITK_CREATE_MODULE
#
#! Creates a module for the automatic module dependency system within MITK.
#! Configurations are generated in the moduleConf directory.
#!
#! USAGE:
#!
#! \code
#! MITK_CREATE_MODULE(
#! [INCLUDE_DIRS ]
#! [INTERNAL_INCLUDE_DIRS ]
#! [DEPENDS ]
#! [PACKAGE_DEPENDS ]
#! [TARGET_DEPENDS
#! [EXPORT_DEFINE ]
#! [QT_MODULE]
#! [HEADERS_ONLY]
#! [WARNINGS_AS_ERRORS]
#! \endcode
#!
#! \param MODULE_NAME_IN The name for the new module
#! \param HEADERS_ONLY specify this if the modules just contains header files.
##################################################################
macro(MITK_CREATE_MODULE MODULE_NAME_IN)
set(_macro_params
SUBPROJECTS # list of CDash labels
VERSION # module version number, e.g. "1.2.0"
INCLUDE_DIRS # exported include dirs (used in mitkMacroCreateModuleConf.cmake)
INTERNAL_INCLUDE_DIRS # include dirs internal to this module
DEPENDS # list of modules this module depends on
DEPENDS_INTERNAL # list of modules this module internally depends on
PACKAGE_DEPENDS # list of "packages this module depends on (e.g. Qt, VTK, etc.)
TARGET_DEPENDS # list of CMake targets this module should depend on
EXPORT_DEFINE # export macro name for public symbols of this module
AUTOLOAD_WITH # a module target name identifying the module which will trigger the
# automatic loading of this module
ADDITIONAL_LIBS # list of addidtional libraries linked to this module
GENERATED_CPP # not used (?)
)
set(_macro_options
QT_MODULE # the module makes use of Qt features and needs moc and ui generated files
FORCE_STATIC # force building this module as a static library
HEADERS_ONLY # this module is a headers-only library
GCC_DEFAULT_VISIBILITY # do not use gcc visibility flags - all symbols will be exported
NO_INIT # do not create CppMicroServices initialization code
WARNINGS_AS_ERRORS # treat all compiler warnings as errors
)
MACRO_PARSE_ARGUMENTS(MODULE "${_macro_params}" "${_macro_options}" ${ARGN})
set(MODULE_NAME ${MODULE_NAME_IN})
if(MODULE_HEADERS_ONLY)
set(MODULE_PROVIDES )
if(MODULE_AUTOLOAD_WITH)
message(SEND_ERROR "A headers only module cannot be auto-loaded")
endif()
else()
set(MODULE_PROVIDES ${MODULE_NAME})
if(NOT MODULE_NO_INIT AND NOT MODULE_NAME STREQUAL "Mitk")
# Add a dependency to the "Mitk" module
#list(APPEND MODULE_DEPENDS Mitk)
endif()
endif()
if(NOT MODULE_SUBPROJECTS)
if(MITK_DEFAULT_SUBPROJECTS)
set(MODULE_SUBPROJECTS ${MITK_DEFAULT_SUBPROJECTS})
endif()
endif()
# check if the subprojects exist as targets
if(MODULE_SUBPROJECTS)
foreach(subproject ${MODULE_SUBPROJECTS})
if(NOT TARGET ${subproject})
message(SEND_ERROR "The subproject ${subproject} does not have a corresponding target")
endif()
endforeach()
endif()
# check and set-up auto-loading
if(MODULE_AUTOLOAD_WITH)
if(NOT TARGET "${MODULE_AUTOLOAD_WITH}")
message(SEND_ERROR "The module target \"${MODULE_AUTOLOAD_WITH}\" specified as the auto-loading module for \"${MODULE_NAME}\" does not exist")
endif()
# create a meta-target if it does not already exist
set(_module_autoload_meta_target "${MODULE_AUTOLOAD_WITH}-universe")
if(NOT TARGET ${_module_autoload_meta_target})
add_custom_target(${_module_autoload_meta_target})
endif()
endif()
# assume worst case
set(MODULE_IS_ENABLED 0)
# first we check if we have an explicit module build list
if(MITK_MODULES_TO_BUILD)
list(FIND MITK_MODULES_TO_BUILD ${MODULE_NAME} _MOD_INDEX)
if(_MOD_INDEX EQUAL -1)
set(MODULE_IS_EXCLUDED 1)
endif()
endif()
if(NOT MODULE_IS_EXCLUDED AND NOT (MODULE_QT_MODULE AND NOT MITK_USE_QT))
# first of all we check for the dependencies
MITK_CHECK_MODULE(_MISSING_DEP ${MODULE_DEPENDS})
if(_MISSING_DEP)
message("Module ${MODULE_NAME} won't be built, missing dependency: ${_MISSING_DEP}")
set(MODULE_IS_ENABLED 0)
else(_MISSING_DEP)
set(MODULE_IS_ENABLED 1)
# now check for every package if it is enabled. This overlaps a bit with
# MITK_CHECK_MODULE ...
foreach(_package ${MODULE_PACKAGE_DEPENDS})
if((DEFINED MITK_USE_${_package}) AND NOT (MITK_USE_${_package}))
message("Module ${MODULE_NAME} won't be built. Turn on MITK_USE_${_package} if you want to use it.")
set(MODULE_IS_ENABLED 0)
endif()
endforeach()
if(MODULE_IS_ENABLED)
# clear variables defined in files.cmake
set(RESOURCE_FILES )
set(CPP_FILES )
set(H_FILES )
set(TXX_FILES )
set(DOX_FILES )
set(UI_FILES )
set(MOC_H_FILES )
set(QRC_FILES )
# clear other variables
set(Q${KITNAME}_GENERATED_MOC_CPP )
set(Q${KITNAME}_GENERATED_QRC_CPP )
set(Q${KITNAME}_GENERATED_UI_CPP )
+ set(Q${KITNAME}_GENERATED_CPP )
_MITK_CREATE_MODULE_CONF()
if(NOT MODULE_EXPORT_DEFINE)
set(MODULE_EXPORT_DEFINE ${MODULE_NAME}_EXPORT)
endif(NOT MODULE_EXPORT_DEFINE)
if(MITK_GENERATE_MODULE_DOT)
message("MODULEDOTNAME ${MODULE_NAME}")
foreach(dep ${MODULE_DEPENDS})
message("MODULEDOT \"${MODULE_NAME}\" -> \"${dep}\" ; ")
endforeach(dep)
endif(MITK_GENERATE_MODULE_DOT)
set(DEPENDS "${MODULE_DEPENDS}")
if(NOT MODULE_NO_INIT)
# Add a CppMicroServices dependency implicitly, since it is
# needed for the generated "module initialization" code.
set(DEPENDS "CppMicroServices;${DEPENDS}")
endif()
set(DEPENDS_BEFORE "not initialized")
set(PACKAGE_DEPENDS "${MODULE_PACKAGE_DEPENDS}")
MITK_USE_MODULE(${DEPENDS})
# ok, now create the module itself
include_directories(. ${ALL_INCLUDE_DIRECTORIES})
include(files.cmake)
set(module_c_flags )
set(module_c_flags_debug )
set(module_c_flags_release )
set(module_cxx_flags )
set(module_cxx_flags_debug )
set(module_cxx_flags_release )
if(MODULE_GCC_DEFAULT_VISIBILITY)
set(use_visibility_flags 0)
else()
# We only support hidden visibility for gcc for now. Clang 3.0 still has troubles with
# correctly marking template declarations and explicit template instantiations as exported.
# See http://comments.gmane.org/gmane.comp.compilers.clang.scm/50028
# and http://llvm.org/bugs/show_bug.cgi?id=10113
if(CMAKE_COMPILER_IS_GNUCXX)
set(use_visibility_flags 1)
else()
# set(use_visibility_flags 0)
endif()
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
# MinGW does not export all symbols automatically, so no need to set flags.
#
# With gcc < 4.5, RTTI symbols from classes declared in third-party libraries
# which are not "gcc visibility aware" are marked with hidden visibility in
# DSOs which include the class declaration and which are compiled with
# hidden visibility. This leads to dynamic_cast and exception handling problems.
# While this problem could be worked around by sandwiching the include
# directives for the third-party headers between "#pragma visibility push/pop"
# statements, it is generally safer to just use default visibility with
# gcc < 4.5.
if(${GCC_VERSION} VERSION_LESS "4.5" OR MINGW)
set(use_visibility_flags 0)
endif()
endif()
if(use_visibility_flags)
mitkFunctionCheckCAndCXXCompilerFlags("-fvisibility=hidden" module_c_flags module_cxx_flags)
mitkFunctionCheckCAndCXXCompilerFlags("-fvisibility-inlines-hidden" module_c_flags module_cxx_flags)
endif()
configure_file(${MITK_SOURCE_DIR}/CMake/moduleExports.h.in ${CMAKE_BINARY_DIR}/${MODULES_CONF_DIRNAME}/${MODULE_NAME}Exports.h @ONLY)
if(MODULE_WARNINGS_AS_ERRORS)
if(MSVC_VERSION)
mitkFunctionCheckCAndCXXCompilerFlags("/WX" module_c_flags module_cxx_flags)
else()
mitkFunctionCheckCAndCXXCompilerFlags("-Werror" module_c_flags module_cxx_flags)
# The flag "c++0x-static-nonintegral-init" has been renamed in newer Clang
# versions to "static-member-init", see
# http://clang-developers.42468.n3.nabble.com/Wc-0x-static-nonintegral-init-gone-td3999651.html
#
# Also, older Clang and seemingly all gcc versions do not warn if unknown
# "-no-*" flags are used, so CMake will happily append any -Wno-* flag to the
# command line. This may get confusing if unrelated compiler errors happen and
# the error output then additionally contains errors about unknown flags (which
# is not the case if there were no compile errors).
#
# So instead of using -Wno-* we use -Wno-error=*, which will be properly rejected by
# the compiler and if applicable, prints the specific warning as a real warning and
# not as an error (although -Werror was given).
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=c++0x-static-nonintegral-init" module_c_flags module_cxx_flags)
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=static-member-init" module_c_flags module_cxx_flags)
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=unknown-warning" module_c_flags module_cxx_flags)
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=gnu" module_c_flags module_cxx_flags)
# VNL headers throw a lot of these, not fixable for us at least in ITK 3
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=unused-parameter" module_c_flags module_cxx_flags)
# Some DICOM header file in ITK
mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=cast-align" module_c_flags module_cxx_flags)
endif()
endif(MODULE_WARNINGS_AS_ERRORS)
if(MODULE_FORCE_STATIC)
set(_STATIC STATIC)
else()
set(_STATIC )
endif(MODULE_FORCE_STATIC)
if(NOT MODULE_NO_INIT AND NOT MODULE_HEADERS_ONLY)
set(MODULE_LIBNAME ${MODULE_PROVIDES})
usFunctionGenerateModuleInit(CPP_FILES
NAME ${MODULE_NAME}
LIBRARY_NAME ${MODULE_LIBNAME}
DEPENDS ${MODULE_DEPENDS} ${MODULE_DEPENDS_INTERNAL} ${MODULE_PACKAGE_DEPENDS}
#VERSION ${MODULE_VERSION}
)
if(RESOURCE_FILES)
set(res_dir Resources)
set(binary_res_files )
set(source_res_files )
foreach(res_file ${RESOURCE_FILES})
if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${res_dir}/${res_file})
list(APPEND binary_res_files "${res_file}")
else()
list(APPEND source_res_files "${res_file}")
endif()
endforeach()
set(res_macro_args )
if(binary_res_files)
list(APPEND res_macro_args ROOT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${res_dir}
FILES ${binary_res_files})
endif()
if(source_res_files)
list(APPEND res_macro_args ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${res_dir}
FILES ${source_res_files})
endif()
usFunctionEmbedResources(CPP_FILES
LIBRARY_NAME ${MODULE_LIBNAME}
${res_macro_args})
endif()
endif()
if(MODULE_QT_MODULE)
if(UI_FILES)
QT4_WRAP_UI(Q${KITNAME}_GENERATED_UI_CPP ${UI_FILES})
endif(UI_FILES)
if(MOC_H_FILES)
QT4_WRAP_CPP(Q${KITNAME}_GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
endif(MOC_H_FILES)
if(QRC_FILES)
QT4_ADD_RESOURCES(Q${KITNAME}_GENERATED_QRC_CPP ${QRC_FILES})
endif(QRC_FILES)
set(Q${KITNAME}_GENERATED_CPP ${Q${KITNAME}_GENERATED_CPP} ${Q${KITNAME}_GENERATED_UI_CPP} ${Q${KITNAME}_GENERATED_MOC_CPP} ${Q${KITNAME}_GENERATED_QRC_CPP})
endif()
ORGANIZE_SOURCES(SOURCE ${CPP_FILES}
HEADER ${H_FILES}
TXX ${TXX_FILES}
DOC ${DOX_FILES}
UI ${UI_FILES}
QRC ${QRC_FILES}
MOC ${Q${KITNAME}_GENERATED_MOC_CPP}
GEN_QRC ${Q${KITNAME}_GENERATED_QRC_CPP}
GEN_UI ${Q${KITNAME}_GENERATED_UI_CPP})
set(coverage_sources
${CPP_FILES} ${H_FILES} ${GLOBBED__H_FILES} ${CORRESPONDING__H_FILES} ${TXX_FILES}
${TOOL_CPPS} ${TOOL_GUI_CPPS})
if(MODULE_SUBPROJECTS)
set_property(SOURCE ${coverage_sources} APPEND PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK)
endif()
if(NOT MODULE_HEADERS_ONLY)
if(ALL_LIBRARY_DIRS)
# LINK_DIRECTORIES applies only to targets which are added after the call to LINK_DIRECTORIES
link_directories(${ALL_LIBRARY_DIRS})
endif(ALL_LIBRARY_DIRS)
add_library(${MODULE_PROVIDES} ${_STATIC}
${coverage_sources} ${CPP_FILES_GENERATED} ${Q${KITNAME}_GENERATED_CPP}
${DOX_FILES} ${UI_FILES} ${QRC_FILES})
if(MODULE_TARGET_DEPENDS)
add_dependencies(${MODULE_PROVIDES} ${MODULE_TARGET_DEPENDS})
endif()
if(MODULE_SUBPROJECTS)
set_property(TARGET ${MODULE_PROVIDES} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK)
foreach(subproject ${MODULE_SUBPROJECTS})
add_dependencies(${subproject} ${MODULE_PROVIDES})
endforeach()
endif()
if(ALL_LIBRARIES)
target_link_libraries(${MODULE_PROVIDES} ${ALL_LIBRARIES})
endif(ALL_LIBRARIES)
if(MODULE_QT_MODULE AND QT_LIBRARIES)
target_link_libraries(${MODULE_PROVIDES} ${QT_LIBRARIES})
endif()
if(MINGW)
target_link_libraries(${MODULE_PROVIDES} ssp) # add stack smash protection lib
endif()
# Apply properties to the module target.
# We cannot use set_target_properties like below since there is no way to
# differentiate C/C++ and Releas/Debug flags using target properties.
# See http://www.cmake.org/Bug/view.php?id=6493
#set_target_properties(${MODULE_PROVIDES} PROPERTIES
# COMPILE_FLAGS "${module_compile_flags}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${module_c_flags}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${module_c_flags_debug}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${module_c_flags_release}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${module_cxx_flags}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${module_cxx_flags_debug}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${module_cxx_flags_release}")
# Add additional library search directories to a global property which
# can be evaluated by other CMake macros, e.g. our install scripts.
if(MODULE_ADDITIONAL_LIBS)
get_property(_mitk_additional_library_search_paths GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS)
foreach(_lib_filepath ${MODULE_ADDITIONAL_LIBS})
get_filename_component(_search_path "${_lib_filepath}" PATH)
if(_search_path)
list(APPEND _mitk_additional_library_search_paths "${_search_path}")
endif()
endforeach()
if(_mitk_additional_library_search_paths)
list(REMOVE_DUPLICATES _mitk_additional_library_search_paths)
set_property(GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS ${_mitk_additional_library_search_paths})
endif()
endif()
# add the target name to a global property which is used in the top-level
# CMakeLists.txt file to export the target
set_property(GLOBAL APPEND PROPERTY MITK_MODULE_TARGETS ${MODULE_PROVIDES})
if(MODULE_AUTOLOAD_WITH)
# for auto-loaded modules, adapt the output directory
add_dependencies(${_module_autoload_meta_target} ${MODULE_PROVIDES})
if(WIN32)
set(_module_output_prop RUNTIME_OUTPUT_DIRECTORY)
else()
set(_module_output_prop LIBRARY_OUTPUT_DIRECTORY)
endif()
set(_module_output_dir ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH})
get_target_property(_module_is_imported ${MODULE_AUTOLOAD_WITH} IMPORTED)
if(NOT _module_is_imported)
# if the auto-loading module is not imported, get its location
# and put the auto-load module relative to it.
get_target_property(_module_output_dir ${MODULE_AUTOLOAD_WITH} ${_module_output_prop})
set_target_properties(${MODULE_PROVIDES} PROPERTIES
${_module_output_prop} ${_module_output_dir}/${MODULE_AUTOLOAD_WITH})
else()
set_target_properties(${MODULE_PROVIDES} PROPERTIES
${_module_output_prop} ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH})
endif()
set_target_properties(${MODULE_PROVIDES} PROPERTIES
MITK_AUTOLOAD_DIRECTORY ${MODULE_AUTOLOAD_WITH})
# add the auto-load module name as a property
set_property(TARGET ${MODULE_AUTOLOAD_WITH} APPEND PROPERTY MITK_AUTOLOAD_TARGETS ${MODULE_PROVIDES})
else()
# Add meta dependencies (e.g. on auto-load modules from depending modules)
if(ALL_META_DEPENDENCIES)
add_dependencies(${MODULE_PROVIDES} ${ALL_META_DEPENDENCIES})
endif()
endif()
endif()
endif(MODULE_IS_ENABLED)
endif(_MISSING_DEP)
endif(NOT MODULE_IS_EXCLUDED AND NOT (MODULE_QT_MODULE AND NOT MITK_USE_QT))
if(NOT MODULE_IS_ENABLED)
_MITK_CREATE_MODULE_CONF()
endif(NOT MODULE_IS_ENABLED)
endmacro(MITK_CREATE_MODULE)
diff --git a/CMakeExternals/Patchtinyxml-2.6.2.cmake b/CMakeExternals/Patchtinyxml-2.6.2.cmake
index 058bf87612..0ecf539b30 100644
--- a/CMakeExternals/Patchtinyxml-2.6.2.cmake
+++ b/CMakeExternals/Patchtinyxml-2.6.2.cmake
@@ -1,15 +1,18 @@
# Called by tinyxml.cmake (ExternalProject_Add) as a patch for tinyxml.
# Adds #define TIXML_USE_STL to enable STL string support
+configure_file(${CMAKE_CURRENT_LIST_DIR}/Patchtinyxml-2.6.2.h tinyxml.h COPYONLY)
+configure_file(${CMAKE_CURRENT_LIST_DIR}/Patchtinyxml-2.6.2.cpp tinyxml.cpp COPYONLY)
+
# read whole file
file(STRINGS tinyxml.h sourceCode NEWLINE_CONSUME)
# Add the TIXML_USE_STL define
string(REGEX REPLACE "#define TINYXML_INCLUDED" "#define TINYXML_INCLUDED\n\n#ifndef TIXML_USE_STL\n #define TIXML_USE_STL\n#endif" sourceCode ${sourceCode})
# set variable CONTENTS, which is substituted in TEMPLATE_FILE
set(CONTENTS ${sourceCode})
configure_file(${CMAKE_CURRENT_LIST_DIR}/EmptyFileForPatching.dummy tinyxml.h @ONLY)
# Add the default CMake build system
include(${CMAKE_CURRENT_LIST_DIR}/GenerateDefaultCMakeBuildSystem.cmake)
diff --git a/CMakeExternals/Patchtinyxml-2.6.2.cpp b/CMakeExternals/Patchtinyxml-2.6.2.cpp
new file mode 100644
index 0000000000..df8c540b47
--- /dev/null
+++ b/CMakeExternals/Patchtinyxml-2.6.2.cpp
@@ -0,0 +1,1950 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+ */
+
+#include
+
+#ifdef TIXML_USE_STL
+#include
+#include
+#endif
+
+#include "tinyxml.h"
+
+FILE* TiXmlFOpen( const char* filename, const char* mode );
+
+bool TiXmlBase::condenseWhiteSpace = true;
+static unsigned int required_decimal_places = 14+1; // Need 14 for mitk default accuracy plus 1 to make sure we're within tolerance.
+
+
+
+// Microsoft compiler security
+FILE* TiXmlFOpen( const char* filename, const char* mode )
+{
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+ FILE* fp = 0;
+ errno_t err = fopen_s( &fp, filename, mode );
+ if ( !err && fp )
+ return fp;
+ return 0;
+#else
+ return fopen( filename, mode );
+#endif
+}
+
+void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString )
+{
+ int i=0;
+
+ while( i<(int)str.length() )
+ {
+ unsigned char c = (unsigned char) str[i];
+
+ if ( c == '&'
+ && i < ( (int)str.length() - 2 )
+ && str[i+1] == '#'
+ && str[i+2] == 'x' )
+ {
+ // Hexadecimal character reference.
+ // Pass through unchanged.
+ // © -- copyright symbol, for example.
+ //
+ // The -1 is a bug fix from Rob Laveaux. It keeps
+ // an overflow from happening if there is no ';'.
+ // There are actually 2 ways to exit this loop -
+ // while fails (error case) and break (semicolon found).
+ // However, there is no mechanism (currently) for
+ // this function to return an error.
+ while ( i<(int)str.length()-1 )
+ {
+ outString->append( str.c_str() + i, 1 );
+ ++i;
+ if ( str[i] == ';' )
+ break;
+ }
+ }
+ else if ( c == '&' )
+ {
+ outString->append( entity[0].str, entity[0].strLength );
+ ++i;
+ }
+ else if ( c == '<' )
+ {
+ outString->append( entity[1].str, entity[1].strLength );
+ ++i;
+ }
+ else if ( c == '>' )
+ {
+ outString->append( entity[2].str, entity[2].strLength );
+ ++i;
+ }
+ else if ( c == '\"' )
+ {
+ outString->append( entity[3].str, entity[3].strLength );
+ ++i;
+ }
+ else if ( c == '\'' )
+ {
+ outString->append( entity[4].str, entity[4].strLength );
+ ++i;
+ }
+ else if ( c < 32 )
+ {
+ // Easy pass at non-alpha/numeric/symbol
+ // Below 32 is symbolic.
+ char buf[ 32 ];
+
+#if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF( buf, sizeof(buf), "%02X;", (unsigned) ( c & 0xff ) );
+#else
+ sprintf( buf, "%02X;", (unsigned) ( c & 0xff ) );
+#endif
+
+ //*ME: warning C4267: convert 'size_t' to 'int'
+ //*ME: Int-Cast to make compiler happy ...
+ outString->append( buf, (int)strlen( buf ) );
+ ++i;
+ }
+ else
+ {
+ //char realc = (char) c;
+ //outString->append( &realc, 1 );
+ *outString += (char) c; // somewhat more efficient function call.
+ ++i;
+ }
+ }
+}
+
+
+TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase()
+{
+ parent = 0;
+ type = _type;
+ firstChild = 0;
+ lastChild = 0;
+ prev = 0;
+ next = 0;
+}
+
+
+TiXmlNode::~TiXmlNode()
+{
+ TiXmlNode* node = firstChild;
+ TiXmlNode* temp = 0;
+
+ while ( node )
+ {
+ temp = node;
+ node = node->next;
+ delete temp;
+ }
+}
+
+
+void TiXmlNode::CopyTo( TiXmlNode* target ) const
+{
+ target->SetValue (value.c_str() );
+ target->userData = userData;
+ target->location = location;
+}
+
+
+void TiXmlNode::Clear()
+{
+ TiXmlNode* node = firstChild;
+ TiXmlNode* temp = 0;
+
+ while ( node )
+ {
+ temp = node;
+ node = node->next;
+ delete temp;
+ }
+
+ firstChild = 0;
+ lastChild = 0;
+}
+
+
+TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
+{
+ assert( node->parent == 0 || node->parent == this );
+ assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() );
+
+ if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ delete node;
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ node->parent = this;
+
+ node->prev = lastChild;
+ node->next = 0;
+
+ if ( lastChild )
+ lastChild->next = node;
+ else
+ firstChild = node; // it was an empty list.
+
+ lastChild = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )
+{
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+
+ return LinkEndChild( node );
+}
+
+
+TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis )
+{
+ if ( !beforeThis || beforeThis->parent != this ) {
+ return 0;
+ }
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+ node->parent = this;
+
+ node->next = beforeThis;
+ node->prev = beforeThis->prev;
+ if ( beforeThis->prev )
+ {
+ beforeThis->prev->next = node;
+ }
+ else
+ {
+ assert( firstChild == beforeThis );
+ firstChild = node;
+ }
+ beforeThis->prev = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis )
+{
+ if ( !afterThis || afterThis->parent != this ) {
+ return 0;
+ }
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+ node->parent = this;
+
+ node->prev = afterThis;
+ node->next = afterThis->next;
+ if ( afterThis->next )
+ {
+ afterThis->next->prev = node;
+ }
+ else
+ {
+ assert( lastChild == afterThis );
+ lastChild = node;
+ }
+ afterThis->next = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
+{
+ if ( !replaceThis )
+ return 0;
+
+ if ( replaceThis->parent != this )
+ return 0;
+
+ if ( withThis.ToDocument() ) {
+ // A document can never be a child. Thanks to Noam.
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = withThis.Clone();
+ if ( !node )
+ return 0;
+
+ node->next = replaceThis->next;
+ node->prev = replaceThis->prev;
+
+ if ( replaceThis->next )
+ replaceThis->next->prev = node;
+ else
+ lastChild = node;
+
+ if ( replaceThis->prev )
+ replaceThis->prev->next = node;
+ else
+ firstChild = node;
+
+ delete replaceThis;
+ node->parent = this;
+ return node;
+}
+
+
+bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
+{
+ if ( !removeThis ) {
+ return false;
+ }
+
+ if ( removeThis->parent != this )
+ {
+ assert( 0 );
+ return false;
+ }
+
+ if ( removeThis->next )
+ removeThis->next->prev = removeThis->prev;
+ else
+ lastChild = removeThis->prev;
+
+ if ( removeThis->prev )
+ removeThis->prev->next = removeThis->next;
+ else
+ firstChild = removeThis->next;
+
+ delete removeThis;
+ return true;
+}
+
+const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = firstChild; node; node = node->next )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = lastChild; node; node = node->prev )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const
+{
+ if ( !previous )
+ {
+ return FirstChild();
+ }
+ else
+ {
+ assert( previous->parent == this );
+ return previous->NextSibling();
+ }
+}
+
+
+const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const
+{
+ if ( !previous )
+ {
+ return FirstChild( val );
+ }
+ else
+ {
+ assert( previous->parent == this );
+ return previous->NextSibling( val );
+ }
+}
+
+
+const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = next; node; node = node->next )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = prev; node; node = node->prev )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+void TiXmlElement::RemoveAttribute( const char * name )
+{
+#ifdef TIXML_USE_STL
+ TIXML_STRING str( name );
+ TiXmlAttribute* node = attributeSet.Find( str );
+#else
+ TiXmlAttribute* node = attributeSet.Find( name );
+#endif
+ if ( node )
+ {
+ attributeSet.Remove( node );
+ delete node;
+ }
+}
+
+const TiXmlElement* TiXmlNode::FirstChildElement() const
+{
+ const TiXmlNode* node;
+
+ for ( node = FirstChild();
+ node;
+ node = node->NextSibling() )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const
+{
+ const TiXmlNode* node;
+
+ for ( node = FirstChild( _value );
+ node;
+ node = node->NextSibling( _value ) )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::NextSiblingElement() const
+{
+ const TiXmlNode* node;
+
+ for ( node = NextSibling();
+ node;
+ node = node->NextSibling() )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const
+{
+ const TiXmlNode* node;
+
+ for ( node = NextSibling( _value );
+ node;
+ node = node->NextSibling( _value ) )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlDocument* TiXmlNode::GetDocument() const
+{
+ const TiXmlNode* node;
+
+ for( node = this; node; node = node->parent )
+ {
+ if ( node->ToDocument() )
+ return node->ToDocument();
+ }
+ return 0;
+}
+
+
+TiXmlElement::TiXmlElement (const char * _value)
+: TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ value = _value;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlElement::TiXmlElement( const std::string& _value )
+: TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ value = _value;
+}
+#endif
+
+
+TiXmlElement::TiXmlElement( const TiXmlElement& copy)
+: TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ copy.CopyTo( this );
+}
+
+
+TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base )
+{
+ ClearThis();
+ base.CopyTo( this );
+ return *this;
+}
+
+
+TiXmlElement::~TiXmlElement()
+{
+ ClearThis();
+}
+
+
+void TiXmlElement::ClearThis()
+{
+ Clear();
+ while( attributeSet.First() )
+ {
+ TiXmlAttribute* node = attributeSet.First();
+ attributeSet.Remove( node );
+ delete node;
+ }
+}
+
+
+const char* TiXmlElement::Attribute( const char* name ) const
+{
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( node )
+ return node->Value();
+ return 0;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( attrib )
+ return &attrib->ValueStr();
+ return 0;
+}
+#endif
+
+
+const char* TiXmlElement::Attribute( const char* name, int* i ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const char* result = 0;
+
+ if ( attrib ) {
+ result = attrib->Value();
+ if ( i ) {
+ attrib->QueryIntValue( i );
+ }
+ }
+ return result;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const std::string* result = 0;
+
+ if ( attrib ) {
+ result = &attrib->ValueStr();
+ if ( i ) {
+ attrib->QueryIntValue( i );
+ }
+ }
+ return result;
+}
+#endif
+
+
+const char* TiXmlElement::Attribute( const char* name, double* d ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const char* result = 0;
+
+ if ( attrib ) {
+ result = attrib->Value();
+ if ( d ) {
+ attrib->QueryDoubleValue( d );
+ }
+ }
+ return result;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const std::string* result = 0;
+
+ if ( attrib ) {
+ result = &attrib->ValueStr();
+ if ( d ) {
+ attrib->QueryDoubleValue( d );
+ }
+ }
+ return result;
+}
+#endif
+
+
+int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryIntValue( ival );
+}
+
+
+int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const
+{
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+
+ int ival = 0;
+ int result = node->QueryIntValue( &ival );
+ *value = (unsigned)ival;
+ return result;
+}
+
+
+int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const
+{
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+
+ int result = TIXML_WRONG_TYPE;
+ if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) )
+ {
+ *bval = true;
+ result = TIXML_SUCCESS;
+ }
+ else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) )
+ {
+ *bval = false;
+ result = TIXML_SUCCESS;
+ }
+ return result;
+}
+
+
+
+#ifdef TIXML_USE_STL
+int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryIntValue( ival );
+}
+#endif
+
+
+int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryDoubleValue( dval );
+}
+
+
+#ifdef TIXML_USE_STL
+int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryDoubleValue( dval );
+}
+#endif
+
+
+void TiXmlElement::SetAttribute( const char * name, int val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetIntValue( val );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute( const std::string& name, int val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetIntValue( val );
+ }
+}
+#endif
+
+
+void TiXmlElement::SetDoubleAttribute( const char * name, double val, const unsigned int requiredDecimalPlaces )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetDoubleValue( val, requiredDecimalPlaces );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetDoubleAttribute( const std::string& name, double val, const unsigned int requiredDecimalPlaces )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetDoubleValue( val, requiredDecimalPlaces );
+ }
+}
+#endif
+
+
+
+
+
+void TiXmlElement::SetAttribute( const char * cname, const char * cvalue )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname );
+ if ( attrib ) {
+ attrib->SetValue( cvalue );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name );
+ if ( attrib ) {
+ attrib->SetValue( _value );
+ }
+}
+#endif
+
+
+void TiXmlElement::Print( FILE* cfile, int depth ) const
+{
+ int i;
+ assert( cfile );
+ for ( i=0; iNext() )
+ {
+ fprintf( cfile, " " );
+ attrib->Print( cfile, depth );
+ }
+
+ // There are 3 different formatting approaches:
+ // 1) An element without children is printed as a node
+ // 2) An element with only a text child is printed as text
+ // 3) An element with children is printed on multiple lines.
+ TiXmlNode* node;
+ if ( !firstChild )
+ {
+ fprintf( cfile, " />" );
+ }
+ else if ( firstChild == lastChild && firstChild->ToText() )
+ {
+ fprintf( cfile, ">" );
+ firstChild->Print( cfile, depth + 1 );
+ fprintf( cfile, "%s>", value.c_str() );
+ }
+ else
+ {
+ fprintf( cfile, ">" );
+
+ for ( node = firstChild; node; node=node->NextSibling() )
+ {
+ if ( !node->ToText() )
+ {
+ fprintf( cfile, "\n" );
+ }
+ node->Print( cfile, depth+1 );
+ }
+ fprintf( cfile, "\n" );
+ for( i=0; i", value.c_str() );
+ }
+}
+
+
+void TiXmlElement::CopyTo( TiXmlElement* target ) const
+{
+ // superclass:
+ TiXmlNode::CopyTo( target );
+
+ // Element class:
+ // Clone the attributes, then clone the children.
+ const TiXmlAttribute* attribute = 0;
+ for( attribute = attributeSet.First();
+ attribute;
+ attribute = attribute->Next() )
+ {
+ target->SetAttribute( attribute->Name(), attribute->Value() );
+ }
+
+ TiXmlNode* node = 0;
+ for ( node = firstChild; node; node = node->NextSibling() )
+ {
+ target->LinkEndChild( node->Clone() );
+ }
+}
+
+bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const
+{
+ if ( visitor->VisitEnter( *this, attributeSet.First() ) )
+ {
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ if ( !node->Accept( visitor ) )
+ break;
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+TiXmlNode* TiXmlElement::Clone() const
+{
+ TiXmlElement* clone = new TiXmlElement( Value() );
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+const char* TiXmlElement::GetText() const
+{
+ const TiXmlNode* child = this->FirstChild();
+ if ( child ) {
+ const TiXmlText* childText = child->ToText();
+ if ( childText ) {
+ return childText->Value();
+ }
+ }
+ return 0;
+}
+
+
+TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ ClearError();
+}
+
+TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ value = documentName;
+ ClearError();
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ value = documentName;
+ ClearError();
+}
+#endif
+
+
+TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ copy.CopyTo( this );
+}
+
+
+TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy )
+{
+ Clear();
+ copy.CopyTo( this );
+ return *this;
+}
+
+
+bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
+{
+ return LoadFile( Value(), encoding );
+}
+
+
+bool TiXmlDocument::SaveFile() const
+{
+ return SaveFile( Value() );
+}
+
+bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding )
+{
+ TIXML_STRING filename( _filename );
+ value = filename;
+
+ // reading in binary mode so that tinyxml can normalize the EOL
+ FILE* file = TiXmlFOpen( value.c_str (), "rb" );
+
+ if ( file )
+ {
+ bool result = LoadFile( file, encoding );
+ fclose( file );
+ return result;
+ }
+ else
+ {
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+}
+
+bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
+{
+ if ( !file )
+ {
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Delete the existing data:
+ Clear();
+ location.Clear();
+
+ // Get the file size, so we can pre-allocate the string. HUGE speed impact.
+ long length = 0;
+ fseek( file, 0, SEEK_END );
+ length = ftell( file );
+ fseek( file, 0, SEEK_SET );
+
+ // Strange case, but good to handle up front.
+ if ( length <= 0 )
+ {
+ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Subtle bug here. TinyXml did use fgets. But from the XML spec:
+ // 2.11 End-of-Line Handling
+ //
+ //
+ // ...the XML processor MUST behave as if it normalized all line breaks in external
+ // parsed entities (including the document entity) on input, before parsing, by translating
+ // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
+ // a single #xA character.
+ //
+ //
+ // It is not clear fgets does that, and certainly isn't clear it works cross platform.
+ // Generally, you expect fgets to translate from the convention of the OS to the c/unix
+ // convention, and not work generally.
+
+ /*
+ while( fgets( buf, sizeof(buf), file ) )
+ {
+ data += buf;
+ }
+ */
+
+ char* buf = new char[ length+1 ];
+ buf[0] = 0;
+
+ if ( fread( buf, length, 1, file ) != 1 ) {
+ delete [] buf;
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Process the buffer in place to normalize new lines. (See comment above.)
+ // Copies from the 'p' to 'q' pointer, where p can advance faster if
+ // a newline-carriage return is hit.
+ //
+ // Wikipedia:
+ // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
+ // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
+ // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
+ // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS
+ // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
+
+ const char* p = buf; // the read head
+ char* q = buf; // the write head
+ const char CR = 0x0d;
+ const char LF = 0x0a;
+
+ buf[length] = 0;
+ while( *p ) {
+ assert( p < (buf+length) );
+ assert( q <= (buf+length) );
+ assert( q <= p );
+
+ if ( *p == CR ) {
+ *q++ = LF;
+ p++;
+ if ( *p == LF ) { // check for CR+LF (and skip LF)
+ p++;
+ }
+ }
+ else {
+ *q++ = *p++;
+ }
+ }
+ assert( q <= (buf+length) );
+ *q = 0;
+
+ Parse( buf, 0, encoding );
+
+ delete [] buf;
+ return !Error();
+}
+
+
+bool TiXmlDocument::SaveFile( const char * filename ) const
+{
+ // The old c stuff lives on...
+ FILE* fp = TiXmlFOpen( filename, "w" );
+ if ( fp )
+ {
+ bool result = SaveFile( fp );
+ fclose( fp );
+ return result;
+ }
+ return false;
+}
+
+
+bool TiXmlDocument::SaveFile( FILE* fp ) const
+{
+ if ( useMicrosoftBOM )
+ {
+ const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+ const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+ const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+ fputc( TIXML_UTF_LEAD_0, fp );
+ fputc( TIXML_UTF_LEAD_1, fp );
+ fputc( TIXML_UTF_LEAD_2, fp );
+ }
+ Print( fp, 0 );
+ return (ferror(fp) == 0);
+}
+
+
+void TiXmlDocument::CopyTo( TiXmlDocument* target ) const
+{
+ TiXmlNode::CopyTo( target );
+
+ target->error = error;
+ target->errorId = errorId;
+ target->errorDesc = errorDesc;
+ target->tabsize = tabsize;
+ target->errorLocation = errorLocation;
+ target->useMicrosoftBOM = useMicrosoftBOM;
+
+ TiXmlNode* node = 0;
+ for ( node = firstChild; node; node = node->NextSibling() )
+ {
+ target->LinkEndChild( node->Clone() );
+ }
+}
+
+
+TiXmlNode* TiXmlDocument::Clone() const
+{
+ TiXmlDocument* clone = new TiXmlDocument();
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlDocument::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ node->Print( cfile, depth );
+ fprintf( cfile, "\n" );
+ }
+}
+
+
+bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const
+{
+ if ( visitor->VisitEnter( *this ) )
+ {
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ if ( !node->Accept( visitor ) )
+ break;
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+const TiXmlAttribute* TiXmlAttribute::Next() const
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( next->value.empty() && next->name.empty() )
+ return 0;
+ return next;
+}
+
+/*
+TiXmlAttribute* TiXmlAttribute::Next()
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( next->value.empty() && next->name.empty() )
+ return 0;
+ return next;
+}
+ */
+
+const TiXmlAttribute* TiXmlAttribute::Previous() const
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( prev->value.empty() && prev->name.empty() )
+ return 0;
+ return prev;
+}
+
+/*
+TiXmlAttribute* TiXmlAttribute::Previous()
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( prev->value.empty() && prev->name.empty() )
+ return 0;
+ return prev;
+}
+ */
+
+void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
+{
+ TIXML_STRING n, v;
+
+ EncodeString( name, &n );
+ EncodeString( value, &v );
+
+ if (value.find ('\"') == TIXML_STRING::npos) {
+ if ( cfile ) {
+ fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() );
+ }
+ if ( str ) {
+ (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\"";
+ }
+ }
+ else {
+ if ( cfile ) {
+ fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() );
+ }
+ if ( str ) {
+ (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'";
+ }
+ }
+}
+
+
+int TiXmlAttribute::QueryIntValue( int* ival ) const
+{
+ if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+}
+
+int TiXmlAttribute::QueryDoubleValue( double* dval ) const
+{
+ if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+}
+
+void TiXmlAttribute::SetIntValue( int _value )
+{
+ char buf [64];
+#if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value);
+#else
+ sprintf (buf, "%d", _value);
+#endif
+ SetValue (buf);
+}
+
+
+
+
+void TiXmlAttribute::SetDoubleValue( double _value, const unsigned int requiredDecimalPlaces )
+{
+#if defined(TIXML_USE_STL)
+ std::ostringstream ss;
+ ss.imbue(std::locale("C"));
+ ss.precision(TiXmlBase::Precision(_value, requiredDecimalPlaces));
+ ss << _value;
+ SetValue( ss.str() );
+#else
+ char buf [256];
+#if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF( buf, sizeof(buf), TiXmlBase::Format(_value, requiredDecimalPlaces).c_str(), _value);
+#else
+ sprintf (buf, TiXmlBase::Format(_value, requiredDecimalPlaces).c_str(), _value);
+#endif
+ SetValue (buf);
+#endif
+
+}
+
+
+int TiXmlAttribute::IntValue() const
+{
+ return atoi (value.c_str ());
+}
+
+double TiXmlAttribute::DoubleValue() const
+{
+#if defined(TIXML_USE_STL)
+ std::istringstream ss(value);
+ ss.imbue(std::locale("C"));
+ double dval;
+ ss >> dval;
+ return dval;
+#else
+ return atof (value.c_str ());
+#endif
+}
+
+
+
+TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT )
+{
+ copy.CopyTo( this );
+}
+
+
+TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base )
+{
+ Clear();
+ base.CopyTo( this );
+ return *this;
+}
+
+
+void TiXmlComment::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ for ( int i=0; i", value.c_str() );
+}
+
+
+void TiXmlComment::CopyTo( TiXmlComment* target ) const
+{
+ TiXmlNode::CopyTo( target );
+}
+
+
+bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlComment::Clone() const
+{
+ TiXmlComment* clone = new TiXmlComment();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlText::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ if ( cdata )
+ {
+ int i;
+ fprintf( cfile, "\n" );
+ for ( i=0; i\n", value.c_str() ); // unformatted output
+ }
+ else
+ {
+ TIXML_STRING buffer;
+ EncodeString( value, &buffer );
+ fprintf( cfile, "%s", buffer.c_str() );
+ }
+}
+
+
+void TiXmlText::CopyTo( TiXmlText* target ) const
+{
+ TiXmlNode::CopyTo( target );
+ target->cdata = cdata;
+}
+
+
+bool TiXmlText::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlText::Clone() const
+{
+ TiXmlText* clone = 0;
+ clone = new TiXmlText( "" );
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+TiXmlDeclaration::TiXmlDeclaration( const char * _version,
+ const char * _encoding,
+ const char * _standalone )
+: TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ version = _version;
+ encoding = _encoding;
+ standalone = _standalone;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
+ const std::string& _encoding,
+ const std::string& _standalone )
+: TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ version = _version;
+ encoding = _encoding;
+ standalone = _standalone;
+}
+#endif
+
+
+TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
+: TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ copy.CopyTo( this );
+}
+
+
+TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy )
+{
+ Clear();
+ copy.CopyTo( this );
+ return *this;
+}
+
+
+void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
+{
+ if ( cfile ) fprintf( cfile, "" );
+ if ( str ) (*str) += "?>";
+}
+
+
+void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const
+{
+ TiXmlNode::CopyTo( target );
+
+ target->version = version;
+ target->encoding = encoding;
+ target->standalone = standalone;
+}
+
+
+bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlDeclaration::Clone() const
+{
+ TiXmlDeclaration* clone = new TiXmlDeclaration();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlUnknown::Print( FILE* cfile, int depth ) const
+{
+ for ( int i=0; i", value.c_str() );
+}
+
+
+void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const
+{
+ TiXmlNode::CopyTo( target );
+}
+
+
+bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlUnknown::Clone() const
+{
+ TiXmlUnknown* clone = new TiXmlUnknown();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+TiXmlAttributeSet::TiXmlAttributeSet()
+{
+ sentinel.next = &sentinel;
+ sentinel.prev = &sentinel;
+}
+
+
+TiXmlAttributeSet::~TiXmlAttributeSet()
+{
+ assert( sentinel.next == &sentinel );
+ assert( sentinel.prev == &sentinel );
+}
+
+
+void TiXmlAttributeSet::Add( TiXmlAttribute* addMe )
+{
+#ifdef TIXML_USE_STL
+ assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set.
+#else
+ assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set.
+#endif
+
+ addMe->next = &sentinel;
+ addMe->prev = sentinel.prev;
+
+ sentinel.prev->next = addMe;
+ sentinel.prev = addMe;
+}
+
+void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
+{
+ TiXmlAttribute* node;
+
+ for( node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( node == removeMe )
+ {
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+ return;
+ }
+ }
+ assert( 0 ); // we tried to remove a non-linked attribute.
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const
+{
+ for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( node->name == name )
+ return node;
+ }
+ return 0;
+}
+
+TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name )
+{
+ TiXmlAttribute* attrib = Find( _name );
+ if ( !attrib ) {
+ attrib = new TiXmlAttribute();
+ Add( attrib );
+ attrib->SetName( _name );
+ }
+ return attrib;
+}
+#endif
+
+
+TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const
+{
+ for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( strcmp( node->name.c_str(), name ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name )
+{
+ TiXmlAttribute* attrib = Find( _name );
+ if ( !attrib ) {
+ attrib = new TiXmlAttribute();
+ Add( attrib );
+ attrib->SetName( _name );
+ }
+ return attrib;
+}
+
+
+#ifdef TIXML_USE_STL
+std::istream& operator>> (std::istream & in, TiXmlNode & base)
+{
+ TIXML_STRING tag;
+ tag.reserve( 8 * 1000 );
+ base.StreamIn( &in, &tag );
+
+ base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING );
+ return in;
+}
+#endif
+
+
+#ifdef TIXML_USE_STL
+std::ostream& operator<< (std::ostream & out, const TiXmlNode & base)
+{
+ TiXmlPrinter printer;
+ printer.SetStreamPrinting();
+ base.Accept( &printer );
+ out << printer.Str();
+
+ return out;
+}
+
+
+std::string& operator<< (std::string& out, const TiXmlNode& base )
+{
+ TiXmlPrinter printer;
+ printer.SetStreamPrinting();
+ base.Accept( &printer );
+ out.append( printer.Str() );
+
+ return out;
+}
+#endif
+
+
+TiXmlHandle TiXmlHandle::FirstChild() const
+{
+ if ( node )
+ {
+ TiXmlNode* child = node->FirstChild();
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const
+{
+ if ( node )
+ {
+ TiXmlNode* child = node->FirstChild( value );
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement() const
+{
+ if ( node )
+ {
+ TiXmlElement* child = node->FirstChildElement();
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const
+{
+ if ( node )
+ {
+ TiXmlElement* child = node->FirstChildElement( value );
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::Child( int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlNode* child = node->FirstChild();
+ for ( i=0;
+ child && iNextSibling(), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlNode* child = node->FirstChild( value );
+ for ( i=0;
+ child && iNextSibling( value ), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement( int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlElement* child = node->FirstChildElement();
+ for ( i=0;
+ child && iNextSiblingElement(), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlElement* child = node->FirstChildElement( value );
+ for ( i=0;
+ child && iNextSiblingElement( value ), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+bool TiXmlPrinter::VisitEnter( const TiXmlDocument& )
+{
+ return true;
+}
+
+bool TiXmlPrinter::VisitExit( const TiXmlDocument& )
+{
+ return true;
+}
+
+bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute )
+{
+ DoIndent();
+ buffer += "<";
+ buffer += element.Value();
+
+ for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() )
+ {
+ buffer += " ";
+ attrib->Print( 0, 0, &buffer );
+ }
+
+ if ( !element.FirstChild() )
+ {
+ buffer += " />";
+ DoLineBreak();
+ }
+ else
+ {
+ buffer += ">";
+ if ( element.FirstChild()->ToText()
+ && element.LastChild() == element.FirstChild()
+ && element.FirstChild()->ToText()->CDATA() == false )
+ {
+ simpleTextPrint = true;
+ // no DoLineBreak()!
+ }
+ else
+ {
+ DoLineBreak();
+ }
+ }
+ ++depth;
+ return true;
+}
+
+
+bool TiXmlPrinter::VisitExit( const TiXmlElement& element )
+{
+ --depth;
+ if ( !element.FirstChild() )
+ {
+ // nothing.
+ }
+ else
+ {
+ if ( simpleTextPrint )
+ {
+ simpleTextPrint = false;
+ }
+ else
+ {
+ DoIndent();
+ }
+ buffer += "";
+ buffer += element.Value();
+ buffer += ">";
+ DoLineBreak();
+ }
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlText& text )
+{
+ if ( text.CDATA() )
+ {
+ DoIndent();
+ buffer += "";
+ DoLineBreak();
+ }
+ else if ( simpleTextPrint )
+ {
+ TIXML_STRING str;
+ TiXmlBase::EncodeString( text.ValueTStr(), &str );
+ buffer += str;
+ }
+ else
+ {
+ DoIndent();
+ TIXML_STRING str;
+ TiXmlBase::EncodeString( text.ValueTStr(), &str );
+ buffer += str;
+ DoLineBreak();
+ }
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration )
+{
+ DoIndent();
+ declaration.Print( 0, 0, &buffer );
+ DoLineBreak();
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlComment& comment )
+{
+ DoIndent();
+ buffer += "";
+ DoLineBreak();
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown )
+{
+ DoIndent();
+ buffer += "<";
+ buffer += unknown.Value();
+ buffer += ">";
+ DoLineBreak();
+ return true;
+}
+
+unsigned int TiXmlBase::Precision( const double value, const unsigned int requiredDecimalPlaces ) const
+{
+ unsigned int lhs = 0;
+ unsigned int one_for_plus_minus_sign = 1;
+
+ lhs = 0;
+ double temp(value);
+ while (temp >= 1.0)
+ {
+ lhs++;
+ temp /= 10.0;
+ }
+
+ return( lhs + requiredDecimalPlaces + one_for_plus_minus_sign );
+}
+
+/**
+ Return the printf format string that will ensure that the double value
+ passed in will be stored with 'required_decimal_places' worth of decimal
+ points as well as enough digits for the left hand side of the decimal point.
+ */
+TIXML_STRING TiXmlBase::Format( const double value, const unsigned int requiredDecimalPlaces ) const
+{
+ char buf[ 32 ];
+
+#if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF( buf, sizeof(buf), "%%%d.%dlf", Precision(value, requiredDecimalPlaces), requiredDecimalPlaces);
+#else
+ sprintf( buf, "%%%d.%dlf", Precision(value, requiredDecimalPlaces), requiredDecimalPlaces);
+#endif
+
+ return(TIXML_STRING(buf));
+}
+
+
+
diff --git a/CMakeExternals/Patchtinyxml-2.6.2.h b/CMakeExternals/Patchtinyxml-2.6.2.h
new file mode 100644
index 0000000000..3be7e726ee
--- /dev/null
+++ b/CMakeExternals/Patchtinyxml-2.6.2.h
@@ -0,0 +1,1823 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+ */
+
+
+#ifndef TINYXML_INCLUDED
+#define TINYXML_INCLUDED
+
+#ifndef TIXML_USE_STL
+#define TIXML_USE_STL
+#endif
+
+#ifndef TIXML_USE_STL
+#define TIXML_USE_STL
+#endif
+
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 4530 )
+#pragma warning( disable : 4786 )
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+// Help out windows:
+#if defined( _DEBUG ) && !defined( DEBUG )
+#define DEBUG
+#endif
+
+#ifdef TIXML_USE_STL
+#include
+#include
+#include
+#define TIXML_STRING std::string
+#else
+#include "tinystr.h"
+#define TIXML_STRING TiXmlString
+#endif
+
+// Deprecated library function hell. Compilers want to use the
+// new safe versions. This probably doesn't fully address the problem,
+// but it gets closer. There are too many compilers for me to fully
+// test. If you get compilation troubles, undefine TIXML_SAFE
+#define TIXML_SAFE
+
+#ifdef TIXML_SAFE
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+// Microsoft visual studio, version 2005 and higher.
+#define TIXML_SNPRINTF _snprintf_s
+#define TIXML_SSCANF sscanf_s
+#elif defined(_MSC_VER) && (_MSC_VER >= 1200 )
+// Microsoft visual studio, version 6 and higher.
+//#pragma message( "Using _sn* functions." )
+#define TIXML_SNPRINTF _snprintf
+#define TIXML_SSCANF sscanf
+#elif defined(__GNUC__) && (__GNUC__ >= 3 )
+// GCC version 3 and higher.s
+//#warning( "Using sn* functions." )
+#define TIXML_SNPRINTF snprintf
+#define TIXML_SSCANF sscanf
+#else
+#define TIXML_SNPRINTF snprintf
+#define TIXML_SSCANF sscanf
+#endif
+#endif
+
+class TiXmlDocument;
+class TiXmlElement;
+class TiXmlComment;
+class TiXmlUnknown;
+class TiXmlAttribute;
+class TiXmlText;
+class TiXmlDeclaration;
+class TiXmlParsingData;
+
+const int TIXML_MAJOR_VERSION = 2;
+const int TIXML_MINOR_VERSION = 6;
+const int TIXML_PATCH_VERSION = 2;
+
+#define DEFAULT_REQUIRED_DECIMAL_PLACES 14
+
+/* Internal structure for tracking location of items
+ in the XML file.
+ */
+struct TiXmlCursor
+{
+ TiXmlCursor() { Clear(); }
+ void Clear() { row = col = -1; }
+
+ int row; // 0 based.
+ int col; // 0 based.
+};
+
+
+/**
+ Implements the interface to the "Visitor pattern" (see the Accept() method.)
+ If you call the Accept() method, it requires being passed a TiXmlVisitor
+ class to handle callbacks. For nodes that contain other nodes (Document, Element)
+ you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves
+ are simply called with Visit().
+
+ If you return 'true' from a Visit method, recursive parsing will continue. If you return
+ false, no children of this node or its sibilings will be Visited.
+
+ All flavors of Visit methods have a default implementation that returns 'true' (continue
+ visiting). You need to only override methods that are interesting to you.
+
+ Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting.
+
+ You should never change the document from a callback.
+
+ @sa TiXmlNode::Accept()
+ */
+class TiXmlVisitor
+{
+public:
+ virtual ~TiXmlVisitor() {}
+
+ /// Visit a document.
+ virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; }
+ /// Visit a document.
+ virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; }
+
+ /// Visit an element.
+ virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; }
+ /// Visit an element.
+ virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; }
+
+ /// Visit a declaration
+ virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; }
+ /// Visit a text node
+ virtual bool Visit( const TiXmlText& /*text*/ ) { return true; }
+ /// Visit a comment node
+ virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; }
+ /// Visit an unknown node
+ virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; }
+};
+
+// Only used by Attribute::Query functions
+enum
+{
+ TIXML_SUCCESS,
+ TIXML_NO_ATTRIBUTE,
+ TIXML_WRONG_TYPE
+};
+
+
+// Used by the parsing routines.
+enum TiXmlEncoding
+{
+ TIXML_ENCODING_UNKNOWN,
+ TIXML_ENCODING_UTF8,
+ TIXML_ENCODING_LEGACY
+};
+
+const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
+
+/** TiXmlBase is a base class for every class in TinyXml.
+ It does little except to establish that TinyXml classes
+ can be printed and provide some utility functions.
+
+ In XML, the document and elements can contain
+ other elements and other types of nodes.
+
+ @verbatim
+ A Document can contain: Element (container or leaf)
+ Comment (leaf)
+ Unknown (leaf)
+ Declaration( leaf )
+
+ An Element can contain: Element (container or leaf)
+ Text (leaf)
+ Attributes (not on tree)
+ Comment (leaf)
+ Unknown (leaf)
+
+ A Decleration contains: Attributes (not on tree)
+ @endverbatim
+ */
+class TiXmlBase
+{
+ friend class TiXmlNode;
+ friend class TiXmlElement;
+ friend class TiXmlDocument;
+
+public:
+ TiXmlBase() : userData(0) {}
+ virtual ~TiXmlBase() {}
+
+ /** All TinyXml classes can print themselves to a filestream
+ or the string class (TiXmlString in non-STL mode, std::string
+ in STL mode.) Either or both cfile and str can be null.
+
+ This is a formatted print, and will insert
+ tabs and newlines.
+
+ (For an unformatted stream, use the << operator.)
+ */
+ virtual void Print( FILE* cfile, int depth ) const = 0;
+
+ /** The world does not agree on whether white space should be kept or
+ not. In order to make everyone happy, these global, static functions
+ are provided to set whether or not TinyXml will condense all white space
+ into a single space or not. The default is to condense. Note changing this
+ value is not thread safe.
+ */
+ static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; }
+
+ /// Return the current white space setting.
+ static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; }
+
+ /** Return the position, in the original source file, of this node or attribute.
+ The row and column are 1-based. (That is the first row and first column is
+ 1,1). If the returns values are 0 or less, then the parser does not have
+ a row and column value.
+
+ Generally, the row and column value will be set when the TiXmlDocument::Load(),
+ TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set
+ when the DOM was created from operator>>.
+
+ The values reflect the initial load. Once the DOM is modified programmatically
+ (by adding or changing nodes and attributes) the new values will NOT update to
+ reflect changes in the document.
+
+ There is a minor performance cost to computing the row and column. Computation
+ can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.
+
+ @sa TiXmlDocument::SetTabSize()
+ */
+ int Row() const { return location.row + 1; }
+ int Column() const { return location.col + 1; } ///< See Row()
+
+ void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data.
+ void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data.
+ const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data.
+
+ // Table that returs, for a given lead byte, the total number of bytes
+ // in the UTF-8 sequence.
+ static const int utf8ByteTable[256];
+
+ virtual const char* Parse( const char* p,
+ TiXmlParsingData* data,
+ TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;
+
+ /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc,
+ or they will be transformed into entities!
+ */
+ static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out );
+
+ enum
+ {
+ TIXML_NO_ERROR = 0,
+ TIXML_ERROR,
+ TIXML_ERROR_OPENING_FILE,
+ TIXML_ERROR_PARSING_ELEMENT,
+ TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
+ TIXML_ERROR_READING_ELEMENT_VALUE,
+ TIXML_ERROR_READING_ATTRIBUTES,
+ TIXML_ERROR_PARSING_EMPTY,
+ TIXML_ERROR_READING_END_TAG,
+ TIXML_ERROR_PARSING_UNKNOWN,
+ TIXML_ERROR_PARSING_COMMENT,
+ TIXML_ERROR_PARSING_DECLARATION,
+ TIXML_ERROR_DOCUMENT_EMPTY,
+ TIXML_ERROR_EMBEDDED_NULL,
+ TIXML_ERROR_PARSING_CDATA,
+ TIXML_ERROR_DOCUMENT_TOP_ONLY,
+
+ TIXML_ERROR_STRING_COUNT
+ };
+
+ unsigned int Precision( const double value, const unsigned int requiredDecimalPlaces ) const ;
+
+ TIXML_STRING Format( const double value, const unsigned int requiredDecimalPlaces ) const;
+
+protected:
+
+ static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
+
+ inline static bool IsWhiteSpace( char c )
+ {
+ return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' );
+ }
+ inline static bool IsWhiteSpace( int c )
+ {
+ if ( c < 256 )
+ return IsWhiteSpace( (char) c );
+ return false; // Again, only truly correct for English/Latin...but usually works.
+ }
+
+#ifdef TIXML_USE_STL
+ static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag );
+ static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag );
+#endif
+
+ /* Reads an XML name into the string provided. Returns
+ a pointer just past the last character of the name,
+ or 0 if the function has an error.
+ */
+ static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );
+
+ /* Reads text. Returns a pointer past the given end tag.
+ Wickedly complex options, but it keeps the (sensitive) code in one place.
+ */
+ static const char* ReadText( const char* in, // where to start
+ TIXML_STRING* text, // the string read
+ bool ignoreWhiteSpace, // whether to keep the white space
+ const char* endTag, // what ends this text
+ bool ignoreCase, // whether to ignore case in the end tag
+ TiXmlEncoding encoding ); // the current encoding
+
+ // If an entity has been found, transform it into a character.
+ static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );
+
+ // Get a character, while interpreting entities.
+ // The length can be from 0 to 4 bytes.
+ inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )
+ {
+ assert( p );
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ *length = utf8ByteTable[ *((const unsigned char*)p) ];
+ assert( *length >= 0 && *length < 5 );
+ }
+ else
+ {
+ *length = 1;
+ }
+
+ if ( *length == 1 )
+ {
+ if ( *p == '&' )
+ return GetEntity( p, _value, length, encoding );
+ *_value = *p;
+ return p+1;
+ }
+ else if ( *length )
+ {
+ //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe),
+ // and the null terminator isn't needed
+ for( int i=0; p[i] && i<*length; ++i ) {
+ _value[i] = p[i];
+ }
+ return p + (*length);
+ }
+ else
+ {
+ // Not valid text.
+ return 0;
+ }
+ }
+
+ // Return true if the next characters in the stream are any of the endTag sequences.
+ // Ignore case only works for english, and should only be relied on when comparing
+ // to English words: StringEqual( p, "version", true ) is fine.
+ static bool StringEqual( const char* p,
+ const char* endTag,
+ bool ignoreCase,
+ TiXmlEncoding encoding );
+
+ static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
+
+ TiXmlCursor location;
+
+ /// Field containing a generic user pointer
+ void* userData;
+
+ // None of these methods are reliable for any language except English.
+ // Good for approximation, not great for accuracy.
+ static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );
+ static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );
+ inline static int ToLower( int v, TiXmlEncoding encoding )
+ {
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ if ( v < 128 ) return tolower( v );
+ return v;
+ }
+ else
+ {
+ return tolower( v );
+ }
+ }
+ static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
+
+private:
+ TiXmlBase( const TiXmlBase& ); // not implemented.
+ void operator=( const TiXmlBase& base ); // not allowed.
+
+ struct Entity
+ {
+ const char* str;
+ unsigned int strLength;
+ char chr;
+ };
+ enum
+ {
+ NUM_ENTITY = 5,
+ MAX_ENTITY_LENGTH = 6
+
+ };
+ static Entity entity[ NUM_ENTITY ];
+ static bool condenseWhiteSpace;
+
+
+};
+
+
+/** The parent class for everything in the Document Object Model.
+ (Except for attributes).
+ Nodes have siblings, a parent, and children. A node can be
+ in a document, or stand on its own. The type of a TiXmlNode
+ can be queried, and it can be cast to its more defined type.
+ */
+class TiXmlNode : public TiXmlBase
+{
+ friend class TiXmlDocument;
+ friend class TiXmlElement;
+
+public:
+#ifdef TIXML_USE_STL
+
+ /** An input stream operator, for every class. Tolerant of newlines and
+ formatting, but doesn't expect them.
+ */
+ friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
+
+ /** An output stream operator, for every class. Note that this outputs
+ without any newlines or formatting, as opposed to Print(), which
+ includes tabs and new lines.
+
+ The operator<< and operator>> are not completely symmetric. Writing
+ a node to a stream is very well defined. You'll get a nice stream
+ of output, without any extra whitespace or newlines.
+
+ But reading is not as well defined. (As it always is.) If you create
+ a TiXmlElement (for example) and read that from an input stream,
+ the text needs to define an element or junk will result. This is
+ true of all input streams, but it's worth keeping in mind.
+
+ A TiXmlDocument will read nodes until it reads a root element, and
+ all the children of that root element.
+ */
+ friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
+
+ /// Appends the XML node or attribute to a std::string.
+ friend std::string& operator<< (std::string& out, const TiXmlNode& base );
+
+#endif
+
+ /** The types of XML nodes supported by TinyXml. (All the
+ unsupported types are picked up by UNKNOWN.)
+ */
+ enum NodeType
+ {
+ TINYXML_DOCUMENT,
+ TINYXML_ELEMENT,
+ TINYXML_COMMENT,
+ TINYXML_UNKNOWN,
+ TINYXML_TEXT,
+ TINYXML_DECLARATION,
+ TINYXML_TYPECOUNT
+ };
+
+ virtual ~TiXmlNode();
+
+ /** The meaning of 'value' changes for the specific type of
+ TiXmlNode.
+ @verbatim
+ Document: filename of the xml file
+ Element: name of the element
+ Comment: the comment text
+ Unknown: the tag contents
+ Text: the text string
+ @endverbatim
+
+ The subclasses will wrap this function.
+ */
+ const char *Value() const { return value.c_str (); }
+
+#ifdef TIXML_USE_STL
+ /** Return Value() as a std::string. If you only use STL,
+ this is more efficient than calling Value().
+ Only available in STL mode.
+ */
+ const std::string& ValueStr() const { return value; }
+#endif
+
+ const TIXML_STRING& ValueTStr() const { return value; }
+
+ /** Changes the value of the node. Defined as:
+ @verbatim
+ Document: filename of the xml file
+ Element: name of the element
+ Comment: the comment text
+ Unknown: the tag contents
+ Text: the text string
+ @endverbatim
+ */
+ void SetValue(const char * _value) { value = _value;}
+
+#ifdef TIXML_USE_STL
+ /// STL std::string form.
+ void SetValue( const std::string& _value ) { value = _value; }
+#endif
+
+ /// Delete all the children of this node. Does not affect 'this'.
+ void Clear();
+
+ /// One step up the DOM.
+ TiXmlNode* Parent() { return parent; }
+ const TiXmlNode* Parent() const { return parent; }
+
+ const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children.
+ TiXmlNode* FirstChild() { return firstChild; }
+ const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found.
+ /// The first child of this node with the matching 'value'. Will be null if none found.
+ TiXmlNode* FirstChild( const char * _value ) {
+ // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe)
+ // call the method, cast the return back to non-const.
+ return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value ));
+ }
+ const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children.
+ TiXmlNode* LastChild() { return lastChild; }
+
+ const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children.
+ TiXmlNode* LastChild( const char * _value ) {
+ return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value ));
+ }
+
+#ifdef TIXML_USE_STL
+ const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form.
+ const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form.
+#endif
+
+ /** An alternate way to walk the children of a node.
+ One way to iterate over nodes is:
+ @verbatim
+ for( child = parent->FirstChild(); child; child = child->NextSibling() )
+ @endverbatim
+
+ IterateChildren does the same thing with the syntax:
+ @verbatim
+ child = 0;
+ while( child = parent->IterateChildren( child ) )
+ @endverbatim
+
+ IterateChildren takes the previous child as input and finds
+ the next one. If the previous child is null, it returns the
+ first. IterateChildren will return null when done.
+ */
+ const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const;
+ TiXmlNode* IterateChildren( const TiXmlNode* previous ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) );
+ }
+
+ /// This flavor of IterateChildren searches for children with a particular 'value'
+ const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const;
+ TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) );
+ }
+
+#ifdef TIXML_USE_STL
+ const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
+ TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
+#endif
+
+ /** Add a new node related to this. Adds a child past the LastChild.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
+
+
+ /** Add a new node related to this. Adds a child past the LastChild.
+
+ NOTE: the node to be added is passed by pointer, and will be
+ henceforth owned (and deleted) by tinyXml. This method is efficient
+ and avoids an extra copy, but should be used with care as it
+ uses a different memory model than the other insert functions.
+
+ @sa InsertEndChild
+ */
+ TiXmlNode* LinkEndChild( TiXmlNode* addThis );
+
+ /** Add a new node related to this. Adds a child before the specified child.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
+
+ /** Add a new node related to this. Adds a child after the specified child.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis );
+
+ /** Replace a child of this node.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
+
+ /// Delete a child of this node.
+ bool RemoveChild( TiXmlNode* removeThis );
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* PreviousSibling() const { return prev; }
+ TiXmlNode* PreviousSibling() { return prev; }
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* PreviousSibling( const char * ) const;
+ TiXmlNode* PreviousSibling( const char *_prev ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) );
+ }
+
+#ifdef TIXML_USE_STL
+ const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
+ const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form.
+#endif
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* NextSibling() const { return next; }
+ TiXmlNode* NextSibling() { return next; }
+
+ /// Navigate to a sibling node with the given 'value'.
+ const TiXmlNode* NextSibling( const char * ) const;
+ TiXmlNode* NextSibling( const char* _next ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) );
+ }
+
+ /** Convenience function to get through elements.
+ Calls NextSibling and ToElement. Will skip all non-Element
+ nodes. Returns 0 if there is not another element.
+ */
+ const TiXmlElement* NextSiblingElement() const;
+ TiXmlElement* NextSiblingElement() {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() );
+ }
+
+ /** Convenience function to get through elements.
+ Calls NextSibling and ToElement. Will skip all non-Element
+ nodes. Returns 0 if there is not another element.
+ */
+ const TiXmlElement* NextSiblingElement( const char * ) const;
+ TiXmlElement* NextSiblingElement( const char *_next ) {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) );
+ }
+
+#ifdef TIXML_USE_STL
+ const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
+ TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
+#endif
+
+ /// Convenience function to get through elements.
+ const TiXmlElement* FirstChildElement() const;
+ TiXmlElement* FirstChildElement() {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() );
+ }
+
+ /// Convenience function to get through elements.
+ const TiXmlElement* FirstChildElement( const char * _value ) const;
+ TiXmlElement* FirstChildElement( const char * _value ) {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) );
+ }
+
+#ifdef TIXML_USE_STL
+ const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
+ TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
+#endif
+
+ /** Query the type (as an enumerated value, above) of this node.
+ The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT,
+ TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION.
+ */
+ int Type() const { return type; }
+
+ /** Return a pointer to the Document this node lives in.
+ Returns null if not in a document.
+ */
+ const TiXmlDocument* GetDocument() const;
+ TiXmlDocument* GetDocument() {
+ return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() );
+ }
+
+ /// Returns true if this node has no children.
+ bool NoChildren() const { return !firstChild; }
+
+ virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+ virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+ /** Create an exact duplicate of this node and return it. The memory must be deleted
+ by the caller.
+ */
+ virtual TiXmlNode* Clone() const = 0;
+
+ /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the
+ XML tree will be conditionally visited and the host will be called back
+ via the TiXmlVisitor interface.
+
+ This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse
+ the XML for the callbacks, so the performance of TinyXML is unchanged by using this
+ interface versus any other.)
+
+ The interface has been based on ideas from:
+
+ - http://www.saxproject.org/
+ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern
+
+ Which are both good references for "visiting".
+
+ An example of using Accept():
+ @verbatim
+ TiXmlPrinter printer;
+ tinyxmlDoc.Accept( &printer );
+ const char* xmlcstr = printer.CStr();
+ @endverbatim
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const = 0;
+
+protected:
+ TiXmlNode( NodeType _type );
+
+ // Copy to the allocated object. Shared functionality between Clone, Copy constructor,
+ // and the assignment operator.
+ void CopyTo( TiXmlNode* target ) const;
+
+#ifdef TIXML_USE_STL
+ // The real work of the input operator.
+ virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0;
+#endif
+
+ // Figure out what is at *p, and parse it. Returns null if it is not an xml node.
+ TiXmlNode* Identify( const char* start, TiXmlEncoding encoding );
+
+ TiXmlNode* parent;
+ NodeType type;
+
+ TiXmlNode* firstChild;
+ TiXmlNode* lastChild;
+
+ TIXML_STRING value;
+
+ TiXmlNode* prev;
+ TiXmlNode* next;
+
+private:
+ TiXmlNode( const TiXmlNode& ); // not implemented.
+ void operator=( const TiXmlNode& base ); // not allowed.
+};
+
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+ number of attributes, each with a unique name.
+
+ @note The attributes are not TiXmlNodes, since they are not
+ part of the tinyXML document object model. There are other
+ suggested ways to look at this problem.
+ */
+class TiXmlAttribute : public TiXmlBase
+{
+ friend class TiXmlAttributeSet;
+
+public:
+ /// Construct an empty attribute.
+ TiXmlAttribute() : TiXmlBase()
+{
+ document = 0;
+ prev = next = 0;
+}
+
+#ifdef TIXML_USE_STL
+ /// std::string constructor.
+ TiXmlAttribute( const std::string& _name, const std::string& _value )
+ {
+ name = _name;
+ value = _value;
+ document = 0;
+ prev = next = 0;
+ }
+#endif
+
+ /// Construct an attribute with a name and value.
+ TiXmlAttribute( const char * _name, const char * _value )
+ {
+ name = _name;
+ value = _value;
+ document = 0;
+ prev = next = 0;
+ }
+
+ const char* Name() const { return name.c_str(); } ///< Return the name of this attribute.
+ const char* Value() const { return value.c_str(); } ///< Return the value of this attribute.
+#ifdef TIXML_USE_STL
+ const std::string& ValueStr() const { return value; } ///< Return the value of this attribute.
+#endif
+ int IntValue() const; ///< Return the value of this attribute, converted to an integer.
+ double DoubleValue() const; ///< Return the value of this attribute, converted to a double.
+
+ // Get the tinyxml string representation
+ const TIXML_STRING& NameTStr() const { return name; }
+
+ /** QueryIntValue examines the value string. It is an alternative to the
+ IntValue() method with richer error checking.
+ If the value is an integer, it is stored in 'value' and
+ the call returns TIXML_SUCCESS. If it is not
+ an integer, it returns TIXML_WRONG_TYPE.
+
+ A specialized but useful call. Note that for success it returns 0,
+ which is the opposite of almost all other TinyXml calls.
+ */
+ int QueryIntValue( int* _value ) const;
+ /// QueryDoubleValue examines the value string. See QueryIntValue().
+ int QueryDoubleValue( double* _value ) const;
+
+ void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute.
+ void SetValue( const char* _value ) { value = _value; } ///< Set the value.
+
+ void SetIntValue( int _value ); ///< Set the value from an integer.
+ void SetDoubleValue( double _value, const unsigned int requiredDecimalPlaces = DEFAULT_REQUIRED_DECIMAL_PLACES ); ///< Set the value from a double.
+
+#ifdef TIXML_USE_STL
+ /// STL std::string form.
+ void SetName( const std::string& _name ) { name = _name; }
+ /// STL std::string form.
+ void SetValue( const std::string& _value ) { value = _value; }
+#endif
+
+ /// Get the next sibling attribute in the DOM. Returns null at end.
+ const TiXmlAttribute* Next() const;
+ TiXmlAttribute* Next() {
+ return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() );
+ }
+
+ /// Get the previous sibling attribute in the DOM. Returns null at beginning.
+ const TiXmlAttribute* Previous() const;
+ TiXmlAttribute* Previous() {
+ return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() );
+ }
+
+ bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; }
+ bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; }
+ bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; }
+
+ /* Attribute parsing starts: first letter of the name
+ returns: the next char after the value end quote
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ // Prints this Attribute to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const {
+ Print( cfile, depth, 0 );
+ }
+ void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
+
+ // [internal use]
+ // Set the document pointer so the attribute can report errors.
+ void SetDocument( TiXmlDocument* doc ) { document = doc; }
+
+private:
+ TiXmlAttribute( const TiXmlAttribute& ); // not implemented.
+ void operator=( const TiXmlAttribute& base ); // not allowed.
+
+ TiXmlDocument* document; // A pointer back to a document, for error reporting.
+ TIXML_STRING name;
+ TIXML_STRING value;
+ TiXmlAttribute* prev;
+ TiXmlAttribute* next;
+};
+
+
+/* A class used to manage a group of attributes.
+ It is only used internally, both by the ELEMENT and the DECLARATION.
+
+ The set can be changed transparent to the Element and Declaration
+ classes that use it, but NOT transparent to the Attribute
+ which has to implement a next() and previous() method. Which makes
+ it a bit problematic and prevents the use of STL.
+
+ This version is implemented with circular lists because:
+ - I like circular lists
+ - it demonstrates some independence from the (typical) doubly linked list.
+ */
+class TiXmlAttributeSet
+{
+public:
+ TiXmlAttributeSet();
+ ~TiXmlAttributeSet();
+
+ void Add( TiXmlAttribute* attribute );
+ void Remove( TiXmlAttribute* attribute );
+
+ const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
+ TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
+ const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
+ TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
+
+ TiXmlAttribute* Find( const char* _name ) const;
+ TiXmlAttribute* FindOrCreate( const char* _name );
+
+# ifdef TIXML_USE_STL
+ TiXmlAttribute* Find( const std::string& _name ) const;
+ TiXmlAttribute* FindOrCreate( const std::string& _name );
+# endif
+
+
+private:
+ //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),
+ //*ME: this class must be also use a hidden/disabled copy-constructor !!!
+ TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed
+ void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute)
+
+ TiXmlAttribute sentinel;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+ and can contain other elements, text, comments, and unknowns.
+ Elements also contain an arbitrary number of attributes.
+ */
+class TiXmlElement : public TiXmlNode
+{
+public:
+ /// Construct an element.
+ TiXmlElement (const char * in_value);
+
+#ifdef TIXML_USE_STL
+ /// std::string constructor.
+ TiXmlElement( const std::string& _value );
+#endif
+
+ TiXmlElement( const TiXmlElement& );
+
+ TiXmlElement& operator=( const TiXmlElement& base );
+
+ virtual ~TiXmlElement();
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ */
+ const char* Attribute( const char* name ) const;
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ If the attribute exists and can be converted to an integer,
+ the integer value will be put in the return 'i', if 'i'
+ is non-null.
+ */
+ const char* Attribute( const char* name, int* i ) const;
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ If the attribute exists and can be converted to an double,
+ the double value will be put in the return 'd', if 'd'
+ is non-null.
+ */
+ const char* Attribute( const char* name, double* d ) const;
+
+ /** QueryIntAttribute examines the attribute - it is an alternative to the
+ Attribute() method with richer error checking.
+ If the attribute is an integer, it is stored in 'value' and
+ the call returns TIXML_SUCCESS. If it is not
+ an integer, it returns TIXML_WRONG_TYPE. If the attribute
+ does not exist, then TIXML_NO_ATTRIBUTE is returned.
+ */
+ int QueryIntAttribute( const char* name, int* _value ) const;
+ /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute().
+ int QueryUnsignedAttribute( const char* name, unsigned* _value ) const;
+ /** QueryBoolAttribute examines the attribute - see QueryIntAttribute().
+ Note that '1', 'true', or 'yes' are considered true, while '0', 'false'
+ and 'no' are considered false.
+ */
+ int QueryBoolAttribute( const char* name, bool* _value ) const;
+ /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
+ int QueryDoubleAttribute( const char* name, double* _value ) const;
+ /// QueryFloatAttribute examines the attribute - see QueryIntAttribute().
+ int QueryFloatAttribute( const char* name, float* _value ) const {
+ double d;
+ int result = QueryDoubleAttribute( name, &d );
+ if ( result == TIXML_SUCCESS ) {
+ *_value = (float)d;
+ }
+ return result;
+ }
+
+#ifdef TIXML_USE_STL
+ /// QueryStringAttribute examines the attribute - see QueryIntAttribute().
+ int QueryStringAttribute( const char* name, std::string* _value ) const {
+ const char* cstr = Attribute( name );
+ if ( cstr ) {
+ *_value = std::string( cstr );
+ return TIXML_SUCCESS;
+ }
+ return TIXML_NO_ATTRIBUTE;
+ }
+
+ /** Template form of the attribute query which will try to read the
+ attribute into the specified type. Very easy, very powerful, but
+ be careful to make sure to call this with the correct type.
+
+ NOTE: This method doesn't work correctly for 'string' types that contain spaces.
+
+ @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE
+ */
+ template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const
+ {
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+
+ std::stringstream sstream( node->ValueStr() );
+ sstream >> *outValue;
+ if ( !sstream.fail() )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+ }
+
+ int QueryValueAttribute( const std::string& name, std::string* outValue ) const
+ {
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+ *outValue = node->ValueStr();
+ return TIXML_SUCCESS;
+ }
+#endif
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetAttribute( const char* name, const char * _value );
+
+#ifdef TIXML_USE_STL
+ const std::string* Attribute( const std::string& name ) const;
+ const std::string* Attribute( const std::string& name, int* i ) const;
+ const std::string* Attribute( const std::string& name, double* d ) const;
+ int QueryIntAttribute( const std::string& name, int* _value ) const;
+ int QueryDoubleAttribute( const std::string& name, double* _value ) const;
+
+ /// STL std::string form.
+ void SetAttribute( const std::string& name, const std::string& _value );
+ ///< STL std::string form.
+ void SetAttribute( const std::string& name, int _value );
+ ///< STL std::string form.
+ void SetDoubleAttribute( const std::string& name, double value, const unsigned int requiredDecimalPlaces = DEFAULT_REQUIRED_DECIMAL_PLACES );
+#endif
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetAttribute( const char * name, int value );
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetDoubleAttribute( const char * name, double value, const unsigned int requiredDecimalPlaces = DEFAULT_REQUIRED_DECIMAL_PLACES );
+
+ /** Deletes an attribute with the given name.
+ */
+ void RemoveAttribute( const char * name );
+#ifdef TIXML_USE_STL
+ void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form.
+#endif
+
+ const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element.
+ TiXmlAttribute* FirstAttribute() { return attributeSet.First(); }
+ const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element.
+ TiXmlAttribute* LastAttribute() { return attributeSet.Last(); }
+
+ /** Convenience function for easy access to the text inside an element. Although easy
+ and concise, GetText() is limited compared to getting the TiXmlText child
+ and accessing it directly.
+
+ If the first child of 'this' is a TiXmlText, the GetText()
+ returns the character string of the Text node, else null is returned.
+
+ This is a convenient method for getting the text of simple contained text:
+ @verbatim
+ This is text
+ const char* str = fooElement->GetText();
+ @endverbatim
+
+ 'str' will be a pointer to "This is text".
+
+ Note that this function can be misleading. If the element foo was created from
+ this XML:
+ @verbatim
+ This is text
+ @endverbatim
+
+ then the value of str would be null. The first child node isn't a text node, it is
+ another element. From this XML:
+ @verbatim
+ This is text
+ @endverbatim
+ GetText() will return "This is ".
+
+ WARNING: GetText() accesses a child node - don't become confused with the
+ similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are
+ safe type casts on the referenced node.
+ */
+ const char* GetText() const;
+
+ /// Creates a new Element and returns it - the returned element is a copy.
+ virtual TiXmlNode* Clone() const;
+ // Print the Element to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ /* Attribtue parsing starts: next char past '<'
+ returns: next char past '>'
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+
+ void CopyTo( TiXmlElement* target ) const;
+ void ClearThis(); // like clear, but initializes 'this' object as well
+
+ // Used to be public [internal use]
+#ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+#endif
+ /* [internal use]
+ Reads the "value" of the element -- another element, or text.
+ This should terminate with the current end tag.
+ */
+ const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
+
+private:
+ TiXmlAttributeSet attributeSet;
+};
+
+
+/** An XML comment.
+ */
+class TiXmlComment : public TiXmlNode
+{
+public:
+ /// Constructs an empty comment.
+ TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {}
+ /// Construct a comment from text.
+ TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {
+ SetValue( _value );
+ }
+ TiXmlComment( const TiXmlComment& );
+ TiXmlComment& operator=( const TiXmlComment& base );
+
+ virtual ~TiXmlComment() {}
+
+ /// Returns a copy of this Comment.
+ virtual TiXmlNode* Clone() const;
+ // Write this Comment to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ /* Attribtue parsing starts: at the ! of the !--
+ returns: next char past '>'
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+ void CopyTo( TiXmlComment* target ) const;
+
+ // used to be public
+#ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+#endif
+ // virtual void StreamOut( TIXML_OSTREAM * out ) const;
+
+private:
+
+};
+
+
+/** XML text. A text node can have 2 ways to output the next. "normal" output
+ and CDATA. It will default to the mode it was parsed from the XML file and
+ you generally want to leave it alone, but you can change the output mode with
+ SetCDATA() and query it with CDATA().
+ */
+class TiXmlText : public TiXmlNode
+{
+ friend class TiXmlElement;
+public:
+ /** Constructor for text element. By default, it is treated as
+ normal, encoded text. If you want it be output as a CDATA text
+ element, set the parameter _cdata to 'true'
+ */
+ TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
+{
+ SetValue( initValue );
+ cdata = false;
+}
+ virtual ~TiXmlText() {}
+
+#ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
+ {
+ SetValue( initValue );
+ cdata = false;
+ }
+#endif
+
+ TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); }
+ TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; }
+
+ // Write this text object to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ /// Queries whether this represents text using a CDATA section.
+ bool CDATA() const { return cdata; }
+ /// Turns on or off a CDATA representation of text.
+ void SetCDATA( bool _cdata ) { cdata = _cdata; }
+
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected :
+ /// [internal use] Creates a new Element and returns it.
+ virtual TiXmlNode* Clone() const;
+ void CopyTo( TiXmlText* target ) const;
+
+ bool Blank() const; // returns true if all white space and new lines
+ // [internal use]
+#ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+#endif
+
+private:
+ bool cdata; // true if this should be input and output as a CDATA style text element
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+ @verbatim
+
+ @endverbatim
+
+ TinyXml will happily read or write files without a declaration,
+ however. There are 3 possible attributes to the declaration:
+ version, encoding, and standalone.
+
+ Note: In this version of the code, the attributes are
+ handled as special cases, not generic attributes, simply
+ because there can only be at most 3 and they are always the same.
+ */
+class TiXmlDeclaration : public TiXmlNode
+{
+public:
+ /// Construct an empty declaration.
+ TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {}
+
+#ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlDeclaration( const std::string& _version,
+ const std::string& _encoding,
+ const std::string& _standalone );
+#endif
+
+ /// Construct.
+ TiXmlDeclaration( const char* _version,
+ const char* _encoding,
+ const char* _standalone );
+
+ TiXmlDeclaration( const TiXmlDeclaration& copy );
+ TiXmlDeclaration& operator=( const TiXmlDeclaration& copy );
+
+ virtual ~TiXmlDeclaration() {}
+
+ /// Version. Will return an empty string if none was found.
+ const char *Version() const { return version.c_str (); }
+ /// Encoding. Will return an empty string if none was found.
+ const char *Encoding() const { return encoding.c_str (); }
+ /// Is this a standalone document?
+ const char *Standalone() const { return standalone.c_str (); }
+
+ /// Creates a copy of this Declaration and returns it.
+ virtual TiXmlNode* Clone() const;
+ // Print this declaration to a FILE stream.
+ virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
+ virtual void Print( FILE* cfile, int depth ) const {
+ Print( cfile, depth, 0 );
+ }
+
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+ void CopyTo( TiXmlDeclaration* target ) const;
+ // used to be public
+#ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+#endif
+
+private:
+
+ TIXML_STRING version;
+ TIXML_STRING encoding;
+ TIXML_STRING standalone;
+};
+
+
+/** Any tag that tinyXml doesn't recognize is saved as an
+ unknown. It is a tag of text, but should not be modified.
+ It will be written back to the XML, unchanged, when the file
+ is saved.
+
+ DTD tags get thrown into TiXmlUnknowns.
+ */
+class TiXmlUnknown : public TiXmlNode
+{
+public:
+ TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {}
+ virtual ~TiXmlUnknown() {}
+
+ TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); }
+ TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; }
+
+ /// Creates a copy of this Unknown and returns it.
+ virtual TiXmlNode* Clone() const;
+ // Print this Unknown to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected:
+ void CopyTo( TiXmlUnknown* target ) const;
+
+#ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+#endif
+
+private:
+
+};
+
+
+/** Always the top level node. A document binds together all the
+ XML pieces. It can be saved, loaded, and printed to the screen.
+ The 'value' of a document node is the xml file name.
+ */
+class TiXmlDocument : public TiXmlNode
+{
+public:
+ /// Create an empty document, that has no name.
+ TiXmlDocument();
+ /// Create a document with a name. The name of the document is also the filename of the xml.
+ TiXmlDocument( const char * documentName );
+
+#ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlDocument( const std::string& documentName );
+#endif
+
+ TiXmlDocument( const TiXmlDocument& copy );
+ TiXmlDocument& operator=( const TiXmlDocument& copy );
+
+ virtual ~TiXmlDocument() {}
+
+ /** Load a file using the current document value.
+ Returns true if successful. Will delete any existing
+ document data before loading.
+ */
+ bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+ /// Save a file using the current document value. Returns true if successful.
+ bool SaveFile() const;
+ /// Load a file using the given filename. Returns true if successful.
+ bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+ /// Save a file using the given filename. Returns true if successful.
+ bool SaveFile( const char * filename ) const;
+ /** Load a file using the given FILE*. Returns true if successful. Note that this method
+ doesn't stream - the entire object pointed at by the FILE*
+ will be interpreted as an XML file. TinyXML doesn't stream in XML from the current
+ file location. Streaming may be added in the future.
+ */
+ bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+ /// Save a file using the given FILE*. Returns true if successful.
+ bool SaveFile( FILE* ) const;
+
+#ifdef TIXML_USE_STL
+ bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version.
+ {
+ return LoadFile( filename.c_str(), encoding );
+ }
+ bool SaveFile( const std::string& filename ) const ///< STL std::string version.
+ {
+ return SaveFile( filename.c_str() );
+ }
+#endif
+
+ /** Parse the given null terminated block of xml data. Passing in an encoding to this
+ method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml
+ to use that encoding, regardless of what TinyXml might otherwise try to detect.
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+
+ /** Get the root element -- the only top level element -- of the document.
+ In well formed XML, there should only be one. TinyXml is tolerant of
+ multiple elements at the document level.
+ */
+ const TiXmlElement* RootElement() const { return FirstChildElement(); }
+ TiXmlElement* RootElement() { return FirstChildElement(); }
+
+ /** If an error occurs, Error will be set to true. Also,
+ - The ErrorId() will contain the integer identifier of the error (not generally useful)
+ - The ErrorDesc() method will return the name of the error. (very useful)
+ - The ErrorRow() and ErrorCol() will return the location of the error (if known)
+ */
+ bool Error() const { return error; }
+
+ /// Contains a textual (english) description of the error if one occurs.
+ const char * ErrorDesc() const { return errorDesc.c_str (); }
+
+ /** Generally, you probably want the error string ( ErrorDesc() ). But if you
+ prefer the ErrorId, this function will fetch it.
+ */
+ int ErrorId() const { return errorId; }
+
+ /** Returns the location (if known) of the error. The first column is column 1,
+ and the first row is row 1. A value of 0 means the row and column wasn't applicable
+ (memory errors, for example, have no row/column) or the parser lost the error. (An
+ error in the error reporting, in that case.)
+
+ @sa SetTabSize, Row, Column
+ */
+ int ErrorRow() const { return errorLocation.row+1; }
+ int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow()
+
+ /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol())
+ to report the correct values for row and column. It does not change the output
+ or input in any way.
+
+ By calling this method, with a tab size
+ greater than 0, the row and column of each node and attribute is stored
+ when the file is loaded. Very useful for tracking the DOM back in to
+ the source file.
+
+ The tab size is required for calculating the location of nodes. If not
+ set, the default of 4 is used. The tabsize is set per document. Setting
+ the tabsize to 0 disables row/column tracking.
+
+ Note that row and column tracking is not supported when using operator>>.
+
+ The tab size needs to be enabled before the parse or load. Correct usage:
+ @verbatim
+ TiXmlDocument doc;
+ doc.SetTabSize( 8 );
+ doc.Load( "myfile.xml" );
+ @endverbatim
+
+ @sa Row, Column
+ */
+ void SetTabSize( int _tabsize ) { tabsize = _tabsize; }
+
+ int TabSize() const { return tabsize; }
+
+ /** If you have handled the error, it can be reset with this call. The error
+ state is automatically cleared if you Parse a new XML block.
+ */
+ void ClearError() { error = false;
+ errorId = 0;
+ errorDesc = "";
+ errorLocation.row = errorLocation.col = 0;
+ //errorLocation.last = 0;
+ }
+
+ /** Write the document to standard out using formatted printing ("pretty print"). */
+ void Print() const { Print( stdout, 0 ); }
+
+ /* Write the document to a string using formatted printing ("pretty print"). This
+ will allocate a character array (new char[]) and return it as a pointer. The
+ calling code pust call delete[] on the return char* to avoid a memory leak.
+ */
+ //char* PrintToMemory() const;
+
+ /// Print this Document to a FILE stream.
+ virtual void Print( FILE* cfile, int depth = 0 ) const;
+ // [internal use]
+ void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );
+
+ virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected :
+ // [internal use]
+ virtual TiXmlNode* Clone() const;
+#ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+#endif
+
+private:
+ void CopyTo( TiXmlDocument* target ) const;
+
+ bool error;
+ int errorId;
+ TIXML_STRING errorDesc;
+ int tabsize;
+ TiXmlCursor errorLocation;
+ bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write.
+};
+
+
+/**
+ A TiXmlHandle is a class that wraps a node pointer with null checks; this is
+ an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml
+ DOM structure. It is a separate utility class.
+
+ Take an example:
+ @verbatim
+
+
+
+
+
+
+ @endverbatim
+
+ Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
+ easy to write a *lot* of code that looks like:
+
+ @verbatim
+ TiXmlElement* root = document.FirstChildElement( "Document" );
+ if ( root )
+ {
+ TiXmlElement* element = root->FirstChildElement( "Element" );
+ if ( element )
+ {
+ TiXmlElement* child = element->FirstChildElement( "Child" );
+ if ( child )
+ {
+ TiXmlElement* child2 = child->NextSiblingElement( "Child" );
+ if ( child2 )
+ {
+ // Finally do something useful.
+ @endverbatim
+
+ And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity
+ of such code. A TiXmlHandle checks for null pointers so it is perfectly safe
+ and correct to use:
+
+ @verbatim
+ TiXmlHandle docHandle( &document );
+ TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
+ if ( child2 )
+ {
+ // do something useful
+ @endverbatim
+
+ Which is MUCH more concise and useful.
+
+ It is also safe to copy handles - internally they are nothing more than node pointers.
+ @verbatim
+ TiXmlHandle handleCopy = handle;
+ @endverbatim
+
+ What they should not be used for is iteration:
+
+ @verbatim
+ int i=0;
+ while ( true )
+ {
+ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement();
+ if ( !child )
+ break;
+ // do something
+ ++i;
+ }
+ @endverbatim
+
+ It seems reasonable, but it is in fact two embedded while loops. The Child method is
+ a linear walk to find the element, so this code would iterate much more than it needs
+ to. Instead, prefer:
+
+ @verbatim
+ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement();
+
+ for( child; child; child=child->NextSiblingElement() )
+ {
+ // do something
+ }
+ @endverbatim
+ */
+class TiXmlHandle
+{
+public:
+ /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+ TiXmlHandle( TiXmlNode* _node ) { this->node = _node; }
+ /// Copy constructor
+ TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; }
+ TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; }
+
+ /// Return a handle to the first child node.
+ TiXmlHandle FirstChild() const;
+ /// Return a handle to the first child node with the given name.
+ TiXmlHandle FirstChild( const char * value ) const;
+ /// Return a handle to the first child element.
+ TiXmlHandle FirstChildElement() const;
+ /// Return a handle to the first child element with the given name.
+ TiXmlHandle FirstChildElement( const char * value ) const;
+
+ /** Return a handle to the "index" child with the given name.
+ The first child is 0, the second 1, etc.
+ */
+ TiXmlHandle Child( const char* value, int index ) const;
+ /** Return a handle to the "index" child.
+ The first child is 0, the second 1, etc.
+ */
+ TiXmlHandle Child( int index ) const;
+ /** Return a handle to the "index" child element with the given name.
+ The first child element is 0, the second 1, etc. Note that only TiXmlElements
+ are indexed: other types are not counted.
+ */
+ TiXmlHandle ChildElement( const char* value, int index ) const;
+ /** Return a handle to the "index" child element.
+ The first child element is 0, the second 1, etc. Note that only TiXmlElements
+ are indexed: other types are not counted.
+ */
+ TiXmlHandle ChildElement( int index ) const;
+
+#ifdef TIXML_USE_STL
+ TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); }
+ TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); }
+
+ TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); }
+ TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); }
+#endif
+
+ /** Return the handle as a TiXmlNode. This may return null.
+ */
+ TiXmlNode* ToNode() const { return node; }
+ /** Return the handle as a TiXmlElement. This may return null.
+ */
+ TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
+ /** Return the handle as a TiXmlText. This may return null.
+ */
+ TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
+ /** Return the handle as a TiXmlUnknown. This may return null.
+ */
+ TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
+
+ /** @deprecated use ToNode.
+ Return the handle as a TiXmlNode. This may return null.
+ */
+ TiXmlNode* Node() const { return ToNode(); }
+ /** @deprecated use ToElement.
+ Return the handle as a TiXmlElement. This may return null.
+ */
+ TiXmlElement* Element() const { return ToElement(); }
+ /** @deprecated use ToText()
+ Return the handle as a TiXmlText. This may return null.
+ */
+ TiXmlText* Text() const { return ToText(); }
+ /** @deprecated use ToUnknown()
+ Return the handle as a TiXmlUnknown. This may return null.
+ */
+ TiXmlUnknown* Unknown() const { return ToUnknown(); }
+
+private:
+ TiXmlNode* node;
+};
+
+
+/** Print to memory functionality. The TiXmlPrinter is useful when you need to:
+
+ -# Print to memory (especially in non-STL mode)
+ -# Control formatting (line endings, etc.)
+
+ When constructed, the TiXmlPrinter is in its default "pretty printing" mode.
+ Before calling Accept() you can call methods to control the printing
+ of the XML document. After TiXmlNode::Accept() is called, the printed document can
+ be accessed via the CStr(), Str(), and Size() methods.
+
+ TiXmlPrinter uses the Visitor API.
+ @verbatim
+ TiXmlPrinter printer;
+ printer.SetIndent( "\t" );
+
+ doc.Accept( &printer );
+ fprintf( stdout, "%s", printer.CStr() );
+ @endverbatim
+ */
+class TiXmlPrinter : public TiXmlVisitor
+{
+public:
+ TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ),
+ buffer(), indent( " " ), lineBreak( "\n" ) {}
+
+ virtual bool VisitEnter( const TiXmlDocument& doc );
+ virtual bool VisitExit( const TiXmlDocument& doc );
+
+ virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute );
+ virtual bool VisitExit( const TiXmlElement& element );
+
+ virtual bool Visit( const TiXmlDeclaration& declaration );
+ virtual bool Visit( const TiXmlText& text );
+ virtual bool Visit( const TiXmlComment& comment );
+ virtual bool Visit( const TiXmlUnknown& unknown );
+
+ /** Set the indent characters for printing. By default 4 spaces
+ but tab (\t) is also useful, or null/empty string for no indentation.
+ */
+ void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; }
+ /// Query the indention string.
+ const char* Indent() { return indent.c_str(); }
+ /** Set the line breaking string. By default set to newline (\n).
+ Some operating systems prefer other characters, or can be
+ set to the null/empty string for no indenation.
+ */
+ void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; }
+ /// Query the current line breaking string.
+ const char* LineBreak() { return lineBreak.c_str(); }
+
+ /** Switch over to "stream printing" which is the most dense formatting without
+ linebreaks. Common when the XML is needed for network transmission.
+ */
+ void SetStreamPrinting() { indent = "";
+ lineBreak = "";
+ }
+ /// Return the result.
+ const char* CStr() { return buffer.c_str(); }
+ /// Return the length of the result string.
+ size_t Size() { return buffer.size(); }
+
+#ifdef TIXML_USE_STL
+ /// Return the result.
+ const std::string& Str() { return buffer; }
+#endif
+
+private:
+ void DoIndent() {
+ for( int i=0; i ${_src_dir_length_max})."
"Please move the MITK source code directory to a directory with a shorter path." )
endif()
if(_bin_n GREATER _bin_dir_length_max)
message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})."
"Please move the MITK build directory to a directory with a shorter path." )
endif()
endif()
#-----------------------------------------------------------------------------
# See http://cmake.org/cmake/help/cmake-2-8-docs.html#section_Policies for details
#-----------------------------------------------------------------------------
set(project_policies
CMP0001 # NEW: CMAKE_BACKWARDS_COMPATIBILITY should no longer be used.
CMP0002 # NEW: Logical target names must be globally unique.
CMP0003 # NEW: Libraries linked via full path no longer produce linker search paths.
CMP0004 # NEW: Libraries linked may NOT have leading or trailing whitespace.
CMP0005 # NEW: Preprocessor definition values are now escaped automatically.
CMP0006 # NEW: Installing MACOSX_BUNDLE targets requires a BUNDLE DESTINATION.
CMP0007 # NEW: List command no longer ignores empty elements.
CMP0008 # NEW: Libraries linked by full-path must have a valid library file name.
CMP0009 # NEW: FILE GLOB_RECURSE calls should not follow symlinks by default.
CMP0010 # NEW: Bad variable reference syntax is an error.
CMP0011 # NEW: Included scripts do automatic cmake_policy PUSH and POP.
CMP0012 # NEW: if() recognizes numbers and boolean constants.
CMP0013 # NEW: Duplicate binary directories are not allowed.
CMP0014 # NEW: Input directories must have CMakeLists.txt
)
foreach(policy ${project_policies})
if(POLICY ${policy})
cmake_policy(SET ${policy} NEW)
endif()
endforeach()
#-----------------------------------------------------------------------------
# Update CMake module path
#------------------------------------------------------------------------------
set(CMAKE_MODULE_PATH
${MITK_SOURCE_DIR}/CMake
${CMAKE_MODULE_PATH}
)
#-----------------------------------------------------------------------------
# CMake function(s) and macro(s)
#-----------------------------------------------------------------------------
include(mitkMacroEmptyExternalProject)
include(mitkFunctionGenerateProjectXml)
include(mitkFunctionSuppressWarnings)
SUPPRESS_VC_DEPRECATED_WARNINGS()
#-----------------------------------------------------------------------------
# Output directories.
#-----------------------------------------------------------------------------
foreach(type LIBRARY RUNTIME ARCHIVE)
# Make sure the directory exists
if(DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY
AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY})
message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}")
file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}")
endif()
if(MITK_USE_SUPERBUILD)
set(output_dir ${MITK_BINARY_DIR}/bin)
if(NOT DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY)
set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_BINARY_DIR}/MITK-build/bin)
endif()
else()
if(NOT DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY)
set(output_dir ${MITK_BINARY_DIR}/bin)
else()
set(output_dir ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY})
endif()
endif()
set(CMAKE_${type}_OUTPUT_DIRECTORY ${output_dir} CACHE INTERNAL "Single output directory for building all libraries.")
mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY)
endforeach()
#-----------------------------------------------------------------------------
# Additional MITK Options (also shown during superbuild)
#-----------------------------------------------------------------------------
option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON)
option(WITH_COVERAGE "Enable/Disable coverage" OFF)
option(BUILD_TESTING "Test the project" ON)
option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF)
set(MITK_BUILD_TUTORIAL OFF CACHE INTERNAL "Deprecated! Use MITK_BUILD_EXAMPLES instead!")
option(MITK_BUILD_EXAMPLES "Build the MITK Examples" ${MITK_BUILD_TUTORIAL})
option(MITK_USE_ACVD "Use Approximated Centroidal Voronoi Diagrams" OFF)
option(MITK_USE_GLEW "Use the GLEW library" ON)
option(MITK_USE_Boost "Use the Boost C++ library" OFF)
option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON)
option(MITK_USE_CTK "Use CTK in MITK" ${MITK_USE_BLUEBERRY})
option(MITK_USE_QT "Use Nokia's Qt library" ${MITK_USE_CTK})
option(MITK_USE_DCMTK "EXPERIMENTAL, superbuild only: Use DCMTK in MITK" ${MITK_USE_CTK})
option(MITK_USE_OpenCV "Use Intel's OpenCV library" OFF)
option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF)
option(MITK_USE_Poco "Use the Poco library" ON)
option(MITK_USE_SOFA "Use Simulation Open Framework Architecture" OFF)
option(MITK_USE_Python "Use Python wrapping in MITK" OFF)
set(MITK_USE_CableSwig ${MITK_USE_Python})
if(MITK_USE_Python)
FIND_PACKAGE(PythonLibs REQUIRED)
FIND_PACKAGE(PythonInterp REQUIRED)
endif()
mark_as_advanced(MITK_BUILD_ALL_APPS
MITK_USE_GLEW
MITK_USE_CTK
MITK_USE_DCMTK
)
if(MITK_USE_Boost)
option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF)
set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries")
endif()
if(MITK_USE_BLUEBERRY)
option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF)
mark_as_advanced(MITK_BUILD_ALL_PLUGINS)
if(NOT MITK_USE_CTK)
message("Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY")
set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE)
endif()
endif()
if(MITK_USE_CTK)
if(NOT MITK_USE_QT)
message("Forcing MITK_USE_QT to ON because of MITK_USE_CTK")
set(MITK_USE_QT ON CACHE BOOL "Use Nokia's Qt library in MITK" FORCE)
endif()
if(NOT MITK_USE_DCMTK)
message("Setting MITK_USE_DCMTK to ON because DCMTK needs to be build for CTK")
set(MITK_USE_DCMTK ON CACHE BOOL "Use DCMTK in MITK" FORCE)
endif()
endif()
if(MITK_USE_QT)
# find the package at the very beginning, so that QT4_FOUND is available
find_package(Qt4 4.6.2 REQUIRED)
endif()
if(MITK_USE_SOFA)
# SOFA requires at least CMake 2.8.8
set(SOFA_CMAKE_VERSION 2.8.8)
if(${CMAKE_VERSION} VERSION_LESS ${SOFA_CMAKE_VERSION})
set(MITK_USE_SOFA OFF CACHE BOOL "" FORCE)
message(WARNING "Switched off MITK_USE_SOFA\n Minimum required CMake version: ${SOFA_CMAKE_VERSION}\n Installed CMake version: ${CMAKE_VERSION}")
endif()
# SOFA doesn't support Clang
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(MITK_USE_SOFA OFF CACHE BOOL "" FORCE)
message(WARNING "Switched off MITK_USE_SOFA\n Clang is not supported, use GCC instead.")
endif()
# SOFA/ITK combination requires at least MSVC 2010
if(MSVC_VERSION AND MSVC_VERSION LESS 1600)
set(MITK_USE_SOFA OFF CACHE BOOL "" FORCE)
message(WARNING "Switched off MITK_USE_SOFA\n MSVC versions less than 2010 are not supported.")
endif()
# SOFA requires boost library
if(MITK_USE_SOFA AND NOT MITK_USE_Boost)
message("Forcing MITK_USE_Boost to ON because of MITK_USE_SOFA")
set(MITK_USE_Boost ON CACHE BOOL "" FORCE)
endif()
# SOFA requires boost system library
list(FIND MITK_USE_Boost_LIBRARIES system _result)
if(_result LESS 0)
message("Adding 'system' to MITK_USE_Boost_LIBRARIES.")
list(APPEND MITK_USE_Boost_LIBRARIES system)
endif()
# SOFA requires boost thread library
list(FIND MITK_USE_Boost_LIBRARIES thread _result)
if(_result LESS 0)
message("Adding 'thread' to MITK_USE_Boost_LIBRARIES.")
list(APPEND MITK_USE_Boost_LIBRARIES thread)
endif()
set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "" FORCE)
endif()
# Customize the default pixel types for multiplex macros
set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES
"int, unsigned int, short, unsigned short, char, unsigned char"
CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros")
set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES
"double, float"
CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros")
set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES
"itk::RGBPixel, itk::RGBAPixel"
CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros")
set(MITK_ACCESSBYITK_DIMENSIONS
"2,3"
CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros")
mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES
MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES
MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES
MITK_ACCESSBYITK_DIMENSIONS
)
# consistency checks
if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES)
set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES
"int, unsigned int, short, unsigned short, char, unsigned char"
CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE)
endif()
if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES)
set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES
"double, float"
CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE)
endif()
if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES)
set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES
"itk::RGBPixel, itk::RGBAPixel"
CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE)
endif()
if(NOT MITK_ACCESSBYITK_DIMENSIONS)
set(MITK_ACCESSBYITK_DIMENSIONS
"2,3"
CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros")
endif()
#-----------------------------------------------------------------------------
# Project.xml
#-----------------------------------------------------------------------------
# A list of topologically ordered targets
set(CTEST_PROJECT_SUBPROJECTS)
if(MITK_USE_BLUEBERRY)
list(APPEND CTEST_PROJECT_SUBPROJECTS BlueBerry)
endif()
list(APPEND CTEST_PROJECT_SUBPROJECTS
MITK-Core
MITK-CoreUI
MITK-IGT
MITK-ToF
MITK-DTI
MITK-Registration
MITK-Modules # all modules not contained in a specific subproject
MITK-Plugins # all plugins not contained in a specific subproject
MITK-Examples
Unlabeled # special "subproject" catching all unlabeled targets and tests
)
# Configure CTestConfigSubProject.cmake that could be used by CTest scripts
configure_file(${MITK_SOURCE_DIR}/CTestConfigSubProject.cmake.in
${MITK_BINARY_DIR}/CTestConfigSubProject.cmake)
if(CTEST_PROJECT_ADDITIONAL_TARGETS)
# those targets will be executed at the end of the ctest driver script
# and they also get their own subproject label
set(subproject_list "${CTEST_PROJECT_SUBPROJECTS};${CTEST_PROJECT_ADDITIONAL_TARGETS}")
else()
set(subproject_list "${CTEST_PROJECT_SUBPROJECTS}")
endif()
# Generate Project.xml file expected by the CTest driver script
mitkFunctionGenerateProjectXml(${MITK_BINARY_DIR} MITK "${subproject_list}" ${MITK_USE_SUPERBUILD})
#-----------------------------------------------------------------------------
# Superbuild script
#-----------------------------------------------------------------------------
if(MITK_USE_SUPERBUILD)
include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake")
return()
endif()
#*****************************************************************************
#**************************** END OF SUPERBUILD ****************************
#*****************************************************************************
#-----------------------------------------------------------------------------
# CMake function(s) and macro(s)
#-----------------------------------------------------------------------------
include(CheckCXXSourceCompiles)
include(mitkFunctionCheckCompilerFlags)
include(mitkFunctionGetGccVersion)
include(MacroParseArguments)
include(mitkFunctionSuppressWarnings) # includes several functions
include(mitkFunctionOrganizeSources)
include(mitkFunctionGetVersion)
include(mitkFunctionGetVersionDescription)
include(mitkFunctionCreateWindowsBatchScript)
include(mitkFunctionInstallProvisioningFiles)
include(mitkFunctionInstallAutoLoadModules)
include(mitkFunctionGetLibrarySearchPaths)
include(mitkFunctionCompileSnippets)
include(mitkMacroCreateModuleConf)
include(mitkMacroCreateModule)
include(mitkMacroCheckModule)
include(mitkMacroCreateModuleTests)
include(mitkFunctionAddCustomModuleTest)
include(mitkMacroUseModule)
include(mitkMacroMultiplexPicType)
include(mitkMacroInstall)
include(mitkMacroInstallHelperApp)
include(mitkMacroInstallTargets)
include(mitkMacroGenerateToolsLibrary)
include(mitkMacroGetLinuxDistribution)
include(mitkMacroGetPMDPlatformString)
#-----------------------------------------------------------------------------
# Prerequesites
#-----------------------------------------------------------------------------
find_package(ITK REQUIRED)
find_package(VTK REQUIRED)
find_package(GDCM PATHS ${ITK_GDCM_DIR} REQUIRED)
#-----------------------------------------------------------------------------
# Set MITK specific options and variables (NOT available during superbuild)
#-----------------------------------------------------------------------------
# ASK THE USER TO SHOW THE CONSOLE WINDOW FOR CoreApp and mitkWorkbench
option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON)
mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW)
# TODO: check if necessary
option(USE_ITKZLIB "Use the ITK zlib for pic compression." ON)
mark_as_advanced(USE_ITKZLIB)
if(NOT MITK_FAST_TESTING)
if(DEFINED MITK_CTEST_SCRIPT_MODE
AND (MITK_CTEST_SCRIPT_MODE STREQUAL "continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "experimental") )
set(MITK_FAST_TESTING 1)
endif()
endif()
#-----------------------------------------------------------------------------
# Get MITK version info
#-----------------------------------------------------------------------------
mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK)
mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK)
#-----------------------------------------------------------------------------
# Installation preparation
#
# These should be set before any MITK install macros are used
#-----------------------------------------------------------------------------
# on Mac OSX all BlueBerry plugins get copied into every
# application bundle (.app directory) specified here
if(MITK_USE_BLUEBERRY AND APPLE)
include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake")
foreach(mitk_app ${MITK_APPS})
# extract option_name
string(REPLACE "^^" "\\;" target_info ${mitk_app})
set(target_info_list ${target_info})
list(GET target_info_list 1 option_name)
list(GET target_info_list 0 app_name)
# check if the application is enabled
if(${option_name} OR MITK_BUILD_ALL_APPS)
set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} ${app_name})
endif()
endforeach()
endif()
#-----------------------------------------------------------------------------
# Set symbol visibility Flags
#-----------------------------------------------------------------------------
# MinGW does not export all symbols automatically, so no need to set flags
if(CMAKE_COMPILER_IS_GNUCXX AND NOT MINGW)
set(VISIBILITY_CXX_FLAGS ) #"-fvisibility=hidden -fvisibility-inlines-hidden")
endif()
#-----------------------------------------------------------------------------
# Set coverage Flags
#-----------------------------------------------------------------------------
if(WITH_COVERAGE)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG")
set(COVERAGE_CXX_FLAGS ${coverage_flags})
set(COVERAGE_C_FLAGS ${coverage_flags})
endif()
endif()
#-----------------------------------------------------------------------------
# MITK C/CXX Flags
#-----------------------------------------------------------------------------
set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}")
set(MITK_C_FLAGS_DEBUG )
set(MITK_C_FLAGS_RELEASE )
set(MITK_CXX_FLAGS "${VISIBILITY_CXX_FLAGS} ${COVERAGE_CXX_FLAGS}")
set(MITK_CXX_FLAGS_DEBUG )
set(MITK_CXX_FLAGS_RELEASE )
set(MITK_EXE_LINKER_FLAGS )
set(MITK_SHARED_LINKER_FLAGS )
include(mitkSetupC++0xVariables)
if(WIN32)
set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -D_WIN32_WINNT=0x0501 -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN")
set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} /wd4231") # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation
endif()
if(NOT MSVC_VERSION)
foreach(_flag
-Wall
-Wextra
-Wpointer-arith
-Winvalid-pch
-Wcast-align
-Wwrite-strings
-Wno-error=gnu
-Woverloaded-virtual
-Wstrict-null-sentinel
#-Wold-style-cast
#-Wsign-promo
# the following two lines should be removed after ITK-3097 has
# been resolved, see also MITK bug 15279
-Wno-unused-local-typedefs
-Wno-array-bounds
-fdiagnostics-show-option
)
mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS)
endforeach()
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS)
mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS)
if(MITK_USE_C++0x)
mitkFunctionCheckCompilerFlags("-std=c++0x" MITK_CXX_FLAGS)
endif()
mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION)
# With older version of gcc supporting the flag -fstack-protector-all, an extra dependency to libssp.so
# is introduced. If gcc is smaller than 4.4.0 and the build type is Release let's not include the flag.
# Doing so should allow to build package made for distribution using older linux distro.
if(${GCC_VERSION} VERSION_GREATER "4.4.0" OR (CMAKE_BUILD_TYPE STREQUAL "Debug" AND ${GCC_VERSION} VERSION_LESS "4.4.0"))
mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS)
endif()
if(MINGW)
# suppress warnings about auto imported symbols
set(MITK_SHARED_LINKER_FLAGS "-Wl,--enable-auto-import ${MITK_SHARED_LINKER_FLAGS}")
endif()
set(MITK_CXX_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}")
endif()
set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS})
set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS})
#-----------------------------------------------------------------------------
# MITK Packages
#-----------------------------------------------------------------------------
set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends)
set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR})
#-----------------------------------------------------------------------------
# Testing
#-----------------------------------------------------------------------------
if(BUILD_TESTING)
enable_testing()
include(CTest)
mark_as_advanced(TCL_TCLSH DART_ROOT)
option(MITK_ENABLE_RENDERING_TESTING OFF "Enable the MITK rendering tests. Requires x-server in Linux.")
#Rendering testing does not work for Linux nightlies, thus it is disabled per default
#and activated for Mac and Windows.
if(WIN32 OR APPLE)
set(MITK_ENABLE_RENDERING_TESTING ON)
endif()
mark_as_advanced( MITK_ENABLE_RENDERING_TESTING )
# Setup file for setting custom ctest vars
configure_file(
CMake/CTestCustom.cmake.in
${MITK_BINARY_DIR}/CTestCustom.cmake
@ONLY
)
# Configuration for the CMake-generated test driver
set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ")
set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN "
try
{")
set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " }
catch( std::exception & excp )
{
fprintf(stderr,\"%s\\n\",excp.what());
return EXIT_FAILURE;
}
catch( ... )
{
printf(\"Exception caught in the test driver\\n\");
return EXIT_FAILURE;
}
")
set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output")
if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR})
file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR})
endif()
# Test the external project template
if(MITK_USE_BLUEBERRY)
include(mitkTestProjectTemplate)
endif()
# Test the package target
include(mitkPackageTest)
endif()
configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h)
#-----------------------------------------------------------------------------
# MITK_SUPERBUILD_BINARY_DIR
#-----------------------------------------------------------------------------
# If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild.
# In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR
if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR)
set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR})
endif()
#-----------------------------------------------------------------------------
# Compile Utilities and set-up MITK variables
#-----------------------------------------------------------------------------
include(mitkSetupVariables)
#-----------------------------------------------------------------------------
# Cleanup
#-----------------------------------------------------------------------------
file(GLOB _MODULES_CONF_FILES ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/*.cmake)
if(_MODULES_CONF_FILES)
file(REMOVE ${_MODULES_CONF_FILES})
endif()
add_subdirectory(Utilities)
if(MITK_USE_BLUEBERRY)
# We need to hack a little bit because MITK applications may need
# to enable certain BlueBerry plug-ins. However, these plug-ins
# are validated separately from the MITK plug-ins and know nothing
# about potential MITK plug-in dependencies of the applications. Hence
# we cannot pass the MITK application list to the BlueBerry
# ctkMacroSetupPlugins call but need to extract the BlueBerry dependencies
# from the applications and set them explicitly.
include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake")
foreach(mitk_app ${MITK_APPS})
# extract target_dir and option_name
string(REPLACE "^^" "\\;" target_info ${mitk_app})
set(target_info_list ${target_info})
list(GET target_info_list 0 target_dir)
list(GET target_info_list 1 option_name)
# check if the application is enabled and if target_libraries.cmake exists
if((${option_name} OR MITK_BUILD_ALL_APPS) AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/target_libraries.cmake")
include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/target_libraries.cmake")
foreach(_target_dep ${target_libraries})
if(_target_dep MATCHES org_blueberry_)
string(REPLACE _ . _app_bb_dep ${_target_dep})
# explicitly set the build option for the BlueBerry plug-in
set(BLUEBERRY_BUILD_${_app_bb_dep} ON CACHE BOOL "Build the ${_app_bb_dep} plug-in")
endif()
endforeach()
endif()
endforeach()
set(mbilog_DIR "${mbilog_BINARY_DIR}")
if(MITK_BUILD_ALL_PLUGINS)
set(BLUEBERRY_BUILD_ALL_PLUGINS ON)
endif()
set(BLUEBERRY_XPDOC_OUTPUT_DIR ${MITK_DOXYGEN_OUTPUT_DIR}/html/extension-points/html/)
add_subdirectory(BlueBerry)
set(BlueBerry_DIR ${CMAKE_CURRENT_BINARY_DIR}/BlueBerry
CACHE PATH "The directory containing a CMake configuration file for BlueBerry" FORCE)
include(mitkMacroCreateCTKPlugin)
endif()
#-----------------------------------------------------------------------------
# Set C/CXX and linker flags for MITK code
#-----------------------------------------------------------------------------
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}")
#-----------------------------------------------------------------------------
# Add custom targets representing CDash subprojects
#-----------------------------------------------------------------------------
foreach(subproject ${CTEST_PROJECT_SUBPROJECTS})
if(NOT TARGET ${subproject} AND NOT subproject MATCHES "Unlabeled")
add_custom_target(${subproject})
endif()
endforeach()
#-----------------------------------------------------------------------------
# Add subdirectories
#-----------------------------------------------------------------------------
link_directories(${MITK_LINK_DIRECTORIES})
add_subdirectory(Core)
add_subdirectory(Modules)
if(MITK_USE_BLUEBERRY)
find_package(BlueBerry REQUIRED)
set(MITK_DEFAULT_SUBPROJECTS MITK-Plugins)
# Plug-in testing (needs some work to be enabled again)
if(BUILD_TESTING)
include(berryTestingHelpers)
set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp")
get_target_property(_is_macosx_bundle CoreApp MACOSX_BUNDLE)
if(APPLE AND _is_macosx_bundle)
set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp.app/Contents/MacOS/CoreApp")
endif()
set(BLUEBERRY_TEST_APP_ID "org.mitk.qt.coreapplication")
endif()
include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake")
set(mitk_plugins_fullpath )
foreach(mitk_plugin ${MITK_EXT_PLUGINS})
list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin})
endforeach()
if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake)
include(${MITK_PRIVATE_MODULES}/PluginList.cmake)
foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS})
list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin})
endforeach()
endif()
# Specify which plug-ins belong to this project
macro(GetMyTargetLibraries all_target_libraries varname)
set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$")
set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$")
set(_tmp_list)
list(APPEND _tmp_list ${all_target_libraries})
ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb OUTPUT_VARIABLE ${varname})
endmacro()
# Get infos about application directories and build options
include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake")
set(mitk_apps_fullpath )
foreach(mitk_app ${MITK_APPS})
list(APPEND mitk_apps_fullpath "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${mitk_app}")
endforeach()
ctkMacroSetupPlugins(${mitk_plugins_fullpath}
BUILD_OPTION_PREFIX MITK_BUILD_
APPS ${mitk_apps_fullpath}
BUILD_ALL ${MITK_BUILD_ALL_PLUGINS}
COMPACT_OPTIONS)
set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake")
if(${PROJECT_NAME}_PLUGIN_LIBRARIES)
ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE})
else()
file(REMOVE ${MITK_PLUGIN_USE_FILE})
set(MITK_PLUGIN_USE_FILE )
endif()
# 11.3.13, change, muellerm: activate python bundle if python and blueberry is active
if( MITK_USE_Python )
set(MITK_BUILD_org.mitk.gui.qt.python ON)
endif()
endif()
#-----------------------------------------------------------------------------
# Python Wrapping
#-----------------------------------------------------------------------------
option(MITK_USE_Python "Build Python integration for MITK (requires CableSwig)." OFF)
#-----------------------------------------------------------------------------
# Documentation
#-----------------------------------------------------------------------------
add_subdirectory(Documentation)
#-----------------------------------------------------------------------------
# Installation
#-----------------------------------------------------------------------------
# set MITK cpack variables
# These are the default variables, which can be overwritten ( see below )
include(mitkSetupCPack)
set(use_default_config ON)
# MITK_APPS is set in Applications/AppList.cmake (included somewhere above
# if MITK_USE_BLUEBERRY is set to ON).
if(MITK_APPS)
set(activated_apps_no 0)
list(LENGTH MITK_APPS app_count)
# Check how many apps have been enabled
# If more than one app has been activated, the we use the
# default CPack configuration. Otherwise that apps configuration
# will be used, if present.
foreach(mitk_app ${MITK_APPS})
# extract option_name
string(REPLACE "^^" "\\;" target_info ${mitk_app})
set(target_info_list ${target_info})
list(GET target_info_list 1 option_name)
# check if the application is enabled
if(${option_name} OR MITK_BUILD_ALL_APPS)
MATH(EXPR activated_apps_no "${activated_apps_no} + 1")
endif()
endforeach()
if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS))
# Corner case if there is only one app in total
set(use_project_cpack ON)
elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS)
# Only one app is enabled (no "build all" flag set)
set(use_project_cpack ON)
else()
# Less or more then one app is enabled
set(use_project_cpack OFF)
endif()
foreach(mitk_app ${MITK_APPS})
# extract target_dir and option_name
string(REPLACE "^^" "\\;" target_info ${mitk_app})
set(target_info_list ${target_info})
list(GET target_info_list 0 target_dir)
list(GET target_info_list 1 option_name)
# check if the application is enabled
if(${option_name} OR MITK_BUILD_ALL_APPS)
# check whether application specific configuration files will be used
if(use_project_cpack)
# use files if they exist
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/CPackOptions.cmake")
include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/CPackOptions.cmake")
endif()
if(EXISTS "${PROJECT_SOURCE_DIR}/Applications/${target_dir}/CPackConfig.cmake.in")
set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/Applications/${target_dir}/CPackConfig.cmake")
configure_file(${PROJECT_SOURCE_DIR}/Applications/${target_dir}/CPackConfig.cmake.in
${CPACK_PROJECT_CONFIG_FILE} @ONLY)
set(use_default_config OFF)
endif()
endif()
# add link to the list
list(APPEND CPACK_CREATE_DESKTOP_LINKS "${target_dir}")
endif()
endforeach()
endif()
# if no application specific configuration file was used, use default
if(use_default_config)
configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in
${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY)
set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake")
endif()
# include CPack model once all variables are set
include(CPack)
# Additional installation rules
include(mitkInstallRules)
#-----------------------------------------------------------------------------
# Last configuration steps
#-----------------------------------------------------------------------------
# This is for installation support of external projects depending on
# MITK plugins and modules. The export file should not be used for linking to MITK
# libraries without using LINK_DIRECTORIES, since the exports are incomplete
# yet (depending libraries are not exported).
set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake")
file(REMOVE ${MITK_EXPORTS_FILE})
set(targets_to_export)
get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS)
if(module_targets)
list(APPEND targets_to_export ${module_targets})
endif()
if(MITK_USE_BLUEBERRY)
if(MITK_PLUGIN_LIBRARIES)
list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES})
endif()
endif()
export(TARGETS ${targets_to_export} APPEND
FILE ${MITK_EXPORTS_FILE})
set(MITK_EXPORTED_TARGET_PROPERTIES )
foreach(target_to_export ${targets_to_export})
get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS)
if(autoload_targets)
set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES}
set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")")
endif()
get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY)
if(autoload_dir)
set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES}
set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")")
endif()
endforeach()
get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS)
configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in
${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY)
configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in
${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY)
configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in
${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY)
set(VISIBILITY_AVAILABLE 0)
set(visibility_test_flag "")
mitkFunctionCheckCompilerFlags("-fvisibility=hidden" visibility_test_flag)
if(visibility_test_flag)
# The compiler understands -fvisiblity=hidden (probably gcc >= 4 or Clang)
set(VISIBILITY_AVAILABLE 1)
endif()
configure_file(mitkExportMacros.h.in ${MITK_BINARY_DIR}/mitkExportMacros.h)
configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h)
configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h)
set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc)
set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities)
file(GLOB _MODULES_CONF_FILES RELATIVE ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME} ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/*.cmake)
set(MITK_MODULE_NAMES)
foreach(_module ${_MODULES_CONF_FILES})
string(REPLACE Config.cmake "" _module_name ${_module})
list(APPEND MITK_MODULE_NAMES ${_module_name})
endforeach()
configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h)
configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY)
# If we are under Windows, create two batch files which correctly
# set up the environment for the application and for Visual Studio
if(WIN32)
include(mitkFunctionCreateWindowsBatchScript)
set(VS_SOLUTION_FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.sln")
foreach(VS_BUILD_TYPE debug release)
mitkFunctionCreateWindowsBatchScript("${MITK_SOURCE_DIR}/CMake/StartVS.bat.in"
${PROJECT_BINARY_DIR}/StartVS_${VS_BUILD_TYPE}.bat
${VS_BUILD_TYPE})
endforeach()
endif(WIN32)
#-----------------------------------------------------------------------------
# MITK Applications
#-----------------------------------------------------------------------------
# This must come after MITKConfig.h was generated, since applications
# might do a find_package(MITK REQUIRED).
add_subdirectory(Applications)
#-----------------------------------------------------------------------------
# MITK Examples
#-----------------------------------------------------------------------------
if(MITK_BUILD_EXAMPLES)
# This must come after MITKConfig.h was generated, since applications
# might do a find_package(MITK REQUIRED).
add_subdirectory(Examples)
endif()
-
diff --git a/Core/Code/Algorithms/mitkImageTimeSelector.cpp b/Core/Code/Algorithms/mitkImageTimeSelector.cpp
index 7fa5352ae7..57a8952a17 100644
--- a/Core/Code/Algorithms/mitkImageTimeSelector.cpp
+++ b/Core/Code/Algorithms/mitkImageTimeSelector.cpp
@@ -1,78 +1,99 @@
/*===================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#include "mitkImageTimeSelector.h"
mitk::ImageTimeSelector::ImageTimeSelector() : m_TimeNr(0), m_ChannelNr(0)
{
}
mitk::ImageTimeSelector::~ImageTimeSelector()
{
}
void mitk::ImageTimeSelector::GenerateOutputInformation()
{
Image::ConstPointer input = this->GetInput();
Image::Pointer output = this->GetOutput();
itkDebugMacro(<<"GenerateOutputInformation()");
int dim=(input->GetDimension()<3?input->GetDimension():3);
output->Initialize(input->GetPixelType(), dim, input->GetDimensions());
if( (unsigned int) m_TimeNr >= input->GetDimension(3) )
{
m_TimeNr = input->GetDimension(3)-1;
}
// initialize geometry
- output->SetGeometry(dynamic_cast(input->GetSlicedGeometry(m_TimeNr)->Clone().GetPointer()));
+ mitk::SlicedGeometry3D::Pointer sliced_geo = input->GetSlicedGeometry(m_TimeNr);
+ if( sliced_geo.IsNull() )
+ {
+ mitkThrow() << "Failed to retrieve SlicedGeometry from input at timestep " << m_TimeNr;
+ }
+
+ mitk::SlicedGeometry3D::Pointer sliced_geo_clone = sliced_geo->Clone();
+ if( sliced_geo_clone.IsNull() )
+ {
+ mitkThrow() << "Failed to clone the retrieved sliced geometry.";
+ }
+
+ mitk::Geometry3D::Pointer geom_3d = dynamic_cast(sliced_geo_clone.GetPointer());
+ if( geom_3d.IsNotNull() )
+ {
+ output->SetGeometry(geom_3d.GetPointer() );
+ }
+ else
+ {
+ mitkThrow() << "Failed to cast the retrieved SlicedGeometry to a Geometry3D object.";
+ }
+
output->SetPropertyList(input->GetPropertyList()->Clone());
}
void mitk::ImageTimeSelector::GenerateData()
{
const Image::RegionType& requestedRegion = this->GetOutput()->GetRequestedRegion();
//do we really need a complete volume at a time?
if(requestedRegion.GetSize(2)>1)
this->SetVolumeItem( this->GetVolumeData(m_TimeNr, m_ChannelNr), 0 );
else
//no, so take just a slice!
this->SetSliceItem( this->GetSliceData(requestedRegion.GetIndex(2), m_TimeNr, m_ChannelNr), requestedRegion.GetIndex(2), 0 );
}
void mitk::ImageTimeSelector::GenerateInputRequestedRegion()
{
Superclass::GenerateInputRequestedRegion();
ImageToImageFilter::InputImagePointer input =
const_cast< mitk::ImageToImageFilter::InputImageType * > ( this->GetInput() );
Image::Pointer output = this->GetOutput();
Image::RegionType requestedRegion;
requestedRegion = output->GetRequestedRegion();
requestedRegion.SetIndex(3, m_TimeNr);
requestedRegion.SetIndex(4, m_ChannelNr);
requestedRegion.SetSize(3, 1);
requestedRegion.SetSize(4, 1);
input->SetRequestedRegion( & requestedRegion );
}
diff --git a/Core/Code/DataManagement/mitkImage.h b/Core/Code/DataManagement/mitkImage.h
index cfc35a788e..16e2642efc 100644
--- a/Core/Code/DataManagement/mitkImage.h
+++ b/Core/Code/DataManagement/mitkImage.h
@@ -1,712 +1,712 @@
/*===================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#ifndef MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2
#define MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2
#include
#include "mitkSlicedData.h"
#include "mitkBaseData.h"
#include "mitkLevelWindow.h"
#include "mitkPlaneGeometry.h"
#include
#include "mitkImageDataItem.h"
#include "mitkImageDescriptor.h"
#include "mitkImageAccessorBase.h"
#include "mitkImageVtkAccessor.h"
//DEPRECATED
#include
#ifndef __itkHistogram_h
#include
#endif
class vtkImageData;
namespace mitk {
class SubImageSelector;
class ImageTimeSelector;
class ImageStatisticsHolder;
//##Documentation
//## @brief Image class for storing images
//##
//## Can be asked for header information, the data vector,
//## the mitkIpPicDescriptor struct or vtkImageData objects. If not the complete
//## data is required, the appropriate SubImageSelector class should be used
//## for access.
//## Image organizes sets of slices (s x 2D), volumes (t x 3D) and channels (n
//## x ND). Channels are for different kind of data, e.g., morphology in
//## channel 0, velocities in channel 1. All channels must have the same Geometry! In
//## particular, the dimensions of all channels are the same, only the pixel-type
//## may differ between channels.
//##
//## For importing ITK images use of mitk::ITKImageImport is recommended, see
//## \ref Adaptor.
//##
//## For ITK v3.8 and older: Converting coordinates from the ITK physical
//## coordinate system (which does not support rotated images) to the MITK world
//## coordinate system should be performed via the Geometry3D of the Image, see
//## Geometry3D::WorldToItkPhysicalPoint.
//## @ingroup Data
class MITK_CORE_EXPORT Image : public SlicedData
{
friend class SubImageSelector;
friend class ImageAccessorBase;
friend class ImageVtkAccessor;
friend class ImageReadAccessor;
friend class ImageWriteAccessor;
public:
mitkClassMacro(Image, SlicedData);
itkNewMacro(Self);
mitkCloneMacro(Image);
/** Smart Pointer type to a ImageDataItem. */
typedef itk::SmartPointer ImageDataItemPointer;
typedef itk::Statistics::Histogram HistogramType;
typedef mitk::ImageStatisticsHolder* StatisticsHolderPointer;
//## @param ImportMemoryManagementType This parameter is evaluated when setting new data to an image.
//## The different options are:
//## CopyMemory: Data to be set is copied and assigned to a new memory block. Data memory block will be freed on deletion of mitk::Image.
//## MamageMemory: Data to be set will be referenced, and Data memory block will be freed on deletion of mitk::Image.
//## Reference Memory: Data to be set will be referenced, but Data memory block will not be freed on deletion of mitk::Image.
//## DontManageMemory = ReferenceMemory.
enum ImportMemoryManagementType { CopyMemory, ManageMemory, ReferenceMemory, DontManageMemory = ReferenceMemory };
//##Documentation
//## @brief Vector container of SmartPointers to ImageDataItems;
//## Class is only for internal usage to allow convenient access to all slices over iterators;
//## See documentation of ImageDataItem for details.
typedef std::vector ImageDataItemPointerArray;
public:
//##Documentation
//## @brief Returns the PixelType of channel @a n.
const mitk::PixelType GetPixelType(int n = 0) const;
//##Documentation
//## @brief Get dimension of the image
//##
unsigned int GetDimension() const;
//##Documentation
//## @brief Get the size of dimension @a i (e.g., i=0 results in the number of pixels in x-direction).
//##
//## @sa GetDimensions()
unsigned int GetDimension(int i) const;
/** @brief Get the data vector of the complete image, i.e., of all channels linked together.
If you only want to access a slice, volume at a specific time or single channel
use one of the SubImageSelector classes.
\deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by ImageWriteAccessor::GetData() or ImageReadAccessor::GetData() */
DEPRECATED(virtual void* GetData());
public:
/** @brief Get the pixel value at one specific index position.
The pixel type is always being converted to double.
\deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by a method from ImagePixelWriteAccessor or ImagePixelReadAccessor */
DEPRECATED(double GetPixelValueByIndex(const mitk::Index3D& position, unsigned int timestep = 0));
/** @brief Get the pixel value at one specific world position.
The pixel type is always being converted to double.
\deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by a method from ImagePixelWriteAccessor or ImagePixelReadAccessor */
DEPRECATED(double GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep = 0));
//##Documentation
//## @brief Get a volume at a specific time @a t of channel @a n as a vtkImageData.
virtual ImageVtkAccessor* GetVtkImageData(int t = 0, int n = 0);
//##Documentation
//## @brief Get the complete image, i.e., all channels linked together, as a @a mitkIpPicDescriptor.
//##
//## If you only want to access a slice, volume at a specific time or single channel
//## use one of the SubImageSelector classes.
//virtual mitkIpPicDescriptor* GetPic();
//##Documentation
//## @brief Check whether slice @a s at time @a t in channel @a n is set
virtual bool IsSliceSet(int s = 0, int t = 0, int n = 0) const;
//##Documentation
//## @brief Check whether volume at time @a t in channel @a n is set
virtual bool IsVolumeSet(int t = 0, int n = 0) const;
//##Documentation
//## @brief Check whether the channel @a n is set
virtual bool IsChannelSet(int n = 0) const;
//##Documentation
//## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in
//## the responsibility of the caller to ensure that the data vector @a data
//## is really a slice (at least is not smaller than a slice), since there is
//## no chance to check this.
//##
//## The data is copied to an array managed by the image. If the image shall
//## reference the data, use SetImportSlice with ImportMemoryManagementType
//## set to ReferenceMemory. For importing ITK images use of mitk::
//## ITKImageImport is recommended.
//## @sa SetPicSlice, SetImportSlice, SetImportVolume
virtual bool SetSlice(const void *data, int s = 0, int t = 0, int n = 0);
//##Documentation
//## @brief Set @a data as volume at time @a t in channel @a n. It is in
//## the responsibility of the caller to ensure that the data vector @a data
//## is really a volume (at least is not smaller than a volume), since there is
//## no chance to check this.
//##
//## The data is copied to an array managed by the image. If the image shall
//## reference the data, use SetImportVolume with ImportMemoryManagementType
//## set to ReferenceMemory. For importing ITK images use of mitk::
//## ITKImageImport is recommended.
//## @sa SetPicVolume, SetImportVolume
virtual bool SetVolume(const void *data, int t = 0, int n = 0);
//##Documentation
//## @brief Set @a data in channel @a n. It is in
//## the responsibility of the caller to ensure that the data vector @a data
//## is really a channel (at least is not smaller than a channel), since there is
//## no chance to check this.
//##
//## The data is copied to an array managed by the image. If the image shall
//## reference the data, use SetImportChannel with ImportMemoryManagementType
//## set to ReferenceMemory. For importing ITK images use of mitk::
//## ITKImageImport is recommended.
//## @sa SetPicChannel, SetImportChannel
virtual bool SetChannel(const void *data, int n = 0);
//##Documentation
//## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in
//## the responsibility of the caller to ensure that the data vector @a data
//## is really a slice (at least is not smaller than a slice), since there is
//## no chance to check this.
//##
//## The data is managed according to the parameter \a importMemoryManagement.
//## @sa SetPicSlice
virtual bool SetImportSlice(void *data, int s = 0, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory );
//##Documentation
//## @brief Set @a data as volume at time @a t in channel @a n. It is in
//## the responsibility of the caller to ensure that the data vector @a data
//## is really a volume (at least is not smaller than a volume), since there is
//## no chance to check this.
//##
//## The data is managed according to the parameter \a importMemoryManagement.
//## @sa SetPicVolume
virtual bool SetImportVolume(void *data, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory );
//##Documentation
//## @brief Set @a data in channel @a n. It is in
//## the responsibility of the caller to ensure that the data vector @a data
//## is really a channel (at least is not smaller than a channel), since there is
//## no chance to check this.
//##
//## The data is managed according to the parameter \a importMemoryManagement.
//## @sa SetPicChannel
virtual bool SetImportChannel(void *data, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory );
//##Documentation
//## initialize new (or re-initialize) image information
//## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0).
virtual void Initialize(const mitk::PixelType& type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels = 1);
//##Documentation
//## initialize new (or re-initialize) image information by a Geometry3D
//##
//## @param tDim defines the number of time steps for which the Image should be initialized
virtual void Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int channels = 1, int tDim=1);
/**
* initialize new (or re-initialize) image information by a Geometry3D
*
* @param tDim defines the number of time steps for which the Image should be initialized
* \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201
*/
- DEPRECATED(virtual void Initialize(const mitk::PixelType& type, const mitk::TimeSlicedGeometry* geometry, unsigned int channels = 1, int tDim=1)){}
+ DEPRECATED(virtual void Initialize(const mitk::PixelType& /*type*/, const mitk::TimeSlicedGeometry* /*geometry*/, unsigned int /*channels = 1*/, int /*tDim=1*/)){}
/**
* \brief Initialize new (or re-initialize) image information by a TimeGeometry
*
* \param tDim override time dimension if the value is bigger than 0 (Default -1)
*/
virtual void Initialize(const mitk::PixelType& type, const mitk::TimeGeometry& geometry, unsigned int channels = 1, int tDim=-1 );
//##Documentation
//## initialize new (or re-initialize) image information by a Geometry2D and number of slices
//##
//## Initializes the bounding box according to the width/height of the
//## Geometry2D and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced.
//## The spacing is calculated from the Geometry2D.
//## \sa SlicedGeometry3D::InitializeEvenlySpaced
virtual void Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped = false, unsigned int channels = 1, int tDim=1);
//##Documentation
//## initialize new (or re-initialize) image information by another
//## mitk-image.
//## Only the header is used, not the data vector!
//##
virtual void Initialize(const mitk::Image* image);
virtual void Initialize(const mitk::ImageDescriptor::Pointer inDesc);
//##Documentation
//## initialize new (or re-initialize) image information by @a pic.
//## Dimensions and @a Geometry3D /@a Geometry2D are set according
//## to the tags in @a pic.
//## Only the header is used, not the data vector! Use SetPicVolume(pic)
//## to set the data vector.
//##
//## @param tDim override time dimension (@a n[3]) in @a pic (if >0)
//## @param sDim override z-space dimension (@a n[2]) in @a pic (if >0)
//## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0).
//virtual void Initialize(const mitkIpPicDescriptor* pic, int channels = 1, int tDim = -1, int sDim = -1);
//##Documentation
//## initialize new (or re-initialize) image information by @a vtkimagedata,
//## a vtk-image.
//## Only the header is used, not the data vector! Use
//## SetVolume(vtkimage->GetScalarPointer()) to set the data vector.
//##
//## @param tDim override time dimension in @a vtkimagedata (if >0 and <)
//## @param sDim override z-space dimension in @a vtkimagedata (if >0 and <)
//## @param pDim override y-space dimension in @a vtkimagedata (if >0 and <)
virtual void Initialize(vtkImageData* vtkimagedata, int channels = 1, int tDim = -1, int sDim = -1, int pDim = -1);
//##Documentation
//## initialize new (or re-initialize) image information by @a itkimage,
//## a templated itk-image.
//## Only the header is used, not the data vector! Use
//## SetVolume(itkimage->GetBufferPointer()) to set the data vector.
//##
//## @param tDim override time dimension in @a itkimage (if >0 and <)
//## @param sDim override z-space dimension in @a itkimage (if >0 and <)
template void InitializeByItk(const itkImageType* itkimage, int channels = 1, int tDim = -1, int sDim=-1)
{
if(itkimage==NULL) return;
MITK_DEBUG << "Initializing MITK image from ITK image.";
// build array with dimensions in each direction with at least 4 entries
m_Dimension=itkimage->GetImageDimension();
unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4];
for(i=0;iGetLargestPossibleRegion().GetSize().GetSize()[i];
if(m_Dimension<4)
{
unsigned int *p;
for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p)
*p=1;
}
// overwrite number of slices if sDim is set
if((m_Dimension>2) && (sDim>=0))
tmpDimensions[2]=sDim;
// overwrite number of time points if tDim is set
if((m_Dimension>3) && (tDim>=0))
tmpDimensions[3]=tDim;
// rough initialization of Image
// mitk::PixelType importType = ImportItkPixelType( itkimage::PixelType );
Initialize(MakePixelType(),
m_Dimension,
tmpDimensions,
channels);
const typename itkImageType::SpacingType & itkspacing = itkimage->GetSpacing();
MITK_DEBUG << "ITK spacing " << itkspacing;
// access spacing of itk::Image
Vector3D spacing;
FillVector3D(spacing, itkspacing[0], 1.0, 1.0);
if(m_Dimension >= 2)
spacing[1]=itkspacing[1];
if(m_Dimension >= 3)
spacing[2]=itkspacing[2];
// access origin of itk::Image
Point3D origin;
const typename itkImageType::PointType & itkorigin = itkimage->GetOrigin();
MITK_DEBUG << "ITK origin " << itkorigin;
FillVector3D(origin, itkorigin[0], 0.0, 0.0);
if(m_Dimension>=2)
origin[1]=itkorigin[1];
if(m_Dimension>=3)
origin[2]=itkorigin[2];
// access direction of itk::Imagm_PixelType = new mitk::PixelType(type);e and include spacing
const typename itkImageType::DirectionType & itkdirection = itkimage->GetDirection();
MITK_DEBUG << "ITK direction " << itkdirection;
mitk::Matrix3D matrix;
matrix.SetIdentity();
unsigned int j, itkDimMax3 = (m_Dimension >= 3? 3 : m_Dimension);
// check if spacing has no zero entry and itkdirection has no zero columns
bool itkdirectionOk = true;
mitk::ScalarType columnSum;
for( j=0; j < itkDimMax3; ++j )
{
columnSum = 0.0;
for ( i=0; i < itkDimMax3; ++i)
{
columnSum += fabs(itkdirection[i][j]);
}
if(columnSum < mitk::eps)
{
itkdirectionOk = false;
}
if ( (spacing[j] < - mitk::eps) // (normally sized) negative value
&& (j==2) && (m_Dimensions[2] == 1) )
{
// Negative spacings can occur when reading single DICOM slices with ITK via GDCMIO
// In these cases spacing is not determind by ITK correctly (because it distinguishes correctly
// between slice thickness and inter slice distance -- slice distance is meaningless for
// single slices).
// I experienced that ITK produced something meaningful nonetheless because is is
// evaluating the tag "(0018,0088) Spacing between slices" as a fallback. This tag is not
// reliable (http://www.itk.org/pipermail/insight-users/2005-September/014711.html)
// but gives at least a hint.
// In real world cases I experienced that this tag contained the correct inter slice distance
// with a negative sign, so we just invert such negative spacings.
MITK_WARN << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using inverted value " << -spacing[j];
spacing[j] = -spacing[j];
}
else if (spacing[j] < mitk::eps) // value near zero
{
MITK_ERROR << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using 1.0 instead.";
spacing[j] = 1.0;
}
}
if(itkdirectionOk == false)
{
MITK_ERROR << "Illegal matrix returned by itk::Image::GetDirection():" << itkdirection << " Using identity instead.";
for ( i=0; i < itkDimMax3; ++i)
for( j=0; j < itkDimMax3; ++j )
if ( i == j )
matrix[i][j] = spacing[j];
else
matrix[i][j] = 0.0;
}
else
{
for ( i=0; i < itkDimMax3; ++i)
for( j=0; j < itkDimMax3; ++j )
matrix[i][j] = itkdirection[i][j]*spacing[j];
}
// re-initialize PlaneGeometry with origin and direction
PlaneGeometry* planeGeometry = static_cast(GetSlicedGeometry(0)->GetGeometry2D(0));
planeGeometry->SetOrigin(origin);
planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix);
// re-initialize SlicedGeometry3D
SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0);
slicedGeometry->InitializeEvenlySpaced(planeGeometry, m_Dimensions[2]);
slicedGeometry->SetSpacing(spacing);
// re-initialize TimeGeometry
ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New();
timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]);
SetTimeGeometry(timeGeometry);
// clean-up
delete [] tmpDimensions;
this->Initialize();
};
//##Documentation
//## @brief Check whether slice @a s at time @a t in channel @a n is valid, i.e.,
//## is (or can be) inside of the image
virtual bool IsValidSlice(int s = 0, int t = 0, int n = 0) const;
//##Documentation
//## @brief Check whether volume at time @a t in channel @a n is valid, i.e.,
//## is (or can be) inside of the image
virtual bool IsValidVolume(int t = 0, int n = 0) const;
//##Documentation
//## @brief Check whether the channel @a n is valid, i.e.,
//## is (or can be) inside of the image
virtual bool IsValidChannel(int n = 0) const;
//##Documentation
//## @brief Returns true if an image is rotated, i.e. its geometry's
//## transformation matrix has nonzero elements besides the diagonal.
//## Non-diagonal elements are checked if larger then 1/1000 of the matrix' trace.
bool IsRotated() const;
//##Documentation
//## @brief Get the sizes of all dimensions as an integer-array.
//##
//## @sa GetDimension(int i);
unsigned int* GetDimensions() const;
ImageDescriptor::Pointer GetImageDescriptor() const
{ return m_ImageDescriptor; }
ChannelDescriptor GetChannelDescriptor( int id = 0 ) const
{ return m_ImageDescriptor->GetChannelDescriptor(id); }
/** \brief Sets a geometry to an image.
*/
virtual void SetGeometry(Geometry3D* aGeometry3D);
/**
* @warning for internal use only
*/
virtual ImageDataItemPointer GetSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory);
/**
* @warning for internal use only
*/
virtual ImageDataItemPointer GetVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory);
/**
* @warning for internal use only
*/
virtual ImageDataItemPointer GetChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory);
/**
\brief (DEPRECATED) Get the minimum for scalar images
*/
DEPRECATED (ScalarType GetScalarValueMin(int t=0) const);
/**
\brief (DEPRECATED) Get the maximum for scalar images
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (ScalarType GetScalarValueMax(int t=0) const);
/**
\brief (DEPRECATED) Get the second smallest value for scalar images
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (ScalarType GetScalarValue2ndMin(int t=0) const);
/**
\brief (DEPRECATED) Get the smallest value for scalar images, but do not recompute it first
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (ScalarType GetScalarValueMinNoRecompute( unsigned int t = 0 ) const);
/**
\brief (DEPRECATED) Get the second smallest value for scalar images, but do not recompute it first
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (ScalarType GetScalarValue2ndMinNoRecompute( unsigned int t = 0 ) const);
/**
\brief (DEPRECATED) Get the second largest value for scalar images
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (ScalarType GetScalarValue2ndMax(int t=0) const);
/**
\brief (DEPRECATED) Get the largest value for scalar images, but do not recompute it first
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (ScalarType GetScalarValueMaxNoRecompute( unsigned int t = 0 ) const );
/**
\brief (DEPRECATED) Get the second largest value for scalar images, but do not recompute it first
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (ScalarType GetScalarValue2ndMaxNoRecompute( unsigned int t = 0 ) const);
/**
\brief (DEPRECATED) Get the count of voxels with the smallest scalar value in the dataset
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (ScalarType GetCountOfMinValuedVoxels(int t = 0) const);
/**
\brief (DEPRECATED) Get the count of voxels with the largest scalar value in the dataset
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (ScalarType GetCountOfMaxValuedVoxels(int t = 0) const);
/**
\brief (DEPRECATED) Get the count of voxels with the largest scalar value in the dataset
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (unsigned int GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t = 0 ) const);
/**
\brief (DEPRECATED) Get the count of voxels with the smallest scalar value in the dataset
\warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead
*/
DEPRECATED (unsigned int GetCountOfMinValuedVoxelsNoRecompute( unsigned int t = 0 ) const);
/**
\brief Returns a pointer to the ImageStatisticsHolder object that holds all statistics information for the image.
All Get-methods for statistics properties formerly accessible directly from an Image object are now moved to the
new \a ImageStatisticsHolder object.
*/
StatisticsHolderPointer GetStatistics() const
{
return m_ImageStatistics;
}
protected:
int GetSliceIndex(int s = 0, int t = 0, int n = 0) const;
int GetVolumeIndex(int t = 0, int n = 0) const;
void ComputeOffsetTable();
virtual bool IsValidTimeStep(int t) const;
virtual void Expand( unsigned int timeSteps );
virtual ImageDataItemPointer AllocateSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory);
virtual ImageDataItemPointer AllocateVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory);
virtual ImageDataItemPointer AllocateChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory);
Image();
Image(const Image &other);
virtual ~Image();
virtual void Clear();
//## @warning Has to be called by every Initialize method!
virtual void Initialize();
virtual void PrintSelf(std::ostream& os, itk::Indent indent) const;
mutable ImageDataItemPointerArray m_Channels;
mutable ImageDataItemPointerArray m_Volumes;
mutable ImageDataItemPointerArray m_Slices;
unsigned int m_Dimension;
unsigned int* m_Dimensions;
ImageDescriptor::Pointer m_ImageDescriptor;
size_t *m_OffsetTable;
ImageDataItemPointer m_CompleteData;
// Image statistics Holder replaces the former implementation directly inside this class
friend class ImageStatisticsHolder;
StatisticsHolderPointer m_ImageStatistics;
private:
/** Stores all existing ImageReadAccessors */
std::vector m_Readers;
/** Stores all existing ImageWriteAccessors */
std::vector m_Writers;
/** Stores all existing ImageVtkAccessors */
std::vector m_VtkReaders;
/** A mutex, which needs to be locked to manage m_Readers and m_Writers */
itk::SimpleFastMutexLock m_ReadWriteLock;
/** A mutex, which needs to be locked to manage m_VtkReaders */
itk::SimpleFastMutexLock m_VtkReadersLock;
};
/**
* @brief Equal A function comparing two images for beeing equal in meta- and imagedata
*
* @ingroup MITKTestingAPI
*
* Following aspects are tested for equality:
* - dimension of the images
* - size of the images
* - pixel type
* - pixel values : pixel values are expected to be identical at each position ( for other options see mitk::CompareImageFilter )
*
* @param rightHandSide An image to be compared
* @param leftHandSide An image to be compared
* @param eps Tolarence for comparison. You can use mitk::eps in most cases.
* @param verbose Flag indicating if the user wants detailed console output or not.
* @return true, if all subsequent comparisons are true, false otherwise
*/
MITK_CORE_EXPORT bool Equal( const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose );
//}
//##Documentation
//## @brief Cast an itk::Image (with a specific type) to an mitk::Image.
//##
//## CastToMitkImage does not cast pixel types etc., just image data
//## Needs "mitkImage.h" header included.
//## If you get a compile error, try image.GetPointer();
//## @ingroup Adaptor
//## \sa mitkITKImageImport
template
void CastToMitkImage(const itk::SmartPointer& itkimage, itk::SmartPointer& mitkoutputimage)
{
if(mitkoutputimage.IsNull())
{
mitkoutputimage = mitk::Image::New();
}
mitkoutputimage->InitializeByItk(itkimage.GetPointer());
mitkoutputimage->SetChannel(itkimage->GetBufferPointer());
}
//##Documentation
//## @brief Cast an itk::Image (with a specific type) to an mitk::Image.
//##
//## CastToMitkImage does not cast pixel types etc., just image data
//## Needs "mitkImage.h" header included.
//## If you get a compile error, try image.GetPointer();
//## @ingroup Adaptor
//## \sa mitkITKImageImport
template
void CastToMitkImage(const ItkOutputImageType* itkimage, itk::SmartPointer& mitkoutputimage)
{
if(mitkoutputimage.IsNull())
{
mitkoutputimage = mitk::Image::New();
}
mitkoutputimage->InitializeByItk(itkimage);
mitkoutputimage->SetChannel(itkimage->GetBufferPointer());
}
} // namespace mitk
#endif /* MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 */
diff --git a/Core/Code/IO/mitkDicomSR_GantryTiltInformation.cpp b/Core/Code/IO/mitkDicomSR_GantryTiltInformation.cpp
new file mode 100644
index 0000000000..6a324d2b64
--- /dev/null
+++ b/Core/Code/IO/mitkDicomSR_GantryTiltInformation.cpp
@@ -0,0 +1,201 @@
+/*===================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics.
+All rights reserved.
+
+This software is distributed WITHOUT ANY WARRANTY; without
+even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE.
+
+See LICENSE.txt or http://www.mitk.org for details.
+
+===================================================================*/
+
+#include
+
+namespace mitk
+{
+
+DicomSeriesReader::GantryTiltInformation::GantryTiltInformation()
+: m_ShiftUp(0.0)
+, m_ShiftRight(0.0)
+, m_ShiftNormal(0.0)
+, m_ITKAssumedSliceSpacing(0.0)
+, m_NumberOfSlicesApart(1)
+{
+}
+
+
+#define doublepoint(x) \
+ Point3Dd x; \
+ x[0] = x ## f[0]; \
+ x[1] = x ## f[1]; \
+ x[2] = x ## f[2];
+
+
+#define doublevector(x) \
+ Vector3Dd x; \
+ x[0] = x ## f[0]; \
+ x[1] = x ## f[1]; \
+ x[2] = x ## f[2];
+
+DicomSeriesReader::GantryTiltInformation::GantryTiltInformation(
+ const Point3D& origin1f, const Point3D& origin2f,
+ const Vector3D& rightf, const Vector3D& upf,
+ unsigned int numberOfSlicesApart)
+: m_ShiftUp(0.0)
+, m_ShiftRight(0.0)
+, m_ShiftNormal(0.0)
+, m_NumberOfSlicesApart(numberOfSlicesApart)
+{
+ assert(numberOfSlicesApart);
+
+ doublepoint(origin1);
+ doublepoint(origin2);
+ doublevector(right);
+ doublevector(up);
+
+ // determine if slice 1 (imagePosition1 and imageOrientation1) and slice 2 can be in one orthogonal slice stack:
+ // calculate a line from origin 1, directed along the normal of slice (calculated as the cross product of orientation 1)
+ // check if this line passes through origin 2
+
+ /*
+ Determine if line (imagePosition2 + l * normal) contains imagePosition1.
+ Done by calculating the distance of imagePosition1 from line (imagePosition2 + l *normal)
+
+ E.g. http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
+
+ squared distance = | (pointAlongNormal - origin2) x (origin2 - origin1) | ^ 2
+ /
+ |pointAlongNormal - origin2| ^ 2
+
+ ( x meaning the cross product )
+ */
+
+ Vector3Dd normal = itk::CrossProduct(right, up);
+ Point3Dd pointAlongNormal = origin2 + normal;
+
+ double numerator = itk::CrossProduct( pointAlongNormal - origin2 , origin2 - origin1 ).GetSquaredNorm();
+ double denominator = (pointAlongNormal - origin2).GetSquaredNorm();
+
+ double distance = sqrt(numerator / denominator);
+
+ if ( distance > 0.001 ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt
+ {
+ MITK_DEBUG << " Series seems to contain a tilted (or sheared) geometry";
+ MITK_DEBUG << " Distance of expected slice origin from actual slice origin: " << distance;
+ MITK_DEBUG << " ==> storing this shift for later analysis:";
+ MITK_DEBUG << " v right: " << right;
+ MITK_DEBUG << " v up: " << up;
+ MITK_DEBUG << " v normal: " << normal;
+
+ Point3Dd projectionRight = projectPointOnLine( origin1, origin2, right );
+ Point3Dd projectionNormal = projectPointOnLine( origin1, origin2, normal );
+
+ m_ShiftRight = (projectionRight - origin2).GetNorm();
+ m_ShiftNormal = (projectionNormal - origin2).GetNorm();
+
+ /*
+ now also check to which side the image is shifted.
+
+ Calculation e.g. from
+ http://mathworld.wolfram.com/Point-PlaneDistance.html
+ */
+
+ Point3Dd testPoint = origin1;
+ Vector3Dd planeNormal = up;
+
+ double signedDistance = (
+ planeNormal[0] * testPoint[0]
+ + planeNormal[1] * testPoint[1]
+ + planeNormal[2] * testPoint[2]
+ - (
+ planeNormal[0] * origin2[0]
+ + planeNormal[1] * origin2[1]
+ + planeNormal[2] * origin2[2]
+ )
+ )
+ /
+ sqrt( planeNormal[0] * planeNormal[0]
+ + planeNormal[1] * planeNormal[1]
+ + planeNormal[2] * planeNormal[2]
+ );
+
+ m_ShiftUp = signedDistance;
+
+ m_ITKAssumedSliceSpacing = (origin2 - origin1).GetNorm();
+ // How do we now this is assumed? See header documentation for ITK code references
+ //double itkAssumedSliceSpacing = sqrt( m_ShiftUp * m_ShiftUp + m_ShiftNormal * m_ShiftNormal );
+
+ MITK_DEBUG << " shift normal: " << m_ShiftNormal;
+ MITK_DEBUG << " shift normal assumed by ITK: " << m_ITKAssumedSliceSpacing;
+ MITK_DEBUG << " shift up: " << m_ShiftUp;
+ MITK_DEBUG << " shift right: " << m_ShiftRight;
+
+ MITK_DEBUG << " tilt angle (deg): " << atan( m_ShiftUp / m_ShiftNormal ) * 180.0 / 3.1415926535;
+ }
+}
+
+Point3D
+DicomSeriesReader::GantryTiltInformation::projectPointOnLine( Point3Dd p, Point3Dd lineOrigin, Vector3Dd lineDirection )
+{
+ /**
+ See illustration at http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage472/
+
+ vector(lineOrigin,p) = normal * ( innerproduct((p - lineOrigin),normal) / squared-length(normal) )
+ */
+
+ Vector3Dd lineOriginToP = p - lineOrigin;
+ double innerProduct = lineOriginToP * lineDirection;
+
+ double factor = innerProduct / lineDirection.GetSquaredNorm();
+ Point3Dd projection = lineOrigin + factor * lineDirection;
+
+ return projection;
+}
+
+double
+DicomSeriesReader::GantryTiltInformation::GetTiltCorrectedAdditionalSize() const
+{
+ return fabs(m_ShiftUp);
+}
+
+double
+DicomSeriesReader::GantryTiltInformation::GetTiltAngleInDegrees() const
+{
+ return atan( fabs(m_ShiftUp) / m_ShiftNormal ) * 180.0 / 3.1415926535;
+}
+
+double
+DicomSeriesReader::GantryTiltInformation::GetMatrixCoefficientForCorrectionInWorldCoordinates() const
+{
+ // so many mm need to be shifted per slice!
+ return m_ShiftUp / static_cast(m_NumberOfSlicesApart);
+}
+
+double
+DicomSeriesReader::GantryTiltInformation::GetRealZSpacing() const
+{
+ return m_ShiftNormal / static_cast(m_NumberOfSlicesApart);
+}
+
+
+bool
+DicomSeriesReader::GantryTiltInformation::IsSheared() const
+{
+ return ( fabs(m_ShiftRight) > 0.001
+ || fabs(m_ShiftUp) > 0.001);
+}
+
+
+bool
+DicomSeriesReader::GantryTiltInformation::IsRegularGantryTilt() const
+{
+ return ( fabs(m_ShiftRight) < 0.001
+ && fabs(m_ShiftUp) > 0.001);
+}
+
+} // end namespace mitk
diff --git a/Core/Code/IO/mitkDicomSR_ImageBlockDescriptor.cpp b/Core/Code/IO/mitkDicomSR_ImageBlockDescriptor.cpp
new file mode 100644
index 0000000000..92ecbe1763
--- /dev/null
+++ b/Core/Code/IO/mitkDicomSR_ImageBlockDescriptor.cpp
@@ -0,0 +1,242 @@
+/*===================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics.
+All rights reserved.
+
+This software is distributed WITHOUT ANY WARRANTY; without
+even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE.
+
+See LICENSE.txt or http://www.mitk.org for details.
+
+===================================================================*/
+
+#include
+
+#include
+
+namespace mitk
+{
+
+DicomSeriesReader::ImageBlockDescriptor::ImageBlockDescriptor()
+:m_HasGantryTiltCorrected(false)
+,m_HasMultipleTimePoints(false)
+,m_IsMultiFrameImage(false)
+{
+}
+
+DicomSeriesReader::ImageBlockDescriptor::~ImageBlockDescriptor()
+{
+ // nothing
+}
+
+DicomSeriesReader::ImageBlockDescriptor::ImageBlockDescriptor(const StringContainer& files)
+:m_HasGantryTiltCorrected(false)
+,m_HasMultipleTimePoints(false)
+,m_IsMultiFrameImage(false)
+{
+ m_Filenames = files;
+}
+
+
+void DicomSeriesReader::ImageBlockDescriptor::AddFile(const std::string& filename)
+{
+ m_Filenames.push_back( filename );
+}
+
+void DicomSeriesReader::ImageBlockDescriptor::AddFiles(const StringContainer& files)
+{
+ m_Filenames.insert( m_Filenames.end(), files.begin(), files.end() );
+}
+
+DicomSeriesReader::StringContainer DicomSeriesReader::ImageBlockDescriptor::GetFilenames() const
+{
+ return m_Filenames;
+}
+
+std::string DicomSeriesReader::ImageBlockDescriptor::GetImageBlockUID() const
+{
+ return m_ImageBlockUID;
+}
+
+std::string DicomSeriesReader::ImageBlockDescriptor::GetSeriesInstanceUID() const
+{
+ return m_SeriesInstanceUID;
+}
+
+std::string DicomSeriesReader::ImageBlockDescriptor::GetModality() const
+{
+ return m_Modality;
+}
+
+std::string DicomSeriesReader::ImageBlockDescriptor::GetSOPClassUIDAsString() const
+{
+ gdcm::UIDs uidKnowledge;
+ uidKnowledge.SetFromUID( m_SOPClassUID.c_str() );
+ return uidKnowledge.GetName();
+}
+
+std::string DicomSeriesReader::ImageBlockDescriptor::GetSOPClassUID() const
+{
+ return m_SOPClassUID;
+}
+
+bool DicomSeriesReader::ImageBlockDescriptor::IsMultiFrameImage() const
+{
+ return m_IsMultiFrameImage;
+}
+
+DicomSeriesReader::ReaderImplementationLevel DicomSeriesReader::ImageBlockDescriptor::GetReaderImplementationLevel() const
+{
+ if ( this->IsMultiFrameImage() )
+ return ReaderImplementationLevel_Unsupported;
+
+ gdcm::UIDs uidKnowledge;
+ uidKnowledge.SetFromUID( m_SOPClassUID.c_str() );
+
+ gdcm::UIDs::TSType uid = uidKnowledge;
+
+ switch (uid)
+ {
+ case gdcm::UIDs::CTImageStorage:
+ case gdcm::UIDs::MRImageStorage:
+ case gdcm::UIDs::PositronEmissionTomographyImageStorage:
+ case gdcm::UIDs::ComputedRadiographyImageStorage:
+ case gdcm::UIDs::DigitalXRayImageStorageForPresentation:
+ case gdcm::UIDs::DigitalXRayImageStorageForProcessing:
+ return ReaderImplementationLevel_Supported;
+
+ case gdcm::UIDs::NuclearMedicineImageStorage:
+ return ReaderImplementationLevel_PartlySupported;
+
+ case gdcm::UIDs::SecondaryCaptureImageStorage:
+ return ReaderImplementationLevel_Implemented;
+
+ default:
+ return ReaderImplementationLevel_Unsupported;
+ }
+}
+
+bool DicomSeriesReader::ImageBlockDescriptor::HasGantryTiltCorrected() const
+{
+ return m_HasGantryTiltCorrected;
+}
+
+/*
+ PS defined IPS defined PS==IPS
+ 0 0 --> UNKNOWN spacing, loader will invent
+ 0 1 --> spacing as at detector surface
+ 1 0 --> spacing as in patient
+ 1 1 0 --> detector surface spacing CORRECTED for geometrical magnifications: spacing as in patient
+ 1 1 1 --> detector surface spacing NOT corrected for geometrical magnifications: spacing as at detector
+*/
+DicomSeriesReader::PixelSpacingInterpretation DicomSeriesReader::ImageBlockDescriptor::GetPixelSpacingType() const
+{
+ if (m_PixelSpacing.empty())
+ {
+ if (m_ImagerPixelSpacing.empty())
+ {
+ return PixelSpacingInterpretation_SpacingUnknown;
+ }
+ else
+ {
+ return PixelSpacingInterpretation_SpacingAtDetector;
+ }
+ }
+ else // Pixel Spacing defined
+ {
+ if (m_ImagerPixelSpacing.empty())
+ {
+ return PixelSpacingInterpretation_SpacingInPatient;
+ }
+ else if (m_PixelSpacing != m_ImagerPixelSpacing)
+ {
+ return PixelSpacingInterpretation_SpacingInPatient;
+ }
+ else
+ {
+ return PixelSpacingInterpretation_SpacingAtDetector;
+ }
+ }
+}
+
+bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingRelatesToPatient() const
+{
+ return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingInPatient;
+}
+
+bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingRelatesToDetector() const
+{
+ return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingAtDetector;
+}
+
+bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingIsUnknown() const
+{
+ return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingUnknown;
+}
+
+void DicomSeriesReader::ImageBlockDescriptor::SetPixelSpacingInformation(const std::string& pixelSpacing, const std::string& imagerPixelSpacing)
+{
+ m_PixelSpacing = pixelSpacing;
+ m_ImagerPixelSpacing = imagerPixelSpacing;
+}
+
+void DicomSeriesReader::ImageBlockDescriptor::GetDesiredMITKImagePixelSpacing( ScalarType& spacingX, ScalarType& spacingY) const
+{
+ // preference for "in patient" pixel spacing
+ if ( !DICOMStringToSpacing( m_PixelSpacing, spacingX, spacingY ) )
+ {
+ // fallback to "on detector" spacing
+ if ( !DICOMStringToSpacing( m_ImagerPixelSpacing, spacingX, spacingY ) )
+ {
+ // last resort: invent something
+ spacingX = spacingY = 1.0;
+ }
+ }
+}
+
+bool DicomSeriesReader::ImageBlockDescriptor::HasMultipleTimePoints() const
+{
+ return m_HasMultipleTimePoints;
+}
+
+void DicomSeriesReader::ImageBlockDescriptor::SetImageBlockUID(const std::string& uid)
+{
+ m_ImageBlockUID = uid;
+}
+
+void DicomSeriesReader::ImageBlockDescriptor::SetSeriesInstanceUID(const std::string& uid)
+{
+ m_SeriesInstanceUID = uid;
+}
+
+void DicomSeriesReader::ImageBlockDescriptor::SetModality(const std::string& modality)
+{
+ m_Modality = modality;
+}
+
+void DicomSeriesReader::ImageBlockDescriptor::SetNumberOfFrames(const std::string& numberOfFrames)
+{
+ m_IsMultiFrameImage = !numberOfFrames.empty();
+}
+
+void DicomSeriesReader::ImageBlockDescriptor::SetSOPClassUID(const std::string& sopClassUID)
+{
+ m_SOPClassUID = sopClassUID;
+}
+
+
+void DicomSeriesReader::ImageBlockDescriptor::SetHasGantryTiltCorrected(bool on)
+{
+ m_HasGantryTiltCorrected = on;
+}
+
+void DicomSeriesReader::ImageBlockDescriptor::SetHasMultipleTimePoints(bool on)
+{
+ m_HasMultipleTimePoints = on;
+}
+
+} // end namespace mitk
diff --git a/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel.cpp b/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel.cpp
new file mode 100644
index 0000000000..55e4e38ddd
--- /dev/null
+++ b/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel.cpp
@@ -0,0 +1,54 @@
+/*===================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics.
+All rights reserved.
+
+This software is distributed WITHOUT ANY WARRANTY; without
+even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE.
+
+See LICENSE.txt or http://www.mitk.org for details.
+
+===================================================================*/
+
+#include "mitkDicomSeriesReader.txx"
+
+namespace mitk
+{
+
+Image::Pointer
+DicomSeriesReader
+::MultiplexLoadDICOMByITKRGBPixel(const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock)
+{
+ switch (io->GetComponentType())
+ {
+ case DcmIoType::UCHAR:
+ return LoadDICOMByITK< itk::RGBPixel >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::CHAR:
+ return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::USHORT:
+ return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::SHORT:
+ return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::UINT:
+ return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::INT:
+ return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::ULONG:
+ return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::LONG:
+ return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::FLOAT:
+ return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::DOUBLE:
+ return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ default:
+ MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType();
+ return NULL;
+ }
+}
+
+} // end namespace mitk
diff --git a/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp b/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp
new file mode 100644
index 0000000000..caad59e3e2
--- /dev/null
+++ b/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp
@@ -0,0 +1,54 @@
+/*===================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics.
+All rights reserved.
+
+This software is distributed WITHOUT ANY WARRANTY; without
+even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE.
+
+See LICENSE.txt or http://www.mitk.org for details.
+
+===================================================================*/
+
+#include "mitkDicomSeriesReader.txx"
+
+namespace mitk
+{
+
+Image::Pointer
+DicomSeriesReader
+::MultiplexLoadDICOMByITK4DRGBPixel( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock)
+{
+ switch (io->GetComponentType())
+ {
+ case DcmIoType::UCHAR:
+ return LoadDICOMByITK4D< itk::RGBPixel >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::CHAR:
+ return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::USHORT:
+ return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::SHORT:
+ return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::UINT:
+ return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::INT:
+ return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::ULONG:
+ return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::LONG:
+ return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::FLOAT:
+ return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::DOUBLE:
+ return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ default:
+ MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType();
+ return NULL;
+ }
+}
+
+} // end namespace mitk
diff --git a/Core/Code/IO/mitkDicomSR_LoadDICOMScalar.cpp b/Core/Code/IO/mitkDicomSR_LoadDICOMScalar.cpp
new file mode 100644
index 0000000000..b469205b30
--- /dev/null
+++ b/Core/Code/IO/mitkDicomSR_LoadDICOMScalar.cpp
@@ -0,0 +1,57 @@
+/*===================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics.
+All rights reserved.
+
+This software is distributed WITHOUT ANY WARRANTY; without
+even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE.
+
+See LICENSE.txt or http://www.mitk.org for details.
+
+===================================================================*/
+
+#include "mitkDicomSeriesReader.txx"
+
+namespace mitk
+{
+
+Image::Pointer
+DicomSeriesReader
+::MultiplexLoadDICOMByITKScalar(const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock)
+{
+ switch (io->GetComponentType())
+ {
+ case DcmIoType::UCHAR:
+ return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::CHAR:
+ return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::USHORT:
+ return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::SHORT:
+ return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::UINT:
+ return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::INT:
+ return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::ULONG:
+ return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::LONG:
+ return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::FLOAT:
+ return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::DOUBLE:
+ return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ default:
+ MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType();
+ return NULL;
+ }
+}
+
+} // end namespace mitk
+
+#include
+
diff --git a/Core/Code/IO/mitkDicomSR_LoadDICOMScalar4D.cpp b/Core/Code/IO/mitkDicomSR_LoadDICOMScalar4D.cpp
new file mode 100644
index 0000000000..953d9a32d0
--- /dev/null
+++ b/Core/Code/IO/mitkDicomSR_LoadDICOMScalar4D.cpp
@@ -0,0 +1,57 @@
+/*===================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics.
+All rights reserved.
+
+This software is distributed WITHOUT ANY WARRANTY; without
+even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE.
+
+See LICENSE.txt or http://www.mitk.org for details.
+
+===================================================================*/
+
+#include "mitkDicomSeriesReader.txx"
+
+namespace mitk
+{
+
+Image::Pointer
+DicomSeriesReader
+::MultiplexLoadDICOMByITK4DScalar( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock)
+{
+ switch (io->GetComponentType())
+ {
+ case DcmIoType::UCHAR:
+ return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::CHAR:
+ return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::USHORT:
+ return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::SHORT:
+ return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::UINT:
+ return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::INT:
+ return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::ULONG:
+ return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::LONG:
+ return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::FLOAT:
+ return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ case DcmIoType::DOUBLE:
+ return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ default:
+ MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType();
+ return NULL;
+ }
+}
+
+} // end namespace mitk
+
+#include
+
diff --git a/Core/Code/IO/mitkDicomSR_SliceGroupingResult.cpp b/Core/Code/IO/mitkDicomSR_SliceGroupingResult.cpp
new file mode 100644
index 0000000000..e853bb5604
--- /dev/null
+++ b/Core/Code/IO/mitkDicomSR_SliceGroupingResult.cpp
@@ -0,0 +1,69 @@
+/*===================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics.
+All rights reserved.
+
+This software is distributed WITHOUT ANY WARRANTY; without
+even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE.
+
+See LICENSE.txt or http://www.mitk.org for details.
+
+===================================================================*/
+
+#include
+
+namespace mitk
+{
+
+DicomSeriesReader::SliceGroupingAnalysisResult::SliceGroupingAnalysisResult()
+:m_GantryTilt(false)
+{
+}
+
+DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetBlockFilenames()
+{
+ return m_GroupedFiles;
+}
+
+DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetUnsortedFilenames()
+{
+ return m_UnsortedFiles;
+}
+
+bool DicomSeriesReader::SliceGroupingAnalysisResult::ContainsGantryTilt()
+{
+ return m_GantryTilt;
+}
+
+void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToSortedBlock(const std::string& filename)
+{
+ m_GroupedFiles.push_back( filename );
+}
+
+void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToUnsortedBlock(const std::string& filename)
+{
+ m_UnsortedFiles.push_back( filename );
+}
+
+void DicomSeriesReader::SliceGroupingAnalysisResult::AddFilesToUnsortedBlock(const StringContainer& filenames)
+{
+ m_UnsortedFiles.insert( m_UnsortedFiles.end(), filenames.begin(), filenames.end() );
+}
+
+void DicomSeriesReader::SliceGroupingAnalysisResult::FlagGantryTilt()
+{
+ m_GantryTilt = true;
+}
+
+void DicomSeriesReader::SliceGroupingAnalysisResult::UndoPrematureGrouping()
+{
+ assert( !m_GroupedFiles.empty() );
+ m_UnsortedFiles.insert( m_UnsortedFiles.begin(), m_GroupedFiles.back() );
+ m_GroupedFiles.pop_back();
+}
+
+} // end namespace mitk
diff --git a/Core/Code/IO/mitkDicomSeriesReader.cpp b/Core/Code/IO/mitkDicomSeriesReader.cpp
index 19c1a9e0bb..bf3b680f4d 100644
--- a/Core/Code/IO/mitkDicomSeriesReader.cpp
+++ b/Core/Code/IO/mitkDicomSeriesReader.cpp
@@ -1,1981 +1,1779 @@
/*===================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
// uncomment for learning more about the internal sorting mechanisms
//#define MBILOG_ENABLE_DEBUG
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mitkProperties.h"
namespace mitk
{
-typedef itk::GDCMSeriesFileNames DcmFileNamesGeneratorType;
-
-DicomSeriesReader::ImageBlockDescriptor::ImageBlockDescriptor()
-:m_HasGantryTiltCorrected(false)
-,m_HasMultipleTimePoints(false)
-,m_IsMultiFrameImage(false)
-{
-}
-
-DicomSeriesReader::ImageBlockDescriptor::~ImageBlockDescriptor()
-{
- // nothing
-}
-
-DicomSeriesReader::ImageBlockDescriptor::ImageBlockDescriptor(const StringContainer& files)
-:m_HasGantryTiltCorrected(false)
-,m_HasMultipleTimePoints(false)
-,m_IsMultiFrameImage(false)
-{
- m_Filenames = files;
-}
-
-
-void DicomSeriesReader::ImageBlockDescriptor::AddFile(const std::string& filename)
-{
- m_Filenames.push_back( filename );
-}
-
-void DicomSeriesReader::ImageBlockDescriptor::AddFiles(const StringContainer& files)
-{
- m_Filenames.insert( m_Filenames.end(), files.begin(), files.end() );
-}
-
-DicomSeriesReader::StringContainer DicomSeriesReader::ImageBlockDescriptor::GetFilenames() const
-{
- return m_Filenames;
-}
-
-std::string DicomSeriesReader::ImageBlockDescriptor::GetImageBlockUID() const
-{
- return m_ImageBlockUID;
-}
-
-std::string DicomSeriesReader::ImageBlockDescriptor::GetSeriesInstanceUID() const
-{
- return m_SeriesInstanceUID;
-}
-
-std::string DicomSeriesReader::ImageBlockDescriptor::GetModality() const
-{
- return m_Modality;
-}
-
-std::string DicomSeriesReader::ImageBlockDescriptor::GetSOPClassUIDAsString() const
-{
- gdcm::UIDs uidKnowledge;
- uidKnowledge.SetFromUID( m_SOPClassUID.c_str() );
- return uidKnowledge.GetName();
-}
-
-std::string DicomSeriesReader::ImageBlockDescriptor::GetSOPClassUID() const
-{
- return m_SOPClassUID;
-}
-
-bool DicomSeriesReader::ImageBlockDescriptor::IsMultiFrameImage() const
-{
- return m_IsMultiFrameImage;
-}
-
-DicomSeriesReader::ReaderImplementationLevel DicomSeriesReader::ImageBlockDescriptor::GetReaderImplementationLevel() const
-{
- if ( this->IsMultiFrameImage() )
- return ReaderImplementationLevel_Unsupported;
-
- gdcm::UIDs uidKnowledge;
- uidKnowledge.SetFromUID( m_SOPClassUID.c_str() );
-
- gdcm::UIDs::TSType uid = uidKnowledge;
-
- switch (uid)
- {
- case gdcm::UIDs::CTImageStorage:
- case gdcm::UIDs::MRImageStorage:
- case gdcm::UIDs::PositronEmissionTomographyImageStorage:
- case gdcm::UIDs::ComputedRadiographyImageStorage:
- case gdcm::UIDs::DigitalXRayImageStorageForPresentation:
- case gdcm::UIDs::DigitalXRayImageStorageForProcessing:
- return ReaderImplementationLevel_Supported;
-
- case gdcm::UIDs::NuclearMedicineImageStorage:
- return ReaderImplementationLevel_PartlySupported;
-
- case gdcm::UIDs::SecondaryCaptureImageStorage:
- return ReaderImplementationLevel_Implemented;
-
- default:
- return ReaderImplementationLevel_Unsupported;
- }
-}
-
std::string DicomSeriesReader::ReaderImplementationLevelToString( const ReaderImplementationLevel& enumValue )
{
switch (enumValue)
{
case ReaderImplementationLevel_Supported: return "Supported";
case ReaderImplementationLevel_PartlySupported: return "PartlySupported";
case ReaderImplementationLevel_Implemented: return "Implemented";
case ReaderImplementationLevel_Unsupported: return "Unsupported";
default: return "";
};
}
std::string DicomSeriesReader::PixelSpacingInterpretationToString( const PixelSpacingInterpretation& enumValue )
{
switch (enumValue)
{
case PixelSpacingInterpretation_SpacingInPatient: return "In Patient";
case PixelSpacingInterpretation_SpacingAtDetector: return "At Detector";
case PixelSpacingInterpretation_SpacingUnknown: return "Unknown spacing";
default: return "";
};
}
-
-bool DicomSeriesReader::ImageBlockDescriptor::HasGantryTiltCorrected() const
-{
- return m_HasGantryTiltCorrected;
-}
-
-/*
- PS defined IPS defined PS==IPS
- 0 0 --> UNKNOWN spacing, loader will invent
- 0 1 --> spacing as at detector surface
- 1 0 --> spacing as in patient
- 1 1 0 --> detector surface spacing CORRECTED for geometrical magnifications: spacing as in patient
- 1 1 1 --> detector surface spacing NOT corrected for geometrical magnifications: spacing as at detector
-*/
-DicomSeriesReader::PixelSpacingInterpretation DicomSeriesReader::ImageBlockDescriptor::GetPixelSpacingType() const
-{
- if (m_PixelSpacing.empty())
- {
- if (m_ImagerPixelSpacing.empty())
- {
- return PixelSpacingInterpretation_SpacingUnknown;
- }
- else
- {
- return PixelSpacingInterpretation_SpacingAtDetector;
- }
- }
- else // Pixel Spacing defined
- {
- if (m_ImagerPixelSpacing.empty())
- {
- return PixelSpacingInterpretation_SpacingInPatient;
- }
- else if (m_PixelSpacing != m_ImagerPixelSpacing)
- {
- return PixelSpacingInterpretation_SpacingInPatient;
- }
- else
- {
- return PixelSpacingInterpretation_SpacingAtDetector;
- }
- }
-}
-
-bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingRelatesToPatient() const
-{
- return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingInPatient;
-}
-
-bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingRelatesToDetector() const
-{
- return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingAtDetector;
-}
-
-bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingIsUnknown() const
-{
- return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingUnknown;
-}
-
-void DicomSeriesReader::ImageBlockDescriptor::SetPixelSpacingInformation(const std::string& pixelSpacing, const std::string& imagerPixelSpacing)
-{
- m_PixelSpacing = pixelSpacing;
- m_ImagerPixelSpacing = imagerPixelSpacing;
-}
-
-void DicomSeriesReader::ImageBlockDescriptor::GetDesiredMITKImagePixelSpacing( ScalarType& spacingX, ScalarType& spacingY) const
-{
- // preference for "in patient" pixel spacing
- if ( !DICOMStringToSpacing( m_PixelSpacing, spacingX, spacingY ) )
- {
- // fallback to "on detector" spacing
- if ( !DICOMStringToSpacing( m_ImagerPixelSpacing, spacingX, spacingY ) )
- {
- // last resort: invent something
- spacingX = spacingY = 1.0;
- }
- }
-}
-
-bool DicomSeriesReader::ImageBlockDescriptor::HasMultipleTimePoints() const
-{
- return m_HasMultipleTimePoints;
-}
-
-void DicomSeriesReader::ImageBlockDescriptor::SetImageBlockUID(const std::string& uid)
-{
- m_ImageBlockUID = uid;
-}
-
-void DicomSeriesReader::ImageBlockDescriptor::SetSeriesInstanceUID(const std::string& uid)
-{
- m_SeriesInstanceUID = uid;
-}
-
-void DicomSeriesReader::ImageBlockDescriptor::SetModality(const std::string& modality)
-{
- m_Modality = modality;
-}
-
-void DicomSeriesReader::ImageBlockDescriptor::SetNumberOfFrames(const std::string& numberOfFrames)
-{
- m_IsMultiFrameImage = !numberOfFrames.empty();
-}
-
-void DicomSeriesReader::ImageBlockDescriptor::SetSOPClassUID(const std::string& sopClassUID)
-{
- m_SOPClassUID = sopClassUID;
-}
-
-
-void DicomSeriesReader::ImageBlockDescriptor::SetHasGantryTiltCorrected(bool on)
-{
- m_HasGantryTiltCorrected = on;
-}
-
-void DicomSeriesReader::ImageBlockDescriptor::SetHasMultipleTimePoints(bool on)
-{
- m_HasMultipleTimePoints = on;
-}
-
-
-
-DicomSeriesReader::SliceGroupingAnalysisResult::SliceGroupingAnalysisResult()
-:m_GantryTilt(false)
-{
-}
-
-DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetBlockFilenames()
-{
- return m_GroupedFiles;
-}
-
-DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetUnsortedFilenames()
-{
- return m_UnsortedFiles;
-}
-
-bool DicomSeriesReader::SliceGroupingAnalysisResult::ContainsGantryTilt()
-{
- return m_GantryTilt;
-}
-
-void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToSortedBlock(const std::string& filename)
-{
- m_GroupedFiles.push_back( filename );
-}
-
-void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToUnsortedBlock(const std::string& filename)
-{
- m_UnsortedFiles.push_back( filename );
-}
-
-void DicomSeriesReader::SliceGroupingAnalysisResult::AddFilesToUnsortedBlock(const StringContainer& filenames)
-{
- m_UnsortedFiles.insert( m_UnsortedFiles.end(), filenames.begin(), filenames.end() );
-}
-
-void DicomSeriesReader::SliceGroupingAnalysisResult::FlagGantryTilt()
-{
- m_GantryTilt = true;
-}
-
-void DicomSeriesReader::SliceGroupingAnalysisResult::UndoPrematureGrouping()
-{
- assert( !m_GroupedFiles.empty() );
- m_UnsortedFiles.insert( m_UnsortedFiles.begin(), m_GroupedFiles.back() );
- m_GroupedFiles.pop_back();
-}
-
-
-
-
-
-
-
const DicomSeriesReader::TagToPropertyMapType& DicomSeriesReader::GetDICOMTagsToMITKPropertyMap()
{
static bool initialized = false;
static TagToPropertyMapType dictionary;
if (!initialized)
{
/*
Selection criteria:
- no sequences because we cannot represent that
- nothing animal related (specied, breed registration number), MITK focusses on human medical image processing.
- only general attributes so far
When extending this, we should make use of a real dictionary (GDCM/DCMTK and lookup the tag names there)
*/
// Patient module
dictionary["0010|0010"] = "dicom.patient.PatientsName";
dictionary["0010|0020"] = "dicom.patient.PatientID";
dictionary["0010|0030"] = "dicom.patient.PatientsBirthDate";
dictionary["0010|0040"] = "dicom.patient.PatientsSex";
dictionary["0010|0032"] = "dicom.patient.PatientsBirthTime";
dictionary["0010|1000"] = "dicom.patient.OtherPatientIDs";
dictionary["0010|1001"] = "dicom.patient.OtherPatientNames";
dictionary["0010|2160"] = "dicom.patient.EthnicGroup";
dictionary["0010|4000"] = "dicom.patient.PatientComments";
dictionary["0012|0062"] = "dicom.patient.PatientIdentityRemoved";
dictionary["0012|0063"] = "dicom.patient.DeIdentificationMethod";
// General Study module
dictionary["0020|000d"] = "dicom.study.StudyInstanceUID";
dictionary["0008|0020"] = "dicom.study.StudyDate";
dictionary["0008|0030"] = "dicom.study.StudyTime";
dictionary["0008|0090"] = "dicom.study.ReferringPhysiciansName";
dictionary["0020|0010"] = "dicom.study.StudyID";
dictionary["0008|0050"] = "dicom.study.AccessionNumber";
dictionary["0008|1030"] = "dicom.study.StudyDescription";
dictionary["0008|1048"] = "dicom.study.PhysiciansOfRecord";
dictionary["0008|1060"] = "dicom.study.NameOfPhysicianReadingStudy";
// General Series module
dictionary["0008|0060"] = "dicom.series.Modality";
dictionary["0020|000e"] = "dicom.series.SeriesInstanceUID";
dictionary["0020|0011"] = "dicom.series.SeriesNumber";
dictionary["0020|0060"] = "dicom.series.Laterality";
dictionary["0008|0021"] = "dicom.series.SeriesDate";
dictionary["0008|0031"] = "dicom.series.SeriesTime";
dictionary["0008|1050"] = "dicom.series.PerformingPhysiciansName";
dictionary["0018|1030"] = "dicom.series.ProtocolName";
dictionary["0008|103e"] = "dicom.series.SeriesDescription";
dictionary["0008|1070"] = "dicom.series.OperatorsName";
dictionary["0018|0015"] = "dicom.series.BodyPartExamined";
dictionary["0018|5100"] = "dicom.series.PatientPosition";
dictionary["0028|0108"] = "dicom.series.SmallestPixelValueInSeries";
dictionary["0028|0109"] = "dicom.series.LargestPixelValueInSeries";
// VOI LUT module
dictionary["0028|1050"] = "dicom.voilut.WindowCenter";
dictionary["0028|1051"] = "dicom.voilut.WindowWidth";
dictionary["0028|1055"] = "dicom.voilut.WindowCenterAndWidthExplanation";
// Image Pixel module
dictionary["0028|0004"] = "dicom.pixel.PhotometricInterpretation";
dictionary["0028|0010"] = "dicom.pixel.Rows";
dictionary["0028|0011"] = "dicom.pixel.Columns";
// Image Plane module
dictionary["0028|0030"] = "dicom.PixelSpacing";
dictionary["0018|1164"] = "dicom.ImagerPixelSpacing";
initialized = true;
}
return dictionary;
}
DataNode::Pointer
DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock)
{
DataNode::Pointer node = DataNode::New();
if (DicomSeriesReader::LoadDicomSeries(filenames, *node, sort, check_4d, correctTilt, callback, preLoadedImageBlock))
{
if( filenames.empty() )
{
return NULL;
}
return node;
}
else
{
return NULL;
}
}
bool
DicomSeriesReader::LoadDicomSeries(
const StringContainer &filenames,
DataNode &node,
bool sort,
bool check_4d,
bool correctTilt,
UpdateCallBackMethod callback,
Image::Pointer preLoadedImageBlock)
{
if( filenames.empty() )
{
MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic.";
node.SetData(NULL);
return true; // this is not actually an error but the result is very simple
}
DcmIoType::Pointer io = DcmIoType::New();
try
{
if (io->CanReadFile(filenames.front().c_str()))
{
io->SetFileName(filenames.front().c_str());
io->ReadImageInformation();
- if (io->GetPixelType() == itk::ImageIOBase::SCALAR)
+ if (io->GetPixelType() == itk::ImageIOBase::SCALAR ||
+ io->GetPixelType() == itk::ImageIOBase::RGB)
{
- switch (io->GetComponentType())
- {
- case DcmIoType::UCHAR:
- DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::CHAR:
- DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::USHORT:
- DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::SHORT:
- DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::UINT:
- DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::INT:
- DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::ULONG:
- DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::LONG:
- DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::FLOAT:
- DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::DOUBLE:
- DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- default:
- MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType();
- }
- }
- else if (io->GetPixelType() == itk::ImageIOBase::RGB)
- {
- switch (io->GetComponentType())
- {
- case DcmIoType::UCHAR:
- DicomSeriesReader::LoadDicom< itk::RGBPixel >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::CHAR:
- DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::USHORT:
- DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::SHORT:
- DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::UINT:
- DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::INT:
- DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::ULONG:
- DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::LONG:
- DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::FLOAT:
- DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- case DcmIoType::DOUBLE:
- DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
- break;
- default:
- MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType();
- }
+ LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock);
}
if (node.GetData())
{
return true;
}
}
}
catch(itk::MemoryAllocationError& e)
{
MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what();
}
catch(std::exception& e)
{
MITK_ERROR << "Error encountered when loading DICOM series:" << e.what();
}
catch(...)
{
MITK_ERROR << "Unspecified error encountered when loading DICOM series.";
}
return false;
}
bool
DicomSeriesReader::IsDicom(const std::string &filename)
{
DcmIoType::Pointer io = DcmIoType::New();
return io->CanReadFile(filename.c_str());
}
bool
DicomSeriesReader::IsPhilips3DDicom(const std::string &filename)
{
DcmIoType::Pointer io = DcmIoType::New();
if (io->CanReadFile(filename.c_str()))
{
//Look at header Tag 3001,0010 if it is "Philips3D"
gdcm::Reader reader;
reader.SetFileName(filename.c_str());
reader.Read();
gdcm::DataSet &data_set = reader.GetFile().GetDataSet();
gdcm::StringFilter sf;
sf.SetFile(reader.GetFile());
if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) &&
(sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D "))
{
return true;
}
}
return false;
}
bool
DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, mitk::Image::Pointer output_image)
{
// Now get PhilipsSpecific Tags
gdcm::PixmapReader reader;
reader.SetFileName(filename.c_str());
reader.Read();
gdcm::DataSet &data_set = reader.GetFile().GetDataSet();
gdcm::StringFilter sf;
sf.SetFile(reader.GetFile());
gdcm::Attribute<0x0028,0x0011> dimTagX; // coloumns || sagittal
gdcm::Attribute<0x3001,0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; //I have no idea what is VM1. // (Philips specific) // axial
gdcm::Attribute<0x0028,0x0010> dimTagY; // rows || coronal
gdcm::Attribute<0x0028,0x0008> dimTagT; // how many frames
gdcm::Attribute<0x0018,0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter)
gdcm::Attribute<0x0018,0x602e> spaceTagY;
gdcm::Attribute<0x3001,0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific)
gdcm::Attribute<0x0018,0x6024> physicalTagX; // if 3, then spacing params are centimeter
gdcm::Attribute<0x0018,0x6026> physicalTagY;
gdcm::Attribute<0x3001,0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific)
dimTagX.Set(data_set);
dimTagY.Set(data_set);
dimTagZ.Set(data_set);
dimTagT.Set(data_set);
spaceTagX.Set(data_set);
spaceTagY.Set(data_set);
spaceTagZ.Set(data_set);
physicalTagX.Set(data_set);
physicalTagY.Set(data_set);
physicalTagZ.Set(data_set);
unsigned int
dimX = dimTagX.GetValue(),
dimY = dimTagY.GetValue(),
dimZ = dimTagZ.GetValue(),
dimT = dimTagT.GetValue(),
physicalX = physicalTagX.GetValue(),
physicalY = physicalTagY.GetValue(),
physicalZ = physicalTagZ.GetValue();
float
spaceX = spaceTagX.GetValue(),
spaceY = spaceTagY.GetValue(),
spaceZ = spaceTagZ.GetValue();
if (physicalX == 3) // spacing parameter in cm, have to convert it to mm.
spaceX = spaceX * 10;
if (physicalY == 3) // spacing parameter in cm, have to convert it to mm.
spaceY = spaceY * 10;
if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm.
spaceZ = spaceZ * 10;
// Ok, got all necessary Tags!
// Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements
const gdcm::Pixmap &pixels = reader.GetPixmap();
gdcm::RAWCodec codec;
codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2);
codec.SetPixelFormat(pixels.GetPixelFormat());
codec.SetPlanarConfiguration(0);
gdcm::DataElement out;
codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out);
const gdcm::ByteValue *bv = out.GetByteValue();
const char *new_pixels = bv->GetPointer();
// Create MITK Image + Geometry
typedef itk::Image ImageType; //Pixeltype might be different sometimes? Maybe read it out from header
ImageType::RegionType myRegion;
ImageType::SizeType mySize;
ImageType::IndexType myIndex;
ImageType::SpacingType mySpacing;
ImageType::Pointer imageItk = ImageType::New();
mySpacing[0] = spaceX;
mySpacing[1] = spaceY;
mySpacing[2] = spaceZ;
mySpacing[3] = 1;
myIndex[0] = 0;
myIndex[1] = 0;
myIndex[2] = 0;
myIndex[3] = 0;
mySize[0] = dimX;
mySize[1] = dimY;
mySize[2] = dimZ;
mySize[3] = dimT;
myRegion.SetSize( mySize);
myRegion.SetIndex( myIndex );
imageItk->SetSpacing(mySpacing);
imageItk->SetRegions( myRegion);
imageItk->Allocate();
imageItk->FillBuffer(0);
itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion());
iterator.GoToBegin();
unsigned long pixCount = 0;
unsigned long planeSize = dimX*dimY;
unsigned long planeCount = 0;
unsigned long timeCount = 0;
unsigned long numberOfSlices = dimZ;
while (!iterator.IsAtEnd())
{
unsigned long adressedPixel =
pixCount
+ (numberOfSlices-1-planeCount)*planeSize // add offset to adress the first pixel of current plane
+ timeCount*numberOfSlices*planeSize; // add time offset
iterator.Set( new_pixels[ adressedPixel ] );
pixCount++;
++iterator;
if (pixCount == planeSize)
{
pixCount = 0;
planeCount++;
}
if (planeCount == numberOfSlices)
{
planeCount = 0;
timeCount++;
}
if (timeCount == dimT)
{
break;
}
}
mitk::CastToMitkImage(imageItk, output_image);
return true; // actually never returns false yet.. but exception possible
}
-DicomSeriesReader::GantryTiltInformation::GantryTiltInformation()
-: m_ShiftUp(0.0)
-, m_ShiftRight(0.0)
-, m_ShiftNormal(0.0)
-, m_ITKAssumedSliceSpacing(0.0)
-, m_NumberOfSlicesApart(1)
-{
-}
-
-
-#define doublepoint(x) \
- Point3Dd x; \
- x[0] = x ## f[0]; \
- x[1] = x ## f[1]; \
- x[2] = x ## f[2];
-
-
-#define doublevector(x) \
- Vector3Dd x; \
- x[0] = x ## f[0]; \
- x[1] = x ## f[1]; \
- x[2] = x ## f[2];
-
-DicomSeriesReader::GantryTiltInformation::GantryTiltInformation(
- const Point3D& origin1f, const Point3D& origin2f,
- const Vector3D& rightf, const Vector3D& upf,
- unsigned int numberOfSlicesApart)
-: m_ShiftUp(0.0)
-, m_ShiftRight(0.0)
-, m_ShiftNormal(0.0)
-, m_NumberOfSlicesApart(numberOfSlicesApart)
-{
- assert(numberOfSlicesApart);
-
- doublepoint(origin1);
- doublepoint(origin2);
- doublevector(right);
- doublevector(up);
-
- // determine if slice 1 (imagePosition1 and imageOrientation1) and slice 2 can be in one orthogonal slice stack:
- // calculate a line from origin 1, directed along the normal of slice (calculated as the cross product of orientation 1)
- // check if this line passes through origin 2
-
- /*
- Determine if line (imagePosition2 + l * normal) contains imagePosition1.
- Done by calculating the distance of imagePosition1 from line (imagePosition2 + l *normal)
-
- E.g. http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
-
- squared distance = | (pointAlongNormal - origin2) x (origin2 - origin1) | ^ 2
- /
- |pointAlongNormal - origin2| ^ 2
-
- ( x meaning the cross product )
- */
-
- Vector3Dd normal = itk::CrossProduct(right, up);
- Point3Dd pointAlongNormal = origin2 + normal;
-
- double numerator = itk::CrossProduct( pointAlongNormal - origin2 , origin2 - origin1 ).GetSquaredNorm();
- double denominator = (pointAlongNormal - origin2).GetSquaredNorm();
-
- double distance = sqrt(numerator / denominator);
-
- if ( distance > 0.001 ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt
- {
- MITK_DEBUG << " Series seems to contain a tilted (or sheared) geometry";
- MITK_DEBUG << " Distance of expected slice origin from actual slice origin: " << distance;
- MITK_DEBUG << " ==> storing this shift for later analysis:";
- MITK_DEBUG << " v right: " << right;
- MITK_DEBUG << " v up: " << up;
- MITK_DEBUG << " v normal: " << normal;
-
- Point3Dd projectionRight = projectPointOnLine( origin1, origin2, right );
- Point3Dd projectionNormal = projectPointOnLine( origin1, origin2, normal );
-
- m_ShiftRight = (projectionRight - origin2).GetNorm();
- m_ShiftNormal = (projectionNormal - origin2).GetNorm();
-
- /*
- now also check to which side the image is shifted.
-
- Calculation e.g. from
- http://mathworld.wolfram.com/Point-PlaneDistance.html
- */
-
- Point3Dd testPoint = origin1;
- Vector3Dd planeNormal = up;
-
- double signedDistance = (
- planeNormal[0] * testPoint[0]
- + planeNormal[1] * testPoint[1]
- + planeNormal[2] * testPoint[2]
- - (
- planeNormal[0] * origin2[0]
- + planeNormal[1] * origin2[1]
- + planeNormal[2] * origin2[2]
- )
- )
- /
- sqrt( planeNormal[0] * planeNormal[0]
- + planeNormal[1] * planeNormal[1]
- + planeNormal[2] * planeNormal[2]
- );
-
- m_ShiftUp = signedDistance;
-
- m_ITKAssumedSliceSpacing = (origin2 - origin1).GetNorm();
- // How do we now this is assumed? See header documentation for ITK code references
- //double itkAssumedSliceSpacing = sqrt( m_ShiftUp * m_ShiftUp + m_ShiftNormal * m_ShiftNormal );
-
- MITK_DEBUG << " shift normal: " << m_ShiftNormal;
- MITK_DEBUG << " shift normal assumed by ITK: " << m_ITKAssumedSliceSpacing;
- MITK_DEBUG << " shift up: " << m_ShiftUp;
- MITK_DEBUG << " shift right: " << m_ShiftRight;
-
- MITK_DEBUG << " tilt angle (deg): " << atan( m_ShiftUp / m_ShiftNormal ) * 180.0 / 3.1415926535;
- }
-}
-
-Point3D
-DicomSeriesReader::GantryTiltInformation::projectPointOnLine( Point3Dd p, Point3Dd lineOrigin, Vector3Dd lineDirection )
-{
- /**
- See illustration at http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage472/
-
- vector(lineOrigin,p) = normal * ( innerproduct((p - lineOrigin),normal) / squared-length(normal) )
- */
-
- Vector3Dd lineOriginToP = p - lineOrigin;
- double innerProduct = lineOriginToP * lineDirection;
-
- double factor = innerProduct / lineDirection.GetSquaredNorm();
- Point3Dd projection = lineOrigin + factor * lineDirection;
-
- return projection;
-}
-
-double
-DicomSeriesReader::GantryTiltInformation::GetTiltCorrectedAdditionalSize() const
-{
- return fabs(m_ShiftUp);
-}
-
-double
-DicomSeriesReader::GantryTiltInformation::GetTiltAngleInDegrees() const
-{
- return atan( fabs(m_ShiftUp) / m_ShiftNormal ) * 180.0 / 3.1415926535;
-}
-
-double
-DicomSeriesReader::GantryTiltInformation::GetMatrixCoefficientForCorrectionInWorldCoordinates() const
-{
- // so many mm need to be shifted per slice!
- return m_ShiftUp / static_cast(m_NumberOfSlicesApart);
-}
-
-double
-DicomSeriesReader::GantryTiltInformation::GetRealZSpacing() const
-{
- return m_ShiftNormal / static_cast(m_NumberOfSlicesApart);
-}
-
-
-bool
-DicomSeriesReader::GantryTiltInformation::IsSheared() const
-{
- return ( fabs(m_ShiftRight) > 0.001
- || fabs(m_ShiftUp) > 0.001);
-}
-
-
-bool
-DicomSeriesReader::GantryTiltInformation::IsRegularGantryTilt() const
-{
- return ( fabs(m_ShiftRight) < 0.001
- && fabs(m_ShiftUp) > 0.001);
-}
-
-
std::string
DicomSeriesReader::ConstCharStarToString(const char* s)
{
return s ? std::string(s) : std::string();
}
bool
DicomSeriesReader::DICOMStringToSpacing(const std::string& s, ScalarType& spacingX, ScalarType& spacingY)
{
bool successful = false;
std::istringstream spacingReader(s);
std::string spacing;
if ( std::getline( spacingReader, spacing, '\\' ) )
{
spacingY = atof( spacing.c_str() );
if ( std::getline( spacingReader, spacing, '\\' ) )
{
spacingX = atof( spacing.c_str() );
successful = true;
}
}
return successful;
}
Point3D
DicomSeriesReader::DICOMStringToPoint3D(const std::string& s, bool& successful)
{
Point3D p;
successful = true;
std::istringstream originReader(s);
std::string coordinate;
unsigned int dim(0);
while( std::getline( originReader, coordinate, '\\' ) && dim < 3)
{
p[dim++]= atof(coordinate.c_str());
}
if (dim && dim != 3)
{
successful = false;
MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values.";
}
else if (dim == 0)
{
successful = false;
p.Fill(0.0); // assume default (0,0,0)
}
return p;
}
void
DicomSeriesReader::DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful)
{
successful = true;
std::istringstream orientationReader(s);
std::string coordinate;
unsigned int dim(0);
while( std::getline( orientationReader, coordinate, '\\' ) && dim < 6 )
{
if (dim<3)
{
right[dim++] = atof(coordinate.c_str());
}
else
{
up[dim++ - 3] = atof(coordinate.c_str());
}
}
if (dim && dim != 6)
{
successful = false;
MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values.";
}
else if (dim == 0)
{
// fill with defaults
right.Fill(0.0);
right[0] = 1.0;
up.Fill(0.0);
up[1] = 1.0;
successful = false;
}
}
DicomSeriesReader::SliceGroupingAnalysisResult
DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption(
const StringContainer& files,
bool groupImagesWithGantryTilt,
const gdcm::Scanner::MappingType& tagValueMappings_)
{
// result.first = files that fit ITK's assumption
// result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again
SliceGroupingAnalysisResult result;
// we const_cast here, because I could not use a map.at(), which would make the code much more readable
gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_);
const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient)
const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation
const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt
Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0);
bool fromFirstToSecondOriginInitialized(false);
Point3D thisOrigin;
thisOrigin.Fill(0.0f);
Point3D lastOrigin;
lastOrigin.Fill(0.0f);
Point3D lastDifferentOrigin;
lastDifferentOrigin.Fill(0.0f);
bool lastOriginInitialized(false);
MITK_DEBUG << "--------------------------------------------------------------------------------";
MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")";
unsigned int fileIndex(0);
for (StringContainer::const_iterator fileIter = files.begin();
fileIter != files.end();
++fileIter, ++fileIndex)
{
bool fileFitsIntoPattern(false);
std::string thisOriginString;
// Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that
thisOriginString = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagImagePositionPatient] );
if (thisOriginString.empty())
{
// don't let such files be in a common group. Everything without position information will be loaded as a single slice:
// with standard DICOM files this can happen to: CR, DX, SC
MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis (no position information)"; // we already have one occupying this position
if ( result.GetBlockFilenames().empty() ) // nothing WITH position information yet
{
// ==> this is a group of its own, stop processing, come back later
result.AddFileToSortedBlock( *fileIter );
StringContainer remainingFiles;
remainingFiles.insert( remainingFiles.end(), fileIter+1, files.end() );
result.AddFilesToUnsortedBlock( remainingFiles );
fileFitsIntoPattern = false;
break; // no files anymore
}
else
{
// ==> this does not match, consider later
result.AddFileToUnsortedBlock( *fileIter );
fileFitsIntoPattern = false;
continue; // next file
}
}
bool ignoredConversionError(-42); // hard to get here, no graceful way to react
thisOrigin = DICOMStringToPoint3D( thisOriginString, ignoredConversionError );
MITK_DEBUG << " " << fileIndex << " " << *fileIter
<< " at "
/* << thisOriginString */ << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")";
if ( lastOriginInitialized && (thisOrigin == lastOrigin) )
{
MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position
result.AddFileToUnsortedBlock( *fileIter );
fileFitsIntoPattern = false;
}
else
{
if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position
{
fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin;
fromFirstToSecondOriginInitialized = true;
// Here we calculate if this slice and the previous one are well aligned,
// i.e. we test if the previous origin is on a line through the current
// origin, directed into the normal direction of the current slice.
// If this is NOT the case, then we have a data set with a TILTED GANTRY geometry,
// which cannot be simply loaded into a single mitk::Image at the moment.
// For this case, we flag this finding in the result and DicomSeriesReader
// can correct for that later.
Vector3D right; right.Fill(0.0);
Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point
DICOMStringToOrientationVectors( tagValueMappings[fileIter->c_str()][tagImageOrientation], right, up, ignoredConversionError );
GantryTiltInformation tiltInfo( lastDifferentOrigin, thisOrigin, right, up, 1 );
if ( tiltInfo.IsSheared() ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt
{
/* optimistic approach, accepting gantry tilt: save file for later, check all further files */
// at this point we have TWO slices analyzed! if they are the only two files, we still split, because there is no third to verify our tilting assumption.
// later with a third being available, we must check if the initial tilting vector is still valid. if yes, continue.
// if NO, we need to split the already sorted part (result.first) and the currently analyzed file (*fileIter)
// tell apart gantry tilt from overall skewedness
// sort out irregularly sheared slices, that IS NOT tilting
if ( groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt() )
{
// check if this is at least roughly the same angle as recorded in DICOM tags
if ( tagValueMappings[fileIter->c_str()].find(tagGantryTilt) != tagValueMappings[fileIter->c_str()].end() )
{
// read value, compare to calculated angle
std::string tiltStr = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagGantryTilt] );
double angle = atof(tiltStr.c_str());
MITK_DEBUG << "Comparing recorded tilt angle " << angle << " against calculated value " << tiltInfo.GetTiltAngleInDegrees();
// TODO we probably want the signs correct, too (that depends: this is just a rough check, nothing serious)
if ( fabs(angle) - tiltInfo.GetTiltAngleInDegrees() > 0.25)
{
result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis
fileFitsIntoPattern = false;
}
else // tilt angle from header is less than 0.25 degrees different from what we calculated, assume this is fine
{
result.FlagGantryTilt();
result.AddFileToSortedBlock(*fileIter); // this file is good for current block
fileFitsIntoPattern = true;
}
}
else // we cannot check the calculated tilt angle against the one from the dicom header (so we assume we are right)
{
result.FlagGantryTilt();
result.AddFileToSortedBlock(*fileIter); // this file is good for current block
fileFitsIntoPattern = true;
}
}
else // caller does not want tilt compensation OR shearing is more complicated than tilt
{
result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis
fileFitsIntoPattern = false;
}
}
else // not sheared
{
result.AddFileToSortedBlock(*fileIter); // this file is good for current block
fileFitsIntoPattern = true;
}
}
else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices
{
Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin;
Vector3D originError = assumedOrigin - thisOrigin;
double norm = originError.GetNorm();
double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction
if (norm > toleratedError)
{
MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = "
<< norm << ", allowed "
<< toleratedError << ").";
MITK_DEBUG << " Expected position (" << assumedOrigin[0] << ","
<< assumedOrigin[1] << ","
<< assumedOrigin[2] << "), got position ("
<< thisOrigin[0] << ","
<< thisOrigin[1] << ","
<< thisOrigin[2] << ")";
MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis";
// At this point we know we deviated from the expectation of ITK's ImageSeriesReader
// We split the input file list at this point, i.e. all files up to this one (excluding it)
// are returned as group 1, the remaining files (including the faulty one) are group 2
/* Optimistic approach: check if any of the remaining slices fits in */
result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis
fileFitsIntoPattern = false;
}
else
{
result.AddFileToSortedBlock(*fileIter); // this file is good for current block
fileFitsIntoPattern = true;
}
}
else // this should be the very first slice
{
result.AddFileToSortedBlock(*fileIter); // this file is good for current block
fileFitsIntoPattern = true;
}
}
// record current origin for reference in later iterations
if ( !lastOriginInitialized || ( fileFitsIntoPattern && (thisOrigin != lastOrigin) ) )
{
lastDifferentOrigin = thisOrigin;
}
lastOrigin = thisOrigin;
lastOriginInitialized = true;
}
if ( result.ContainsGantryTilt() )
{
// check here how many files were grouped.
// IF it was only two files AND we assume tiltedness (e.g. save "distance")
// THEN we would want to also split the two previous files (simple) because
// we don't have any reason to assume they belong together
if ( result.GetBlockFilenames().size() == 2 )
{
result.UndoPrematureGrouping();
}
}
return result;
}
DicomSeriesReader::FileNamesGrouping
DicomSeriesReader::GetSeries(const StringContainer& files, bool groupImagesWithGantryTilt, const StringContainer &restrictions)
{
return GetSeries(files, true, groupImagesWithGantryTilt, restrictions);
}
DicomSeriesReader::FileNamesGrouping
DicomSeriesReader::GetSeries(const StringContainer& files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer& /*restrictions*/)
{
/**
assumption about this method:
returns a map of uid-like-key --> list(filename)
each entry should contain filenames that have images of same
- series instance uid (automatically done by GDCMSeriesFileNames
- 0020,0037 image orientation (patient)
- 0028,0030 pixel spacing (x,y)
- 0018,0050 slice thickness
*/
// use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2
// PART I: scan files for sorting relevant DICOM tags,
// separate images that differ in any of those
// attributes (they cannot possibly form a 3D block)
// scan for relevant tags in dicom files
gdcm::Scanner scanner;
const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID
scanner.AddTag( tagSOPClassUID );
const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID
scanner.AddTag( tagSeriesInstanceUID );
const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation
scanner.AddTag( tagImageOrientation );
const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing
scanner.AddTag( tagPixelSpacing );
const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing
scanner.AddTag( tagImagerPixelSpacing );
const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness
scanner.AddTag( tagSliceThickness );
const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows
scanner.AddTag( tagNumberOfRows );
const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols
scanner.AddTag( tagNumberOfColumns );
const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt
scanner.AddTag( tagGantryTilt );
const gdcm::Tag tagModality(0x0008, 0x0060); // modality
scanner.AddTag( tagModality );
const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames
scanner.AddTag( tagNumberOfFrames );
// additional tags read in this scan to allow later analysis
// THESE tag are not used for initial separating of files
const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient)
scanner.AddTag( tagImagePositionPatient );
// TODO add further restrictions from arguments (when anybody asks for it)
FileNamesGrouping result;
// let GDCM scan files
if ( !scanner.Scan( files ) )
{
MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files.";
return result;
}
// assign files IDs that will separate them for loading into image blocks
for (gdcm::Scanner::ConstIterator fileIter = scanner.Begin();
fileIter != scanner.End();
++fileIter)
{
if ( std::string(fileIter->first).empty() ) continue; // TODO understand why Scanner has empty string entries
if ( std::string(fileIter->first) == std::string("DICOMDIR") ) continue;
/* sort out multi-frame
if ( scanner.GetValue( fileIter->first , tagNumberOfFrames ) )
{
MITK_INFO << "Ignoring " << fileIter->first << " because we cannot handle multi-frame images.";
continue;
}
*/
// we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier.
// doing the same thing with find would make the code less readable. Since we forget the Scanner results
// anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access
std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier( const_cast(fileIter->second) );
result[ moreUniqueSeriesId ].AddFile( fileIter->first );
}
// PART II: sort slices spatially (or at least consistently if this is NOT possible, see method)
for ( FileNamesGrouping::const_iterator groupIter = result.begin();
groupIter != result.end();
++groupIter )
{
try
{
result[ groupIter->first ] = ImageBlockDescriptor( SortSeriesSlices( groupIter->second.GetFilenames() ) ); // sort each slice group spatially
} catch(...)
{
MITK_ERROR << "Caught something.";
}
}
// PART III: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing),
// separate into multiple blocks if necessary.
//
// Analysis performs the following steps:
// * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing
// * check what images actually fulfill ITK's z-spacing assumption
// * separate all images that fail the test into new blocks, re-iterate analysis for these blocks
// * this includes images which DO NOT PROVIDE spatial information, i.e. all images w/o ImagePositionPatient will be loaded separately
FileNamesGrouping groupsOf3DPlusTBlocks; // final result of this function
for ( FileNamesGrouping::const_iterator groupIter = result.begin();
groupIter != result.end();
++groupIter )
{
FileNamesGrouping groupsOf3DBlocks; // intermediate result for only this group(!)
std::map mapOf3DBlockAnalysisResults;
StringContainer filesStillToAnalyze = groupIter->second.GetFilenames();
std::string groupUID = groupIter->first;
unsigned int subgroup(0);
MITK_DEBUG << "Analyze group " << groupUID;
while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow
{
SliceGroupingAnalysisResult analysisResult =
AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze,
groupImagesWithGantryTilt,
scanner.GetMappings() );
// enhance the UID for additional groups
std::stringstream newGroupUID;
newGroupUID << groupUID << '.' << subgroup;
ImageBlockDescriptor thisBlock( analysisResult.GetBlockFilenames() );
std::string firstFileInBlock = thisBlock.GetFilenames().front();
thisBlock.SetImageBlockUID( newGroupUID.str() );
thisBlock.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagSeriesInstanceUID ) ) );
thisBlock.SetHasGantryTiltCorrected( analysisResult.ContainsGantryTilt() );
thisBlock.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagSOPClassUID ) ) );
thisBlock.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagNumberOfFrames ) ) );
thisBlock.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagModality ) ) );
thisBlock.SetPixelSpacingInformation( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagPixelSpacing ) ),
DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagImagerPixelSpacing ) ) );
thisBlock.SetHasMultipleTimePoints( false );
groupsOf3DBlocks[ newGroupUID.str() ] = thisBlock;
//MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << groupsOf3DBlocks[ newGroupUID.str() ].GetFilenames().size() << " files";
MITK_DEBUG << "Result: sorted 3D group with " << groupsOf3DBlocks[ newGroupUID.str() ].GetFilenames().size() << " files";
StringContainer debugOutputFiles = analysisResult.GetBlockFilenames();
for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter)
MITK_DEBUG << " IN " << *siter;
++subgroup;
filesStillToAnalyze = analysisResult.GetUnsortedFilenames(); // remember what needs further analysis
for (StringContainer::const_iterator siter = filesStillToAnalyze.begin(); siter != filesStillToAnalyze.end(); ++siter)
MITK_DEBUG << " OUT " << *siter;
}
// end of grouping, now post-process groups
// PART IV: attempt to group blocks to 3D+t blocks if requested
// inspect entries of groupsOf3DBlocks
// - if number of files is identical to previous entry, collect for 3D+t block
// - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, start a new one, continue
// decide whether or not to group 3D blocks into 3D+t blocks where possible
if ( !sortTo3DPlust )
{
// copy 3D blocks to output
groupsOf3DPlusTBlocks.insert( groupsOf3DBlocks.begin(), groupsOf3DBlocks.end() );
}
else
{
// sort 3D+t (as described in "PART IV")
MITK_DEBUG << "================================================================================";
MITK_DEBUG << "3D+t analysis:";
unsigned int numberOfFilesInPreviousBlock(0);
std::string previousBlockKey;
for ( FileNamesGrouping::const_iterator block3DIter = groupsOf3DBlocks.begin();
block3DIter != groupsOf3DBlocks.end();
++block3DIter )
{
unsigned int numberOfFilesInThisBlock = block3DIter->second.GetFilenames().size();
std::string thisBlockKey = block3DIter->first;
if (numberOfFilesInPreviousBlock == 0)
{
numberOfFilesInPreviousBlock = numberOfFilesInThisBlock;
groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second;
MITK_DEBUG << " 3D+t group " << thisBlockKey;
previousBlockKey = thisBlockKey;
}
else
{
bool identicalOrigins;
try {
// check whether this and the previous block share a comon origin
// TODO should be safe, but a little try/catch or other error handling wouldn't hurt
const char
*origin_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient ),
*previous_origin_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient ),
*destination_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient ),
*previous_destination_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient );
if (!origin_value || !previous_origin_value || !destination_value || !previous_destination_value)
{
identicalOrigins = false;
}
else
{
std::string thisOriginString = ConstCharStarToString( origin_value );
std::string previousOriginString = ConstCharStarToString( previous_origin_value );
// also compare last origin, because this might differ if z-spacing is different
std::string thisDestinationString = ConstCharStarToString( destination_value );
std::string previousDestinationString = ConstCharStarToString( previous_destination_value );
identicalOrigins = ( (thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString) );
}
} catch(...)
{
identicalOrigins = false;
}
if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock))
{
// group with previous block
groupsOf3DPlusTBlocks[previousBlockKey].AddFiles( block3DIter->second.GetFilenames() );
groupsOf3DPlusTBlocks[previousBlockKey].SetHasMultipleTimePoints(true);
MITK_DEBUG << " --> group enhanced with another timestep";
}
else
{
// start a new block
groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second;
int numberOfTimeSteps = groupsOf3DPlusTBlocks[previousBlockKey].GetFilenames().size() / numberOfFilesInPreviousBlock;
MITK_DEBUG << " ==> group closed with " << numberOfTimeSteps << " time steps";
previousBlockKey = thisBlockKey;
MITK_DEBUG << " 3D+t group " << thisBlockKey << " started";
}
}
numberOfFilesInPreviousBlock = numberOfFilesInThisBlock;
}
}
}
MITK_DEBUG << "================================================================================";
MITK_DEBUG << "Summary: ";
for ( FileNamesGrouping::const_iterator groupIter = groupsOf3DPlusTBlocks.begin(); groupIter != groupsOf3DPlusTBlocks.end(); ++groupIter )
{
ImageBlockDescriptor block = groupIter->second;
MITK_DEBUG << " " << block.GetFilenames().size() << " '" << block.GetModality() << "' images (" << block.GetSOPClassUIDAsString() << ") in volume " << block.GetImageBlockUID();
MITK_DEBUG << " (gantry tilt : " << (block.HasGantryTiltCorrected()?"Yes":"No") << "; "
"pixel spacing : " << PixelSpacingInterpretationToString( block.GetPixelSpacingType() ) << "; "
"3D+t: " << (block.HasMultipleTimePoints()?"Yes":"No") << "; "
"reader support: " << ReaderImplementationLevelToString( block.GetReaderImplementationLevel() ) << ")";
StringContainer debugOutputFiles = block.GetFilenames();
for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter)
MITK_DEBUG << " F " << *siter;
}
MITK_DEBUG << "================================================================================";
return groupsOf3DPlusTBlocks;
}
DicomSeriesReader::FileNamesGrouping
DicomSeriesReader::GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions)
{
gdcm::Directory directoryLister;
directoryLister.Load( dir.c_str(), false ); // non-recursive
return GetSeries(directoryLister.GetFilenames(), groupImagesWithGantryTilt, restrictions);
}
std::string
DicomSeriesReader::CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag )
{
std::string result;
try
{
result = IDifyTagValue( tagValueMap[ tag ] ? tagValueMap[ tag ] : std::string("") );
}
catch (std::exception&)
{
// we are happy with even nothing, this will just group images of a series
//MITK_WARN << "Could not access tag " << tag << ": " << e.what();
}
return result;
}
std::string
DicomSeriesReader::CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap )
{
const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID
const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation
const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing
const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing
const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness
const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows
const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols
const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames
const char* tagSeriesInstanceUid = tagValueMap[tagSeriesInstanceUID];
if (!tagSeriesInstanceUid)
{
mitkThrow() << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously wrong with this image, so stopping here.";
}
std::string constructedID = tagSeriesInstanceUid;
constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfRows );
constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfColumns );
constructedID += CreateSeriesIdentifierPart( tagValueMap, tagPixelSpacing );
constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImagerPixelSpacing );
constructedID += CreateSeriesIdentifierPart( tagValueMap, tagSliceThickness );
constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfFrames );
// be a bit tolerant for orienatation, let only the first few digits matter (http://bugs.mitk.org/show_bug.cgi?id=12263)
// NOT constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation );
if (tagValueMap.find(tagImageOrientation) != tagValueMap.end())
{
bool conversionError(false);
Vector3D right; right.Fill(0.0);
Vector3D up; right.Fill(0.0);
DICOMStringToOrientationVectors( tagValueMap[tagImageOrientation], right, up, conversionError );
//string newstring sprintf(simplifiedOrientationString, "%.3f\\%.3f\\%.3f\\%.3f\\%.3f\\%.3f", right[0], right[1], right[2], up[0], up[1], up[2]);
std::ostringstream ss;
ss.setf(std::ios::fixed, std::ios::floatfield);
ss.precision(5);
ss << right[0] << "\\"
<< right[1] << "\\"
<< right[2] << "\\"
<< up[0] << "\\"
<< up[1] << "\\"
<< up[2];
std::string simplifiedOrientationString(ss.str());
constructedID += IDifyTagValue( simplifiedOrientationString );
}
constructedID.resize( constructedID.length() - 1 ); // cut of trailing '.'
return constructedID;
}
std::string
DicomSeriesReader::IDifyTagValue(const std::string& value)
{
std::string IDifiedValue( value );
if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value");
// Eliminate non-alnum characters, including whitespace...
// that may have been introduced by concats.
for(std::size_t i=0; i= 'a' && IDifiedValue[i] <= 'z')
|| (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9')
|| (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z')))
{
IDifiedValue.erase(i, 1);
}
}
IDifiedValue += ".";
return IDifiedValue;
}
DicomSeriesReader::StringContainer
DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions)
{
FileNamesGrouping allSeries = GetSeries(dir, groupImagesWithGantryTilt, restrictions);
StringContainer resultingFileList;
for ( FileNamesGrouping::const_iterator idIter = allSeries.begin();
idIter != allSeries.end();
++idIter )
{
if ( idIter->first.find( series_uid ) == 0 ) // this ID starts with given series_uid
{
return idIter->second.GetFilenames();
}
}
return resultingFileList;
}
DicomSeriesReader::StringContainer
DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames)
{
/* we CAN expect a group of equal
- series instance uid
- image orientation
- pixel spacing
- imager pixel spacing
- slice thickness
- number of rows/columns
(each piece of information except the rows/columns might be missing)
sorting with GdcmSortFunction tries its best by sorting by spatial position
and more hints (acquisition number, acquisition time, trigger time) but will
always produce a sorting by falling back to SOP Instance UID.
*/
gdcm::Sorter sorter;
sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction);
try
{
if (sorter.Sort(unsortedFilenames))
{
return sorter.GetFilenames();
}
else
{
MITK_WARN << "Sorting error. Leaving series unsorted.";
return unsortedFilenames;
}
}
catch(std::logic_error&)
{
MITK_WARN << "Sorting error. Leaving series unsorted.";
return unsortedFilenames;
}
}
bool
DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2)
{
// This method MUST accept missing location and position information (and all else, too)
// because we cannot rely on anything
// (restriction on the sentence before: we have to provide consistent sorting, so we
// rely on the minimum information all DICOM files need to provide: SOP Instance UID)
/* we CAN expect a group of equal
- series instance uid
- image orientation
- pixel spacing
- imager pixel spacing
- slice thickness
- number of rows/columns
*/
static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient)
static const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation
// see if we have Image Position and Orientation
if ( ds1.FindDataElement(tagImagePositionPatient) && ds1.FindDataElement(tagImageOrientation) &&
ds2.FindDataElement(tagImagePositionPatient) && ds2.FindDataElement(tagImageOrientation) )
{
gdcm::Attribute<0x0020,0x0032> image_pos1; // Image Position (Patient)
gdcm::Attribute<0x0020,0x0037> image_orientation1; // Image Orientation (Patient)
image_pos1.Set(ds1);
image_orientation1.Set(ds1);
gdcm::Attribute<0x0020,0x0032> image_pos2;
gdcm::Attribute<0x0020,0x0037> image_orientation2;
image_pos2.Set(ds2);
image_orientation2.Set(ds2);
/*
we tolerate very small differences in image orientation, since we got to know about
acquisitions where these values change across a single series (7th decimal digit)
(http://bugs.mitk.org/show_bug.cgi?id=12263)
still, we want to check if our assumption of 'almost equal' orientations is valid
*/
for (unsigned int dim = 0; dim < 6; ++dim)
{
if ( fabs(image_orientation2[dim] - image_orientation1[dim]) > 0.0001 )
{
MITK_ERROR << "Dicom images have different orientations.";
throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images.");
}
}
double normal[3];
normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4];
normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5];
normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3];
double
dist1 = 0.0,
dist2 = 0.0;
// this computes the distance from world origin (0,0,0) ALONG THE NORMAL of the image planes
for (unsigned char i = 0u; i < 3u; ++i)
{
dist1 += normal[i] * image_pos1[i];
dist2 += normal[i] * image_pos2[i];
}
// if we can sort by just comparing the distance, we do exactly that
if ( fabs(dist1 - dist2) >= mitk::eps)
{
// default: compare position
return dist1 < dist2;
}
else // we need to check more properties to distinguish slices
{
// try to sort by Acquisition Number
static const gdcm::Tag tagAcquisitionNumber(0x0020, 0x0012);
if (ds1.FindDataElement(tagAcquisitionNumber) && ds2.FindDataElement(tagAcquisitionNumber))
{
gdcm::Attribute<0x0020,0x0012> acquisition_number1; // Acquisition number
gdcm::Attribute<0x0020,0x0012> acquisition_number2;
acquisition_number1.Set(ds1);
acquisition_number2.Set(ds2);
if (acquisition_number1 != acquisition_number2)
{
return acquisition_number1 < acquisition_number2;
}
else // neither position nor acquisition number are good for sorting, so check more
{
// try to sort by Acquisition Time
static const gdcm::Tag tagAcquisitionTime(0x0008, 0x0032);
if (ds1.FindDataElement(tagAcquisitionTime) && ds2.FindDataElement(tagAcquisitionTime))
{
gdcm::Attribute<0x0008,0x0032> acquisition_time1; // Acquisition time
gdcm::Attribute<0x0008,0x0032> acquisition_time2;
acquisition_time1.Set(ds1);
acquisition_time2.Set(ds2);
if (acquisition_time1 != acquisition_time2)
{
return acquisition_time1 < acquisition_time2;
}
else // we gave up on image position, acquisition number and acquisition time now
{
// let's try trigger time
static const gdcm::Tag tagTriggerTime(0x0018, 0x1060);
if (ds1.FindDataElement(tagTriggerTime) && ds2.FindDataElement(tagTriggerTime))
{
gdcm::Attribute<0x0018,0x1060> trigger_time1; // Trigger time
gdcm::Attribute<0x0018,0x1060> trigger_time2;
trigger_time1.Set(ds1);
trigger_time2.Set(ds2);
if (trigger_time1 != trigger_time2)
{
return trigger_time1 < trigger_time2;
}
// ELSE!
// for this and many previous ifs we fall through if nothing lets us sort
} // .
} // .
} // .
}
}
}
} // .
// LAST RESORT: all valuable information for sorting is missing.
// Sort by some meaningless but unique identifiers to satisfy the sort function
static const gdcm::Tag tagSOPInstanceUID(0x0008, 0x0018);
if (ds1.FindDataElement(tagSOPInstanceUID) && ds2.FindDataElement(tagSOPInstanceUID))
{
MITK_DEBUG << "Dicom images are missing attributes for a meaningful sorting, falling back to SOP instance UID comparison.";
gdcm::Attribute<0x0008,0x0018> SOPInstanceUID1; // SOP instance UID is mandatory and unique
gdcm::Attribute<0x0008,0x0018> SOPInstanceUID2;
SOPInstanceUID1.Set(ds1);
SOPInstanceUID2.Set(ds2);
return SOPInstanceUID1 < SOPInstanceUID2;
}
else
{
// no DICOM file should really come down here, this should only be reached with unskillful and unlucky manipulation of files
std::string error_message("Malformed DICOM images, which do not even contain a SOP Instance UID.");
MITK_ERROR << error_message;
throw std::logic_error( error_message );
}
}
std::string DicomSeriesReader::GetConfigurationString()
{
std::stringstream configuration;
configuration << "MITK_USE_GDCMIO: ";
configuration << "true";
configuration << "\n";
configuration << "GDCM_VERSION: ";
#ifdef GDCM_MAJOR_VERSION
configuration << GDCM_VERSION;
#endif
//configuration << "\n";
return configuration.str();
}
void DicomSeriesReader::CopyMetaDataToImageProperties(StringContainer filenames, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, const ImageBlockDescriptor& blockInfo, Image *image)
{
std::list imageBlock;
imageBlock.push_back(filenames);
CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, blockInfo, image);
}
void DicomSeriesReader::CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, const ImageBlockDescriptor& blockInfo, Image* image)
{
if (!io || !image) return;
StringLookupTable filesForSlices;
StringLookupTable sliceLocationForSlices;
StringLookupTable instanceNumberForSlices;
StringLookupTable SOPInstanceNumberForSlices;
gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_);
//DICOM tags which should be added to the image properties
const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location
const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number
const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number
unsigned int timeStep(0);
std::string propertyKeySliceLocation = "dicom.image.0020.1041";
std::string propertyKeyInstanceNumber = "dicom.image.0020.0013";
std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018";
// tags for each image
for ( std::list::iterator i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++ )
{
const StringContainer& files = (*i);
unsigned int slice(0);
for ( StringContainer::const_iterator fIter = files.begin();
fIter != files.end();
++fIter, ++slice )
{
filesForSlices.SetTableValue( slice, *fIter );
gdcm::Scanner::TagToValue tagValueMapForFile = tagValueMappings[fIter->c_str()];
if(tagValueMapForFile.find(tagSliceLocation) != tagValueMapForFile.end())
sliceLocationForSlices.SetTableValue(slice, tagValueMapForFile[tagSliceLocation]);
if(tagValueMapForFile.find(tagInstanceNumber) != tagValueMapForFile.end())
instanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagInstanceNumber]);
if(tagValueMapForFile.find(tagSOPInstanceNumber) != tagValueMapForFile.end())
SOPInstanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagSOPInstanceNumber]);
}
image->SetProperty( "files", StringLookupTableProperty::New( filesForSlices ) );
//If more than one time step add postfix ".t" + timestep
if(timeStep != 0)
{
std::ostringstream postfix;
postfix << ".t" << timeStep;
propertyKeySliceLocation.append(postfix.str());
propertyKeyInstanceNumber.append(postfix.str());
propertyKeySOPInstanceNumber.append(postfix.str());
}
image->SetProperty( propertyKeySliceLocation.c_str(), StringLookupTableProperty::New( sliceLocationForSlices ) );
image->SetProperty( propertyKeyInstanceNumber.c_str(), StringLookupTableProperty::New( instanceNumberForSlices ) );
image->SetProperty( propertyKeySOPInstanceNumber.c_str(), StringLookupTableProperty::New( SOPInstanceNumberForSlices ) );
}
// Copy tags for series, study, patient level (leave interpretation to application).
// These properties will be copied to the DataNode by DicomSeriesReader.
// tags for the series (we just use the one that ITK copied to its dictionary (proably that of the last slice)
const itk::MetaDataDictionary& dict = io->GetMetaDataDictionary();
const TagToPropertyMapType& propertyLookup = DicomSeriesReader::GetDICOMTagsToMITKPropertyMap();
itk::MetaDataDictionary::ConstIterator dictIter = dict.Begin();
while ( dictIter != dict.End() )
{
//MITK_DEBUG << "Key " << dictIter->first;
std::string value;
if ( itk::ExposeMetaData( dict, dictIter->first, value ) )
{
//MITK_DEBUG << "Value " << value;
TagToPropertyMapType::const_iterator valuePosition = propertyLookup.find( dictIter->first );
if ( valuePosition != propertyLookup.end() )
{
std::string propertyKey = valuePosition->second;
//MITK_DEBUG << "--> " << propertyKey;
image->SetProperty( propertyKey.c_str(), StringProperty::New(value) );
}
}
else
{
MITK_WARN << "Tag " << dictIter->first << " not read as string as expected. Ignoring..." ;
}
++dictIter;
}
// copy imageblockdescriptor as properties
image->SetProperty("dicomseriesreader.SOPClass", StringProperty::New(blockInfo.GetSOPClassUIDAsString()));
image->SetProperty("dicomseriesreader.ReaderImplementationLevelString", StringProperty::New(ReaderImplementationLevelToString( blockInfo.GetReaderImplementationLevel() )));
image->SetProperty("dicomseriesreader.ReaderImplementationLevel", GenericProperty::New( blockInfo.GetReaderImplementationLevel() ));
image->SetProperty("dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New(PixelSpacingInterpretationToString( blockInfo.GetPixelSpacingType() )));
image->SetProperty("dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New(blockInfo.GetPixelSpacingType()));
image->SetProperty("dicomseriesreader.MultiFrameImage", BoolProperty::New(blockInfo.IsMultiFrameImage()));
image->SetProperty("dicomseriesreader.GantyTiltCorrected", BoolProperty::New(blockInfo.HasGantryTiltCorrected()));
image->SetProperty("dicomseriesreader.3D+t", BoolProperty::New(blockInfo.HasMultipleTimePoints()));
}
void DicomSeriesReader::FixSpacingInformation( mitk::Image* image, const ImageBlockDescriptor& imageBlockDescriptor )
{
// spacing provided by ITK/GDCM
Vector3D imageSpacing = image->GetGeometry()->GetSpacing();
ScalarType imageSpacingX = imageSpacing[0];
ScalarType imageSpacingY = imageSpacing[1];
// spacing as desired by MITK (preference for "in patient", else "on detector", or "1.0/1.0")
ScalarType desiredSpacingX = imageSpacingX;
ScalarType desiredSpacingY = imageSpacingY;
imageBlockDescriptor.GetDesiredMITKImagePixelSpacing( desiredSpacingX, desiredSpacingY );
MITK_DEBUG << "Loaded spacing: " << imageSpacingX << "/" << imageSpacingY;
MITK_DEBUG << "Corrected spacing: " << desiredSpacingX << "/" << desiredSpacingY;
imageSpacing[0] = desiredSpacingX;
imageSpacing[1] = desiredSpacingY;
image->GetGeometry()->SetSpacing( imageSpacing );
}
-} // end namespace mitk
+void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock)
+{
+ const char* previousCLocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ std::locale previousCppLocale( std::cin.getloc() );
+ std::locale l( "C" );
+ std::cin.imbue(l);
+
+ ImageBlockDescriptor imageBlockDescriptor;
+
+ const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient)
+ const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation
+ const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID
+ const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID
+ const gdcm::Tag tagModality(0x0008, 0x0060); // modality
+ const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing
+ const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing
+ const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames
+
+ try
+ {
+ Image::Pointer image = preLoadedImageBlock.IsNull() ? Image::New() : preLoadedImageBlock;
+ CallbackCommand *command = callback ? new CallbackCommand(callback) : 0;
+ bool initialize_node = false;
+
+ /* special case for Philips 3D+t ultrasound images */
+ if ( DicomSeriesReader::IsPhilips3DDicom(filenames.front().c_str()) )
+ {
+ // TODO what about imageBlockDescriptor?
+ // TODO what about preLoadedImageBlock?
+ ReadPhilips3DDicom(filenames.front().c_str(), image);
+ initialize_node = true;
+ }
+ else
+ {
+ /* default case: assume "normal" image blocks, possibly 3D+t */
+ bool canLoadAs4D(true);
+ gdcm::Scanner scanner;
+ ScanForSliceInformation(filenames, scanner);
+
+ // need non-const access for map
+ gdcm::Scanner::MappingType& tagValueMappings = const_cast(scanner.GetMappings());
+
+ std::list imageBlocks = SortIntoBlocksFor3DplusT( filenames, tagValueMappings, sort, canLoadAs4D );
+ unsigned int volume_count = imageBlocks.size();
+
+ imageBlockDescriptor.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSeriesInstanceUID ) ) );
+ imageBlockDescriptor.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSOPClassUID ) ) );
+ imageBlockDescriptor.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagModality ) ) );
+ imageBlockDescriptor.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagNumberOfFrames ) ) );
+ imageBlockDescriptor.SetPixelSpacingInformation( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagPixelSpacing ) ),
+ ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagImagerPixelSpacing ) ) );
+
+ GantryTiltInformation tiltInfo;
+
+ // check possibility of a single slice with many timesteps. In this case, don't check for tilt, no second slice possible
+ if ( !imageBlocks.empty() && imageBlocks.front().size() > 1 && correctTilt)
+ {
+ // check tiltedness here, potentially fixup ITK's loading result by shifting slice contents
+ // check first and last position slice from tags, make some calculations to detect tilt
+
+ std::string firstFilename(imageBlocks.front().front());
+ // calculate from first and last slice to minimize rounding errors
+ std::string secondFilename(imageBlocks.front().back());
+
+ std::string imagePosition1( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImagePositionPatient ] ) );
+ std::string imageOrientation( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImageOrientation ] ) );
+ std::string imagePosition2( ConstCharStarToString( tagValueMappings[secondFilename.c_str() ][ tagImagePositionPatient ] ) );
+
+ bool ignoredConversionError(-42); // hard to get here, no graceful way to react
+ Point3D origin1( DICOMStringToPoint3D( imagePosition1, ignoredConversionError ) );
+ Point3D origin2( DICOMStringToPoint3D( imagePosition2, ignoredConversionError ) );
+
+ Vector3D right; right.Fill(0.0);
+ Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point
+ DICOMStringToOrientationVectors( imageOrientation, right, up, ignoredConversionError );
+
+ tiltInfo = GantryTiltInformation ( origin1, origin2, right, up, filenames.size()-1 );
+ correctTilt = tiltInfo.IsSheared() && tiltInfo.IsRegularGantryTilt();
+ }
+ else
+ {
+ correctTilt = false; // we CANNOT do that
+ }
+
+ imageBlockDescriptor.SetHasGantryTiltCorrected( correctTilt );
+
+ if (volume_count == 1 || !canLoadAs4D || !load4D)
+ {
+
+ DcmIoType::Pointer io;
+ image = MultiplexLoadDICOMByITK( imageBlocks.front(), correctTilt, tiltInfo, io, command, preLoadedImageBlock ); // load first 3D block
+
+ imageBlockDescriptor.AddFiles(imageBlocks.front()); // only the first part is loaded
+ imageBlockDescriptor.SetHasMultipleTimePoints( false );
-#include
+ FixSpacingInformation( image, imageBlockDescriptor );
+ CopyMetaDataToImageProperties( imageBlocks.front(), scanner.GetMappings(), io, imageBlockDescriptor, image);
+
+ initialize_node = true;
+ }
+ else if (volume_count > 1)
+ {
+ imageBlockDescriptor.AddFiles(filenames); // all is loaded
+ imageBlockDescriptor.SetHasMultipleTimePoints( true );
+
+ DcmIoType::Pointer io;
+ image = MultiplexLoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock );
+
+ initialize_node = true;
+ }
+ }
+
+ if (initialize_node)
+ {
+ // forward some image properties to node
+ node.GetPropertyList()->ConcatenatePropertyList( image->GetPropertyList(), true );
+
+ node.SetData( image );
+ setlocale(LC_NUMERIC, previousCLocale);
+ std::cin.imbue(previousCppLocale);
+ }
+
+ MITK_DEBUG << "--------------------------------------------------------------------------------";
+ MITK_DEBUG << "DICOM files loaded (from series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << "):";
+ MITK_DEBUG << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image";
+ MITK_DEBUG << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage()?"Yes":"No");
+ MITK_DEBUG << " reader support: " << ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel());
+ MITK_DEBUG << " pixel spacing type: " << PixelSpacingInterpretationToString( imageBlockDescriptor.GetPixelSpacingType() ) << " " << image->GetGeometry()->GetSpacing()[0] << "/" << image->GetGeometry()->GetSpacing()[0];
+ MITK_DEBUG << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected()?"Yes":"No");
+ MITK_DEBUG << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints()?"Yes":"No");
+ MITK_DEBUG << "--------------------------------------------------------------------------------";
+ }
+ catch (std::exception& e)
+ {
+ // reset locale then throw up
+ setlocale(LC_NUMERIC, previousCLocale);
+ std::cin.imbue(previousCppLocale);
+
+ MITK_DEBUG << "Caught exception in DicomSeriesReader::LoadDicom";
+
+ throw e;
+ }
+}
+
+void
+DicomSeriesReader::ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner& scanner)
+{
+ const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient)
+ scanner.AddTag(tagImagePositionPatient);
+
+ const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID
+ scanner.AddTag(tagSeriesInstanceUID);
+
+ const gdcm::Tag tagImageOrientation(0x0020,0x0037); //Image orientation
+ scanner.AddTag(tagImageOrientation);
+
+ const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location
+ scanner.AddTag( tagSliceLocation );
+
+ const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number
+ scanner.AddTag( tagInstanceNumber );
+
+ const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number
+ scanner.AddTag( tagSOPInstanceNumber );
+
+ const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // Pixel Spacing
+ scanner.AddTag( tagPixelSpacing );
+
+ const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // Imager Pixel Spacing
+ scanner.AddTag( tagImagerPixelSpacing );
+
+ const gdcm::Tag tagModality(0x0008, 0x0060); // Modality
+ scanner.AddTag( tagModality );
+
+ const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP Class UID
+ scanner.AddTag( tagSOPClassUID );
+
+ const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames
+ scanner.AddTag( tagNumberOfFrames );
+
+ scanner.Scan(filenames); // make available image information for each file
+}
+
+std::list
+DicomSeriesReader::SortIntoBlocksFor3DplusT(
+ const StringContainer& presortedFilenames,
+ const gdcm::Scanner::MappingType& tagValueMappings,
+ bool /*sort*/,
+ bool& canLoadAs4D )
+{
+ std::list imageBlocks;
+
+ // ignore sort request, because most likely re-sorting is now needed due to changes in GetSeries(bug #8022)
+ StringContainer sorted_filenames = DicomSeriesReader::SortSeriesSlices(presortedFilenames);
+
+ std::string firstPosition;
+ unsigned int numberOfBlocks(0); // number of 3D image blocks
+
+ static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient)
+
+ // loop files to determine number of image blocks
+ for (StringContainer::const_iterator fileIter = sorted_filenames.begin();
+ fileIter != sorted_filenames.end();
+ ++fileIter)
+ {
+ gdcm::Scanner::TagToValue tagToValueMap = tagValueMappings.find( fileIter->c_str() )->second;
+
+ if(tagToValueMap.find(tagImagePositionPatient) == tagToValueMap.end())
+ {
+ // we expect to get images w/ missing position information ONLY as separated blocks.
+ assert( presortedFilenames.size() == 1 );
+ numberOfBlocks = 1;
+ break;
+ }
+
+ std::string position = tagToValueMap.find(tagImagePositionPatient)->second;
+ MITK_DEBUG << " " << *fileIter << " at " << position;
+ if (firstPosition.empty())
+ {
+ firstPosition = position;
+ }
+
+ if ( position == firstPosition )
+ {
+ ++numberOfBlocks;
+ }
+ else
+ {
+ break; // enough information to know the number of image blocks
+ }
+ }
+
+ MITK_DEBUG << " ==> Assuming " << numberOfBlocks << " time steps";
+
+ if (numberOfBlocks == 0) return imageBlocks; // only possible if called with no files
+
+
+ // loop files to sort them into image blocks
+ unsigned int numberOfExpectedSlices(0);
+ for (unsigned int block = 0; block < numberOfBlocks; ++block)
+ {
+ StringContainer filesOfCurrentBlock;
+
+ for ( StringContainer::const_iterator fileIter = sorted_filenames.begin() + block;
+ fileIter != sorted_filenames.end();
+ //fileIter += numberOfBlocks) // TODO shouldn't this work? give invalid iterators on first attempts
+ )
+ {
+ filesOfCurrentBlock.push_back( *fileIter );
+ for (unsigned int b = 0; b < numberOfBlocks; ++b)
+ {
+ if (fileIter != sorted_filenames.end())
+ ++fileIter;
+ }
+ }
+
+ imageBlocks.push_back(filesOfCurrentBlock);
+
+ if (block == 0)
+ {
+ numberOfExpectedSlices = filesOfCurrentBlock.size();
+ }
+ else
+ {
+ if (filesOfCurrentBlock.size() != numberOfExpectedSlices)
+ {
+ MITK_WARN << "DicomSeriesReader expected " << numberOfBlocks
+ << " image blocks of "
+ << numberOfExpectedSlices
+ << " images each. Block "
+ << block
+ << " got "
+ << filesOfCurrentBlock.size()
+ << " instead. Cannot load this as 3D+t"; // TODO implement recovery (load as many slices 3D+t as much as possible)
+ canLoadAs4D = false;
+ }
+ }
+ }
+
+ return imageBlocks;
+}
+
+Image::Pointer
+DicomSeriesReader
+::MultiplexLoadDICOMByITK(const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock)
+{
+ io = DcmIoType::New();
+ io->SetFileName(filenames.front().c_str());
+ io->ReadImageInformation();
+
+ if (io->GetPixelType() == itk::ImageIOBase::SCALAR)
+ {
+ return MultiplexLoadDICOMByITKScalar(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ }
+ else if (io->GetPixelType() == itk::ImageIOBase::RGB)
+ {
+ return MultiplexLoadDICOMByITKRGBPixel(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+Image::Pointer
+DicomSeriesReader
+::MultiplexLoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock)
+{
+ io = DcmIoType::New();
+ io->SetFileName(imageBlocks.front().front().c_str());
+ io->ReadImageInformation();
+
+ if (io->GetPixelType() == itk::ImageIOBase::SCALAR)
+ {
+ return MultiplexLoadDICOMByITK4DScalar(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ }
+ else if (io->GetPixelType() == itk::ImageIOBase::RGB)
+ {
+ return MultiplexLoadDICOMByITK4DRGBPixel(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+} // end namespace mitk
diff --git a/Core/Code/IO/mitkDicomSeriesReader.h b/Core/Code/IO/mitkDicomSeriesReader.h
index 05ff5edbd4..c35cd71ea5 100644
--- a/Core/Code/IO/mitkDicomSeriesReader.h
+++ b/Core/Code/IO/mitkDicomSeriesReader.h
@@ -1,939 +1,967 @@
/*===================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#ifndef mitkDicomSeriesReader_h
#define mitkDicomSeriesReader_h
#include "mitkDataNode.h"
#include "mitkConfig.h"
#include
#include
#include
#ifdef NOMINMAX
# define DEF_NOMINMAX
# undef NOMINMAX
#endif
#include
#ifdef DEF_NOMINMAX
# ifndef NOMINMAX
# define NOMINMAX
# endif
# undef DEF_NOMINMAX
#endif
#include
#include
namespace mitk
{
/**
\brief Loading DICOM images as MITK images.
- \ref DicomSeriesReader_purpose
- \ref DicomSeriesReader_limitations
- \ref DicomSeriesReader_usage
- \ref DicomSeriesReader_sorting
- \ref DicomSeriesReader_sorting1
- \ref DicomSeriesReader_sorting2
- \ref DicomSeriesReader_sorting3
- \ref DicomSeriesReader_sorting4
- \ref DicomSeriesReader_gantrytilt
- \ref DicomSeriesReader_pixelspacing
- \ref DicomSeriesReader_nextworkitems
- \ref DicomSeriesReader_whynotinitk
- \ref DicomSeriesReader_tests
\section DicomSeriesReader_purpose Purpose
DicomSeriesReader serves as a central class for loading DICOM images as mitk::Image.
As the term "DICOM image" covers a huge variety of possible modalities and
implementations, and since MITK assumes that 3D images are made up of continuous blocks
of slices without any gaps or changes in orientation, the loading mechanism must
implement a number of decisions and compromises.
The main intention of this implementation is not efficiency but correctness of generated slice positions and pixel spacings!
\section DicomSeriesReader_limitations Assumptions and limitations
The class is working only with GDCM 2.0.14 (or possibly newer). This version is the
default of an MITK super-build. Support for other versions or ITK's DicomIO was dropped
because of the associated complexity of DicomSeriesReader.
\b Assumptions
- expected to work with certain SOP Classes (mostly CT Image Storage and MR Image Storage)
- see ImageBlockDescriptor.GetReaderImplementationLevel() method for the details
- special treatment for a certain type of Philips 3D ultrasound (recogized by tag 3001,0010 set to "Philips3D")
- loader will always attempt to read multiple single slices as a single 3D image volume (i.e. mitk::Image)
- slices will be grouped by basic properties such as orientation, rows, columns, spacing and grouped into as large blocks as possible
- images which do NOT report a position or orientation in space (Image Position Patient, Image Orientation) will be assigned defaults
- image position (0,0,0)
- image orientation (1,0,0), (0,1,0)
- such images will always be grouped separately since spatial grouping / sorting makes no sense for them
\b Options
- images that cover the same piece of space (i.e. position, orientation, and dimensions are equal)
can be interpreted as time-steps of the same image, i.e. a series will be loaded as 3D+t
\b Limitations
- the 3D+t assumption only works if all time-steps have an equal number of slices and if all
have the Acquisition Time attribute set to meaningful values
\section DicomSeriesReader_usage Usage
The starting point for an application is a set of DICOM files that should be loaded.
For convenience, DicomSeriesReader can also parse a whole directory for DICOM files,
but an application should better know exactly what to load.
Loading is then done in two steps:
1. Group the files into spatial blocks by calling GetSeries().
This method will sort all passed files into meaningful blocks that
could fit into an mitk::Image. Sorting for 3D+t loading is optional but default.
The \b return value of this function is a list of descriptors, which
describe a grouped list of files with its most basic properties:
- SOP Class (CT Image Storage, Secondary Capture Image Storage, etc.)
- Modality
- What type of pixel spacing can be read from the provided DICOM tags
- How well DicomSeriesReader is prepared to load this type of data
2. Load a sorted set of files by calling LoadDicomSeries().
This method expects go receive the sorting output of GetSeries().
The method will then invoke ITK methods configured with GDCM-IO
classes to actually load the files into memory and put them into
mitk::Images. Again, loading as 3D+t is optional.
Example:
\code
// only a directory is known at this point: /home/who/dicom
DicomSeriesReader::FileNamesGrouping allImageBlocks = DicomSeriesReader::GetSeries("/home/who/dicom/");
// file now divided into groups of identical image size, orientation, spacing, etc.
// each of these lists should be loadable as an mitk::Image.
DicomSeriesReader::StringContainer seriesToLoad = allImageBlocks[...]; // decide what to load
// final step: load into DataNode (can result in 3D+t image)
DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( oneBlockSorted );
Image::Pointer image = dynamic_cast( node->GetData() );
\endcode
\section DicomSeriesReader_sorting Logic for sorting 2D slices from DICOM images into 3D+t blocks for mitk::Image
The general sorting mechanism (implemented in GetSeries) groups and sorts a set of DICOM files, each assumed to contain a single CT/MR slice.
In the following we refer to those file groups as "blocks", since this is what they are meant to become when loaded into an mitk::Image.
\subsection DicomSeriesReader_sorting1 Step 1: Avoiding pure non-sense
A first pass separates slices that cannot possibly be loaded together because of restrictions of mitk::Image.
After this steps, each block contains only slices that match in all of the following DICOM tags:
- (0020,000e) Series Instance UID
- (0020,0037) Image Orientation
- (0028,0030) Pixel Spacing
- (0018,1164) Imager Pixel Spacing
- (0018,0050) Slice Thickness
- (0028,0010) Number Of Rows
- (0028,0011) Number Of Columns
- (0028,0008) Number Of Frames
\subsection DicomSeriesReader_sorting2 Step 2: Sort slices spatially
Before slices are further analyzed, they are sorted spatially. As implemented by GdcmSortFunction(),
slices are sorted by
1. distance from origin (calculated using (0020,0032) Image Position Patient and (0020,0037) Image Orientation)
2. when distance is equal, (0020,0012) Aquisition Number, (0008,0032) Acquisition Time and (0018,1060) Trigger Time are
used as a backup criterions (necessary for meaningful 3D+t sorting)
\subsection DicomSeriesReader_sorting3 Step 3: Ensure equal z spacing
Since inter-slice distance is not recorded in DICOM tags, we must ensure that blocks are made up of
slices that have equal distances between neighboring slices. This is especially necessary because itk::ImageSeriesReader
is later used for the actual loading, and this class expects (and does nocht verify) equal inter-slice distance (see \ref DicomSeriesReader_whatweknowaboutitk).
To achieve such grouping, the inter-slice distance is calculated from the first two different slice positions of a block.
Following slices are added to a block as long as they can be added by adding the calculated inter-slice distance to the
last slice of the block. Slices that do not fit into the expected distance pattern, are set aside for further analysis.
This grouping is done until each file has been assigned to a group.
Slices that share a position in space are also sorted into separate blocks during this step.
So the result of this step is a set of blocks that contain only slices with equal z spacing
and uniqe slices at each position.
\subsection DicomSeriesReader_sorting4 Step 4 (optional): group 3D blocks as 3D+t when possible
This last step depends on an option of GetSeries(). When requested, image blocks from the previous step are merged again
whenever two blocks occupy the same portion of space (i.e. same origin, number of slices and z-spacing).
\section DicomSeriesReader_gantrytilt Handling of gantry tilt
When CT gantry tilt is used, the gantry plane (= X-Ray source and detector ring) and the vertical plane do not align
anymore. This scanner feature is used for example to reduce metal artifacs (e.g. Lee C , Evaluation of Using CT
Gantry Tilt Scan on Head and Neck Cancer Patients with Dental Structure: Scans Show Less Metal Artifacts. Presented
at: Radiological Society of North America 2011 Scientific Assembly and Annual Meeting; November 27- December 2,
2011 Chicago IL.).
The acquired planes of such CT series do not match the expectations of a orthogonal geometry in mitk::Image: if you
stack the slices, they show a small shift along the Y axis:
\verbatim
without tilt with tilt
|||||| //////
|||||| //////
-- |||||| --------- ////// -------- table orientation
|||||| //////
|||||| //////
Stacked slices:
without tilt with tilt
-------------- --------------
-------------- --------------
-------------- --------------
-------------- --------------
-------------- --------------
\endverbatim
As such gemetries do not in conjunction with mitk::Image, DicomSeriesReader performs a correction for such series
if the groupImagesWithGantryTilt or correctGantryTilt flag in GetSeries and LoadDicomSeries is set (default = on).
The correction algorithms undoes two errors introduced by ITK's ImageSeriesReader:
- the plane shift that is ignored by ITK's reader is recreated by applying a shearing transformation using itk::ResampleFilter.
- the spacing is corrected (it is calculated by ITK's reader from the distance between two origins, which is NOT the slice distance in this special case)
Both errors are introduced in
itkImageSeriesReader.txx (ImageSeriesReader::GenerateOutputInformation(void)), lines 176 to 245 (as of ITK 3.20)
For the correction, we examine two consecutive slices of a series, both described as a pair (origin/orientation):
- we calculate if the first origin is on a line along the normal of the second slice
- if this is not the case, the geometry will not fit a normal mitk::Image/mitk::Geometry3D
- we then project the second origin into the first slice's coordinate system to quantify the shift
- both is done in class GantryTiltInformation with quite some comments.
The geometry of image stacks with tilted geometries is illustrated below:
- green: the DICOM images as described by their tags: origin as a point with the line indicating the orientation
- red: the output of ITK ImageSeriesReader: wrong, larger spacing, no tilt
- blue: how much a shear must correct
\image tilt-correction.jpg
\section DicomSeriesReader_whatweknowaboutitk The actual image loading process
When calling LoadDicomSeries(), this method "mainly" uses an instance of itk::ImageSeriesReader,
configured with an itk::GDCMImageIO object. Because DicomSeriesReader works around some of the
behaviors of these classes, the following is a list of features that we find in the code and need to work with:
- itk::ImageSeriesReader::GenerateOutputInformation() does the z-spacing handling
+ spacing is directly determined by comparing (euclidean distance) the origins of the first two slices of a series
* this is GOOD because there is no reliable z-spacing information in DICOM images
* this is bad because it does not work with gantry tilt, in which case the slice distance is SMALLER than the distance between two origins (see section on tilt)
- origin and spacing are calculated by GDCMImageIO and re-used in itk::ImageSeriesReader
+ the origins are read from appropriate tags, nothing special about that
+ the spacing is read by gdcm::ImageReader, gdcm::ImageHelper::GetSpacingValue() from a tag determined by gdcm::ImageHelper::GetSpacingTagFromMediaStorage(), which basically determines ONE appropriate pixel spacing tag for each media storage type (ct image, mr image, secondary capture image, etc.)
* this is fine for modalities such as CT/MR where the "Pixel Spacing" tag is mandatory, but for other modalities such as CR or Secondary Capture, the tag "Imager Pixel Spacing" is taken, which is no only optional but also has a more complicated relation with the "Pixel Spacing" tag. For this reason we check/modify the pixel spacing reported by itk::ImageSeriesReader after loading the image (see \ref DicomSeriesReader_pixelspacing)
AFTER loading, DicomSeriesReader marks some of its findings as mitk::Properties to the loaded Image and DataNode:
- dicomseriesreader.SOPClass : DICOM SOP Class as readable string (instead of a UID)
- dicomseriesreader.ReaderImplementationLevelString : Confidence /Support level of the reader for this image as readable string
- dicomseriesreader.ReaderImplementationLevel : Confidence /Support level of the reader for this image as enum value of type ReaderImplementationLevel
- dicomseriesreader.PixelSpacingInterpretationString : Appropriate interpreteation of pixel spacing for this Image as readable string
- dicomseriesreader.PixelSpacingInterpretation : Appropriate interpreteation of pixel spacing for this Image as enum value of type PixelSpacingInterpretation
- dicomseriesreader.MultiFrameImage : bool flag to mark multi-frame images
- dicomseriesreader.GantyTiltCorrected : bool flag to mark images where a gantry tilt was corrected to fit slices into an mitk::Image
- dicomseriesreader.3D+t : bool flag to mark images with a time dimension (multiple 3D blocks of the same size at the same position in space)
\section DicomSeriesReader_pixelspacing Handling of pixel spacing
The reader implementes what is described in DICOM Part 3, chapter 10.7 (Basic Pixel Spacing Calibration Macro): Both tags
- (0028,0030) Pixel Spacing and
- (0018,1164) Imager Pixel Spacing
are evaluated and the pixel spacing is set to the spacing within the patient when tags allow that.
The result of pixel spacing interpretation can be read from a property "dicomseriesreader.PixelSpacingInterpretation",
which refers to one of the enumerated values of type PixelSpacingInterpretation;
\section DicomSeriesReader_supportedmodalities Limitations for specific modalities
- Enhanced Computed Tomography / Magnetic Resonance Images are currently NOT supported at all, because we lack general support for multi-frame images.
- Nuclear Medicine Images are not supported fully supported, only the single-frame variants are loaded properly.
\section DicomSeriesReader_nextworkitems Possible enhancements
This is a short list of ideas for enhancement:
- Class has historically grown and should be reviewed again. There is probably too many duplicated scanning code
- Multi-frame images don't mix well with the curent assumption of "one file - one slice", which is assumed by our code
- It should be checked how well GDCM and ITK support these files (some load, some don't)
- Specializations such as the Philips 3D code should be handled in a more generic way. The current handling of Philips 3D images is not nice at all
\section DicomSeriesReader_whynotinitk Why is this not in ITK?
Some of this code would probably be better located in ITK. It is just a matter of resources that this is not the
case yet. Any attempts into this direction are welcome and can be supported. At least the gantry tilt correction
should be a simple addition to itk::ImageSeriesReader.
\section DicomSeriesReader_tests Tests regarding DICOM loading
A number of tests have been implemented to check our assumptions regarding DICOM loading. Please see \ref DICOMTesting
\todo refactor all the protected helper objects/methods into a separate header so we compile faster
*/
class MITK_CORE_EXPORT DicomSeriesReader
{
public:
/**
\brief Lists of filenames.
*/
typedef std::vector StringContainer;
/**
\brief Interface for the progress callback.
*/
typedef void (*UpdateCallBackMethod)(float);
/**
\brief Describes how well the reader is tested for a certain file type.
Applications should not rely on the outcome for images which are reported
ReaderImplementationLevel_Implemented or ReaderImplementationLevel_Unsupported.
Errors to load images which are reported as ReaderImplementationLevel_Supported
are considered bugs. For ReaderImplementationLevel_PartlySupported please check the appropriate paragraph in \ref DicomSeriesReader_supportedmodalities
*/
typedef enum
{
ReaderImplementationLevel_Supported, /// loader code and tests are established
ReaderImplementationLevel_PartlySupported, /// loader code and tests are establised for specific parts of a SOP Class
ReaderImplementationLevel_Implemented, /// loader code is implemented but not accompanied by tests
ReaderImplementationLevel_Unsupported, /// loader code is not working with this SOP Class
} ReaderImplementationLevel;
/**
\brief How the mitk::Image spacing should be interpreted.
Compare DICOM PS 3.3 10.7 (Basic Pixel Spacing Calibration Macro).
*/
typedef enum
{
PixelSpacingInterpretation_SpacingInPatient, /// distances are mm within a patient
PixelSpacingInterpretation_SpacingAtDetector, /// distances are mm at detector surface
PixelSpacingInterpretation_SpacingUnknown /// NO spacing information is present, we use (1,1) as default
} PixelSpacingInterpretation;
/**
\brief Return type of GetSeries, describes a logical group of files.
Files grouped into a single 3D or 3D+t block are described by an instance
of this class. Relevant descriptive properties can be used to provide
the application user with meaningful choices.
*/
class MITK_CORE_EXPORT ImageBlockDescriptor
{
public:
/// List of files in this group
StringContainer GetFilenames() const;
/// A unique ID describing this bloc (enhanced Series Instance UID).
std::string GetImageBlockUID() const;
/// The Series Instance UID.
std::string GetSeriesInstanceUID() const;
/// Series Modality (CT, MR, etc.)
std::string GetModality() const;
/// SOP Class UID as readable string (Computed Tomography Image Storage, Secondary Capture Image Storage, etc.)
std::string GetSOPClassUIDAsString() const;
/// SOP Class UID as DICOM UID
std::string GetSOPClassUID() const;
/// Confidence of the reader that this block can be read successfully.
ReaderImplementationLevel GetReaderImplementationLevel() const;
/// Whether or not the block contains a gantry tilt which will be "corrected" during loading
bool HasGantryTiltCorrected() const;
/// Whether or not mitk::Image spacing relates to the patient
bool PixelSpacingRelatesToPatient() const;
/// Whether or not mitk::Image spacing relates to the detector surface
bool PixelSpacingRelatesToDetector() const;
/// Whether or not mitk::Image spacing is of unknown origin
bool PixelSpacingIsUnknown() const;
/// How the mitk::Image spacing can meaningfully be interpreted.
PixelSpacingInterpretation GetPixelSpacingType() const;
/// 3D+t or not
bool HasMultipleTimePoints() const;
/// Multi-frame image(s) or not
bool IsMultiFrameImage() const;
ImageBlockDescriptor();
~ImageBlockDescriptor();
private:
friend class DicomSeriesReader;
ImageBlockDescriptor(const StringContainer& files);
void AddFile(const std::string& file);
void AddFiles(const StringContainer& files);
void SetImageBlockUID(const std::string& uid);
void SetSeriesInstanceUID(const std::string& uid);
void SetModality(const std::string& modality);
void SetNumberOfFrames(const std::string& );
void SetSOPClassUID(const std::string& mediaStorageSOPClassUID);
void SetHasGantryTiltCorrected(bool);
void SetPixelSpacingInformation(const std::string& pixelSpacing, const std::string& imagerPixelSpacing);
void SetHasMultipleTimePoints(bool);
void GetDesiredMITKImagePixelSpacing(ScalarType& spacingX, ScalarType& spacingY) const;
StringContainer m_Filenames;
std::string m_ImageBlockUID;
std::string m_SeriesInstanceUID;
std::string m_Modality;
std::string m_SOPClassUID;
bool m_HasGantryTiltCorrected;
std::string m_PixelSpacing;
std::string m_ImagerPixelSpacing;
bool m_HasMultipleTimePoints;
bool m_IsMultiFrameImage;
};
typedef std::map FileNamesGrouping;
/**
\brief Provide combination of preprocessor defines that was active during compilation.
Since this class is a combination of several possible implementations, separated only
by ifdef's, calling instances might want to know which flags were active at compile time.
*/
static std::string GetConfigurationString();
/**
\brief Checks if a specific file contains DICOM data.
*/
static
bool
IsDicom(const std::string &filename);
/**
\brief see other GetSeries().
Find all series (and sub-series -- see details) in a particular directory.
*/
static FileNamesGrouping GetSeries(const std::string &dir,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
\brief see other GetSeries().
\warning Untested, could or could not work.
This differs only by having an additional restriction to a single known DICOM series.
Internally, it uses the other GetSeries() method.
*/
static StringContainer GetSeries(const std::string &dir,
const std::string &series_uid,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
\brief PREFERRED version of this method - scan and sort DICOM files.
Parse a list of files for images of DICOM series.
For each series, an enumeration of the files contained in it is created.
\return The resulting maps UID-like keys (based on Series Instance UID and slice properties) to sorted lists of file names.
SeriesInstanceUID will be enhanced to be unique for each set of file names
that is later loadable as a single mitk::Image. This implies that
Image orientation, slice thickness, pixel spacing, rows, and columns
must be the same for each file (i.e. the image slice contained in the file).
If this separation logic requires that a SeriesInstanceUID must be made more specialized,
it will follow the same logic as itk::GDCMSeriesFileNames to enhance the UID with
more digits and dots.
Optionally, more tags can be used to separate files into different logical series by setting
the restrictions parameter.
\warning Adding restrictions is not yet implemented!
*/
static
FileNamesGrouping
GetSeries(const StringContainer& files,
bool sortTo3DPlust,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
\brief See other GetSeries().
Use GetSeries(const StringContainer& files, bool sortTo3DPlust, const StringContainer &restrictions) instead.
*/
static
FileNamesGrouping
GetSeries(const StringContainer& files,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
Loads a DICOM series composed by the file names enumerated in the file names container.
If a callback method is supplied, it will be called after every progress update with a progress value in [0,1].
\param filenames The filenames to load.
\param sort Whether files should be sorted spatially (true) or not (false - maybe useful if presorted)
\param load4D Whether to load the files as 3D+t (if possible)
*/
static DataNode::Pointer LoadDicomSeries(const StringContainer &filenames,
bool sort = true,
bool load4D = true,
bool correctGantryTilt = true,
UpdateCallBackMethod callback = 0,
Image::Pointer preLoadedImageBlock = 0);
/**
\brief See LoadDicomSeries! Just a slightly different interface.
If \p preLoadedImageBlock is provided, the reader will only "fake" loading and create appropriate
mitk::Properties.
*/
static bool LoadDicomSeries(const StringContainer &filenames,
DataNode &node,
bool sort = true,
bool load4D = true,
bool correctGantryTilt = true,
UpdateCallBackMethod callback = 0,
Image::Pointer preLoadedImageBlock = 0);
protected:
/**
\brief Return type of DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption.
Class contains the grouping result of method DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption,
which takes as input a number of images, which are all equally oriented and spatially sorted along their normal direction.
The result contains of two blocks: a first one is the grouping result, all of those images can be loaded
into one image block because they have an equal origin-to-origin distance without any gaps in-between.
*/
class SliceGroupingAnalysisResult
{
public:
SliceGroupingAnalysisResult();
/**
\brief Grouping result, all same origin-to-origin distance w/o gaps.
*/
StringContainer GetBlockFilenames();
/**
\brief Remaining files, which could not be grouped.
*/
StringContainer GetUnsortedFilenames();
/**
\brief Wheter or not the grouped result contain a gantry tilt.
*/
bool ContainsGantryTilt();
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
*/
void AddFileToSortedBlock(const std::string& filename);
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
*/
void AddFileToUnsortedBlock(const std::string& filename);
void AddFilesToUnsortedBlock(const StringContainer& filenames);
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
\todo Could make sense to enhance this with an instance of GantryTiltInformation to store the whole result!
*/
void FlagGantryTilt();
/**
\brief Only meaningful for use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption.
*/
void UndoPrematureGrouping();
protected:
StringContainer m_GroupedFiles;
StringContainer m_UnsortedFiles;
bool m_GantryTilt;
};
/**
\brief Gantry tilt analysis result.
Takes geometry information for two slices of a DICOM series and
calculates whether these fit into an orthogonal block or not.
If NOT, they can either be the result of an acquisition with
gantry tilt OR completly broken by some shearing transformation.
Most calculations are done in the constructor, results can then
be read via the remaining methods.
*/
class GantryTiltInformation
{
public:
// two types to avoid any rounding errors
typedef itk::Point Point3Dd;
typedef itk::Vector Vector3Dd;
/**
\brief Just so we can create empty instances for assigning results later.
*/
GantryTiltInformation();
/**
\brief THE constructor, which does all the calculations.
Determining the amount of tilt is done by checking the distances
of origin1 from planes through origin2. Two planes are considered:
- normal vector along normal of slices (right x up): gives the slice distance
- normal vector along orientation vector "up": gives the shift parallel to the plane orientation
The tilt angle can then be calculated from these distances
\param origin1 origin of the first slice
\param origin2 origin of the second slice
\param right right/up describe the orientatation of borth slices
\param up right/up describe the orientatation of borth slices
\param numberOfSlicesApart how many slices are the given origins apart (1 for neighboring slices)
*/
GantryTiltInformation( const Point3D& origin1,
const Point3D& origin2,
const Vector3D& right,
const Vector3D& up,
unsigned int numberOfSlicesApart);
/**
\brief Whether the slices were sheared.
True if any of the shifts along right or up vector are non-zero.
*/
bool IsSheared() const;
/**
\brief Whether the shearing is a gantry tilt or more complicated.
Gantry tilt will only produce shifts in ONE orientation, not in both.
Since the correction code currently only coveres one tilt direction
AND we don't know of medical images with two tilt directions, the
loading code wants to check if our assumptions are true.
*/
bool IsRegularGantryTilt() const;
/**
\brief The offset distance in Y direction for each slice in mm (describes the tilt result).
*/
double GetMatrixCoefficientForCorrectionInWorldCoordinates() const;
/**
\brief The z / inter-slice spacing. Needed to correct ImageSeriesReader's result.
*/
double GetRealZSpacing() const;
/**
\brief The shift between first and last slice in mm.
Needed to resize an orthogonal image volume.
*/
double GetTiltCorrectedAdditionalSize() const;
/**
\brief Calculated tilt angle in degrees.
*/
double GetTiltAngleInDegrees() const;
protected:
/**
\brief Projection of point p onto line through lineOrigin in direction of lineDirection.
*/
Point3D projectPointOnLine( Point3Dd p, Point3Dd lineOrigin, Vector3Dd lineDirection );
double m_ShiftUp;
double m_ShiftRight;
double m_ShiftNormal;
double m_ITKAssumedSliceSpacing;
unsigned int m_NumberOfSlicesApart;
};
/**
\brief for internal sorting.
*/
typedef std::pair TwoStringContainers;
/**
\brief Maps DICOM tags to MITK properties.
*/
typedef std::map TagToPropertyMapType;
/**
\brief Ensure an equal z-spacing for a group of files.
Takes as input a number of images, which are all equally oriented and spatially sorted along their normal direction.
Internally used by GetSeries. Returns two lists: the first one contins slices of equal inter-slice spacing.
The second list contains remaining files, which need to be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption again.
Relevant code that is matched here is in
itkImageSeriesReader.txx (ImageSeriesReader::GenerateOutputInformation(void)), lines 176 to 245 (as of ITK 3.20)
*/
static
SliceGroupingAnalysisResult
AnalyzeFileForITKImageSeriesReaderSpacingAssumption(const StringContainer& files, bool groupsOfSimilarImages, const gdcm::Scanner::MappingType& tagValueMappings_);
/**
\brief Safely convert const char* to std::string.
*/
static
std::string
ConstCharStarToString(const char* s);
/**
\brief Safely convert a string into pixel spacing x and y.
*/
static
bool
DICOMStringToSpacing(const std::string& s, ScalarType& spacingX, ScalarType& spacingY);
/**
\brief Convert DICOM string describing a point to Point3D.
DICOM tags like ImagePositionPatient contain a position as float numbers separated by backslashes:
\verbatim
42.7131\13.77\0.7
\endverbatim
*/
static
Point3D
DICOMStringToPoint3D(const std::string& s, bool& successful);
/**
\brief Convert DICOM string describing a point two Vector3D.
DICOM tags like ImageOrientationPatient contain two vectors as float numbers separated by backslashes:
\verbatim
42.7131\13.77\0.7\137.76\0.3
\endverbatim
*/
static
void
DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful);
template
static
typename ImageType::Pointer
// TODO this is NOT inplace!
InPlaceFixUpTiltedGeometry( ImageType* input, const GantryTiltInformation& tiltInfo );
/**
\brief Sort a set of file names in an order that is meaningful for loading them into an mitk::Image.
\warning This method assumes that input files are similar in basic properties such as
slice thicknes, image orientation, pixel spacing, rows, columns.
It should always be ok to put the result of a call to GetSeries(..) into this method.
Sorting order is determined by
1. image position along its normal (distance from world origin)
2. acquisition time
If P denotes a position and T denotes a time step, this method will order slices from three timesteps like this:
\verbatim
P1T1 P1T2 P1T3 P2T1 P2T2 P2T3 P3T1 P3T2 P3T3
\endverbatim
*/
static StringContainer SortSeriesSlices(const StringContainer &unsortedFilenames);
public:
/**
\brief Checks if a specific file is a Philips3D ultrasound DICOM file.
*/
static bool IsPhilips3DDicom(const std::string &filename);
static std::string ReaderImplementationLevelToString( const ReaderImplementationLevel& enumValue );
static std::string PixelSpacingInterpretationToString( const PixelSpacingInterpretation& enumValue );
protected:
/**
\brief Read a Philips3D ultrasound DICOM file and put into an mitk::Image.
*/
static bool ReadPhilips3DDicom(const std::string &filename, mitk::Image::Pointer output_image);
/**
\brief Construct a UID that takes into account sorting criteria from GetSeries().
*/
static std::string CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap );
/**
\brief Helper for CreateMoreUniqueSeriesIdentifier
*/
static std::string CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag );
/**
\brief Helper for CreateMoreUniqueSeriesIdentifier
*/
static std::string IDifyTagValue(const std::string& value);
typedef itk::GDCMImageIO DcmIoType;
/**
\brief Progress callback for DicomSeriesReader.
*/
class CallbackCommand : public itk::Command
{
public:
CallbackCommand(UpdateCallBackMethod callback)
: m_Callback(callback)
{
}
void Execute(const itk::Object *caller, const itk::EventObject&)
{
(*this->m_Callback)(static_cast(caller)->GetProgress());
}
void Execute(itk::Object *caller, const itk::EventObject&)
{
(*this->m_Callback)(static_cast(caller)->GetProgress());
}
protected:
UpdateCallBackMethod m_Callback;
};
static void FixSpacingInformation( Image* image, const ImageBlockDescriptor& imageBlockDescriptor );
/**
\brief Scan for slice image information
*/
static void ScanForSliceInformation( const StringContainer &filenames, gdcm::Scanner& scanner );
/**
\brief Performs actual loading of a series and creates an image having the specified pixel type.
*/
- template
static
void
LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock);
/**
\brief Feed files into itk::ImageSeriesReader and retrieve a 3D MITK image.
\param command can be used for progress reporting
*/
template
static
Image::Pointer
- LoadDICOMByITK( const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command = NULL, Image::Pointer preLoadedImageBlock = NULL);
+ LoadDICOMByITK( const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock);
+
+ static
+ Image::Pointer MultiplexLoadDICOMByITK(const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock);
+
+ static
+ Image::Pointer MultiplexLoadDICOMByITKScalar(const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock);
+
+ static
+ Image::Pointer MultiplexLoadDICOMByITKRGBPixel(const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock);
+
+
+ template
+ static
+ Image::Pointer
+ LoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock);
+
+ static
+ Image::Pointer
+ MultiplexLoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock);
+
+ static
+ Image::Pointer
+ MultiplexLoadDICOMByITK4DScalar( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock);
+
+ static
+ Image::Pointer
+ MultiplexLoadDICOMByITK4DRGBPixel( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock);
+
+
/**
\brief Sort files into time step blocks of a 3D+t image.
Called by LoadDicom. Expects to be fed a single list of filenames that have been sorted by
GetSeries previously (one map entry). This method will check how many timestep can be filled
with given files.
Assumption is that the number of time steps is determined by how often the first position in
space repeats. I.e. if the first three files in the input parameter all describe the same
location in space, we'll construct three lists of files. and sort the remaining files into them.
\todo We can probably remove this method if we somehow transfer 3D+t information from GetSeries to LoadDicomSeries.
*/
static
std::list
SortIntoBlocksFor3DplusT( const StringContainer& presortedFilenames, const gdcm::Scanner::MappingType& tagValueMappings_, bool sort, bool& canLoadAs4D);
/**
\brief Defines spatial sorting for sorting by GDCM 2.
Sorts by image position along image normal (distance from world origin).
In cases of conflict, acquisition time is used as a secondary sort criterium.
*/
static
bool
GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2);
/**
\brief Copy information about files and DICOM tags from ITK's MetaDataDictionary
and from the list of input files to the PropertyList of mitk::Image.
\todo Tag copy must follow; image level will cause some additional files parsing, probably.
*/
static void CopyMetaDataToImageProperties( StringContainer filenames, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, const ImageBlockDescriptor& blockInfo, Image* image);
static void CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, const ImageBlockDescriptor& blockInfo, Image* image);
/**
\brief Map between DICOM tags and MITK properties.
Uses as a positive list for copying specified DICOM tags (from ITK's ImageIO)
to MITK properties. ITK provides MetaDataDictionary entries of form
"gggg|eeee" (g = group, e = element), e.g. "0028,0109" (Largest Pixel in Series),
which we want to sort as dicom.series.largest_pixel_in_series".
*/
static const TagToPropertyMapType& GetDICOMTagsToMITKPropertyMap();
};
}
#endif /* mitkDicomSeriesReader_h */
diff --git a/Core/Code/IO/mitkDicomSeriesReader.txx b/Core/Code/IO/mitkDicomSeriesReader.txx
index e28fa06880..ec4058551c 100644
--- a/Core/Code/IO/mitkDicomSeriesReader.txx
+++ b/Core/Code/IO/mitkDicomSeriesReader.txx
@@ -1,568 +1,296 @@
/*===================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#ifndef MITKDICOMSERIESREADER_TXX_
#define MITKDICOMSERIESREADER_TXX_
#include
#include
#include
#include
#include
#include
#include
#include
namespace mitk
{
template
-void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock)
+Image::Pointer DicomSeriesReader::LoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock )
{
- const char* previousCLocale = setlocale(LC_NUMERIC, NULL);
- setlocale(LC_NUMERIC, "C");
- std::locale previousCppLocale( std::cin.getloc() );
- std::locale l( "C" );
- std::cin.imbue(l);
-
- ImageBlockDescriptor imageBlockDescriptor;
-
- const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient)
- const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation
- const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID
- const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID
- const gdcm::Tag tagModality(0x0008, 0x0060); // modality
- const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing
- const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing
- const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames
-
- try
- {
- Image::Pointer image = preLoadedImageBlock.IsNull() ? Image::New() : preLoadedImageBlock;
- CallbackCommand *command = callback ? new CallbackCommand(callback) : 0;
- bool initialize_node = false;
-
- /* special case for Philips 3D+t ultrasound images */
- if ( DicomSeriesReader::IsPhilips3DDicom(filenames.front().c_str()) )
- {
- // TODO what about imageBlockDescriptor?
- // TODO what about preLoadedImageBlock?
- ReadPhilips3DDicom(filenames.front().c_str(), image);
- initialize_node = true;
- }
- else
- {
- /* default case: assume "normal" image blocks, possibly 3D+t */
- bool canLoadAs4D(true);
- gdcm::Scanner scanner;
- ScanForSliceInformation(filenames, scanner);
+ mitk::Image::Pointer image = mitk::Image::New();
- // need non-const access for map
- gdcm::Scanner::MappingType& tagValueMappings = const_cast(scanner.GetMappings());
+ // It is 3D+t! Read it and store into mitk image
+ typedef itk::Image ImageType;
+ typedef itk::ImageSeriesReader ReaderType;
- std::list imageBlocks = SortIntoBlocksFor3DplusT( filenames, tagValueMappings, sort, canLoadAs4D );
- unsigned int volume_count = imageBlocks.size();
+ typename ReaderType::Pointer reader = ReaderType::New();
- imageBlockDescriptor.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSeriesInstanceUID ) ) );
- imageBlockDescriptor.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSOPClassUID ) ) );
- imageBlockDescriptor.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagModality ) ) );
- imageBlockDescriptor.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagNumberOfFrames ) ) );
- imageBlockDescriptor.SetPixelSpacingInformation( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagPixelSpacing ) ),
- ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagImagerPixelSpacing ) ) );
+ reader->SetImageIO(io);
+ reader->ReverseOrderOff();
- GantryTiltInformation tiltInfo;
+ if (command)
+ {
+ reader->AddObserver(itk::ProgressEvent(), command);
+ }
- // check possibility of a single slice with many timesteps. In this case, don't check for tilt, no second slice possible
- if ( !imageBlocks.empty() && imageBlocks.front().size() > 1 && correctTilt)
- {
- // check tiltedness here, potentially fixup ITK's loading result by shifting slice contents
- // check first and last position slice from tags, make some calculations to detect tilt
+ unsigned int act_volume = 1u;
- std::string firstFilename(imageBlocks.front().front());
- // calculate from first and last slice to minimize rounding errors
- std::string secondFilename(imageBlocks.front().back());
+ if (preLoadedImageBlock.IsNull())
+ {
+ reader->SetFileNames(imageBlocks.front());
- std::string imagePosition1( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImagePositionPatient ] ) );
- std::string imageOrientation( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImageOrientation ] ) );
- std::string imagePosition2( ConstCharStarToString( tagValueMappings[secondFilename.c_str() ][ tagImagePositionPatient ] ) );
+ reader->Update();
- bool ignoredConversionError(-42); // hard to get here, no graceful way to react
- Point3D origin1( DICOMStringToPoint3D( imagePosition1, ignoredConversionError ) );
- Point3D origin2( DICOMStringToPoint3D( imagePosition2, ignoredConversionError ) );
+ typename ImageType::Pointer readVolume = reader->GetOutput();
+ // if we detected that the images are from a tilted gantry acquisition, we need to push some pixels into the right position
+ if (correctTilt)
+ {
+ readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo );
+ }
- Vector3D right; right.Fill(0.0);
- Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point
- DICOMStringToOrientationVectors( imageOrientation, right, up, ignoredConversionError );
+ unsigned int volume_count = imageBlocks.size();
+ image->InitializeByItk( readVolume.GetPointer(), 1, volume_count);
+ image->SetImportVolume( readVolume->GetBufferPointer(), 0u);
- tiltInfo = GantryTiltInformation ( origin1, origin2, right, up, filenames.size()-1 );
- correctTilt = tiltInfo.IsSheared() && tiltInfo.IsRegularGantryTilt();
- }
- else
- {
- correctTilt = false; // we CANNOT do that
- }
+ FixSpacingInformation( image, imageBlockDescriptor );
+ }
+ else
+ {
+ StringContainer fakeList;
+ fakeList.push_back( imageBlocks.front().front() );
+ reader->SetFileNames(fakeList); // only ONE first filename to get MetaDataDictionary
- imageBlockDescriptor.SetHasGantryTiltCorrected( correctTilt );
+ image = preLoadedImageBlock;
+ }
- if (volume_count == 1 || !canLoadAs4D || !load4D)
- {
+ gdcm::Scanner scanner;
+ ScanForSliceInformation(imageBlockDescriptor.GetFilenames(), scanner);
+ CopyMetaDataToImageProperties( imageBlocks, scanner.GetMappings(), io, imageBlockDescriptor, image);
- DcmIoType::Pointer io;
- image = LoadDICOMByITK( imageBlocks.front(), correctTilt, tiltInfo, io, command, preLoadedImageBlock ); // load first 3D block
+ MITK_DEBUG << "Volume dimension: [" << image->GetDimension(0) << ", "
+ << image->GetDimension(1) << ", "
+ << image->GetDimension(2) << ", "
+ << image->GetDimension(3) << "]";
- imageBlockDescriptor.AddFiles(imageBlocks.front()); // only the first part is loaded
- imageBlockDescriptor.SetHasMultipleTimePoints( false );
+ MITK_DEBUG << "Volume spacing: [" << image->GetGeometry()->GetSpacing()[0] << ", "
+ << image->GetGeometry()->GetSpacing()[1] << ", "
+ << image->GetGeometry()->GetSpacing()[2] << "]";
- FixSpacingInformation( image, imageBlockDescriptor );
- CopyMetaDataToImageProperties( imageBlocks.front(), scanner.GetMappings(), io, imageBlockDescriptor, image);
+ if ( preLoadedImageBlock.IsNull() )
+ {
+ for (std::list::iterator df_it = ++imageBlocks.begin(); df_it != imageBlocks.end(); ++df_it)
+ {
+ reader->SetFileNames(*df_it);
+ reader->Update();
+ typename ImageType::Pointer readVolume = reader->GetOutput();
- initialize_node = true;
- }
- else if (volume_count > 1)
+ if (correctTilt)
{
- imageBlockDescriptor.AddFiles(filenames); // all is loaded
- imageBlockDescriptor.SetHasMultipleTimePoints( true );
- // It is 3D+t! Read it and store into mitk image
- typedef itk::Image ImageType;
- typedef itk::ImageSeriesReader ReaderType;
-
- DcmIoType::Pointer io = DcmIoType::New();
- typename ReaderType::Pointer reader = ReaderType::New();
-
- reader->SetImageIO(io);
- reader->ReverseOrderOff();
-
- if (command)
- {
- reader->AddObserver(itk::ProgressEvent(), command);
- }
-
- unsigned int act_volume = 1u;
-
- if (preLoadedImageBlock.IsNull())
- {
- reader->SetFileNames(imageBlocks.front());
-
- reader->Update();
-
- typename ImageType::Pointer readVolume = reader->GetOutput();
- // if we detected that the images are from a tilted gantry acquisition, we need to push some pixels into the right position
- if (correctTilt)
- {
- readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo );
- }
-
- image->InitializeByItk( readVolume.GetPointer(), 1, volume_count);
- image->SetImportVolume( readVolume->GetBufferPointer(), 0u);
-
- FixSpacingInformation( image, imageBlockDescriptor );
- }
- else
- {
- StringContainer fakeList;
- fakeList.push_back( imageBlocks.front().front() );
- reader->SetFileNames(fakeList); // only ONE first filename to get MetaDataDictionary
- }
-
- gdcm::Scanner scanner;
- ScanForSliceInformation(filenames, scanner);
- CopyMetaDataToImageProperties( imageBlocks, scanner.GetMappings(), io, imageBlockDescriptor, image);
-
- MITK_DEBUG << "Volume dimension: [" << image->GetDimension(0) << ", "
- << image->GetDimension(1) << ", "
- << image->GetDimension(2) << ", "
- << image->GetDimension(3) << "]";
-
- MITK_DEBUG << "Volume spacing: [" << image->GetGeometry()->GetSpacing()[0] << ", "
- << image->GetGeometry()->GetSpacing()[1] << ", "
- << image->GetGeometry()->GetSpacing()[2] << "]";
-
- if ( preLoadedImageBlock.IsNull() )
- {
- for (std::list::iterator df_it = ++imageBlocks.begin(); df_it != imageBlocks.end(); ++df_it)
- {
- reader->SetFileNames(*df_it);
- reader->Update();
- typename ImageType::Pointer readVolume = reader->GetOutput();
-
- if (correctTilt)
- {
- readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo );
- }
-
- image->SetImportVolume(readVolume->GetBufferPointer(), act_volume++);
- }
- }
-
- initialize_node = true;
+ readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo );
}
+ image->SetImportVolume(readVolume->GetBufferPointer(), act_volume++);
}
-
- if (initialize_node)
- {
- // forward some image properties to node
- node.GetPropertyList()->ConcatenatePropertyList( image->GetPropertyList(), true );
-
- node.SetData( image );
- setlocale(LC_NUMERIC, previousCLocale);
- std::cin.imbue(previousCppLocale);
- }
-
- MITK_DEBUG << "--------------------------------------------------------------------------------";
- MITK_DEBUG << "DICOM files loaded (from series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << "):";
- MITK_DEBUG << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image";
- MITK_DEBUG << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage()?"Yes":"No");
- MITK_DEBUG << " reader support: " << ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel());
- MITK_DEBUG << " pixel spacing type: " << PixelSpacingInterpretationToString( imageBlockDescriptor.GetPixelSpacingType() ) << " " << image->GetGeometry()->GetSpacing()[0] << "/" << image->GetGeometry()->GetSpacing()[0];
- MITK_DEBUG << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected()?"Yes":"No");
- MITK_DEBUG << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints()?"Yes":"No");
- MITK_DEBUG << "--------------------------------------------------------------------------------";
}
- catch (std::exception& e)
- {
- // reset locale then throw up
- setlocale(LC_NUMERIC, previousCLocale);
- std::cin.imbue(previousCppLocale);
-
- MITK_DEBUG << "Caught exception in DicomSeriesReader::LoadDicom";
- throw e;
- }
+ return image;
}
template
Image::Pointer DicomSeriesReader::LoadDICOMByITK( const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock )
{
/******** Normal Case, 3D (also for GDCM < 2 usable) ***************/
mitk::Image::Pointer image = mitk::Image::New();
typedef itk::Image ImageType;
typedef itk::ImageSeriesReader ReaderType;
io = DcmIoType::New();
typename ReaderType::Pointer reader = ReaderType::New();
reader->SetImageIO(io);
reader->ReverseOrderOff();
if (command)
{
reader->AddObserver(itk::ProgressEvent(), command);
}
if (preLoadedImageBlock.IsNull())
{
reader->SetFileNames(filenames);
reader->Update();
typename ImageType::Pointer readVolume = reader->GetOutput();
// if we detected that the images are from a tilted gantry acquisition, we need to push some pixels into the right position
if (correctTilt)
{
readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo );
}
image->InitializeByItk(readVolume.GetPointer());
image->SetImportVolume(readVolume->GetBufferPointer());
}
else
{
image = preLoadedImageBlock;
StringContainer fakeList;
fakeList.push_back( filenames.front() );
reader->SetFileNames( fakeList ); // we always need to load at least one file to get the MetaDataDictionary
reader->Update();
}
MITK_DEBUG << "Volume dimension: [" << image->GetDimension(0) << ", "
<< image->GetDimension(1) << ", "
<< image->GetDimension(2) << "]";
MITK_DEBUG << "Volume spacing: [" << image->GetGeometry()->GetSpacing()[0] << ", "
<< image->GetGeometry()->GetSpacing()[1] << ", "
<< image->GetGeometry()->GetSpacing()[2] << "]";
return image;
}
-void
-DicomSeriesReader::ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner& scanner)
-{
- const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient)
- scanner.AddTag(tagImagePositionPatient);
-
- const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID
- scanner.AddTag(tagSeriesInstanceUID);
-
- const gdcm::Tag tagImageOrientation(0x0020,0x0037); //Image orientation
- scanner.AddTag(tagImageOrientation);
-
- const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location
- scanner.AddTag( tagSliceLocation );
-
- const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number
- scanner.AddTag( tagInstanceNumber );
-
- const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number
- scanner.AddTag( tagSOPInstanceNumber );
-
- const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // Pixel Spacing
- scanner.AddTag( tagPixelSpacing );
-
- const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // Imager Pixel Spacing
- scanner.AddTag( tagImagerPixelSpacing );
-
- const gdcm::Tag tagModality(0x0008, 0x0060); // Modality
- scanner.AddTag( tagModality );
-
- const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP Class UID
- scanner.AddTag( tagSOPClassUID );
-
- const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames
- scanner.AddTag( tagNumberOfFrames );
-
- scanner.Scan(filenames); // make available image information for each file
-}
-
-std::list
-DicomSeriesReader::SortIntoBlocksFor3DplusT(
- const StringContainer& presortedFilenames,
- const gdcm::Scanner::MappingType& tagValueMappings,
- bool /*sort*/,
- bool& canLoadAs4D )
-{
- std::list imageBlocks;
-
- // ignore sort request, because most likely re-sorting is now needed due to changes in GetSeries(bug #8022)
- StringContainer sorted_filenames = DicomSeriesReader::SortSeriesSlices(presortedFilenames);
-
- std::string firstPosition;
- unsigned int numberOfBlocks(0); // number of 3D image blocks
-
- static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient)
-
- // loop files to determine number of image blocks
- for (StringContainer::const_iterator fileIter = sorted_filenames.begin();
- fileIter != sorted_filenames.end();
- ++fileIter)
- {
- gdcm::Scanner::TagToValue tagToValueMap = tagValueMappings.find( fileIter->c_str() )->second;
-
- if(tagToValueMap.find(tagImagePositionPatient) == tagToValueMap.end())
- {
- // we expect to get images w/ missing position information ONLY as separated blocks.
- assert( presortedFilenames.size() == 1 );
- numberOfBlocks = 1;
- break;
- }
-
- std::string position = tagToValueMap.find(tagImagePositionPatient)->second;
- MITK_DEBUG << " " << *fileIter << " at " << position;
- if (firstPosition.empty())
- {
- firstPosition = position;
- }
-
- if ( position == firstPosition )
- {
- ++numberOfBlocks;
- }
- else
- {
- break; // enough information to know the number of image blocks
- }
- }
-
- MITK_DEBUG << " ==> Assuming " << numberOfBlocks << " time steps";
-
- if (numberOfBlocks == 0) return imageBlocks; // only possible if called with no files
-
-
- // loop files to sort them into image blocks
- unsigned int numberOfExpectedSlices(0);
- for (unsigned int block = 0; block < numberOfBlocks; ++block)
- {
- StringContainer filesOfCurrentBlock;
-
- for ( StringContainer::const_iterator fileIter = sorted_filenames.begin() + block;
- fileIter != sorted_filenames.end();
- //fileIter += numberOfBlocks) // TODO shouldn't this work? give invalid iterators on first attempts
- )
- {
- filesOfCurrentBlock.push_back( *fileIter );
- for (unsigned int b = 0; b < numberOfBlocks; ++b)
- {
- if (fileIter != sorted_filenames.end())
- ++fileIter;
- }
- }
-
- imageBlocks.push_back(filesOfCurrentBlock);
-
- if (block == 0)
- {
- numberOfExpectedSlices = filesOfCurrentBlock.size();
- }
- else
- {
- if (filesOfCurrentBlock.size() != numberOfExpectedSlices)
- {
- MITK_WARN << "DicomSeriesReader expected " << numberOfBlocks
- << " image blocks of "
- << numberOfExpectedSlices
- << " images each. Block "
- << block
- << " got "
- << filesOfCurrentBlock.size()
- << " instead. Cannot load this as 3D+t"; // TODO implement recovery (load as many slices 3D+t as much as possible)
- canLoadAs4D = false;
- }
- }
- }
-
- return imageBlocks;
-}
-
template
typename ImageType::Pointer
DicomSeriesReader::InPlaceFixUpTiltedGeometry( ImageType* input, const GantryTiltInformation& tiltInfo )
{
typedef itk::ResampleImageFilter ResampleFilterType;
typename ResampleFilterType::Pointer resampler = ResampleFilterType::New();
resampler->SetInput( input );
/*
Transform for a point is
- transform from actual position to index coordinates
- apply a shear that undoes the gantry tilt
- transform back into world coordinates
Anybody who does this in a simpler way: don't forget to write up how and why your solution works
*/
typedef itk::ScalableAffineTransform< double, ImageType::ImageDimension > TransformType;
typename TransformType::Pointer transformShear = TransformType::New();
/**
- apply a shear and spacing correction to the image block that corrects the ITK reader's error
- ITK ignores the shear and loads slices into an orthogonal volume
- ITK calculates the spacing from the origin distance, which is more than the actual spacing with gantry tilt images
- to undo the effect
- we have calculated some information in tiltInfo:
- the shift in Y direction that is added with each additional slice is the most important information
- the Y-shift is calculated in mm world coordinates
- we apply a shearing transformation to the ITK-read image volume
- to do this locally,
- we transform the image volume back to origin and "normal" orientation by applying the inverse of its transform
(this brings us into the image's "index coordinate" system)
- we apply a shear with the Y-shift factor put into a unit transform at row 1, col 2
- we transform the image volume back to its actual position (from index to world coordinates)
- we lastly apply modify the image spacing in z direction by replacing this number with the correctly calulcated inter-slice distance
*/
ScalarType factor = tiltInfo.GetMatrixCoefficientForCorrectionInWorldCoordinates() / input->GetSpacing()[1];
// row 1, column 2 corrects shear in parallel to Y axis, proportional to distance in Z direction
transformShear->Shear( 1, 2, factor );
typename TransformType::Pointer imageIndexToWorld = TransformType::New();
imageIndexToWorld->SetOffset( input->GetOrigin().GetVectorFromOrigin() );
typename TransformType::MatrixType indexToWorldMatrix;
indexToWorldMatrix = input->GetDirection();
typename ImageType::DirectionType scale;
for ( unsigned int i = 0; i < ImageType::ImageDimension; i++ )
{
scale[i][i] = input->GetSpacing()[i];
}
indexToWorldMatrix *= scale;
imageIndexToWorld->SetMatrix( indexToWorldMatrix );
typename TransformType::Pointer imageWorldToIndex = TransformType::New();
imageIndexToWorld->GetInverse( imageWorldToIndex );
typename TransformType::Pointer gantryTiltCorrection = TransformType::New();
gantryTiltCorrection->Compose( imageWorldToIndex );
gantryTiltCorrection->Compose( transformShear );
gantryTiltCorrection->Compose( imageIndexToWorld );
resampler->SetTransform( gantryTiltCorrection );
typedef itk::LinearInterpolateImageFunction< ImageType, double > InterpolatorType;
typename InterpolatorType::Pointer interpolator = InterpolatorType::New();
resampler->SetInterpolator( interpolator );
/*
This would be the right place to invent a meaningful value for positions outside of the image.
For CT, HU -1000 might be meaningful, but a general solution seems not possible. Even for CT,
-1000 would only look natural for many not all images.
*/
// TODO use (0028,0120) Pixel Padding Value if present
resampler->SetDefaultPixelValue( itk::NumericTraits< typename ImageType::PixelType >::min() );
// adjust size in Y direction! (maybe just transform the outer last pixel to see how much space we would need
resampler->SetOutputParametersFromImage( input ); // we basically need the same image again, just sheared
// if tilt positive, then we need additional pixels BELOW origin, otherwise we need pixels behind the end of the block
// in any case we need more size to accomodate shifted slices
typename ImageType::SizeType largerSize = resampler->GetSize(); // now the resampler already holds the input image's size.
largerSize[1] += static_cast(tiltInfo.GetTiltCorrectedAdditionalSize() / input->GetSpacing()[1]+ 2.0);
resampler->SetSize( largerSize );
// in SOME cases this additional size is below/behind origin
if ( tiltInfo.GetMatrixCoefficientForCorrectionInWorldCoordinates() > 0.0 )
{
typename ImageType::DirectionType imageDirection = input->GetDirection();
Vector3D yDirection;
yDirection[0] = imageDirection[0][1];
yDirection[1] = imageDirection[1][1];
yDirection[2] = imageDirection[2][1];
yDirection.Normalize();
typename ImageType::PointType shiftedOrigin;
shiftedOrigin = input->GetOrigin();
// add some pixels to make everything fit
shiftedOrigin[0] -= yDirection[0] * (tiltInfo.GetTiltCorrectedAdditionalSize() + 1.0 * input->GetSpacing()[1]);
shiftedOrigin[1] -= yDirection[1] * (tiltInfo.GetTiltCorrectedAdditionalSize() + 1.0 * input->GetSpacing()[1]);
shiftedOrigin[2] -= yDirection[2] * (tiltInfo.GetTiltCorrectedAdditionalSize() + 1.0 * input->GetSpacing()[1]);
resampler->SetOutputOrigin( shiftedOrigin );
}
resampler->Update();
typename ImageType::Pointer result = resampler->GetOutput();
// ImageSeriesReader calculates z spacing as the distance between the first two origins.
// This is not correct in case of gantry tilt, so we set our calculated spacing.
typename ImageType::SpacingType correctedSpacing = result->GetSpacing();
correctedSpacing[2] = tiltInfo.GetRealZSpacing();
result->SetSpacing( correctedSpacing );
return result;
}
-
}
#endif
diff --git a/Core/Code/Rendering/mitkPointSetVtkMapper3D.cpp b/Core/Code/Rendering/mitkPointSetVtkMapper3D.cpp
index 0f0931456f..660387006a 100644
--- a/Core/Code/Rendering/mitkPointSetVtkMapper3D.cpp
+++ b/Core/Code/Rendering/mitkPointSetVtkMapper3D.cpp
@@ -1,633 +1,642 @@
/*===================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#include "mitkPointSetVtkMapper3D.h"
#include "mitkDataNode.h"
#include "mitkProperties.h"
#include "mitkColorProperty.h"
#include "mitkVtkPropRenderer.h"
#include "mitkPointSet.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
const mitk::PointSet* mitk::PointSetVtkMapper3D::GetInput()
{
return static_cast ( GetDataNode()->GetData() );
}
mitk::PointSetVtkMapper3D::PointSetVtkMapper3D()
: m_vtkSelectedPointList(NULL),
m_vtkUnselectedPointList(NULL),
m_VtkSelectedPolyDataMapper(NULL),
m_VtkUnselectedPolyDataMapper(NULL),
m_vtkTextList(NULL),
m_NumberOfSelectedAdded(0),
m_NumberOfUnselectedAdded(0),
m_PointSize(1.0),
m_ContourRadius(0.5)
{
//propassembly
m_PointsAssembly = vtkSmartPointer::New();
//creating actors to be able to set transform
m_SelectedActor = vtkSmartPointer::New();
m_UnselectedActor = vtkSmartPointer::New();
m_ContourActor = vtkSmartPointer::New();
}
mitk::PointSetVtkMapper3D::~PointSetVtkMapper3D()
{
}
void mitk::PointSetVtkMapper3D::ReleaseGraphicsResources(vtkWindow *renWin)
{
m_PointsAssembly->ReleaseGraphicsResources(renWin);
m_SelectedActor->ReleaseGraphicsResources(renWin);
m_UnselectedActor->ReleaseGraphicsResources(renWin);
m_ContourActor->ReleaseGraphicsResources(renWin);
}
+void mitk::PointSetVtkMapper3D::ReleaseGraphicsResources(mitk::BaseRenderer* renderer)
+{
+ m_PointsAssembly->ReleaseGraphicsResources(renderer->GetRenderWindow());
+
+ m_SelectedActor->ReleaseGraphicsResources(renderer->GetRenderWindow());
+ m_UnselectedActor->ReleaseGraphicsResources(renderer->GetRenderWindow());
+ m_ContourActor->ReleaseGraphicsResources(renderer->GetRenderWindow());
+
+}
void mitk::PointSetVtkMapper3D::CreateVTKRenderObjects()
{
m_vtkSelectedPointList = vtkSmartPointer::New();
m_vtkUnselectedPointList = vtkSmartPointer::New();
m_PointsAssembly->VisibilityOn();
if(m_PointsAssembly->GetParts()->IsItemPresent(m_SelectedActor))
m_PointsAssembly->RemovePart(m_SelectedActor);
if(m_PointsAssembly->GetParts()->IsItemPresent(m_UnselectedActor))
m_PointsAssembly->RemovePart(m_UnselectedActor);
if(m_PointsAssembly->GetParts()->IsItemPresent(m_ContourActor))
m_PointsAssembly->RemovePart(m_ContourActor);
// exceptional displaying for PositionTracker -> MouseOrientationTool
int mapperID;
bool isInputDevice=false;
if( this->GetDataNode()->GetBoolProperty("inputdevice",isInputDevice) && isInputDevice )
{
if( this->GetDataNode()->GetIntProperty("BaseRendererMapperID",mapperID) && mapperID == 2)
return; //The event for the PositionTracker came from the 3d widget and not needs to be displayed
}
// get and update the PointSet
mitk::PointSet::Pointer input = const_cast(this->GetInput());
/* only update the input data, if the property tells us to */
bool update = true;
this->GetDataNode()->GetBoolProperty("updateDataOnRender", update);
if (update == true)
input->Update();
int timestep = this->GetTimestep();
mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet( timestep );
if ( itkPointSet.GetPointer() == NULL)
{
m_PointsAssembly->VisibilityOff();
return;
}
mitk::PointSet::PointsContainer::Iterator pointsIter;
mitk::PointSet::PointDataContainer::Iterator pointDataIter;
int j;
m_NumberOfSelectedAdded = 0;
m_NumberOfUnselectedAdded = 0;
//create contour
bool makeContour = false;
this->GetDataNode()->GetBoolProperty("show contour", makeContour);
if (makeContour)
{
this->CreateContour();
}
//now fill selected and unselected pointList
//get size of Points in Property
m_PointSize = 2;
mitk::FloatProperty::Pointer pointSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("pointsize"));
if ( pointSizeProp.IsNotNull() )
m_PointSize = pointSizeProp->GetValue();
//get the property for creating a label onto every point only once
bool showLabel = true;
this->GetDataNode()->GetBoolProperty("show label", showLabel);
const char * pointLabel=NULL;
if(showLabel)
{
if(dynamic_cast(this->GetDataNode()->GetPropertyList()->GetProperty("label")) != NULL)
pointLabel =dynamic_cast(this->GetDataNode()->GetPropertyList()->GetProperty("label"))->GetValue();
else
showLabel = false;
}
//check if the list for the PointDataContainer is the same size as the PointsContainer. Is not, then the points were inserted manually and can not be visualized according to the PointData (selected/unselected)
bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size());
//now add an object for each point in data
pointDataIter = itkPointSet->GetPointData()->Begin();
for (j=0, pointsIter=itkPointSet->GetPoints()->Begin();
pointsIter!=itkPointSet->GetPoints()->End();
pointsIter++, j++)
{
//check for the pointtype in data and decide which geom-object to take and then add to the selected or unselected list
int pointType;
if(itkPointSet->GetPointData()->size() == 0 || pointDataBroken)
pointType = mitk::PTUNDEFINED;
else
pointType = pointDataIter.Value().pointSpec;
vtkSmartPointer source;
switch (pointType)
{
case mitk::PTUNDEFINED:
{
vtkSmartPointer sphere = vtkSmartPointer::New();
sphere->SetRadius(m_PointSize);
itk::Point point1 = pointsIter->Value();
sphere->SetCenter(point1[0],point1[1],point1[2]);
//sphere->SetCenter(pointsIter.Value()[0],pointsIter.Value()[1],pointsIter.Value()[2]);
//MouseOrientation Tool (PositionTracker)
if(isInputDevice)
{
sphere->SetThetaResolution(10);
sphere->SetPhiResolution(10);
}
else
{
sphere->SetThetaResolution(20);
sphere->SetPhiResolution(20);
}
source = sphere;
}
break;
case mitk::PTSTART:
{
vtkSmartPointer cube = vtkSmartPointer::New();
cube->SetXLength(m_PointSize/2);
cube->SetYLength(m_PointSize/2);
cube->SetZLength(m_PointSize/2);
itk::Point point1 = pointsIter->Value();
cube->SetCenter(point1[0],point1[1],point1[2]);
source = cube;
}
break;
case mitk::PTCORNER:
{
vtkSmartPointer cone = vtkSmartPointer::New();
cone->SetRadius(m_PointSize);
itk::Point point1 = pointsIter->Value();
cone->SetCenter(point1[0],point1[1],point1[2]);
cone->SetResolution(20);
source = cone;
}
break;
case mitk::PTEDGE:
{
vtkSmartPointer